diff options
Diffstat (limited to 'drivers/acpi/apei/apei-base.c')
-rw-r--r-- | drivers/acpi/apei/apei-base.c | 150 |
1 files changed, 139 insertions, 11 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) |