Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/drivers/acpi/apei/hest.c
26282 views
1
// SPDX-License-Identifier: GPL-2.0-only
2
/*
3
* APEI Hardware Error Source Table support
4
*
5
* HEST describes error sources in detail; communicates operational
6
* parameters (i.e. severity levels, masking bits, and threshold
7
* values) to Linux as necessary. It also allows the BIOS to report
8
* non-standard error sources to Linux (for example, chipset-specific
9
* error registers).
10
*
11
* For more information about HEST, please refer to ACPI Specification
12
* version 4.0, section 17.3.2.
13
*
14
* Copyright 2009 Intel Corp.
15
* Author: Huang Ying <[email protected]>
16
*/
17
18
#include <linux/kernel.h>
19
#include <linux/module.h>
20
#include <linux/init.h>
21
#include <linux/acpi.h>
22
#include <linux/kdebug.h>
23
#include <linux/highmem.h>
24
#include <linux/io.h>
25
#include <linux/platform_device.h>
26
#include <acpi/apei.h>
27
#include <acpi/ghes.h>
28
29
#include "apei-internal.h"
30
31
#define HEST_PFX "HEST: "
32
33
int hest_disable;
34
EXPORT_SYMBOL_GPL(hest_disable);
35
36
/* HEST table parsing */
37
38
static struct acpi_table_hest *__read_mostly hest_tab;
39
40
/*
41
* Since GHES_ASSIST is not supported, skip initialization of GHES_ASSIST
42
* structures for MCA.
43
* During HEST parsing, detected MCA error sources are cached from early
44
* table entries so that the Flags and Source Id fields from these cached
45
* values are then referred to in later table entries to determine if the
46
* encountered GHES_ASSIST structure should be initialized.
47
*/
48
static struct {
49
struct acpi_hest_ia_corrected *cmc;
50
struct acpi_hest_ia_machine_check *mc;
51
struct acpi_hest_ia_deferred_check *dmc;
52
} mces;
53
54
static const int hest_esrc_len_tab[ACPI_HEST_TYPE_RESERVED] = {
55
[ACPI_HEST_TYPE_IA32_CHECK] = -1, /* need further calculation */
56
[ACPI_HEST_TYPE_IA32_CORRECTED_CHECK] = -1,
57
[ACPI_HEST_TYPE_IA32_NMI] = sizeof(struct acpi_hest_ia_nmi),
58
[ACPI_HEST_TYPE_AER_ROOT_PORT] = sizeof(struct acpi_hest_aer_root),
59
[ACPI_HEST_TYPE_AER_ENDPOINT] = sizeof(struct acpi_hest_aer),
60
[ACPI_HEST_TYPE_AER_BRIDGE] = sizeof(struct acpi_hest_aer_bridge),
61
[ACPI_HEST_TYPE_GENERIC_ERROR] = sizeof(struct acpi_hest_generic),
62
[ACPI_HEST_TYPE_GENERIC_ERROR_V2] = sizeof(struct acpi_hest_generic_v2),
63
[ACPI_HEST_TYPE_IA32_DEFERRED_CHECK] = -1,
64
};
65
66
static inline bool is_generic_error(struct acpi_hest_header *hest_hdr)
67
{
68
return hest_hdr->type == ACPI_HEST_TYPE_GENERIC_ERROR ||
69
hest_hdr->type == ACPI_HEST_TYPE_GENERIC_ERROR_V2;
70
}
71
72
static int hest_esrc_len(struct acpi_hest_header *hest_hdr)
73
{
74
u16 hest_type = hest_hdr->type;
75
int len;
76
77
if (hest_type >= ACPI_HEST_TYPE_RESERVED)
78
return 0;
79
80
len = hest_esrc_len_tab[hest_type];
81
82
if (hest_type == ACPI_HEST_TYPE_IA32_CORRECTED_CHECK) {
83
struct acpi_hest_ia_corrected *cmc;
84
cmc = (struct acpi_hest_ia_corrected *)hest_hdr;
85
len = sizeof(*cmc) + cmc->num_hardware_banks *
86
sizeof(struct acpi_hest_ia_error_bank);
87
mces.cmc = cmc;
88
} else if (hest_type == ACPI_HEST_TYPE_IA32_CHECK) {
89
struct acpi_hest_ia_machine_check *mc;
90
mc = (struct acpi_hest_ia_machine_check *)hest_hdr;
91
len = sizeof(*mc) + mc->num_hardware_banks *
92
sizeof(struct acpi_hest_ia_error_bank);
93
mces.mc = mc;
94
} else if (hest_type == ACPI_HEST_TYPE_IA32_DEFERRED_CHECK) {
95
struct acpi_hest_ia_deferred_check *mc;
96
mc = (struct acpi_hest_ia_deferred_check *)hest_hdr;
97
len = sizeof(*mc) + mc->num_hardware_banks *
98
sizeof(struct acpi_hest_ia_error_bank);
99
mces.dmc = mc;
100
}
101
BUG_ON(len == -1);
102
103
return len;
104
};
105
106
/*
107
* GHES and GHESv2 structures share the same format, starting from
108
* Source Id and ending in Error Status Block Length (inclusive).
109
*/
110
static bool is_ghes_assist_struct(struct acpi_hest_header *hest_hdr)
111
{
112
struct acpi_hest_generic *ghes;
113
u16 related_source_id;
114
115
if (hest_hdr->type != ACPI_HEST_TYPE_GENERIC_ERROR &&
116
hest_hdr->type != ACPI_HEST_TYPE_GENERIC_ERROR_V2)
117
return false;
118
119
ghes = (struct acpi_hest_generic *)hest_hdr;
120
related_source_id = ghes->related_source_id;
121
122
if (mces.cmc && mces.cmc->flags & ACPI_HEST_GHES_ASSIST &&
123
related_source_id == mces.cmc->header.source_id)
124
return true;
125
if (mces.mc && mces.mc->flags & ACPI_HEST_GHES_ASSIST &&
126
related_source_id == mces.mc->header.source_id)
127
return true;
128
if (mces.dmc && mces.dmc->flags & ACPI_HEST_GHES_ASSIST &&
129
related_source_id == mces.dmc->header.source_id)
130
return true;
131
132
return false;
133
}
134
135
typedef int (*apei_hest_func_t)(struct acpi_hest_header *hest_hdr, void *data);
136
137
static int apei_hest_parse(apei_hest_func_t func, void *data)
138
{
139
struct acpi_hest_header *hest_hdr;
140
int i, rc, len;
141
142
if (hest_disable || !hest_tab)
143
return -EINVAL;
144
145
hest_hdr = (struct acpi_hest_header *)(hest_tab + 1);
146
for (i = 0; i < hest_tab->error_source_count; i++) {
147
len = hest_esrc_len(hest_hdr);
148
if (!len) {
149
pr_warn(FW_WARN HEST_PFX
150
"Unknown or unused hardware error source "
151
"type: %d for hardware error source: %d.\n",
152
hest_hdr->type, hest_hdr->source_id);
153
return -EINVAL;
154
}
155
if ((void *)hest_hdr + len >
156
(void *)hest_tab + hest_tab->header.length) {
157
pr_warn(FW_BUG HEST_PFX
158
"Table contents overflow for hardware error source: %d.\n",
159
hest_hdr->source_id);
160
return -EINVAL;
161
}
162
163
if (is_ghes_assist_struct(hest_hdr)) {
164
hest_hdr = (void *)hest_hdr + len;
165
continue;
166
}
167
168
rc = func(hest_hdr, data);
169
if (rc)
170
return rc;
171
172
hest_hdr = (void *)hest_hdr + len;
173
}
174
175
return 0;
176
}
177
178
/*
179
* Check if firmware advertises firmware first mode. We need FF bit to be set
180
* along with a set of MC banks which work in FF mode.
181
*/
182
static int __init hest_parse_cmc(struct acpi_hest_header *hest_hdr, void *data)
183
{
184
if (hest_hdr->type != ACPI_HEST_TYPE_IA32_CORRECTED_CHECK)
185
return 0;
186
187
if (!acpi_disable_cmcff)
188
return !arch_apei_enable_cmcff(hest_hdr, data);
189
190
return 0;
191
}
192
193
struct ghes_arr {
194
struct platform_device **ghes_devs;
195
unsigned int count;
196
};
197
198
static int __init hest_parse_ghes_count(struct acpi_hest_header *hest_hdr, void *data)
199
{
200
int *count = data;
201
202
if (is_generic_error(hest_hdr))
203
(*count)++;
204
return 0;
205
}
206
207
static int __init hest_parse_ghes(struct acpi_hest_header *hest_hdr, void *data)
208
{
209
struct platform_device *ghes_dev;
210
struct ghes_arr *ghes_arr = data;
211
int rc, i;
212
213
if (!is_generic_error(hest_hdr))
214
return 0;
215
216
if (!((struct acpi_hest_generic *)hest_hdr)->enabled)
217
return 0;
218
for (i = 0; i < ghes_arr->count; i++) {
219
struct acpi_hest_header *hdr;
220
ghes_dev = ghes_arr->ghes_devs[i];
221
hdr = *(struct acpi_hest_header **)ghes_dev->dev.platform_data;
222
if (hdr->source_id == hest_hdr->source_id) {
223
pr_warn(FW_WARN HEST_PFX "Duplicated hardware error source ID: %d.\n",
224
hdr->source_id);
225
return -EIO;
226
}
227
}
228
ghes_dev = platform_device_alloc("GHES", hest_hdr->source_id);
229
if (!ghes_dev)
230
return -ENOMEM;
231
232
rc = platform_device_add_data(ghes_dev, &hest_hdr, sizeof(void *));
233
if (rc)
234
goto err;
235
236
rc = platform_device_add(ghes_dev);
237
if (rc)
238
goto err;
239
ghes_arr->ghes_devs[ghes_arr->count++] = ghes_dev;
240
241
return 0;
242
err:
243
platform_device_put(ghes_dev);
244
return rc;
245
}
246
247
static int __init hest_ghes_dev_register(unsigned int ghes_count)
248
{
249
int rc, i;
250
struct ghes_arr ghes_arr;
251
252
ghes_arr.count = 0;
253
ghes_arr.ghes_devs = kmalloc_array(ghes_count, sizeof(void *),
254
GFP_KERNEL);
255
if (!ghes_arr.ghes_devs)
256
return -ENOMEM;
257
258
rc = apei_hest_parse(hest_parse_ghes, &ghes_arr);
259
if (rc)
260
goto err;
261
262
rc = ghes_estatus_pool_init(ghes_count);
263
if (rc)
264
goto err;
265
266
out:
267
kfree(ghes_arr.ghes_devs);
268
return rc;
269
err:
270
for (i = 0; i < ghes_arr.count; i++)
271
platform_device_unregister(ghes_arr.ghes_devs[i]);
272
goto out;
273
}
274
275
static int __init setup_hest_disable(char *str)
276
{
277
hest_disable = HEST_DISABLED;
278
return 1;
279
}
280
281
__setup("hest_disable", setup_hest_disable);
282
283
void __init acpi_hest_init(void)
284
{
285
acpi_status status;
286
int rc;
287
unsigned int ghes_count = 0;
288
289
if (hest_disable) {
290
pr_info(HEST_PFX "Table parsing disabled.\n");
291
return;
292
}
293
294
status = acpi_get_table(ACPI_SIG_HEST, 0,
295
(struct acpi_table_header **)&hest_tab);
296
if (status == AE_NOT_FOUND) {
297
hest_disable = HEST_NOT_FOUND;
298
return;
299
} else if (ACPI_FAILURE(status)) {
300
const char *msg = acpi_format_exception(status);
301
pr_err(HEST_PFX "Failed to get table, %s\n", msg);
302
hest_disable = HEST_DISABLED;
303
return;
304
}
305
306
rc = apei_hest_parse(hest_parse_cmc, NULL);
307
if (rc)
308
goto err;
309
310
if (!ghes_disable) {
311
rc = apei_hest_parse(hest_parse_ghes_count, &ghes_count);
312
if (rc)
313
goto err;
314
315
if (ghes_count)
316
rc = hest_ghes_dev_register(ghes_count);
317
if (rc)
318
goto err;
319
}
320
321
pr_info(HEST_PFX "Table parsing has been initialized.\n");
322
return;
323
err:
324
hest_disable = HEST_DISABLED;
325
acpi_put_table((struct acpi_table_header *)hest_tab);
326
}
327
328