aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Documentation/acpi/apei/einj.txt55
-rw-r--r--drivers/acpi/apei/einj.c224
-rw-r--r--include/acpi/actbl1.h3
3 files changed, 234 insertions, 48 deletions
diff --git a/Documentation/acpi/apei/einj.txt b/Documentation/acpi/apei/einj.txt
index 5cc699ba5453..e7cc36397217 100644
--- a/Documentation/acpi/apei/einj.txt
+++ b/Documentation/acpi/apei/einj.txt
@@ -47,20 +47,53 @@ directory apei/einj. The following files are provided.
47 47
48- param1 48- param1
49 This file is used to set the first error parameter value. Effect of 49 This file is used to set the first error parameter value. Effect of
50 parameter depends on error_type specified. For memory error, this is 50 parameter depends on error_type specified.
51 physical memory address. Only available if param_extension module
52 parameter is specified.
53 51
54- param2 52- param2
55 This file is used to set the second error parameter value. Effect of 53 This file is used to set the second error parameter value. Effect of
56 parameter depends on error_type specified. For memory error, this is 54 parameter depends on error_type specified.
57 physical memory address mask. Only available if param_extension 55
58 module parameter is specified. 56BIOS versions based in the ACPI 4.0 specification have limited options
57to control where the errors are injected. Your BIOS may support an
58extension (enabled with the param_extension=1 module parameter, or
59boot command line einj.param_extension=1). This allows the address
60and mask for memory injections to be specified by the param1 and
61param2 files in apei/einj.
62
63BIOS versions using the ACPI 5.0 specification have more control over
64the target of the injection. For processor related errors (type 0x1,
650x2 and 0x4) the APICID of the target should be provided using the
66param1 file in apei/einj. For memory errors (type 0x8, 0x10 and 0x20)
67the address is set using param1 with a mask in param2 (0x0 is equivalent
68to all ones). For PCI express errors (type 0x40, 0x80 and 0x100) the
69segment, bus, device and function are specified using param1:
70
71 31 24 23 16 15 11 10 8 7 0
72 +-------------------------------------------------+
73 | segment | bus | device | function | reserved |
74 +-------------------------------------------------+
75
76An ACPI 5.0 BIOS may also allow vendor specific errors to be injected.
77In this case a file named vendor will contain identifying information
78from the BIOS that hopefully will allow an application wishing to use
79the vendor specific extension to tell that they are running on a BIOS
80that supports it. All vendor extensions have the 0x80000000 bit set in
81error_type. A file vendor_flags controls the interpretation of param1
82and param2 (1 = PROCESSOR, 2 = MEMORY, 4 = PCI). See your BIOS vendor
83documentation for details (and expect changes to this API if vendors
84creativity in using this feature expands beyond our expectations).
85
86Example:
87# cd /sys/kernel/debug/apei/einj
88# cat available_error_type # See which errors can be injected
890x00000002 Processor Uncorrectable non-fatal
900x00000008 Memory Correctable
910x00000010 Memory Uncorrectable non-fatal
92# echo 0x12345000 > param1 # Set memory address for injection
93# echo 0xfffffffffffff000 > param2 # Mask - anywhere in this page
94# echo 0x8 > error_type # Choose correctable memory error
95# echo 1 > error_inject # Inject now
59 96
60Injecting parameter support is a BIOS version specific extension, that
61is, it only works on some BIOS version. If you want to use it, please
62make sure your BIOS version has the proper support and specify
63"param_extension=y" in module parameter.
64 97
65For more information about EINJ, please refer to ACPI specification 98For more information about EINJ, please refer to ACPI specification
66version 4.0, section 17.5. 99version 4.0, section 17.5 and ACPI 5.0, section 18.6.
diff --git a/drivers/acpi/apei/einj.c b/drivers/acpi/apei/einj.c
index 589b96c38704..31546fd21029 100644
--- a/drivers/acpi/apei/einj.c
+++ b/drivers/acpi/apei/einj.c
@@ -43,6 +43,42 @@
43#define FIRMWARE_TIMEOUT (1 * NSEC_PER_MSEC) 43#define FIRMWARE_TIMEOUT (1 * NSEC_PER_MSEC)
44 44
45/* 45/*
46 * ACPI version 5 provides a SET_ERROR_TYPE_WITH_ADDRESS action.
47 */
48static int acpi5;
49
50struct set_error_type_with_address {
51 u32 type;
52 u32 vendor_extension;
53 u32 flags;
54 u32 apicid;
55 u64 memory_address;
56 u64 memory_address_range;
57 u32 pcie_sbdf;
58};
59enum {
60 SETWA_FLAGS_APICID = 1,
61 SETWA_FLAGS_MEM = 2,
62 SETWA_FLAGS_PCIE_SBDF = 4,
63};
64
65/*
66 * Vendor extensions for platform specific operations
67 */
68struct vendor_error_type_extension {
69 u32 length;
70 u32 pcie_sbdf;
71 u16 vendor_id;
72 u16 device_id;
73 u8 rev_id;
74 u8 reserved[3];
75};
76
77static u32 vendor_flags;
78static struct debugfs_blob_wrapper vendor_blob;
79static char vendor_dev[64];
80
81/*
46 * Some BIOSes allow parameters to the SET_ERROR_TYPE entries in the 82 * Some BIOSes allow parameters to the SET_ERROR_TYPE entries in the
47 * EINJ table through an unpublished extension. Use with caution as 83 * EINJ table through an unpublished extension. Use with caution as
48 * most will ignore the parameter and make their own choice of address 84 * most will ignore the parameter and make their own choice of address
@@ -103,7 +139,14 @@ static struct apei_exec_ins_type einj_ins_type[] = {
103 */ 139 */
104static DEFINE_MUTEX(einj_mutex); 140static DEFINE_MUTEX(einj_mutex);
105 141
106static struct einj_parameter *einj_param; 142static void *einj_param;
143
144#ifndef readq
145static inline __u64 readq(volatile void __iomem *addr)
146{
147 return ((__u64)readl(addr+4) << 32) + readl(addr);
148}
149#endif
107 150
108#ifndef writeq 151#ifndef writeq
109static inline void writeq(__u64 val, volatile void __iomem *addr) 152static inline void writeq(__u64 val, volatile void __iomem *addr)
@@ -158,10 +201,31 @@ static int einj_timedout(u64 *t)
158 return 0; 201 return 0;
159} 202}
160 203
161static u64 einj_get_parameter_address(void) 204static void check_vendor_extension(u64 paddr,
205 struct set_error_type_with_address *v5param)
206{
207 int offset = readl(&v5param->vendor_extension);
208 struct vendor_error_type_extension *v;
209 u32 sbdf;
210
211 if (!offset)
212 return;
213 v = ioremap(paddr + offset, sizeof(*v));
214 if (!v)
215 return;
216 sbdf = readl(&v->pcie_sbdf);
217 sprintf(vendor_dev, "%x:%x:%x.%x vendor_id=%x device_id=%x rev_id=%x\n",
218 sbdf >> 24, (sbdf >> 16) & 0xff,
219 (sbdf >> 11) & 0x1f, (sbdf >> 8) & 0x7,
220 readw(&v->vendor_id), readw(&v->device_id),
221 readb(&v->rev_id));
222 iounmap(v);
223}
224
225static void *einj_get_parameter_address(void)
162{ 226{
163 int i; 227 int i;
164 u64 paddr = 0; 228 u64 paddrv4 = 0, paddrv5 = 0;
165 struct acpi_whea_header *entry; 229 struct acpi_whea_header *entry;
166 230
167 entry = EINJ_TAB_ENTRY(einj_tab); 231 entry = EINJ_TAB_ENTRY(einj_tab);
@@ -170,12 +234,40 @@ static u64 einj_get_parameter_address(void)
170 entry->instruction == ACPI_EINJ_WRITE_REGISTER && 234 entry->instruction == ACPI_EINJ_WRITE_REGISTER &&
171 entry->register_region.space_id == 235 entry->register_region.space_id ==
172 ACPI_ADR_SPACE_SYSTEM_MEMORY) 236 ACPI_ADR_SPACE_SYSTEM_MEMORY)
173 memcpy(&paddr, &entry->register_region.address, 237 memcpy(&paddrv4, &entry->register_region.address,
174 sizeof(paddr)); 238 sizeof(paddrv4));
239 if (entry->action == ACPI_EINJ_SET_ERROR_TYPE_WITH_ADDRESS &&
240 entry->instruction == ACPI_EINJ_WRITE_REGISTER &&
241 entry->register_region.space_id ==
242 ACPI_ADR_SPACE_SYSTEM_MEMORY)
243 memcpy(&paddrv5, &entry->register_region.address,
244 sizeof(paddrv5));
175 entry++; 245 entry++;
176 } 246 }
247 if (paddrv5) {
248 struct set_error_type_with_address *v5param;
249
250 v5param = ioremap(paddrv5, sizeof(*v5param));
251 if (v5param) {
252 acpi5 = 1;
253 check_vendor_extension(paddrv5, v5param);
254 return v5param;
255 }
256 }
257 if (paddrv4) {
258 struct einj_parameter *v4param;
259
260 v4param = ioremap(paddrv4, sizeof(*v4param));
261 if (!v4param)
262 return 0;
263 if (readq(&v4param->reserved1) || readq(&v4param->reserved2)) {
264 iounmap(v4param);
265 return 0;
266 }
267 return v4param;
268 }
177 269
178 return paddr; 270 return 0;
179} 271}
180 272
181/* do sanity check to trigger table */ 273/* do sanity check to trigger table */
@@ -293,12 +385,56 @@ static int __einj_error_inject(u32 type, u64 param1, u64 param2)
293 if (rc) 385 if (rc)
294 return rc; 386 return rc;
295 apei_exec_ctx_set_input(&ctx, type); 387 apei_exec_ctx_set_input(&ctx, type);
296 rc = apei_exec_run(&ctx, ACPI_EINJ_SET_ERROR_TYPE); 388 if (acpi5) {
297 if (rc) 389 struct set_error_type_with_address *v5param = einj_param;
298 return rc; 390
299 if (einj_param) { 391 writel(type, &v5param->type);
300 writeq(param1, &einj_param->param1); 392 if (type & 0x80000000) {
301 writeq(param2, &einj_param->param2); 393 switch (vendor_flags) {
394 case SETWA_FLAGS_APICID:
395 writel(param1, &v5param->apicid);
396 break;
397 case SETWA_FLAGS_MEM:
398 writeq(param1, &v5param->memory_address);
399 writeq(param2, &v5param->memory_address_range);
400 break;
401 case SETWA_FLAGS_PCIE_SBDF:
402 writel(param1, &v5param->pcie_sbdf);
403 break;
404 }
405 writel(vendor_flags, &v5param->flags);
406 } else {
407 switch (type) {
408 case ACPI_EINJ_PROCESSOR_CORRECTABLE:
409 case ACPI_EINJ_PROCESSOR_UNCORRECTABLE:
410 case ACPI_EINJ_PROCESSOR_FATAL:
411 writel(param1, &v5param->apicid);
412 writel(SETWA_FLAGS_APICID, &v5param->flags);
413 break;
414 case ACPI_EINJ_MEMORY_CORRECTABLE:
415 case ACPI_EINJ_MEMORY_UNCORRECTABLE:
416 case ACPI_EINJ_MEMORY_FATAL:
417 writeq(param1, &v5param->memory_address);
418 writeq(param2, &v5param->memory_address_range);
419 writel(SETWA_FLAGS_MEM, &v5param->flags);
420 break;
421 case ACPI_EINJ_PCIX_CORRECTABLE:
422 case ACPI_EINJ_PCIX_UNCORRECTABLE:
423 case ACPI_EINJ_PCIX_FATAL:
424 writel(param1, &v5param->pcie_sbdf);
425 writel(SETWA_FLAGS_PCIE_SBDF, &v5param->flags);
426 break;
427 }
428 }
429 } else {
430 rc = apei_exec_run(&ctx, ACPI_EINJ_SET_ERROR_TYPE);
431 if (rc)
432 return rc;
433 if (einj_param) {
434 struct einj_parameter *v4param = einj_param;
435 writeq(param1, &v4param->param1);
436 writeq(param2, &v4param->param2);
437 }
302 } 438 }
303 rc = apei_exec_run(&ctx, ACPI_EINJ_EXECUTE_OPERATION); 439 rc = apei_exec_run(&ctx, ACPI_EINJ_EXECUTE_OPERATION);
304 if (rc) 440 if (rc)
@@ -408,15 +544,25 @@ static int error_type_set(void *data, u64 val)
408{ 544{
409 int rc; 545 int rc;
410 u32 available_error_type = 0; 546 u32 available_error_type = 0;
547 u32 tval, vendor;
548
549 /*
550 * Vendor defined types have 0x80000000 bit set, and
551 * are not enumerated by ACPI_EINJ_GET_ERROR_TYPE
552 */
553 vendor = val & 0x80000000;
554 tval = val & 0x7fffffff;
411 555
412 /* Only one error type can be specified */ 556 /* Only one error type can be specified */
413 if (val & (val - 1)) 557 if (tval & (tval - 1))
414 return -EINVAL;
415 rc = einj_get_available_error_type(&available_error_type);
416 if (rc)
417 return rc;
418 if (!(val & available_error_type))
419 return -EINVAL; 558 return -EINVAL;
559 if (!vendor) {
560 rc = einj_get_available_error_type(&available_error_type);
561 if (rc)
562 return rc;
563 if (!(val & available_error_type))
564 return -EINVAL;
565 }
420 error_type = val; 566 error_type = val;
421 567
422 return 0; 568 return 0;
@@ -455,7 +601,6 @@ static int einj_check_table(struct acpi_table_einj *einj_tab)
455static int __init einj_init(void) 601static int __init einj_init(void)
456{ 602{
457 int rc; 603 int rc;
458 u64 param_paddr;
459 acpi_status status; 604 acpi_status status;
460 struct dentry *fentry; 605 struct dentry *fentry;
461 struct apei_exec_context ctx; 606 struct apei_exec_context ctx;
@@ -509,23 +654,30 @@ static int __init einj_init(void)
509 rc = apei_exec_pre_map_gars(&ctx); 654 rc = apei_exec_pre_map_gars(&ctx);
510 if (rc) 655 if (rc)
511 goto err_release; 656 goto err_release;
512 if (param_extension) { 657
513 param_paddr = einj_get_parameter_address(); 658 einj_param = einj_get_parameter_address();
514 if (param_paddr) { 659 if ((param_extension || acpi5) && einj_param) {
515 einj_param = ioremap(param_paddr, sizeof(*einj_param)); 660 fentry = debugfs_create_x64("param1", S_IRUSR | S_IWUSR,
516 rc = -ENOMEM; 661 einj_debug_dir, &error_param1);
517 if (!einj_param) 662 if (!fentry)
518 goto err_unmap; 663 goto err_unmap;
519 fentry = debugfs_create_x64("param1", S_IRUSR | S_IWUSR, 664 fentry = debugfs_create_x64("param2", S_IRUSR | S_IWUSR,
520 einj_debug_dir, &error_param1); 665 einj_debug_dir, &error_param2);
521 if (!fentry) 666 if (!fentry)
522 goto err_unmap; 667 goto err_unmap;
523 fentry = debugfs_create_x64("param2", S_IRUSR | S_IWUSR, 668 }
524 einj_debug_dir, &error_param2); 669
525 if (!fentry) 670 if (vendor_dev[0]) {
526 goto err_unmap; 671 vendor_blob.data = vendor_dev;
527 } else 672 vendor_blob.size = strlen(vendor_dev);
528 pr_warn(EINJ_PFX "Parameter extension is not supported.\n"); 673 fentry = debugfs_create_blob("vendor", S_IRUSR,
674 einj_debug_dir, &vendor_blob);
675 if (!fentry)
676 goto err_unmap;
677 fentry = debugfs_create_x32("vendor_flags", S_IRUSR | S_IWUSR,
678 einj_debug_dir, &vendor_flags);
679 if (!fentry)
680 goto err_unmap;
529 } 681 }
530 682
531 pr_info(EINJ_PFX "Error INJection is initialized.\n"); 683 pr_info(EINJ_PFX "Error INJection is initialized.\n");
diff --git a/include/acpi/actbl1.h b/include/acpi/actbl1.h
index 7504bc99b29b..f25d7efee630 100644
--- a/include/acpi/actbl1.h
+++ b/include/acpi/actbl1.h
@@ -228,7 +228,8 @@ enum acpi_einj_actions {
228 ACPI_EINJ_EXECUTE_OPERATION = 5, 228 ACPI_EINJ_EXECUTE_OPERATION = 5,
229 ACPI_EINJ_CHECK_BUSY_STATUS = 6, 229 ACPI_EINJ_CHECK_BUSY_STATUS = 6,
230 ACPI_EINJ_GET_COMMAND_STATUS = 7, 230 ACPI_EINJ_GET_COMMAND_STATUS = 7,
231 ACPI_EINJ_ACTION_RESERVED = 8, /* 8 and greater are reserved */ 231 ACPI_EINJ_SET_ERROR_TYPE_WITH_ADDRESS = 8,
232 ACPI_EINJ_ACTION_RESERVED = 9, /* 9 and greater are reserved */
232 ACPI_EINJ_TRIGGER_ERROR = 0xFF /* Except for this value */ 233 ACPI_EINJ_TRIGGER_ERROR = 0xFF /* Except for this value */
233}; 234};
234 235