2 * APEI Generic Hardware Error Source support
4 * Generic Hardware Error Source provides a way to report platform
5 * hardware errors (such as that from chipset). It works in so called
6 * "Firmware First" mode, that is, hardware errors are reported to
7 * firmware firstly, then reported to Linux by firmware. This way,
8 * some non-standard hardware error registers or non-standard hardware
9 * link can be checked by firmware to produce more hardware error
10 * information for Linux.
12 * For more information about Generic Hardware Error Source, please
13 * refer to ACPI Specification version 4.0, section 17.3.2.6
15 * Now, only SCI notification type and memory errors are
16 * supported. More notification type and hardware error type will be
19 * Copyright 2010 Intel Corp.
20 * Author: Huang Ying <ying.huang@intel.com>
22 * This program is free software; you can redistribute it and/or
23 * modify it under the terms of the GNU General Public License version
24 * 2 as published by the Free Software Foundation;
26 * This program is distributed in the hope that it will be useful,
27 * but WITHOUT ANY WARRANTY; without even the implied warranty of
28 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
29 * GNU General Public License for more details.
31 * You should have received a copy of the GNU General Public License
32 * along with this program; if not, write to the Free Software
33 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
36 #include <linux/kernel.h>
37 #include <linux/module.h>
38 #include <linux/init.h>
39 #include <linux/acpi.h>
41 #include <linux/interrupt.h>
42 #include <linux/cper.h>
43 #include <linux/kdebug.h>
44 #include <linux/platform_device.h>
45 #include <linux/mutex.h>
46 #include <linux/ratelimit.h>
47 #include <acpi/apei.h>
48 #include <acpi/atomicio.h>
52 #include "apei-internal.h"
54 #define GHES_PFX "GHES: "
56 #define GHES_ESTATUS_MAX_SIZE 65536
59 * One struct ghes is created for each generic hardware error
62 * It provides the context for APEI hardware error timer/IRQ/SCI/NMI
63 * handler. Handler for one generic hardware error source is only
64 * triggered after the previous one is done. So handler can uses
65 * struct ghes without locking.
67 * estatus: memory buffer for error status block, allocated during
70 #define GHES_TO_CLEAR 0x0001
73 struct acpi_hest_generic *generic;
74 struct acpi_hest_generic_status *estatus;
75 struct list_head list;
81 * Error source lists, one list for each notification method. The
82 * members in lists are struct ghes.
84 * The list members are only added in HEST parsing and deleted during
85 * module_exit, that is, single-threaded. So no lock is needed for
88 * But the mutual exclusion is needed between members adding/deleting
89 * and timer/IRQ/SCI/NMI handler, which may traverse the list. RCU is
92 static LIST_HEAD(ghes_sci);
93 static DEFINE_MUTEX(ghes_list_mutex);
95 static struct ghes *ghes_new(struct acpi_hest_generic *generic)
98 unsigned int error_block_length;
101 ghes = kzalloc(sizeof(*ghes), GFP_KERNEL);
103 return ERR_PTR(-ENOMEM);
104 ghes->generic = generic;
105 INIT_LIST_HEAD(&ghes->list);
106 rc = acpi_pre_map_gar(&generic->error_status_address);
109 error_block_length = generic->error_block_length;
110 if (error_block_length > GHES_ESTATUS_MAX_SIZE) {
111 pr_warning(FW_WARN GHES_PFX
112 "Error status block length is too long: %u for "
113 "generic hardware error source: %d.\n",
114 error_block_length, generic->header.source_id);
115 error_block_length = GHES_ESTATUS_MAX_SIZE;
117 ghes->estatus = kmalloc(error_block_length, GFP_KERNEL);
118 if (!ghes->estatus) {
126 acpi_post_unmap_gar(&generic->error_status_address);
132 static void ghes_fini(struct ghes *ghes)
134 kfree(ghes->estatus);
135 acpi_post_unmap_gar(&ghes->generic->error_status_address);
140 GHES_SEV_CORRECTED = 0x1,
141 GHES_SEV_RECOVERABLE = 0x2,
142 GHES_SEV_PANIC = 0x3,
145 static inline int ghes_severity(int severity)
148 case CPER_SEV_INFORMATIONAL:
150 case CPER_SEV_CORRECTED:
151 return GHES_SEV_CORRECTED;
152 case CPER_SEV_RECOVERABLE:
153 return GHES_SEV_RECOVERABLE;
155 return GHES_SEV_PANIC;
157 /* Unkown, go panic */
158 return GHES_SEV_PANIC;
162 /* SCI handler run in work queue, so ioremap can be used here */
163 static int ghes_copy_tofrom_phys(void *buffer, u64 paddr, u32 len,
168 vaddr = ioremap_cache(paddr, len);
172 memcpy(buffer, vaddr, len);
174 memcpy(vaddr, buffer, len);
180 static int ghes_read_estatus(struct ghes *ghes, int silent)
182 struct acpi_hest_generic *g = ghes->generic;
187 rc = acpi_atomic_read(&buf_paddr, &g->error_status_address);
189 if (!silent && printk_ratelimit())
190 pr_warning(FW_WARN GHES_PFX
191 "Failed to read error status block address for hardware error source: %d.\n",
192 g->header.source_id);
198 rc = ghes_copy_tofrom_phys(ghes->estatus, buf_paddr,
199 sizeof(*ghes->estatus), 1);
202 if (!ghes->estatus->block_status)
205 ghes->buffer_paddr = buf_paddr;
206 ghes->flags |= GHES_TO_CLEAR;
209 len = apei_estatus_len(ghes->estatus);
210 if (len < sizeof(*ghes->estatus))
212 if (len > ghes->generic->error_block_length)
214 if (apei_estatus_check_header(ghes->estatus))
216 rc = ghes_copy_tofrom_phys(ghes->estatus + 1,
217 buf_paddr + sizeof(*ghes->estatus),
218 len - sizeof(*ghes->estatus), 1);
221 if (apei_estatus_check(ghes->estatus))
227 pr_warning(FW_WARN GHES_PFX
228 "Failed to read error status block!\n");
232 static void ghes_clear_estatus(struct ghes *ghes)
234 ghes->estatus->block_status = 0;
235 if (!(ghes->flags & GHES_TO_CLEAR))
237 ghes_copy_tofrom_phys(ghes->estatus, ghes->buffer_paddr,
238 sizeof(ghes->estatus->block_status), 0);
239 ghes->flags &= ~GHES_TO_CLEAR;
242 static void ghes_do_proc(struct ghes *ghes)
244 int sev, processed = 0;
245 struct acpi_hest_generic_data *gdata;
247 sev = ghes_severity(ghes->estatus->error_severity);
248 apei_estatus_for_each_section(ghes->estatus, gdata) {
249 #ifdef CONFIG_X86_MCE
250 if (!uuid_le_cmp(*(uuid_le *)gdata->section_type,
251 CPER_SEC_PLATFORM_MEM)) {
252 apei_mce_report_mem_error(
253 sev == GHES_SEV_CORRECTED,
254 (struct cper_sec_mem_err *)(gdata+1));
261 static void ghes_print_estatus(const char *pfx, struct ghes *ghes)
263 /* Not more than 2 messages every 5 seconds */
264 static DEFINE_RATELIMIT_STATE(ratelimit, 5*HZ, 2);
267 if (ghes_severity(ghes->estatus->error_severity) <=
269 pfx = KERN_WARNING HW_ERR;
271 pfx = KERN_ERR HW_ERR;
273 if (__ratelimit(&ratelimit)) {
275 "%s""Hardware error from APEI Generic Hardware Error Source: %d\n",
276 pfx, ghes->generic->header.source_id);
277 apei_estatus_print(pfx, ghes->estatus);
281 static int ghes_proc(struct ghes *ghes)
285 rc = ghes_read_estatus(ghes, 0);
288 ghes_print_estatus(NULL, ghes);
292 ghes_clear_estatus(ghes);
296 static int ghes_notify_sci(struct notifier_block *this,
297 unsigned long event, void *data)
300 int ret = NOTIFY_DONE;
303 list_for_each_entry_rcu(ghes, &ghes_sci, list) {
304 if (!ghes_proc(ghes))
312 static struct notifier_block ghes_notifier_sci = {
313 .notifier_call = ghes_notify_sci,
316 static int __devinit ghes_probe(struct platform_device *ghes_dev)
318 struct acpi_hest_generic *generic;
319 struct ghes *ghes = NULL;
322 generic = *(struct acpi_hest_generic **)ghes_dev->dev.platform_data;
323 if (!generic->enabled)
326 if (generic->error_block_length <
327 sizeof(struct acpi_hest_generic_status)) {
328 pr_warning(FW_BUG GHES_PFX
329 "Invalid error block length: %u for generic hardware error source: %d\n",
330 generic->error_block_length,
331 generic->header.source_id);
334 if (generic->records_to_preallocate == 0) {
335 pr_warning(FW_BUG GHES_PFX
336 "Invalid records to preallocate: %u for generic hardware error source: %d\n",
337 generic->records_to_preallocate,
338 generic->header.source_id);
341 ghes = ghes_new(generic);
347 if (generic->notify.type == ACPI_HEST_NOTIFY_SCI) {
348 mutex_lock(&ghes_list_mutex);
349 if (list_empty(&ghes_sci))
350 register_acpi_hed_notifier(&ghes_notifier_sci);
351 list_add_rcu(&ghes->list, &ghes_sci);
352 mutex_unlock(&ghes_list_mutex);
354 unsigned char *notify = NULL;
356 switch (generic->notify.type) {
357 case ACPI_HEST_NOTIFY_POLLED:
360 case ACPI_HEST_NOTIFY_EXTERNAL:
361 case ACPI_HEST_NOTIFY_LOCAL:
364 case ACPI_HEST_NOTIFY_NMI:
370 "Generic hardware error source: %d notified via %s is not supported!\n",
371 generic->header.source_id, notify);
373 pr_warning(FW_WARN GHES_PFX
374 "Unknown notification type: %u for generic hardware error source: %d\n",
375 generic->notify.type, generic->header.source_id);
380 platform_set_drvdata(ghes_dev, ghes);
391 static int __devexit ghes_remove(struct platform_device *ghes_dev)
394 struct acpi_hest_generic *generic;
396 ghes = platform_get_drvdata(ghes_dev);
397 generic = ghes->generic;
399 switch (generic->notify.type) {
400 case ACPI_HEST_NOTIFY_SCI:
401 mutex_lock(&ghes_list_mutex);
402 list_del_rcu(&ghes->list);
403 if (list_empty(&ghes_sci))
404 unregister_acpi_hed_notifier(&ghes_notifier_sci);
405 mutex_unlock(&ghes_list_mutex);
416 platform_set_drvdata(ghes_dev, NULL);
421 static struct platform_driver ghes_platform_driver = {
424 .owner = THIS_MODULE,
427 .remove = ghes_remove,
430 static int __init ghes_init(void)
436 pr_info(GHES_PFX "HEST is not enabled!\n");
440 return platform_driver_register(&ghes_platform_driver);
443 static void __exit ghes_exit(void)
445 platform_driver_unregister(&ghes_platform_driver);
448 module_init(ghes_init);
449 module_exit(ghes_exit);
451 MODULE_AUTHOR("Huang Ying");
452 MODULE_DESCRIPTION("APEI Generic Hardware Error Source support");
453 MODULE_LICENSE("GPL");
454 MODULE_ALIAS("platform:GHES");