1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * APEI Error INJection support |
4 | * |
5 | * EINJ provides a hardware error injection mechanism, this is useful |
6 | * for debugging and testing of other APEI and RAS features. |
7 | * |
8 | * For more information about EINJ, please refer to ACPI Specification |
9 | * version 4.0, section 17.5. |
10 | * |
11 | * Copyright 2009-2010 Intel Corp. |
12 | * Author: Huang Ying <ying.huang@intel.com> |
13 | */ |
14 | |
15 | #include <linux/kernel.h> |
16 | #include <linux/module.h> |
17 | #include <linux/init.h> |
18 | #include <linux/io.h> |
19 | #include <linux/debugfs.h> |
20 | #include <linux/seq_file.h> |
21 | #include <linux/nmi.h> |
22 | #include <linux/delay.h> |
23 | #include <linux/mm.h> |
24 | #include <linux/platform_device.h> |
25 | #include <asm/unaligned.h> |
26 | |
27 | #include "apei-internal.h" |
28 | |
29 | #undef pr_fmt |
30 | #define pr_fmt(fmt) "EINJ: " fmt |
31 | |
32 | #define SLEEP_UNIT_MIN 1000 /* 1ms */ |
33 | #define SLEEP_UNIT_MAX 5000 /* 5ms */ |
34 | /* Firmware should respond within 1 seconds */ |
35 | #define FIRMWARE_TIMEOUT (1 * USEC_PER_SEC) |
36 | #define ACPI5_VENDOR_BIT BIT(31) |
37 | #define MEM_ERROR_MASK (ACPI_EINJ_MEMORY_CORRECTABLE | \ |
38 | ACPI_EINJ_MEMORY_UNCORRECTABLE | \ |
39 | ACPI_EINJ_MEMORY_FATAL) |
40 | #define CXL_ERROR_MASK (ACPI_EINJ_CXL_CACHE_CORRECTABLE | \ |
41 | ACPI_EINJ_CXL_CACHE_UNCORRECTABLE | \ |
42 | ACPI_EINJ_CXL_CACHE_FATAL | \ |
43 | ACPI_EINJ_CXL_MEM_CORRECTABLE | \ |
44 | ACPI_EINJ_CXL_MEM_UNCORRECTABLE | \ |
45 | ACPI_EINJ_CXL_MEM_FATAL) |
46 | |
47 | /* |
48 | * ACPI version 5 provides a SET_ERROR_TYPE_WITH_ADDRESS action. |
49 | */ |
50 | static int acpi5; |
51 | |
52 | struct set_error_type_with_address { |
53 | u32 type; |
54 | u32 vendor_extension; |
55 | u32 flags; |
56 | u32 apicid; |
57 | u64 memory_address; |
58 | u64 memory_address_range; |
59 | u32 pcie_sbdf; |
60 | }; |
61 | enum { |
62 | SETWA_FLAGS_APICID = 1, |
63 | SETWA_FLAGS_MEM = 2, |
64 | SETWA_FLAGS_PCIE_SBDF = 4, |
65 | }; |
66 | |
67 | /* |
68 | * Vendor extensions for platform specific operations |
69 | */ |
70 | struct vendor_error_type_extension { |
71 | u32 length; |
72 | u32 pcie_sbdf; |
73 | u16 vendor_id; |
74 | u16 device_id; |
75 | u8 rev_id; |
76 | u8 reserved[3]; |
77 | }; |
78 | |
79 | static u32 notrigger; |
80 | |
81 | static u32 vendor_flags; |
82 | static struct debugfs_blob_wrapper vendor_blob; |
83 | static struct debugfs_blob_wrapper vendor_errors; |
84 | static char vendor_dev[64]; |
85 | |
86 | /* |
87 | * Some BIOSes allow parameters to the SET_ERROR_TYPE entries in the |
88 | * EINJ table through an unpublished extension. Use with caution as |
89 | * most will ignore the parameter and make their own choice of address |
90 | * for error injection. This extension is used only if |
91 | * param_extension module parameter is specified. |
92 | */ |
93 | struct einj_parameter { |
94 | u64 type; |
95 | u64 reserved1; |
96 | u64 reserved2; |
97 | u64 param1; |
98 | u64 param2; |
99 | }; |
100 | |
101 | #define EINJ_OP_BUSY 0x1 |
102 | #define EINJ_STATUS_SUCCESS 0x0 |
103 | #define EINJ_STATUS_FAIL 0x1 |
104 | #define EINJ_STATUS_INVAL 0x2 |
105 | |
106 | #define EINJ_TAB_ENTRY(tab) \ |
107 | ((struct acpi_whea_header *)((char *)(tab) + \ |
108 | sizeof(struct acpi_table_einj))) |
109 | |
110 | static bool param_extension; |
111 | module_param(param_extension, bool, 0); |
112 | |
113 | static struct acpi_table_einj *einj_tab; |
114 | |
115 | static struct apei_resources einj_resources; |
116 | |
117 | static struct apei_exec_ins_type einj_ins_type[] = { |
118 | [ACPI_EINJ_READ_REGISTER] = { |
119 | .flags = APEI_EXEC_INS_ACCESS_REGISTER, |
120 | .run = apei_exec_read_register, |
121 | }, |
122 | [ACPI_EINJ_READ_REGISTER_VALUE] = { |
123 | .flags = APEI_EXEC_INS_ACCESS_REGISTER, |
124 | .run = apei_exec_read_register_value, |
125 | }, |
126 | [ACPI_EINJ_WRITE_REGISTER] = { |
127 | .flags = APEI_EXEC_INS_ACCESS_REGISTER, |
128 | .run = apei_exec_write_register, |
129 | }, |
130 | [ACPI_EINJ_WRITE_REGISTER_VALUE] = { |
131 | .flags = APEI_EXEC_INS_ACCESS_REGISTER, |
132 | .run = apei_exec_write_register_value, |
133 | }, |
134 | [ACPI_EINJ_NOOP] = { |
135 | .flags = 0, |
136 | .run = apei_exec_noop, |
137 | }, |
138 | }; |
139 | |
140 | /* |
141 | * Prevent EINJ interpreter to run simultaneously, because the |
142 | * corresponding firmware implementation may not work properly when |
143 | * invoked simultaneously. |
144 | */ |
145 | static DEFINE_MUTEX(einj_mutex); |
146 | |
147 | /* |
148 | * Exported APIs use this flag to exit early if einj_probe() failed. |
149 | */ |
150 | bool einj_initialized __ro_after_init; |
151 | |
152 | static void *einj_param; |
153 | |
154 | static void einj_exec_ctx_init(struct apei_exec_context *ctx) |
155 | { |
156 | apei_exec_ctx_init(ctx, ins_table: einj_ins_type, ARRAY_SIZE(einj_ins_type), |
157 | EINJ_TAB_ENTRY(einj_tab), entries: einj_tab->entries); |
158 | } |
159 | |
160 | static int __einj_get_available_error_type(u32 *type) |
161 | { |
162 | struct apei_exec_context ctx; |
163 | int rc; |
164 | |
165 | einj_exec_ctx_init(ctx: &ctx); |
166 | rc = apei_exec_run(ctx: &ctx, action: ACPI_EINJ_GET_ERROR_TYPE); |
167 | if (rc) |
168 | return rc; |
169 | *type = apei_exec_ctx_get_output(ctx: &ctx); |
170 | |
171 | return 0; |
172 | } |
173 | |
174 | /* Get error injection capabilities of the platform */ |
175 | int einj_get_available_error_type(u32 *type) |
176 | { |
177 | int rc; |
178 | |
179 | mutex_lock(&einj_mutex); |
180 | rc = __einj_get_available_error_type(type); |
181 | mutex_unlock(lock: &einj_mutex); |
182 | |
183 | return rc; |
184 | } |
185 | |
186 | static int einj_timedout(u64 *t) |
187 | { |
188 | if ((s64)*t < SLEEP_UNIT_MIN) { |
189 | pr_warn(FW_WARN "Firmware does not respond in time\n" ); |
190 | return 1; |
191 | } |
192 | *t -= SLEEP_UNIT_MIN; |
193 | usleep_range(SLEEP_UNIT_MIN, SLEEP_UNIT_MAX); |
194 | |
195 | return 0; |
196 | } |
197 | |
198 | static void get_oem_vendor_struct(u64 paddr, int offset, |
199 | struct vendor_error_type_extension *v) |
200 | { |
201 | unsigned long vendor_size; |
202 | u64 target_pa = paddr + offset + sizeof(struct vendor_error_type_extension); |
203 | |
204 | vendor_size = v->length - sizeof(struct vendor_error_type_extension); |
205 | |
206 | if (vendor_size) |
207 | vendor_errors.data = acpi_os_map_memory(where: target_pa, length: vendor_size); |
208 | |
209 | if (vendor_errors.data) |
210 | vendor_errors.size = vendor_size; |
211 | } |
212 | |
213 | static void check_vendor_extension(u64 paddr, |
214 | struct set_error_type_with_address *v5param) |
215 | { |
216 | int offset = v5param->vendor_extension; |
217 | struct vendor_error_type_extension *v; |
218 | u32 sbdf; |
219 | |
220 | if (!offset) |
221 | return; |
222 | v = acpi_os_map_iomem(phys: paddr + offset, size: sizeof(*v)); |
223 | if (!v) |
224 | return; |
225 | get_oem_vendor_struct(paddr, offset, v); |
226 | sbdf = v->pcie_sbdf; |
227 | sprintf(buf: vendor_dev, fmt: "%x:%x:%x.%x vendor_id=%x device_id=%x rev_id=%x\n" , |
228 | sbdf >> 24, (sbdf >> 16) & 0xff, |
229 | (sbdf >> 11) & 0x1f, (sbdf >> 8) & 0x7, |
230 | v->vendor_id, v->device_id, v->rev_id); |
231 | acpi_os_unmap_iomem(virt: v, size: sizeof(*v)); |
232 | } |
233 | |
234 | static void *einj_get_parameter_address(void) |
235 | { |
236 | int i; |
237 | u64 pa_v4 = 0, pa_v5 = 0; |
238 | struct acpi_whea_header *entry; |
239 | |
240 | entry = EINJ_TAB_ENTRY(einj_tab); |
241 | for (i = 0; i < einj_tab->entries; i++) { |
242 | if (entry->action == ACPI_EINJ_SET_ERROR_TYPE && |
243 | entry->instruction == ACPI_EINJ_WRITE_REGISTER && |
244 | entry->register_region.space_id == |
245 | ACPI_ADR_SPACE_SYSTEM_MEMORY) |
246 | pa_v4 = get_unaligned(&entry->register_region.address); |
247 | if (entry->action == ACPI_EINJ_SET_ERROR_TYPE_WITH_ADDRESS && |
248 | entry->instruction == ACPI_EINJ_WRITE_REGISTER && |
249 | entry->register_region.space_id == |
250 | ACPI_ADR_SPACE_SYSTEM_MEMORY) |
251 | pa_v5 = get_unaligned(&entry->register_region.address); |
252 | entry++; |
253 | } |
254 | if (pa_v5) { |
255 | struct set_error_type_with_address *v5param; |
256 | |
257 | v5param = acpi_os_map_iomem(phys: pa_v5, size: sizeof(*v5param)); |
258 | if (v5param) { |
259 | acpi5 = 1; |
260 | check_vendor_extension(paddr: pa_v5, v5param); |
261 | return v5param; |
262 | } |
263 | } |
264 | if (param_extension && pa_v4) { |
265 | struct einj_parameter *v4param; |
266 | |
267 | v4param = acpi_os_map_iomem(phys: pa_v4, size: sizeof(*v4param)); |
268 | if (!v4param) |
269 | return NULL; |
270 | if (v4param->reserved1 || v4param->reserved2) { |
271 | acpi_os_unmap_iomem(virt: v4param, size: sizeof(*v4param)); |
272 | return NULL; |
273 | } |
274 | return v4param; |
275 | } |
276 | |
277 | return NULL; |
278 | } |
279 | |
280 | /* do sanity check to trigger table */ |
281 | static int (struct acpi_einj_trigger *trigger_tab) |
282 | { |
283 | if (trigger_tab->header_size != sizeof(struct acpi_einj_trigger)) |
284 | return -EINVAL; |
285 | if (trigger_tab->table_size > PAGE_SIZE || |
286 | trigger_tab->table_size < trigger_tab->header_size) |
287 | return -EINVAL; |
288 | if (trigger_tab->entry_count != |
289 | (trigger_tab->table_size - trigger_tab->header_size) / |
290 | sizeof(struct acpi_einj_entry)) |
291 | return -EINVAL; |
292 | |
293 | return 0; |
294 | } |
295 | |
296 | static struct acpi_generic_address *einj_get_trigger_parameter_region( |
297 | struct acpi_einj_trigger *trigger_tab, u64 param1, u64 param2) |
298 | { |
299 | int i; |
300 | struct acpi_whea_header *entry; |
301 | |
302 | entry = (struct acpi_whea_header *) |
303 | ((char *)trigger_tab + sizeof(struct acpi_einj_trigger)); |
304 | for (i = 0; i < trigger_tab->entry_count; i++) { |
305 | if (entry->action == ACPI_EINJ_TRIGGER_ERROR && |
306 | entry->instruction <= ACPI_EINJ_WRITE_REGISTER_VALUE && |
307 | entry->register_region.space_id == |
308 | ACPI_ADR_SPACE_SYSTEM_MEMORY && |
309 | (entry->register_region.address & param2) == (param1 & param2)) |
310 | return &entry->register_region; |
311 | entry++; |
312 | } |
313 | |
314 | return NULL; |
315 | } |
316 | /* Execute instructions in trigger error action table */ |
317 | static int __einj_error_trigger(u64 trigger_paddr, u32 type, |
318 | u64 param1, u64 param2) |
319 | { |
320 | struct acpi_einj_trigger *trigger_tab = NULL; |
321 | struct apei_exec_context trigger_ctx; |
322 | struct apei_resources trigger_resources; |
323 | struct acpi_whea_header *trigger_entry; |
324 | struct resource *r; |
325 | u32 table_size; |
326 | int rc = -EIO; |
327 | struct acpi_generic_address *trigger_param_region = NULL; |
328 | |
329 | r = request_mem_region(trigger_paddr, sizeof(*trigger_tab), |
330 | "APEI EINJ Trigger Table" ); |
331 | if (!r) { |
332 | pr_err("Can not request [mem %#010llx-%#010llx] for Trigger table\n" , |
333 | (unsigned long long)trigger_paddr, |
334 | (unsigned long long)trigger_paddr + |
335 | sizeof(*trigger_tab) - 1); |
336 | goto out; |
337 | } |
338 | trigger_tab = ioremap_cache(offset: trigger_paddr, size: sizeof(*trigger_tab)); |
339 | if (!trigger_tab) { |
340 | pr_err("Failed to map trigger table!\n" ); |
341 | goto out_rel_header; |
342 | } |
343 | rc = einj_check_trigger_header(trigger_tab); |
344 | if (rc) { |
345 | pr_warn(FW_BUG "Invalid trigger error action table.\n" ); |
346 | goto out_rel_header; |
347 | } |
348 | |
349 | /* No action structures in the TRIGGER_ERROR table, nothing to do */ |
350 | if (!trigger_tab->entry_count) |
351 | goto out_rel_header; |
352 | |
353 | rc = -EIO; |
354 | table_size = trigger_tab->table_size; |
355 | r = request_mem_region(trigger_paddr + sizeof(*trigger_tab), |
356 | table_size - sizeof(*trigger_tab), |
357 | "APEI EINJ Trigger Table" ); |
358 | if (!r) { |
359 | pr_err("Can not request [mem %#010llx-%#010llx] for Trigger Table Entry\n" , |
360 | (unsigned long long)trigger_paddr + sizeof(*trigger_tab), |
361 | (unsigned long long)trigger_paddr + table_size - 1); |
362 | goto out_rel_header; |
363 | } |
364 | iounmap(addr: trigger_tab); |
365 | trigger_tab = ioremap_cache(offset: trigger_paddr, size: table_size); |
366 | if (!trigger_tab) { |
367 | pr_err("Failed to map trigger table!\n" ); |
368 | goto out_rel_entry; |
369 | } |
370 | trigger_entry = (struct acpi_whea_header *) |
371 | ((char *)trigger_tab + sizeof(struct acpi_einj_trigger)); |
372 | apei_resources_init(resources: &trigger_resources); |
373 | apei_exec_ctx_init(ctx: &trigger_ctx, ins_table: einj_ins_type, |
374 | ARRAY_SIZE(einj_ins_type), |
375 | action_table: trigger_entry, entries: trigger_tab->entry_count); |
376 | rc = apei_exec_collect_resources(ctx: &trigger_ctx, resources: &trigger_resources); |
377 | if (rc) |
378 | goto out_fini; |
379 | rc = apei_resources_sub(resources1: &trigger_resources, resources2: &einj_resources); |
380 | if (rc) |
381 | goto out_fini; |
382 | /* |
383 | * Some firmware will access target address specified in |
384 | * param1 to trigger the error when injecting memory error. |
385 | * This will cause resource conflict with regular memory. So |
386 | * remove it from trigger table resources. |
387 | */ |
388 | if ((param_extension || acpi5) && (type & MEM_ERROR_MASK) && param2) { |
389 | struct apei_resources addr_resources; |
390 | |
391 | apei_resources_init(resources: &addr_resources); |
392 | trigger_param_region = einj_get_trigger_parameter_region( |
393 | trigger_tab, param1, param2); |
394 | if (trigger_param_region) { |
395 | rc = apei_resources_add(resources: &addr_resources, |
396 | start: trigger_param_region->address, |
397 | size: trigger_param_region->bit_width/8, iomem: true); |
398 | if (rc) |
399 | goto out_fini; |
400 | rc = apei_resources_sub(resources1: &trigger_resources, |
401 | resources2: &addr_resources); |
402 | } |
403 | apei_resources_fini(resources: &addr_resources); |
404 | if (rc) |
405 | goto out_fini; |
406 | } |
407 | rc = apei_resources_request(resources: &trigger_resources, desc: "APEI EINJ Trigger" ); |
408 | if (rc) |
409 | goto out_fini; |
410 | rc = apei_exec_pre_map_gars(ctx: &trigger_ctx); |
411 | if (rc) |
412 | goto out_release; |
413 | |
414 | rc = apei_exec_run(ctx: &trigger_ctx, action: ACPI_EINJ_TRIGGER_ERROR); |
415 | |
416 | apei_exec_post_unmap_gars(ctx: &trigger_ctx); |
417 | out_release: |
418 | apei_resources_release(resources: &trigger_resources); |
419 | out_fini: |
420 | apei_resources_fini(resources: &trigger_resources); |
421 | out_rel_entry: |
422 | release_mem_region(trigger_paddr + sizeof(*trigger_tab), |
423 | table_size - sizeof(*trigger_tab)); |
424 | : |
425 | release_mem_region(trigger_paddr, sizeof(*trigger_tab)); |
426 | out: |
427 | if (trigger_tab) |
428 | iounmap(addr: trigger_tab); |
429 | |
430 | return rc; |
431 | } |
432 | |
433 | static int __einj_error_inject(u32 type, u32 flags, u64 param1, u64 param2, |
434 | u64 param3, u64 param4) |
435 | { |
436 | struct apei_exec_context ctx; |
437 | u64 val, trigger_paddr, timeout = FIRMWARE_TIMEOUT; |
438 | int rc; |
439 | |
440 | einj_exec_ctx_init(ctx: &ctx); |
441 | |
442 | rc = apei_exec_run_optional(ctx: &ctx, action: ACPI_EINJ_BEGIN_OPERATION); |
443 | if (rc) |
444 | return rc; |
445 | apei_exec_ctx_set_input(ctx: &ctx, input: type); |
446 | if (acpi5) { |
447 | struct set_error_type_with_address *v5param = einj_param; |
448 | |
449 | v5param->type = type; |
450 | if (type & ACPI5_VENDOR_BIT) { |
451 | switch (vendor_flags) { |
452 | case SETWA_FLAGS_APICID: |
453 | v5param->apicid = param1; |
454 | break; |
455 | case SETWA_FLAGS_MEM: |
456 | v5param->memory_address = param1; |
457 | v5param->memory_address_range = param2; |
458 | break; |
459 | case SETWA_FLAGS_PCIE_SBDF: |
460 | v5param->pcie_sbdf = param1; |
461 | break; |
462 | } |
463 | v5param->flags = vendor_flags; |
464 | } else if (flags) { |
465 | v5param->flags = flags; |
466 | v5param->memory_address = param1; |
467 | v5param->memory_address_range = param2; |
468 | v5param->apicid = param3; |
469 | v5param->pcie_sbdf = param4; |
470 | } else { |
471 | switch (type) { |
472 | case ACPI_EINJ_PROCESSOR_CORRECTABLE: |
473 | case ACPI_EINJ_PROCESSOR_UNCORRECTABLE: |
474 | case ACPI_EINJ_PROCESSOR_FATAL: |
475 | v5param->apicid = param1; |
476 | v5param->flags = SETWA_FLAGS_APICID; |
477 | break; |
478 | case ACPI_EINJ_MEMORY_CORRECTABLE: |
479 | case ACPI_EINJ_MEMORY_UNCORRECTABLE: |
480 | case ACPI_EINJ_MEMORY_FATAL: |
481 | v5param->memory_address = param1; |
482 | v5param->memory_address_range = param2; |
483 | v5param->flags = SETWA_FLAGS_MEM; |
484 | break; |
485 | case ACPI_EINJ_PCIX_CORRECTABLE: |
486 | case ACPI_EINJ_PCIX_UNCORRECTABLE: |
487 | case ACPI_EINJ_PCIX_FATAL: |
488 | v5param->pcie_sbdf = param1; |
489 | v5param->flags = SETWA_FLAGS_PCIE_SBDF; |
490 | break; |
491 | } |
492 | } |
493 | } else { |
494 | rc = apei_exec_run(ctx: &ctx, action: ACPI_EINJ_SET_ERROR_TYPE); |
495 | if (rc) |
496 | return rc; |
497 | if (einj_param) { |
498 | struct einj_parameter *v4param = einj_param; |
499 | |
500 | v4param->param1 = param1; |
501 | v4param->param2 = param2; |
502 | } |
503 | } |
504 | rc = apei_exec_run(ctx: &ctx, action: ACPI_EINJ_EXECUTE_OPERATION); |
505 | if (rc) |
506 | return rc; |
507 | for (;;) { |
508 | rc = apei_exec_run(ctx: &ctx, action: ACPI_EINJ_CHECK_BUSY_STATUS); |
509 | if (rc) |
510 | return rc; |
511 | val = apei_exec_ctx_get_output(ctx: &ctx); |
512 | if (!(val & EINJ_OP_BUSY)) |
513 | break; |
514 | if (einj_timedout(t: &timeout)) |
515 | return -EIO; |
516 | } |
517 | rc = apei_exec_run(ctx: &ctx, action: ACPI_EINJ_GET_COMMAND_STATUS); |
518 | if (rc) |
519 | return rc; |
520 | val = apei_exec_ctx_get_output(ctx: &ctx); |
521 | if (val == EINJ_STATUS_FAIL) |
522 | return -EBUSY; |
523 | else if (val == EINJ_STATUS_INVAL) |
524 | return -EINVAL; |
525 | |
526 | /* |
527 | * The error is injected into the platform successfully, then it needs |
528 | * to trigger the error. |
529 | */ |
530 | rc = apei_exec_run(ctx: &ctx, action: ACPI_EINJ_GET_TRIGGER_TABLE); |
531 | if (rc) |
532 | return rc; |
533 | trigger_paddr = apei_exec_ctx_get_output(ctx: &ctx); |
534 | if (notrigger == 0) { |
535 | rc = __einj_error_trigger(trigger_paddr, type, param1, param2); |
536 | if (rc) |
537 | return rc; |
538 | } |
539 | rc = apei_exec_run_optional(ctx: &ctx, action: ACPI_EINJ_END_OPERATION); |
540 | |
541 | return rc; |
542 | } |
543 | |
544 | /* Inject the specified hardware error */ |
545 | int einj_error_inject(u32 type, u32 flags, u64 param1, u64 param2, u64 param3, |
546 | u64 param4) |
547 | { |
548 | int rc; |
549 | u64 base_addr, size; |
550 | |
551 | /* If user manually set "flags", make sure it is legal */ |
552 | if (flags && (flags & |
553 | ~(SETWA_FLAGS_APICID|SETWA_FLAGS_MEM|SETWA_FLAGS_PCIE_SBDF))) |
554 | return -EINVAL; |
555 | |
556 | /* |
557 | * We need extra sanity checks for memory errors. |
558 | * Other types leap directly to injection. |
559 | */ |
560 | |
561 | /* ensure param1/param2 existed */ |
562 | if (!(param_extension || acpi5)) |
563 | goto inject; |
564 | |
565 | /* ensure injection is memory related */ |
566 | if (type & ACPI5_VENDOR_BIT) { |
567 | if (vendor_flags != SETWA_FLAGS_MEM) |
568 | goto inject; |
569 | } else if (!(type & MEM_ERROR_MASK) && !(flags & SETWA_FLAGS_MEM)) { |
570 | goto inject; |
571 | } |
572 | |
573 | /* |
574 | * Injections targeting a CXL 1.0/1.1 port have to be injected |
575 | * via the einj_cxl_rch_error_inject() path as that does the proper |
576 | * validation of the given RCRB base (MMIO) address. |
577 | */ |
578 | if (einj_is_cxl_error_type(type) && (flags & SETWA_FLAGS_MEM)) |
579 | return -EINVAL; |
580 | |
581 | /* |
582 | * Disallow crazy address masks that give BIOS leeway to pick |
583 | * injection address almost anywhere. Insist on page or |
584 | * better granularity and that target address is normal RAM or |
585 | * NVDIMM. |
586 | */ |
587 | base_addr = param1 & param2; |
588 | size = ~param2 + 1; |
589 | |
590 | if (((param2 & PAGE_MASK) != PAGE_MASK) || |
591 | ((region_intersects(offset: base_addr, size, IORESOURCE_SYSTEM_RAM, desc: IORES_DESC_NONE) |
592 | != REGION_INTERSECTS) && |
593 | (region_intersects(offset: base_addr, size, IORESOURCE_MEM, desc: IORES_DESC_PERSISTENT_MEMORY) |
594 | != REGION_INTERSECTS) && |
595 | (region_intersects(offset: base_addr, size, IORESOURCE_MEM, desc: IORES_DESC_SOFT_RESERVED) |
596 | != REGION_INTERSECTS) && |
597 | !arch_is_platform_page(paddr: base_addr))) |
598 | return -EINVAL; |
599 | |
600 | if (is_zero_pfn(pfn: base_addr >> PAGE_SHIFT)) |
601 | return -EADDRINUSE; |
602 | |
603 | inject: |
604 | mutex_lock(&einj_mutex); |
605 | rc = __einj_error_inject(type, flags, param1, param2, param3, param4); |
606 | mutex_unlock(lock: &einj_mutex); |
607 | |
608 | return rc; |
609 | } |
610 | |
611 | int einj_cxl_rch_error_inject(u32 type, u32 flags, u64 param1, u64 param2, |
612 | u64 param3, u64 param4) |
613 | { |
614 | int rc; |
615 | |
616 | if (!(einj_is_cxl_error_type(type) && (flags & SETWA_FLAGS_MEM))) |
617 | return -EINVAL; |
618 | |
619 | mutex_lock(&einj_mutex); |
620 | rc = __einj_error_inject(type, flags, param1, param2, param3, param4); |
621 | mutex_unlock(lock: &einj_mutex); |
622 | |
623 | return rc; |
624 | } |
625 | |
626 | static u32 error_type; |
627 | static u32 error_flags; |
628 | static u64 error_param1; |
629 | static u64 error_param2; |
630 | static u64 error_param3; |
631 | static u64 error_param4; |
632 | static struct dentry *einj_debug_dir; |
633 | static struct { u32 mask; const char *str; } const einj_error_type_string[] = { |
634 | { BIT(0), "Processor Correctable" }, |
635 | { BIT(1), "Processor Uncorrectable non-fatal" }, |
636 | { BIT(2), "Processor Uncorrectable fatal" }, |
637 | { BIT(3), "Memory Correctable" }, |
638 | { BIT(4), "Memory Uncorrectable non-fatal" }, |
639 | { BIT(5), "Memory Uncorrectable fatal" }, |
640 | { BIT(6), "PCI Express Correctable" }, |
641 | { BIT(7), "PCI Express Uncorrectable non-fatal" }, |
642 | { BIT(8), "PCI Express Uncorrectable fatal" }, |
643 | { BIT(9), "Platform Correctable" }, |
644 | { BIT(10), "Platform Uncorrectable non-fatal" }, |
645 | { BIT(11), "Platform Uncorrectable fatal" }, |
646 | { BIT(31), "Vendor Defined Error Types" }, |
647 | }; |
648 | |
649 | static int available_error_type_show(struct seq_file *m, void *v) |
650 | { |
651 | int rc; |
652 | u32 error_type = 0; |
653 | |
654 | rc = einj_get_available_error_type(type: &error_type); |
655 | if (rc) |
656 | return rc; |
657 | for (int pos = 0; pos < ARRAY_SIZE(einj_error_type_string); pos++) |
658 | if (error_type & einj_error_type_string[pos].mask) |
659 | seq_printf(m, fmt: "0x%08x\t%s\n" , einj_error_type_string[pos].mask, |
660 | einj_error_type_string[pos].str); |
661 | |
662 | return 0; |
663 | } |
664 | |
665 | DEFINE_SHOW_ATTRIBUTE(available_error_type); |
666 | |
667 | static int error_type_get(void *data, u64 *val) |
668 | { |
669 | *val = error_type; |
670 | |
671 | return 0; |
672 | } |
673 | |
674 | bool einj_is_cxl_error_type(u64 type) |
675 | { |
676 | return (type & CXL_ERROR_MASK) && (!(type & ACPI5_VENDOR_BIT)); |
677 | } |
678 | |
679 | int einj_validate_error_type(u64 type) |
680 | { |
681 | u32 tval, vendor, available_error_type = 0; |
682 | int rc; |
683 | |
684 | /* Only low 32 bits for error type are valid */ |
685 | if (type & GENMASK_ULL(63, 32)) |
686 | return -EINVAL; |
687 | |
688 | /* |
689 | * Vendor defined types have 0x80000000 bit set, and |
690 | * are not enumerated by ACPI_EINJ_GET_ERROR_TYPE |
691 | */ |
692 | vendor = type & ACPI5_VENDOR_BIT; |
693 | tval = type & GENMASK(30, 0); |
694 | |
695 | /* Only one error type can be specified */ |
696 | if (tval & (tval - 1)) |
697 | return -EINVAL; |
698 | if (!vendor) { |
699 | rc = einj_get_available_error_type(type: &available_error_type); |
700 | if (rc) |
701 | return rc; |
702 | if (!(type & available_error_type)) |
703 | return -EINVAL; |
704 | } |
705 | |
706 | return 0; |
707 | } |
708 | |
709 | static int error_type_set(void *data, u64 val) |
710 | { |
711 | int rc; |
712 | |
713 | rc = einj_validate_error_type(type: val); |
714 | if (rc) |
715 | return rc; |
716 | |
717 | error_type = val; |
718 | |
719 | return 0; |
720 | } |
721 | |
722 | DEFINE_DEBUGFS_ATTRIBUTE(error_type_fops, error_type_get, error_type_set, |
723 | "0x%llx\n" ); |
724 | |
725 | static int error_inject_set(void *data, u64 val) |
726 | { |
727 | if (!error_type) |
728 | return -EINVAL; |
729 | |
730 | return einj_error_inject(type: error_type, flags: error_flags, param1: error_param1, param2: error_param2, |
731 | param3: error_param3, param4: error_param4); |
732 | } |
733 | |
734 | DEFINE_DEBUGFS_ATTRIBUTE(error_inject_fops, NULL, error_inject_set, "%llu\n" ); |
735 | |
736 | static int einj_check_table(struct acpi_table_einj *einj_tab) |
737 | { |
738 | if ((einj_tab->header_length != |
739 | (sizeof(struct acpi_table_einj) - sizeof(einj_tab->header))) |
740 | && (einj_tab->header_length != sizeof(struct acpi_table_einj))) |
741 | return -EINVAL; |
742 | if (einj_tab->header.length < sizeof(struct acpi_table_einj)) |
743 | return -EINVAL; |
744 | if (einj_tab->entries != |
745 | (einj_tab->header.length - sizeof(struct acpi_table_einj)) / |
746 | sizeof(struct acpi_einj_entry)) |
747 | return -EINVAL; |
748 | |
749 | return 0; |
750 | } |
751 | |
752 | static int __init einj_probe(struct platform_device *pdev) |
753 | { |
754 | int rc; |
755 | acpi_status status; |
756 | struct apei_exec_context ctx; |
757 | |
758 | if (acpi_disabled) { |
759 | pr_debug("ACPI disabled.\n" ); |
760 | return -ENODEV; |
761 | } |
762 | |
763 | status = acpi_get_table(ACPI_SIG_EINJ, instance: 0, |
764 | out_table: (struct acpi_table_header **)&einj_tab); |
765 | if (status == AE_NOT_FOUND) { |
766 | pr_debug("EINJ table not found.\n" ); |
767 | return -ENODEV; |
768 | } else if (ACPI_FAILURE(status)) { |
769 | pr_err("Failed to get EINJ table: %s\n" , |
770 | acpi_format_exception(status)); |
771 | return -EINVAL; |
772 | } |
773 | |
774 | rc = einj_check_table(einj_tab); |
775 | if (rc) { |
776 | pr_warn(FW_BUG "Invalid EINJ table.\n" ); |
777 | goto err_put_table; |
778 | } |
779 | |
780 | rc = -ENOMEM; |
781 | einj_debug_dir = debugfs_create_dir(name: "einj" , parent: apei_get_debugfs_dir()); |
782 | |
783 | debugfs_create_file(name: "available_error_type" , S_IRUSR, parent: einj_debug_dir, |
784 | NULL, fops: &available_error_type_fops); |
785 | debugfs_create_file_unsafe(name: "error_type" , mode: 0600, parent: einj_debug_dir, |
786 | NULL, fops: &error_type_fops); |
787 | debugfs_create_file_unsafe(name: "error_inject" , mode: 0200, parent: einj_debug_dir, |
788 | NULL, fops: &error_inject_fops); |
789 | |
790 | apei_resources_init(resources: &einj_resources); |
791 | einj_exec_ctx_init(ctx: &ctx); |
792 | rc = apei_exec_collect_resources(ctx: &ctx, resources: &einj_resources); |
793 | if (rc) { |
794 | pr_err("Error collecting EINJ resources.\n" ); |
795 | goto err_fini; |
796 | } |
797 | |
798 | rc = apei_resources_request(resources: &einj_resources, desc: "APEI EINJ" ); |
799 | if (rc) { |
800 | pr_err("Error requesting memory/port resources.\n" ); |
801 | goto err_fini; |
802 | } |
803 | |
804 | rc = apei_exec_pre_map_gars(ctx: &ctx); |
805 | if (rc) { |
806 | pr_err("Error pre-mapping GARs.\n" ); |
807 | goto err_release; |
808 | } |
809 | |
810 | einj_param = einj_get_parameter_address(); |
811 | if ((param_extension || acpi5) && einj_param) { |
812 | debugfs_create_x32(name: "flags" , S_IRUSR | S_IWUSR, parent: einj_debug_dir, |
813 | value: &error_flags); |
814 | debugfs_create_x64(name: "param1" , S_IRUSR | S_IWUSR, parent: einj_debug_dir, |
815 | value: &error_param1); |
816 | debugfs_create_x64(name: "param2" , S_IRUSR | S_IWUSR, parent: einj_debug_dir, |
817 | value: &error_param2); |
818 | debugfs_create_x64(name: "param3" , S_IRUSR | S_IWUSR, parent: einj_debug_dir, |
819 | value: &error_param3); |
820 | debugfs_create_x64(name: "param4" , S_IRUSR | S_IWUSR, parent: einj_debug_dir, |
821 | value: &error_param4); |
822 | debugfs_create_x32(name: "notrigger" , S_IRUSR | S_IWUSR, |
823 | parent: einj_debug_dir, value: ¬rigger); |
824 | } |
825 | |
826 | if (vendor_dev[0]) { |
827 | vendor_blob.data = vendor_dev; |
828 | vendor_blob.size = strlen(vendor_dev); |
829 | debugfs_create_blob(name: "vendor" , S_IRUSR, parent: einj_debug_dir, |
830 | blob: &vendor_blob); |
831 | debugfs_create_x32(name: "vendor_flags" , S_IRUSR | S_IWUSR, |
832 | parent: einj_debug_dir, value: &vendor_flags); |
833 | } |
834 | |
835 | if (vendor_errors.size) |
836 | debugfs_create_blob(name: "oem_error" , mode: 0600, parent: einj_debug_dir, |
837 | blob: &vendor_errors); |
838 | |
839 | pr_info("Error INJection is initialized.\n" ); |
840 | |
841 | return 0; |
842 | |
843 | err_release: |
844 | apei_resources_release(resources: &einj_resources); |
845 | err_fini: |
846 | apei_resources_fini(resources: &einj_resources); |
847 | debugfs_remove_recursive(dentry: einj_debug_dir); |
848 | err_put_table: |
849 | acpi_put_table(table: (struct acpi_table_header *)einj_tab); |
850 | |
851 | return rc; |
852 | } |
853 | |
854 | static void einj_remove(struct platform_device *pdev) |
855 | { |
856 | struct apei_exec_context ctx; |
857 | |
858 | if (einj_param) { |
859 | acpi_size size = (acpi5) ? |
860 | sizeof(struct set_error_type_with_address) : |
861 | sizeof(struct einj_parameter); |
862 | |
863 | acpi_os_unmap_iomem(virt: einj_param, size); |
864 | if (vendor_errors.size) |
865 | acpi_os_unmap_memory(logical_address: vendor_errors.data, size: vendor_errors.size); |
866 | } |
867 | einj_exec_ctx_init(ctx: &ctx); |
868 | apei_exec_post_unmap_gars(ctx: &ctx); |
869 | apei_resources_release(resources: &einj_resources); |
870 | apei_resources_fini(resources: &einj_resources); |
871 | debugfs_remove_recursive(dentry: einj_debug_dir); |
872 | acpi_put_table(table: (struct acpi_table_header *)einj_tab); |
873 | } |
874 | |
875 | static struct platform_device *einj_dev; |
876 | static struct platform_driver einj_driver = { |
877 | .remove_new = einj_remove, |
878 | .driver = { |
879 | .name = "acpi-einj" , |
880 | }, |
881 | }; |
882 | |
883 | static int __init einj_init(void) |
884 | { |
885 | struct platform_device_info einj_dev_info = { |
886 | .name = "acpi-einj" , |
887 | .id = -1, |
888 | }; |
889 | int rc; |
890 | |
891 | einj_dev = platform_device_register_full(pdevinfo: &einj_dev_info); |
892 | if (IS_ERR(ptr: einj_dev)) |
893 | return PTR_ERR(ptr: einj_dev); |
894 | |
895 | rc = platform_driver_probe(&einj_driver, einj_probe); |
896 | einj_initialized = rc == 0; |
897 | |
898 | return 0; |
899 | } |
900 | |
901 | static void __exit einj_exit(void) |
902 | { |
903 | if (einj_initialized) |
904 | platform_driver_unregister(&einj_driver); |
905 | |
906 | platform_device_del(pdev: einj_dev); |
907 | } |
908 | |
909 | module_init(einj_init); |
910 | module_exit(einj_exit); |
911 | |
912 | MODULE_AUTHOR("Huang Ying" ); |
913 | MODULE_DESCRIPTION("APEI Error INJection support" ); |
914 | MODULE_LICENSE("GPL" ); |
915 | |