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 <acpi/apei.h>
45 #include <acpi/atomicio.h>
49 #include "apei-internal.h"
51 #define GHES_PFX "GHES: "
53 #define GHES_ESTATUS_MAX_SIZE 65536
56 * One struct ghes is created for each generic hardware error
59 * It provides the context for APEI hardware error timer/IRQ/SCI/NMI
60 * handler. Handler for one generic hardware error source is only
61 * triggered after the previous one is done. So handler can uses
62 * struct ghes without locking.
64 * estatus: memory buffer for error status block, allocated during
67 #define GHES_TO_CLEAR 0x0001
70 struct acpi_hest_generic *generic;
71 struct acpi_hest_generic_status *estatus;
72 struct list_head list;
78 * Error source lists, one list for each notification method. The
79 * members in lists are struct ghes.
81 * The list members are only added in HEST parsing and deleted during
82 * module_exit, that is, single-threaded. So no lock is needed for
85 * But the mutual exclusion is needed between members adding/deleting
86 * and timer/IRQ/SCI/NMI handler, which may traverse the list. RCU is
89 static LIST_HEAD(ghes_sci);
91 static struct ghes *ghes_new(struct acpi_hest_generic *generic)
94 unsigned int error_block_length;
97 ghes = kzalloc(sizeof(*ghes), GFP_KERNEL);
99 return ERR_PTR(-ENOMEM);
100 ghes->generic = generic;
101 INIT_LIST_HEAD(&ghes->list);
102 rc = acpi_pre_map_gar(&generic->error_status_address);
105 error_block_length = generic->error_block_length;
106 if (error_block_length > GHES_ESTATUS_MAX_SIZE) {
107 pr_warning(FW_WARN GHES_PFX
108 "Error status block length is too long: %u for "
109 "generic hardware error source: %d.\n",
110 error_block_length, generic->header.source_id);
111 error_block_length = GHES_ESTATUS_MAX_SIZE;
113 ghes->estatus = kmalloc(error_block_length, GFP_KERNEL);
114 if (!ghes->estatus) {
122 acpi_post_unmap_gar(&generic->error_status_address);
128 static void ghes_fini(struct ghes *ghes)
130 kfree(ghes->estatus);
131 acpi_post_unmap_gar(&ghes->generic->error_status_address);
136 GHES_SER_CORRECTED = 0x1,
137 GHES_SER_RECOVERABLE = 0x2,
138 GHES_SER_PANIC = 0x3,
141 static inline int ghes_severity(int severity)
144 case CPER_SER_INFORMATIONAL:
146 case CPER_SER_CORRECTED:
147 return GHES_SER_CORRECTED;
148 case CPER_SER_RECOVERABLE:
149 return GHES_SER_RECOVERABLE;
151 return GHES_SER_PANIC;
153 /* Unkown, go panic */
154 return GHES_SER_PANIC;
158 /* SCI handler run in work queue, so ioremap can be used here */
159 static int ghes_copy_tofrom_phys(void *buffer, u64 paddr, u32 len,
164 vaddr = ioremap_cache(paddr, len);
168 memcpy(buffer, vaddr, len);
170 memcpy(vaddr, buffer, len);
176 static int ghes_read_estatus(struct ghes *ghes, int silent)
178 struct acpi_hest_generic *g = ghes->generic;
183 rc = acpi_atomic_read(&buf_paddr, &g->error_status_address);
185 if (!silent && printk_ratelimit())
186 pr_warning(FW_WARN GHES_PFX
187 "Failed to read error status block address for hardware error source: %d.\n",
188 g->header.source_id);
194 rc = ghes_copy_tofrom_phys(ghes->estatus, buf_paddr,
195 sizeof(*ghes->estatus), 1);
198 if (!ghes->estatus->block_status)
201 ghes->buffer_paddr = buf_paddr;
202 ghes->flags |= GHES_TO_CLEAR;
205 len = apei_estatus_len(ghes->estatus);
206 if (len < sizeof(*ghes->estatus))
208 if (len > ghes->generic->error_block_length)
210 if (apei_estatus_check_header(ghes->estatus))
212 rc = ghes_copy_tofrom_phys(ghes->estatus + 1,
213 buf_paddr + sizeof(*ghes->estatus),
214 len - sizeof(*ghes->estatus), 1);
217 if (apei_estatus_check(ghes->estatus))
223 pr_warning(FW_WARN GHES_PFX
224 "Failed to read error status block!\n");
228 static void ghes_clear_estatus(struct ghes *ghes)
230 ghes->estatus->block_status = 0;
231 if (!(ghes->flags & GHES_TO_CLEAR))
233 ghes_copy_tofrom_phys(ghes->estatus, ghes->buffer_paddr,
234 sizeof(ghes->estatus->block_status), 0);
235 ghes->flags &= ~GHES_TO_CLEAR;
238 static void ghes_do_proc(struct ghes *ghes)
240 int ser, processed = 0;
241 struct acpi_hest_generic_data *gdata;
243 ser = ghes_severity(ghes->estatus->error_severity);
244 apei_estatus_for_each_section(ghes->estatus, gdata) {
245 #ifdef CONFIG_X86_MCE
246 if (!uuid_le_cmp(*(uuid_le *)gdata->section_type,
247 CPER_SEC_PLATFORM_MEM)) {
248 apei_mce_report_mem_error(
249 ser == GHES_SER_CORRECTED,
250 (struct cper_sec_mem_err *)(gdata+1));
256 if (!processed && printk_ratelimit())
258 "Unknown error record from generic hardware error source: %d\n",
259 ghes->generic->header.source_id);
262 static int ghes_proc(struct ghes *ghes)
266 rc = ghes_read_estatus(ghes, 0);
272 ghes_clear_estatus(ghes);
276 static int ghes_notify_sci(struct notifier_block *this,
277 unsigned long event, void *data)
280 int ret = NOTIFY_DONE;
283 list_for_each_entry_rcu(ghes, &ghes_sci, list) {
284 if (!ghes_proc(ghes))
292 static struct notifier_block ghes_notifier_sci = {
293 .notifier_call = ghes_notify_sci,
296 static int hest_ghes_parse(struct acpi_hest_header *hest_hdr, void *data)
298 struct acpi_hest_generic *generic;
299 struct ghes *ghes = NULL;
302 if (hest_hdr->type != ACPI_HEST_TYPE_GENERIC_ERROR)
305 generic = (struct acpi_hest_generic *)hest_hdr;
306 if (!generic->enabled)
309 if (generic->error_block_length <
310 sizeof(struct acpi_hest_generic_status)) {
311 pr_warning(FW_BUG GHES_PFX
312 "Invalid error block length: %u for generic hardware error source: %d\n",
313 generic->error_block_length,
314 generic->header.source_id);
317 if (generic->records_to_preallocate == 0) {
318 pr_warning(FW_BUG GHES_PFX
319 "Invalid records to preallocate: %u for generic hardware error source: %d\n",
320 generic->records_to_preallocate,
321 generic->header.source_id);
324 ghes = ghes_new(generic);
330 switch (generic->notify.type) {
331 case ACPI_HEST_NOTIFY_POLLED:
333 "Generic hardware error source: %d notified via POLL is not supported!\n",
334 generic->header.source_id);
336 case ACPI_HEST_NOTIFY_EXTERNAL:
337 case ACPI_HEST_NOTIFY_LOCAL:
339 "Generic hardware error source: %d notified via IRQ is not supported!\n",
340 generic->header.source_id);
342 case ACPI_HEST_NOTIFY_SCI:
343 if (list_empty(&ghes_sci))
344 register_acpi_hed_notifier(&ghes_notifier_sci);
345 list_add_rcu(&ghes->list, &ghes_sci);
347 case ACPI_HEST_NOTIFY_NMI:
349 "Generic hardware error source: %d notified via NMI is not supported!\n",
350 generic->header.source_id);
353 pr_warning(FW_WARN GHES_PFX
354 "Unknown notification type: %u for generic hardware error source: %d\n",
355 generic->notify.type, generic->header.source_id);
366 static void ghes_cleanup(void)
368 struct ghes *ghes, *nghes;
370 if (!list_empty(&ghes_sci))
371 unregister_acpi_hed_notifier(&ghes_notifier_sci);
375 list_for_each_entry_safe(ghes, nghes, &ghes_sci, list) {
376 list_del(&ghes->list);
382 static int __init ghes_init(void)
390 pr_info(GHES_PFX "HEST is not enabled!\n");
394 rc = apei_hest_parse(hest_ghes_parse, NULL);
397 "Error during parsing HEST generic hardware error sources.\n");
401 if (list_empty(&ghes_sci)) {
403 "No functional generic hardware error sources.\n");
409 "Generic Hardware Error Source support is initialized.\n");
417 static void __exit ghes_exit(void)
422 module_init(ghes_init);
423 module_exit(ghes_exit);
425 MODULE_AUTHOR("Huang Ying");
426 MODULE_DESCRIPTION("APEI Generic Hardware Error Source support");
427 MODULE_LICENSE("GPL");