xref: /linux/drivers/acpi/apei/hest.c (revision 5d4a2e29fba5b2bef95b96a46b338ec4d76fa4fd)
1 /*
2  * APEI Hardware Error Souce Table support
3  *
4  * HEST describes error sources in detail; communicates operational
5  * parameters (i.e. severity levels, masking bits, and threshold
6  * values) to Linux as necessary. It also allows the BIOS to report
7  * non-standard error sources to Linux (for example, chipset-specific
8  * error registers).
9  *
10  * For more information about HEST, please refer to ACPI Specification
11  * version 4.0, section 17.3.2.
12  *
13  * Copyright 2009 Intel Corp.
14  *   Author: Huang Ying <ying.huang@intel.com>
15  *
16  * This program is free software; you can redistribute it and/or
17  * modify it under the terms of the GNU General Public License version
18  * 2 as published by the Free Software Foundation;
19  *
20  * This program is distributed in the hope that it will be useful,
21  * but WITHOUT ANY WARRANTY; without even the implied warranty of
22  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
23  * GNU General Public License for more details.
24  *
25  * You should have received a copy of the GNU General Public License
26  * along with this program; if not, write to the Free Software
27  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
28  */
29 
30 #include <linux/kernel.h>
31 #include <linux/module.h>
32 #include <linux/init.h>
33 #include <linux/acpi.h>
34 #include <linux/kdebug.h>
35 #include <linux/highmem.h>
36 #include <linux/io.h>
37 #include <acpi/apei.h>
38 
39 #include "apei-internal.h"
40 
41 #define HEST_PFX "HEST: "
42 
43 int hest_disable;
44 EXPORT_SYMBOL_GPL(hest_disable);
45 
46 /* HEST table parsing */
47 
48 static struct acpi_table_hest *hest_tab;
49 
50 static int hest_void_parse(struct acpi_hest_header *hest_hdr, void *data)
51 {
52 	return 0;
53 }
54 
55 static int hest_esrc_len_tab[ACPI_HEST_TYPE_RESERVED] = {
56 	[ACPI_HEST_TYPE_IA32_CHECK] = -1,	/* need further calculation */
57 	[ACPI_HEST_TYPE_IA32_CORRECTED_CHECK] = -1,
58 	[ACPI_HEST_TYPE_IA32_NMI] = sizeof(struct acpi_hest_ia_nmi),
59 	[ACPI_HEST_TYPE_AER_ROOT_PORT] = sizeof(struct acpi_hest_aer_root),
60 	[ACPI_HEST_TYPE_AER_ENDPOINT] = sizeof(struct acpi_hest_aer),
61 	[ACPI_HEST_TYPE_AER_BRIDGE] = sizeof(struct acpi_hest_aer_bridge),
62 	[ACPI_HEST_TYPE_GENERIC_ERROR] = sizeof(struct acpi_hest_generic),
63 };
64 
65 static int hest_esrc_len(struct acpi_hest_header *hest_hdr)
66 {
67 	u16 hest_type = hest_hdr->type;
68 	int len;
69 
70 	if (hest_type >= ACPI_HEST_TYPE_RESERVED)
71 		return 0;
72 
73 	len = hest_esrc_len_tab[hest_type];
74 
75 	if (hest_type == ACPI_HEST_TYPE_IA32_CORRECTED_CHECK) {
76 		struct acpi_hest_ia_corrected *cmc;
77 		cmc = (struct acpi_hest_ia_corrected *)hest_hdr;
78 		len = sizeof(*cmc) + cmc->num_hardware_banks *
79 			sizeof(struct acpi_hest_ia_error_bank);
80 	} else if (hest_type == ACPI_HEST_TYPE_IA32_CHECK) {
81 		struct acpi_hest_ia_machine_check *mc;
82 		mc = (struct acpi_hest_ia_machine_check *)hest_hdr;
83 		len = sizeof(*mc) + mc->num_hardware_banks *
84 			sizeof(struct acpi_hest_ia_error_bank);
85 	}
86 	BUG_ON(len == -1);
87 
88 	return len;
89 };
90 
91 int apei_hest_parse(apei_hest_func_t func, void *data)
92 {
93 	struct acpi_hest_header *hest_hdr;
94 	int i, rc, len;
95 
96 	if (hest_disable)
97 		return -EINVAL;
98 
99 	hest_hdr = (struct acpi_hest_header *)(hest_tab + 1);
100 	for (i = 0; i < hest_tab->error_source_count; i++) {
101 		len = hest_esrc_len(hest_hdr);
102 		if (!len) {
103 			pr_warning(FW_WARN HEST_PFX
104 				   "Unknown or unused hardware error source "
105 				   "type: %d for hardware error source: %d.\n",
106 				   hest_hdr->type, hest_hdr->source_id);
107 			return -EINVAL;
108 		}
109 		if ((void *)hest_hdr + len >
110 		    (void *)hest_tab + hest_tab->header.length) {
111 			pr_warning(FW_BUG HEST_PFX
112 		"Table contents overflow for hardware error source: %d.\n",
113 				hest_hdr->source_id);
114 			return -EINVAL;
115 		}
116 
117 		rc = func(hest_hdr, data);
118 		if (rc)
119 			return rc;
120 
121 		hest_hdr = (void *)hest_hdr + len;
122 	}
123 
124 	return 0;
125 }
126 EXPORT_SYMBOL_GPL(apei_hest_parse);
127 
128 static int __init setup_hest_disable(char *str)
129 {
130 	hest_disable = 1;
131 	return 0;
132 }
133 
134 __setup("hest_disable", setup_hest_disable);
135 
136 static int __init hest_init(void)
137 {
138 	acpi_status status;
139 	int rc = -ENODEV;
140 
141 	if (acpi_disabled)
142 		goto err;
143 
144 	if (hest_disable) {
145 		pr_info(HEST_PFX "HEST tabling parsing is disabled.\n");
146 		goto err;
147 	}
148 
149 	status = acpi_get_table(ACPI_SIG_HEST, 0,
150 				(struct acpi_table_header **)&hest_tab);
151 	if (status == AE_NOT_FOUND) {
152 		pr_info(HEST_PFX "Table is not found!\n");
153 		goto err;
154 	} else if (ACPI_FAILURE(status)) {
155 		const char *msg = acpi_format_exception(status);
156 		pr_err(HEST_PFX "Failed to get table, %s\n", msg);
157 		rc = -EINVAL;
158 		goto err;
159 	}
160 
161 	rc = apei_hest_parse(hest_void_parse, NULL);
162 	if (rc)
163 		goto err;
164 
165 	pr_info(HEST_PFX "HEST table parsing is initialized.\n");
166 
167 	return 0;
168 err:
169 	hest_disable = 1;
170 	return rc;
171 }
172 
173 subsys_initcall(hest_init);
174