diff options
Diffstat (limited to 'drivers/acpi/apei')
-rw-r--r-- | drivers/acpi/apei/apei-base.c | 150 | ||||
-rw-r--r-- | drivers/acpi/apei/apei-internal.h | 6 | ||||
-rw-r--r-- | drivers/acpi/apei/einj.c | 66 | ||||
-rw-r--r-- | drivers/acpi/apei/erst.c | 5 | ||||
-rw-r--r-- | drivers/acpi/apei/ghes.c | 102 | ||||
-rw-r--r-- | drivers/acpi/apei/hest.c | 5 |
6 files changed, 288 insertions, 46 deletions
diff --git a/drivers/acpi/apei/apei-base.c b/drivers/acpi/apei/apei-base.c index 61540360d5ce..e45350cb6ac8 100644 --- a/drivers/acpi/apei/apei-base.c +++ b/drivers/acpi/apei/apei-base.c | |||
@@ -34,13 +34,13 @@ | |||
34 | #include <linux/module.h> | 34 | #include <linux/module.h> |
35 | #include <linux/init.h> | 35 | #include <linux/init.h> |
36 | #include <linux/acpi.h> | 36 | #include <linux/acpi.h> |
37 | #include <linux/acpi_io.h> | ||
37 | #include <linux/slab.h> | 38 | #include <linux/slab.h> |
38 | #include <linux/io.h> | 39 | #include <linux/io.h> |
39 | #include <linux/kref.h> | 40 | #include <linux/kref.h> |
40 | #include <linux/rculist.h> | 41 | #include <linux/rculist.h> |
41 | #include <linux/interrupt.h> | 42 | #include <linux/interrupt.h> |
42 | #include <linux/debugfs.h> | 43 | #include <linux/debugfs.h> |
43 | #include <acpi/atomicio.h> | ||
44 | 44 | ||
45 | #include "apei-internal.h" | 45 | #include "apei-internal.h" |
46 | 46 | ||
@@ -70,7 +70,7 @@ int __apei_exec_read_register(struct acpi_whea_header *entry, u64 *val) | |||
70 | { | 70 | { |
71 | int rc; | 71 | int rc; |
72 | 72 | ||
73 | rc = acpi_atomic_read(val, &entry->register_region); | 73 | rc = apei_read(val, &entry->register_region); |
74 | if (rc) | 74 | if (rc) |
75 | return rc; | 75 | return rc; |
76 | *val >>= entry->register_region.bit_offset; | 76 | *val >>= entry->register_region.bit_offset; |
@@ -116,13 +116,13 @@ int __apei_exec_write_register(struct acpi_whea_header *entry, u64 val) | |||
116 | val <<= entry->register_region.bit_offset; | 116 | val <<= entry->register_region.bit_offset; |
117 | if (entry->flags & APEI_EXEC_PRESERVE_REGISTER) { | 117 | if (entry->flags & APEI_EXEC_PRESERVE_REGISTER) { |
118 | u64 valr = 0; | 118 | u64 valr = 0; |
119 | rc = acpi_atomic_read(&valr, &entry->register_region); | 119 | rc = apei_read(&valr, &entry->register_region); |
120 | if (rc) | 120 | if (rc) |
121 | return rc; | 121 | return rc; |
122 | valr &= ~(entry->mask << entry->register_region.bit_offset); | 122 | valr &= ~(entry->mask << entry->register_region.bit_offset); |
123 | val |= valr; | 123 | val |= valr; |
124 | } | 124 | } |
125 | rc = acpi_atomic_write(val, &entry->register_region); | 125 | rc = apei_write(val, &entry->register_region); |
126 | 126 | ||
127 | return rc; | 127 | return rc; |
128 | } | 128 | } |
@@ -243,7 +243,7 @@ static int pre_map_gar_callback(struct apei_exec_context *ctx, | |||
243 | u8 ins = entry->instruction; | 243 | u8 ins = entry->instruction; |
244 | 244 | ||
245 | if (ctx->ins_table[ins].flags & APEI_EXEC_INS_ACCESS_REGISTER) | 245 | if (ctx->ins_table[ins].flags & APEI_EXEC_INS_ACCESS_REGISTER) |
246 | return acpi_pre_map_gar(&entry->register_region); | 246 | return acpi_os_map_generic_address(&entry->register_region); |
247 | 247 | ||
248 | return 0; | 248 | return 0; |
249 | } | 249 | } |
@@ -276,7 +276,7 @@ static int post_unmap_gar_callback(struct apei_exec_context *ctx, | |||
276 | u8 ins = entry->instruction; | 276 | u8 ins = entry->instruction; |
277 | 277 | ||
278 | if (ctx->ins_table[ins].flags & APEI_EXEC_INS_ACCESS_REGISTER) | 278 | if (ctx->ins_table[ins].flags & APEI_EXEC_INS_ACCESS_REGISTER) |
279 | acpi_post_unmap_gar(&entry->register_region); | 279 | acpi_os_unmap_generic_address(&entry->register_region); |
280 | 280 | ||
281 | return 0; | 281 | return 0; |
282 | } | 282 | } |
@@ -421,6 +421,17 @@ static int apei_resources_merge(struct apei_resources *resources1, | |||
421 | return 0; | 421 | return 0; |
422 | } | 422 | } |
423 | 423 | ||
424 | int apei_resources_add(struct apei_resources *resources, | ||
425 | unsigned long start, unsigned long size, | ||
426 | bool iomem) | ||
427 | { | ||
428 | if (iomem) | ||
429 | return apei_res_add(&resources->iomem, start, size); | ||
430 | else | ||
431 | return apei_res_add(&resources->ioport, start, size); | ||
432 | } | ||
433 | EXPORT_SYMBOL_GPL(apei_resources_add); | ||
434 | |||
424 | /* | 435 | /* |
425 | * EINJ has two groups of GARs (EINJ table entry and trigger table | 436 | * EINJ has two groups of GARs (EINJ table entry and trigger table |
426 | * entry), so common resources are subtracted from the trigger table | 437 | * entry), so common resources are subtracted from the trigger table |
@@ -438,8 +449,19 @@ int apei_resources_sub(struct apei_resources *resources1, | |||
438 | } | 449 | } |
439 | EXPORT_SYMBOL_GPL(apei_resources_sub); | 450 | EXPORT_SYMBOL_GPL(apei_resources_sub); |
440 | 451 | ||
452 | static int apei_get_nvs_callback(__u64 start, __u64 size, void *data) | ||
453 | { | ||
454 | struct apei_resources *resources = data; | ||
455 | return apei_res_add(&resources->iomem, start, size); | ||
456 | } | ||
457 | |||
458 | static int apei_get_nvs_resources(struct apei_resources *resources) | ||
459 | { | ||
460 | return acpi_nvs_for_each_region(apei_get_nvs_callback, resources); | ||
461 | } | ||
462 | |||
441 | /* | 463 | /* |
442 | * IO memory/port rersource management mechanism is used to check | 464 | * IO memory/port resource management mechanism is used to check |
443 | * whether memory/port area used by GARs conflicts with normal memory | 465 | * whether memory/port area used by GARs conflicts with normal memory |
444 | * or IO memory/port of devices. | 466 | * or IO memory/port of devices. |
445 | */ | 467 | */ |
@@ -448,21 +470,35 @@ int apei_resources_request(struct apei_resources *resources, | |||
448 | { | 470 | { |
449 | struct apei_res *res, *res_bak = NULL; | 471 | struct apei_res *res, *res_bak = NULL; |
450 | struct resource *r; | 472 | struct resource *r; |
473 | struct apei_resources nvs_resources; | ||
451 | int rc; | 474 | int rc; |
452 | 475 | ||
453 | rc = apei_resources_sub(resources, &apei_resources_all); | 476 | rc = apei_resources_sub(resources, &apei_resources_all); |
454 | if (rc) | 477 | if (rc) |
455 | return rc; | 478 | return rc; |
456 | 479 | ||
480 | /* | ||
481 | * Some firmware uses ACPI NVS region, that has been marked as | ||
482 | * busy, so exclude it from APEI resources to avoid false | ||
483 | * conflict. | ||
484 | */ | ||
485 | apei_resources_init(&nvs_resources); | ||
486 | rc = apei_get_nvs_resources(&nvs_resources); | ||
487 | if (rc) | ||
488 | goto res_fini; | ||
489 | rc = apei_resources_sub(resources, &nvs_resources); | ||
490 | if (rc) | ||
491 | goto res_fini; | ||
492 | |||
457 | rc = -EINVAL; | 493 | rc = -EINVAL; |
458 | list_for_each_entry(res, &resources->iomem, list) { | 494 | list_for_each_entry(res, &resources->iomem, list) { |
459 | r = request_mem_region(res->start, res->end - res->start, | 495 | r = request_mem_region(res->start, res->end - res->start, |
460 | desc); | 496 | desc); |
461 | if (!r) { | 497 | if (!r) { |
462 | pr_err(APEI_PFX | 498 | pr_err(APEI_PFX |
463 | "Can not request iomem region <%016llx-%016llx> for GARs.\n", | 499 | "Can not request [mem %#010llx-%#010llx] for %s registers\n", |
464 | (unsigned long long)res->start, | 500 | (unsigned long long)res->start, |
465 | (unsigned long long)res->end); | 501 | (unsigned long long)res->end - 1, desc); |
466 | res_bak = res; | 502 | res_bak = res; |
467 | goto err_unmap_iomem; | 503 | goto err_unmap_iomem; |
468 | } | 504 | } |
@@ -472,9 +508,9 @@ int apei_resources_request(struct apei_resources *resources, | |||
472 | r = request_region(res->start, res->end - res->start, desc); | 508 | r = request_region(res->start, res->end - res->start, desc); |
473 | if (!r) { | 509 | if (!r) { |
474 | pr_err(APEI_PFX | 510 | pr_err(APEI_PFX |
475 | "Can not request ioport region <%016llx-%016llx> for GARs.\n", | 511 | "Can not request [io %#06llx-%#06llx] for %s registers\n", |
476 | (unsigned long long)res->start, | 512 | (unsigned long long)res->start, |
477 | (unsigned long long)res->end); | 513 | (unsigned long long)res->end - 1, desc); |
478 | res_bak = res; | 514 | res_bak = res; |
479 | goto err_unmap_ioport; | 515 | goto err_unmap_ioport; |
480 | } | 516 | } |
@@ -500,6 +536,8 @@ err_unmap_iomem: | |||
500 | break; | 536 | break; |
501 | release_mem_region(res->start, res->end - res->start); | 537 | release_mem_region(res->start, res->end - res->start); |
502 | } | 538 | } |
539 | res_fini: | ||
540 | apei_resources_fini(&nvs_resources); | ||
503 | return rc; | 541 | return rc; |
504 | } | 542 | } |
505 | EXPORT_SYMBOL_GPL(apei_resources_request); | 543 | EXPORT_SYMBOL_GPL(apei_resources_request); |
@@ -553,6 +591,96 @@ static int apei_check_gar(struct acpi_generic_address *reg, u64 *paddr) | |||
553 | return 0; | 591 | return 0; |
554 | } | 592 | } |
555 | 593 | ||
594 | /* read GAR in interrupt (including NMI) or process context */ | ||
595 | int apei_read(u64 *val, struct acpi_generic_address *reg) | ||
596 | { | ||
597 | int rc; | ||
598 | u64 address; | ||
599 | u32 tmp, width = reg->bit_width; | ||
600 | acpi_status status; | ||
601 | |||
602 | rc = apei_check_gar(reg, &address); | ||
603 | if (rc) | ||
604 | return rc; | ||
605 | |||
606 | if (width == 64) | ||
607 | width = 32; /* Break into two 32-bit transfers */ | ||
608 | |||
609 | *val = 0; | ||
610 | switch(reg->space_id) { | ||
611 | case ACPI_ADR_SPACE_SYSTEM_MEMORY: | ||
612 | status = acpi_os_read_memory((acpi_physical_address) | ||
613 | address, &tmp, width); | ||
614 | if (ACPI_FAILURE(status)) | ||
615 | return -EIO; | ||
616 | *val = tmp; | ||
617 | |||
618 | if (reg->bit_width == 64) { | ||
619 | /* Read the top 32 bits */ | ||
620 | status = acpi_os_read_memory((acpi_physical_address) | ||
621 | (address + 4), &tmp, 32); | ||
622 | if (ACPI_FAILURE(status)) | ||
623 | return -EIO; | ||
624 | *val |= ((u64)tmp << 32); | ||
625 | } | ||
626 | break; | ||
627 | case ACPI_ADR_SPACE_SYSTEM_IO: | ||
628 | status = acpi_os_read_port(address, (u32 *)val, reg->bit_width); | ||
629 | if (ACPI_FAILURE(status)) | ||
630 | return -EIO; | ||
631 | break; | ||
632 | default: | ||
633 | return -EINVAL; | ||
634 | } | ||
635 | |||
636 | return 0; | ||
637 | } | ||
638 | EXPORT_SYMBOL_GPL(apei_read); | ||
639 | |||
640 | /* write GAR in interrupt (including NMI) or process context */ | ||
641 | int apei_write(u64 val, struct acpi_generic_address *reg) | ||
642 | { | ||
643 | int rc; | ||
644 | u64 address; | ||
645 | u32 width = reg->bit_width; | ||
646 | acpi_status status; | ||
647 | |||
648 | rc = apei_check_gar(reg, &address); | ||
649 | if (rc) | ||
650 | return rc; | ||
651 | |||
652 | if (width == 64) | ||
653 | width = 32; /* Break into two 32-bit transfers */ | ||
654 | |||
655 | switch (reg->space_id) { | ||
656 | case ACPI_ADR_SPACE_SYSTEM_MEMORY: | ||
657 | status = acpi_os_write_memory((acpi_physical_address) | ||
658 | address, ACPI_LODWORD(val), | ||
659 | width); | ||
660 | if (ACPI_FAILURE(status)) | ||
661 | return -EIO; | ||
662 | |||
663 | if (reg->bit_width == 64) { | ||
664 | status = acpi_os_write_memory((acpi_physical_address) | ||
665 | (address + 4), | ||
666 | ACPI_HIDWORD(val), 32); | ||
667 | if (ACPI_FAILURE(status)) | ||
668 | return -EIO; | ||
669 | } | ||
670 | break; | ||
671 | case ACPI_ADR_SPACE_SYSTEM_IO: | ||
672 | status = acpi_os_write_port(address, val, reg->bit_width); | ||
673 | if (ACPI_FAILURE(status)) | ||
674 | return -EIO; | ||
675 | break; | ||
676 | default: | ||
677 | return -EINVAL; | ||
678 | } | ||
679 | |||
680 | return 0; | ||
681 | } | ||
682 | EXPORT_SYMBOL_GPL(apei_write); | ||
683 | |||
556 | static int collect_res_callback(struct apei_exec_context *ctx, | 684 | static int collect_res_callback(struct apei_exec_context *ctx, |
557 | struct acpi_whea_header *entry, | 685 | struct acpi_whea_header *entry, |
558 | void *data) | 686 | void *data) |
diff --git a/drivers/acpi/apei/apei-internal.h b/drivers/acpi/apei/apei-internal.h index f57050e7a5e7..cca240a33038 100644 --- a/drivers/acpi/apei/apei-internal.h +++ b/drivers/acpi/apei/apei-internal.h | |||
@@ -68,6 +68,9 @@ static inline int apei_exec_run_optional(struct apei_exec_context *ctx, u8 actio | |||
68 | /* IP has been set in instruction function */ | 68 | /* IP has been set in instruction function */ |
69 | #define APEI_EXEC_SET_IP 1 | 69 | #define APEI_EXEC_SET_IP 1 |
70 | 70 | ||
71 | int apei_read(u64 *val, struct acpi_generic_address *reg); | ||
72 | int apei_write(u64 val, struct acpi_generic_address *reg); | ||
73 | |||
71 | int __apei_exec_read_register(struct acpi_whea_header *entry, u64 *val); | 74 | int __apei_exec_read_register(struct acpi_whea_header *entry, u64 *val); |
72 | int __apei_exec_write_register(struct acpi_whea_header *entry, u64 val); | 75 | int __apei_exec_write_register(struct acpi_whea_header *entry, u64 val); |
73 | int apei_exec_read_register(struct apei_exec_context *ctx, | 76 | int apei_exec_read_register(struct apei_exec_context *ctx, |
@@ -95,6 +98,9 @@ static inline void apei_resources_init(struct apei_resources *resources) | |||
95 | } | 98 | } |
96 | 99 | ||
97 | void apei_resources_fini(struct apei_resources *resources); | 100 | void apei_resources_fini(struct apei_resources *resources); |
101 | int apei_resources_add(struct apei_resources *resources, | ||
102 | unsigned long start, unsigned long size, | ||
103 | bool iomem); | ||
98 | int apei_resources_sub(struct apei_resources *resources1, | 104 | int apei_resources_sub(struct apei_resources *resources1, |
99 | struct apei_resources *resources2); | 105 | struct apei_resources *resources2); |
100 | int apei_resources_request(struct apei_resources *resources, | 106 | int apei_resources_request(struct apei_resources *resources, |
diff --git a/drivers/acpi/apei/einj.c b/drivers/acpi/apei/einj.c index 31546fd21029..5b898d4dda99 100644 --- a/drivers/acpi/apei/einj.c +++ b/drivers/acpi/apei/einj.c | |||
@@ -286,8 +286,29 @@ static int einj_check_trigger_header(struct acpi_einj_trigger *trigger_tab) | |||
286 | return 0; | 286 | return 0; |
287 | } | 287 | } |
288 | 288 | ||
289 | static struct acpi_generic_address *einj_get_trigger_parameter_region( | ||
290 | struct acpi_einj_trigger *trigger_tab, u64 param1, u64 param2) | ||
291 | { | ||
292 | int i; | ||
293 | struct acpi_whea_header *entry; | ||
294 | |||
295 | entry = (struct acpi_whea_header *) | ||
296 | ((char *)trigger_tab + sizeof(struct acpi_einj_trigger)); | ||
297 | for (i = 0; i < trigger_tab->entry_count; i++) { | ||
298 | if (entry->action == ACPI_EINJ_TRIGGER_ERROR && | ||
299 | entry->instruction == ACPI_EINJ_WRITE_REGISTER_VALUE && | ||
300 | entry->register_region.space_id == | ||
301 | ACPI_ADR_SPACE_SYSTEM_MEMORY && | ||
302 | (entry->register_region.address & param2) == (param1 & param2)) | ||
303 | return &entry->register_region; | ||
304 | entry++; | ||
305 | } | ||
306 | |||
307 | return NULL; | ||
308 | } | ||
289 | /* Execute instructions in trigger error action table */ | 309 | /* Execute instructions in trigger error action table */ |
290 | static int __einj_error_trigger(u64 trigger_paddr) | 310 | static int __einj_error_trigger(u64 trigger_paddr, u32 type, |
311 | u64 param1, u64 param2) | ||
291 | { | 312 | { |
292 | struct acpi_einj_trigger *trigger_tab = NULL; | 313 | struct acpi_einj_trigger *trigger_tab = NULL; |
293 | struct apei_exec_context trigger_ctx; | 314 | struct apei_exec_context trigger_ctx; |
@@ -296,14 +317,16 @@ static int __einj_error_trigger(u64 trigger_paddr) | |||
296 | struct resource *r; | 317 | struct resource *r; |
297 | u32 table_size; | 318 | u32 table_size; |
298 | int rc = -EIO; | 319 | int rc = -EIO; |
320 | struct acpi_generic_address *trigger_param_region = NULL; | ||
299 | 321 | ||
300 | r = request_mem_region(trigger_paddr, sizeof(*trigger_tab), | 322 | r = request_mem_region(trigger_paddr, sizeof(*trigger_tab), |
301 | "APEI EINJ Trigger Table"); | 323 | "APEI EINJ Trigger Table"); |
302 | if (!r) { | 324 | if (!r) { |
303 | pr_err(EINJ_PFX | 325 | pr_err(EINJ_PFX |
304 | "Can not request iomem region <%016llx-%016llx> for Trigger table.\n", | 326 | "Can not request [mem %#010llx-%#010llx] for Trigger table\n", |
305 | (unsigned long long)trigger_paddr, | 327 | (unsigned long long)trigger_paddr, |
306 | (unsigned long long)trigger_paddr+sizeof(*trigger_tab)); | 328 | (unsigned long long)trigger_paddr + |
329 | sizeof(*trigger_tab) - 1); | ||
307 | goto out; | 330 | goto out; |
308 | } | 331 | } |
309 | trigger_tab = ioremap_cache(trigger_paddr, sizeof(*trigger_tab)); | 332 | trigger_tab = ioremap_cache(trigger_paddr, sizeof(*trigger_tab)); |
@@ -324,9 +347,9 @@ static int __einj_error_trigger(u64 trigger_paddr) | |||
324 | "APEI EINJ Trigger Table"); | 347 | "APEI EINJ Trigger Table"); |
325 | if (!r) { | 348 | if (!r) { |
326 | pr_err(EINJ_PFX | 349 | pr_err(EINJ_PFX |
327 | "Can not request iomem region <%016llx-%016llx> for Trigger Table Entry.\n", | 350 | "Can not request [mem %#010llx-%#010llx] for Trigger Table Entry\n", |
328 | (unsigned long long)trigger_paddr+sizeof(*trigger_tab), | 351 | (unsigned long long)trigger_paddr + sizeof(*trigger_tab), |
329 | (unsigned long long)trigger_paddr + table_size); | 352 | (unsigned long long)trigger_paddr + table_size - 1); |
330 | goto out_rel_header; | 353 | goto out_rel_header; |
331 | } | 354 | } |
332 | iounmap(trigger_tab); | 355 | iounmap(trigger_tab); |
@@ -347,6 +370,30 @@ static int __einj_error_trigger(u64 trigger_paddr) | |||
347 | rc = apei_resources_sub(&trigger_resources, &einj_resources); | 370 | rc = apei_resources_sub(&trigger_resources, &einj_resources); |
348 | if (rc) | 371 | if (rc) |
349 | goto out_fini; | 372 | goto out_fini; |
373 | /* | ||
374 | * Some firmware will access target address specified in | ||
375 | * param1 to trigger the error when injecting memory error. | ||
376 | * This will cause resource conflict with regular memory. So | ||
377 | * remove it from trigger table resources. | ||
378 | */ | ||
379 | if (param_extension && (type & 0x0038) && param2) { | ||
380 | struct apei_resources addr_resources; | ||
381 | apei_resources_init(&addr_resources); | ||
382 | trigger_param_region = einj_get_trigger_parameter_region( | ||
383 | trigger_tab, param1, param2); | ||
384 | if (trigger_param_region) { | ||
385 | rc = apei_resources_add(&addr_resources, | ||
386 | trigger_param_region->address, | ||
387 | trigger_param_region->bit_width/8, true); | ||
388 | if (rc) | ||
389 | goto out_fini; | ||
390 | rc = apei_resources_sub(&trigger_resources, | ||
391 | &addr_resources); | ||
392 | } | ||
393 | apei_resources_fini(&addr_resources); | ||
394 | if (rc) | ||
395 | goto out_fini; | ||
396 | } | ||
350 | rc = apei_resources_request(&trigger_resources, "APEI EINJ Trigger"); | 397 | rc = apei_resources_request(&trigger_resources, "APEI EINJ Trigger"); |
351 | if (rc) | 398 | if (rc) |
352 | goto out_fini; | 399 | goto out_fini; |
@@ -460,7 +507,7 @@ static int __einj_error_inject(u32 type, u64 param1, u64 param2) | |||
460 | if (rc) | 507 | if (rc) |
461 | return rc; | 508 | return rc; |
462 | trigger_paddr = apei_exec_ctx_get_output(&ctx); | 509 | trigger_paddr = apei_exec_ctx_get_output(&ctx); |
463 | rc = __einj_error_trigger(trigger_paddr); | 510 | rc = __einj_error_trigger(trigger_paddr, type, param1, param2); |
464 | if (rc) | 511 | if (rc) |
465 | return rc; | 512 | return rc; |
466 | rc = apei_exec_run_optional(&ctx, ACPI_EINJ_END_OPERATION); | 513 | rc = apei_exec_run_optional(&ctx, ACPI_EINJ_END_OPERATION); |
@@ -610,10 +657,9 @@ static int __init einj_init(void) | |||
610 | 657 | ||
611 | status = acpi_get_table(ACPI_SIG_EINJ, 0, | 658 | status = acpi_get_table(ACPI_SIG_EINJ, 0, |
612 | (struct acpi_table_header **)&einj_tab); | 659 | (struct acpi_table_header **)&einj_tab); |
613 | if (status == AE_NOT_FOUND) { | 660 | if (status == AE_NOT_FOUND) |
614 | pr_info(EINJ_PFX "Table is not found!\n"); | ||
615 | return -ENODEV; | 661 | return -ENODEV; |
616 | } else if (ACPI_FAILURE(status)) { | 662 | else if (ACPI_FAILURE(status)) { |
617 | const char *msg = acpi_format_exception(status); | 663 | const char *msg = acpi_format_exception(status); |
618 | pr_err(EINJ_PFX "Failed to get table, %s\n", msg); | 664 | pr_err(EINJ_PFX "Failed to get table, %s\n", msg); |
619 | return -EINVAL; | 665 | return -EINVAL; |
diff --git a/drivers/acpi/apei/erst.c b/drivers/acpi/apei/erst.c index 631b9477b99c..8e8d786c5d23 100644 --- a/drivers/acpi/apei/erst.c +++ b/drivers/acpi/apei/erst.c | |||
@@ -1125,10 +1125,9 @@ static int __init erst_init(void) | |||
1125 | 1125 | ||
1126 | status = acpi_get_table(ACPI_SIG_ERST, 0, | 1126 | status = acpi_get_table(ACPI_SIG_ERST, 0, |
1127 | (struct acpi_table_header **)&erst_tab); | 1127 | (struct acpi_table_header **)&erst_tab); |
1128 | if (status == AE_NOT_FOUND) { | 1128 | if (status == AE_NOT_FOUND) |
1129 | pr_info(ERST_PFX "Table is not found!\n"); | ||
1130 | goto err; | 1129 | goto err; |
1131 | } else if (ACPI_FAILURE(status)) { | 1130 | else if (ACPI_FAILURE(status)) { |
1132 | const char *msg = acpi_format_exception(status); | 1131 | const char *msg = acpi_format_exception(status); |
1133 | pr_err(ERST_PFX "Failed to get table, %s\n", msg); | 1132 | pr_err(ERST_PFX "Failed to get table, %s\n", msg); |
1134 | rc = -EINVAL; | 1133 | rc = -EINVAL; |
diff --git a/drivers/acpi/apei/ghes.c b/drivers/acpi/apei/ghes.c index b8e08cb67a18..b3207e16670e 100644 --- a/drivers/acpi/apei/ghes.c +++ b/drivers/acpi/apei/ghes.c | |||
@@ -33,6 +33,7 @@ | |||
33 | #include <linux/module.h> | 33 | #include <linux/module.h> |
34 | #include <linux/init.h> | 34 | #include <linux/init.h> |
35 | #include <linux/acpi.h> | 35 | #include <linux/acpi.h> |
36 | #include <linux/acpi_io.h> | ||
36 | #include <linux/io.h> | 37 | #include <linux/io.h> |
37 | #include <linux/interrupt.h> | 38 | #include <linux/interrupt.h> |
38 | #include <linux/timer.h> | 39 | #include <linux/timer.h> |
@@ -45,8 +46,9 @@ | |||
45 | #include <linux/irq_work.h> | 46 | #include <linux/irq_work.h> |
46 | #include <linux/llist.h> | 47 | #include <linux/llist.h> |
47 | #include <linux/genalloc.h> | 48 | #include <linux/genalloc.h> |
49 | #include <linux/pci.h> | ||
50 | #include <linux/aer.h> | ||
48 | #include <acpi/apei.h> | 51 | #include <acpi/apei.h> |
49 | #include <acpi/atomicio.h> | ||
50 | #include <acpi/hed.h> | 52 | #include <acpi/hed.h> |
51 | #include <asm/mce.h> | 53 | #include <asm/mce.h> |
52 | #include <asm/tlbflush.h> | 54 | #include <asm/tlbflush.h> |
@@ -299,7 +301,7 @@ static struct ghes *ghes_new(struct acpi_hest_generic *generic) | |||
299 | if (!ghes) | 301 | if (!ghes) |
300 | return ERR_PTR(-ENOMEM); | 302 | return ERR_PTR(-ENOMEM); |
301 | ghes->generic = generic; | 303 | ghes->generic = generic; |
302 | rc = acpi_pre_map_gar(&generic->error_status_address); | 304 | rc = acpi_os_map_generic_address(&generic->error_status_address); |
303 | if (rc) | 305 | if (rc) |
304 | goto err_free; | 306 | goto err_free; |
305 | error_block_length = generic->error_block_length; | 307 | error_block_length = generic->error_block_length; |
@@ -319,7 +321,7 @@ static struct ghes *ghes_new(struct acpi_hest_generic *generic) | |||
319 | return ghes; | 321 | return ghes; |
320 | 322 | ||
321 | err_unmap: | 323 | err_unmap: |
322 | acpi_post_unmap_gar(&generic->error_status_address); | 324 | acpi_os_unmap_generic_address(&generic->error_status_address); |
323 | err_free: | 325 | err_free: |
324 | kfree(ghes); | 326 | kfree(ghes); |
325 | return ERR_PTR(rc); | 327 | return ERR_PTR(rc); |
@@ -328,7 +330,7 @@ err_free: | |||
328 | static void ghes_fini(struct ghes *ghes) | 330 | static void ghes_fini(struct ghes *ghes) |
329 | { | 331 | { |
330 | kfree(ghes->estatus); | 332 | kfree(ghes->estatus); |
331 | acpi_post_unmap_gar(&ghes->generic->error_status_address); | 333 | acpi_os_unmap_generic_address(&ghes->generic->error_status_address); |
332 | } | 334 | } |
333 | 335 | ||
334 | enum { | 336 | enum { |
@@ -399,7 +401,7 @@ static int ghes_read_estatus(struct ghes *ghes, int silent) | |||
399 | u32 len; | 401 | u32 len; |
400 | int rc; | 402 | int rc; |
401 | 403 | ||
402 | rc = acpi_atomic_read(&buf_paddr, &g->error_status_address); | 404 | rc = apei_read(&buf_paddr, &g->error_status_address); |
403 | if (rc) { | 405 | if (rc) { |
404 | if (!silent && printk_ratelimit()) | 406 | if (!silent && printk_ratelimit()) |
405 | pr_warning(FW_WARN GHES_PFX | 407 | pr_warning(FW_WARN GHES_PFX |
@@ -476,6 +478,27 @@ static void ghes_do_proc(const struct acpi_hest_generic_status *estatus) | |||
476 | } | 478 | } |
477 | #endif | 479 | #endif |
478 | } | 480 | } |
481 | #ifdef CONFIG_ACPI_APEI_PCIEAER | ||
482 | else if (!uuid_le_cmp(*(uuid_le *)gdata->section_type, | ||
483 | CPER_SEC_PCIE)) { | ||
484 | struct cper_sec_pcie *pcie_err; | ||
485 | pcie_err = (struct cper_sec_pcie *)(gdata+1); | ||
486 | if (sev == GHES_SEV_RECOVERABLE && | ||
487 | sec_sev == GHES_SEV_RECOVERABLE && | ||
488 | pcie_err->validation_bits & CPER_PCIE_VALID_DEVICE_ID && | ||
489 | pcie_err->validation_bits & CPER_PCIE_VALID_AER_INFO) { | ||
490 | unsigned int devfn; | ||
491 | int aer_severity; | ||
492 | devfn = PCI_DEVFN(pcie_err->device_id.device, | ||
493 | pcie_err->device_id.function); | ||
494 | aer_severity = cper_severity_to_aer(sev); | ||
495 | aer_recover_queue(pcie_err->device_id.segment, | ||
496 | pcie_err->device_id.bus, | ||
497 | devfn, aer_severity); | ||
498 | } | ||
499 | |||
500 | } | ||
501 | #endif | ||
479 | } | 502 | } |
480 | } | 503 | } |
481 | 504 | ||
@@ -483,16 +506,22 @@ static void __ghes_print_estatus(const char *pfx, | |||
483 | const struct acpi_hest_generic *generic, | 506 | const struct acpi_hest_generic *generic, |
484 | const struct acpi_hest_generic_status *estatus) | 507 | const struct acpi_hest_generic_status *estatus) |
485 | { | 508 | { |
509 | static atomic_t seqno; | ||
510 | unsigned int curr_seqno; | ||
511 | char pfx_seq[64]; | ||
512 | |||
486 | if (pfx == NULL) { | 513 | if (pfx == NULL) { |
487 | if (ghes_severity(estatus->error_severity) <= | 514 | if (ghes_severity(estatus->error_severity) <= |
488 | GHES_SEV_CORRECTED) | 515 | GHES_SEV_CORRECTED) |
489 | pfx = KERN_WARNING HW_ERR; | 516 | pfx = KERN_WARNING; |
490 | else | 517 | else |
491 | pfx = KERN_ERR HW_ERR; | 518 | pfx = KERN_ERR; |
492 | } | 519 | } |
520 | curr_seqno = atomic_inc_return(&seqno); | ||
521 | snprintf(pfx_seq, sizeof(pfx_seq), "%s{%u}" HW_ERR, pfx, curr_seqno); | ||
493 | printk("%s""Hardware error from APEI Generic Hardware Error Source: %d\n", | 522 | printk("%s""Hardware error from APEI Generic Hardware Error Source: %d\n", |
494 | pfx, generic->header.source_id); | 523 | pfx_seq, generic->header.source_id); |
495 | apei_estatus_print(pfx, estatus); | 524 | apei_estatus_print(pfx_seq, estatus); |
496 | } | 525 | } |
497 | 526 | ||
498 | static int ghes_print_estatus(const char *pfx, | 527 | static int ghes_print_estatus(const char *pfx, |
@@ -711,26 +740,34 @@ static int ghes_notify_sci(struct notifier_block *this, | |||
711 | return ret; | 740 | return ret; |
712 | } | 741 | } |
713 | 742 | ||
743 | static struct llist_node *llist_nodes_reverse(struct llist_node *llnode) | ||
744 | { | ||
745 | struct llist_node *next, *tail = NULL; | ||
746 | |||
747 | while (llnode) { | ||
748 | next = llnode->next; | ||
749 | llnode->next = tail; | ||
750 | tail = llnode; | ||
751 | llnode = next; | ||
752 | } | ||
753 | |||
754 | return tail; | ||
755 | } | ||
756 | |||
714 | static void ghes_proc_in_irq(struct irq_work *irq_work) | 757 | static void ghes_proc_in_irq(struct irq_work *irq_work) |
715 | { | 758 | { |
716 | struct llist_node *llnode, *next, *tail = NULL; | 759 | struct llist_node *llnode, *next; |
717 | struct ghes_estatus_node *estatus_node; | 760 | struct ghes_estatus_node *estatus_node; |
718 | struct acpi_hest_generic *generic; | 761 | struct acpi_hest_generic *generic; |
719 | struct acpi_hest_generic_status *estatus; | 762 | struct acpi_hest_generic_status *estatus; |
720 | u32 len, node_len; | 763 | u32 len, node_len; |
721 | 764 | ||
765 | llnode = llist_del_all(&ghes_estatus_llist); | ||
722 | /* | 766 | /* |
723 | * Because the time order of estatus in list is reversed, | 767 | * Because the time order of estatus in list is reversed, |
724 | * revert it back to proper order. | 768 | * revert it back to proper order. |
725 | */ | 769 | */ |
726 | llnode = llist_del_all(&ghes_estatus_llist); | 770 | llnode = llist_nodes_reverse(llnode); |
727 | while (llnode) { | ||
728 | next = llnode->next; | ||
729 | llnode->next = tail; | ||
730 | tail = llnode; | ||
731 | llnode = next; | ||
732 | } | ||
733 | llnode = tail; | ||
734 | while (llnode) { | 771 | while (llnode) { |
735 | next = llnode->next; | 772 | next = llnode->next; |
736 | estatus_node = llist_entry(llnode, struct ghes_estatus_node, | 773 | estatus_node = llist_entry(llnode, struct ghes_estatus_node, |
@@ -750,6 +787,32 @@ static void ghes_proc_in_irq(struct irq_work *irq_work) | |||
750 | } | 787 | } |
751 | } | 788 | } |
752 | 789 | ||
790 | static void ghes_print_queued_estatus(void) | ||
791 | { | ||
792 | struct llist_node *llnode; | ||
793 | struct ghes_estatus_node *estatus_node; | ||
794 | struct acpi_hest_generic *generic; | ||
795 | struct acpi_hest_generic_status *estatus; | ||
796 | u32 len, node_len; | ||
797 | |||
798 | llnode = llist_del_all(&ghes_estatus_llist); | ||
799 | /* | ||
800 | * Because the time order of estatus in list is reversed, | ||
801 | * revert it back to proper order. | ||
802 | */ | ||
803 | llnode = llist_nodes_reverse(llnode); | ||
804 | while (llnode) { | ||
805 | estatus_node = llist_entry(llnode, struct ghes_estatus_node, | ||
806 | llnode); | ||
807 | estatus = GHES_ESTATUS_FROM_NODE(estatus_node); | ||
808 | len = apei_estatus_len(estatus); | ||
809 | node_len = GHES_ESTATUS_NODE_LEN(len); | ||
810 | generic = estatus_node->generic; | ||
811 | ghes_print_estatus(NULL, generic, estatus); | ||
812 | llnode = llnode->next; | ||
813 | } | ||
814 | } | ||
815 | |||
753 | static int ghes_notify_nmi(unsigned int cmd, struct pt_regs *regs) | 816 | static int ghes_notify_nmi(unsigned int cmd, struct pt_regs *regs) |
754 | { | 817 | { |
755 | struct ghes *ghes, *ghes_global = NULL; | 818 | struct ghes *ghes, *ghes_global = NULL; |
@@ -775,7 +838,8 @@ static int ghes_notify_nmi(unsigned int cmd, struct pt_regs *regs) | |||
775 | 838 | ||
776 | if (sev_global >= GHES_SEV_PANIC) { | 839 | if (sev_global >= GHES_SEV_PANIC) { |
777 | oops_begin(); | 840 | oops_begin(); |
778 | __ghes_print_estatus(KERN_EMERG HW_ERR, ghes_global->generic, | 841 | ghes_print_queued_estatus(); |
842 | __ghes_print_estatus(KERN_EMERG, ghes_global->generic, | ||
779 | ghes_global->estatus); | 843 | ghes_global->estatus); |
780 | /* reboot to log the error! */ | 844 | /* reboot to log the error! */ |
781 | if (panic_timeout == 0) | 845 | if (panic_timeout == 0) |
diff --git a/drivers/acpi/apei/hest.c b/drivers/acpi/apei/hest.c index 05fee06f4d6e..f709269d4932 100644 --- a/drivers/acpi/apei/hest.c +++ b/drivers/acpi/apei/hest.c | |||
@@ -221,10 +221,9 @@ void __init acpi_hest_init(void) | |||
221 | 221 | ||
222 | status = acpi_get_table(ACPI_SIG_HEST, 0, | 222 | status = acpi_get_table(ACPI_SIG_HEST, 0, |
223 | (struct acpi_table_header **)&hest_tab); | 223 | (struct acpi_table_header **)&hest_tab); |
224 | if (status == AE_NOT_FOUND) { | 224 | if (status == AE_NOT_FOUND) |
225 | pr_info(HEST_PFX "Table not found.\n"); | ||
226 | goto err; | 225 | goto err; |
227 | } else if (ACPI_FAILURE(status)) { | 226 | else if (ACPI_FAILURE(status)) { |
228 | const char *msg = acpi_format_exception(status); | 227 | const char *msg = acpi_format_exception(status); |
229 | pr_err(HEST_PFX "Failed to get table, %s\n", msg); | 228 | pr_err(HEST_PFX "Failed to get table, %s\n", msg); |
230 | rc = -EINVAL; | 229 | rc = -EINVAL; |