diff options
| -rw-r--r-- | Documentation/acpi/apei/einj.txt | 8 | ||||
| -rw-r--r-- | drivers/acpi/apei/apei-base.c | 61 | ||||
| -rw-r--r-- | drivers/acpi/apei/cper.c | 2 | ||||
| -rw-r--r-- | drivers/acpi/apei/einj.c | 17 | ||||
| -rw-r--r-- | drivers/acpi/apei/erst.c | 2 |
5 files changed, 64 insertions, 26 deletions
diff --git a/Documentation/acpi/apei/einj.txt b/Documentation/acpi/apei/einj.txt index e7cc36397217..e20b6daaced4 100644 --- a/Documentation/acpi/apei/einj.txt +++ b/Documentation/acpi/apei/einj.txt | |||
| @@ -53,6 +53,14 @@ directory apei/einj. The following files are provided. | |||
| 53 | 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 |
| 54 | parameter depends on error_type specified. | 54 | parameter depends on error_type specified. |
| 55 | 55 | ||
| 56 | - notrigger | ||
| 57 | The EINJ mechanism is a two step process. First inject the error, then | ||
| 58 | perform some actions to trigger it. Setting "notrigger" to 1 skips the | ||
| 59 | trigger phase, which *may* allow the user to cause the error in some other | ||
| 60 | context by a simple access to the cpu, memory location, or device that is | ||
| 61 | the target of the error injection. Whether this actually works depends | ||
| 62 | on what operations the BIOS actually includes in the trigger phase. | ||
| 63 | |||
| 56 | BIOS versions based in the ACPI 4.0 specification have limited options | 64 | BIOS versions based in the ACPI 4.0 specification have limited options |
| 57 | to control where the errors are injected. Your BIOS may support an | 65 | to control where the errors are injected. Your BIOS may support an |
| 58 | extension (enabled with the param_extension=1 module parameter, or | 66 | extension (enabled with the param_extension=1 module parameter, or |
diff --git a/drivers/acpi/apei/apei-base.c b/drivers/acpi/apei/apei-base.c index ca773683d87e..5577762daee1 100644 --- a/drivers/acpi/apei/apei-base.c +++ b/drivers/acpi/apei/apei-base.c | |||
| @@ -558,33 +558,48 @@ void apei_resources_release(struct apei_resources *resources) | |||
| 558 | } | 558 | } |
| 559 | EXPORT_SYMBOL_GPL(apei_resources_release); | 559 | EXPORT_SYMBOL_GPL(apei_resources_release); |
| 560 | 560 | ||
| 561 | static int apei_check_gar(struct acpi_generic_address *reg, u64 *paddr) | 561 | static int apei_check_gar(struct acpi_generic_address *reg, u64 *paddr, |
| 562 | u32 *access_bit_width) | ||
| 562 | { | 563 | { |
| 563 | u32 width, space_id; | 564 | u32 bit_width, bit_offset, access_size_code, space_id; |
| 564 | 565 | ||
| 565 | width = reg->bit_width; | 566 | bit_width = reg->bit_width; |
| 567 | bit_offset = reg->bit_offset; | ||
| 568 | access_size_code = reg->access_width; | ||
| 566 | space_id = reg->space_id; | 569 | space_id = reg->space_id; |
| 567 | /* Handle possible alignment issues */ | 570 | /* Handle possible alignment issues */ |
| 568 | memcpy(paddr, ®->address, sizeof(*paddr)); | 571 | memcpy(paddr, ®->address, sizeof(*paddr)); |
| 569 | if (!*paddr) { | 572 | if (!*paddr) { |
| 570 | pr_warning(FW_BUG APEI_PFX | 573 | pr_warning(FW_BUG APEI_PFX |
| 571 | "Invalid physical address in GAR [0x%llx/%u/%u]\n", | 574 | "Invalid physical address in GAR [0x%llx/%u/%u/%u/%u]\n", |
| 572 | *paddr, width, space_id); | 575 | *paddr, bit_width, bit_offset, access_size_code, |
| 576 | space_id); | ||
| 573 | return -EINVAL; | 577 | return -EINVAL; |
| 574 | } | 578 | } |
| 575 | 579 | ||
| 576 | if ((width != 8) && (width != 16) && (width != 32) && (width != 64)) { | 580 | if (access_size_code < 1 || access_size_code > 4) { |
| 577 | pr_warning(FW_BUG APEI_PFX | 581 | pr_warning(FW_BUG APEI_PFX |
| 578 | "Invalid bit width in GAR [0x%llx/%u/%u]\n", | 582 | "Invalid access size code in GAR [0x%llx/%u/%u/%u/%u]\n", |
| 579 | *paddr, width, space_id); | 583 | *paddr, bit_width, bit_offset, access_size_code, |
| 584 | space_id); | ||
| 585 | return -EINVAL; | ||
| 586 | } | ||
| 587 | *access_bit_width = 1UL << (access_size_code + 2); | ||
| 588 | |||
| 589 | if ((bit_width + bit_offset) > *access_bit_width) { | ||
| 590 | pr_warning(FW_BUG APEI_PFX | ||
| 591 | "Invalid bit width + offset in GAR [0x%llx/%u/%u/%u/%u]\n", | ||
| 592 | *paddr, bit_width, bit_offset, access_size_code, | ||
| 593 | space_id); | ||
| 580 | return -EINVAL; | 594 | return -EINVAL; |
| 581 | } | 595 | } |
| 582 | 596 | ||
| 583 | if (space_id != ACPI_ADR_SPACE_SYSTEM_MEMORY && | 597 | if (space_id != ACPI_ADR_SPACE_SYSTEM_MEMORY && |
| 584 | space_id != ACPI_ADR_SPACE_SYSTEM_IO) { | 598 | space_id != ACPI_ADR_SPACE_SYSTEM_IO) { |
| 585 | pr_warning(FW_BUG APEI_PFX | 599 | pr_warning(FW_BUG APEI_PFX |
| 586 | "Invalid address space type in GAR [0x%llx/%u/%u]\n", | 600 | "Invalid address space type in GAR [0x%llx/%u/%u/%u/%u]\n", |
| 587 | *paddr, width, space_id); | 601 | *paddr, bit_width, bit_offset, access_size_code, |
| 602 | space_id); | ||
| 588 | return -EINVAL; | 603 | return -EINVAL; |
| 589 | } | 604 | } |
| 590 | 605 | ||
| @@ -595,23 +610,25 @@ static int apei_check_gar(struct acpi_generic_address *reg, u64 *paddr) | |||
| 595 | int apei_read(u64 *val, struct acpi_generic_address *reg) | 610 | int apei_read(u64 *val, struct acpi_generic_address *reg) |
| 596 | { | 611 | { |
| 597 | int rc; | 612 | int rc; |
| 613 | u32 access_bit_width; | ||
| 598 | u64 address; | 614 | u64 address; |
| 599 | acpi_status status; | 615 | acpi_status status; |
| 600 | 616 | ||
| 601 | rc = apei_check_gar(reg, &address); | 617 | rc = apei_check_gar(reg, &address, &access_bit_width); |
| 602 | if (rc) | 618 | if (rc) |
| 603 | return rc; | 619 | return rc; |
| 604 | 620 | ||
| 605 | *val = 0; | 621 | *val = 0; |
| 606 | switch(reg->space_id) { | 622 | switch(reg->space_id) { |
| 607 | case ACPI_ADR_SPACE_SYSTEM_MEMORY: | 623 | case ACPI_ADR_SPACE_SYSTEM_MEMORY: |
| 608 | status = acpi_os_read_memory((acpi_physical_address) | 624 | status = acpi_os_read_memory((acpi_physical_address) address, |
| 609 | address, val, reg->bit_width); | 625 | val, access_bit_width); |
| 610 | if (ACPI_FAILURE(status)) | 626 | if (ACPI_FAILURE(status)) |
| 611 | return -EIO; | 627 | return -EIO; |
| 612 | break; | 628 | break; |
| 613 | case ACPI_ADR_SPACE_SYSTEM_IO: | 629 | case ACPI_ADR_SPACE_SYSTEM_IO: |
| 614 | status = acpi_os_read_port(address, (u32 *)val, reg->bit_width); | 630 | status = acpi_os_read_port(address, (u32 *)val, |
| 631 | access_bit_width); | ||
| 615 | if (ACPI_FAILURE(status)) | 632 | if (ACPI_FAILURE(status)) |
| 616 | return -EIO; | 633 | return -EIO; |
| 617 | break; | 634 | break; |
| @@ -627,22 +644,23 @@ EXPORT_SYMBOL_GPL(apei_read); | |||
| 627 | int apei_write(u64 val, struct acpi_generic_address *reg) | 644 | int apei_write(u64 val, struct acpi_generic_address *reg) |
| 628 | { | 645 | { |
| 629 | int rc; | 646 | int rc; |
| 647 | u32 access_bit_width; | ||
| 630 | u64 address; | 648 | u64 address; |
| 631 | acpi_status status; | 649 | acpi_status status; |
| 632 | 650 | ||
| 633 | rc = apei_check_gar(reg, &address); | 651 | rc = apei_check_gar(reg, &address, &access_bit_width); |
| 634 | if (rc) | 652 | if (rc) |
| 635 | return rc; | 653 | return rc; |
| 636 | 654 | ||
| 637 | switch (reg->space_id) { | 655 | switch (reg->space_id) { |
| 638 | case ACPI_ADR_SPACE_SYSTEM_MEMORY: | 656 | case ACPI_ADR_SPACE_SYSTEM_MEMORY: |
| 639 | status = acpi_os_write_memory((acpi_physical_address) | 657 | status = acpi_os_write_memory((acpi_physical_address) address, |
| 640 | address, val, reg->bit_width); | 658 | val, access_bit_width); |
| 641 | if (ACPI_FAILURE(status)) | 659 | if (ACPI_FAILURE(status)) |
| 642 | return -EIO; | 660 | return -EIO; |
| 643 | break; | 661 | break; |
| 644 | case ACPI_ADR_SPACE_SYSTEM_IO: | 662 | case ACPI_ADR_SPACE_SYSTEM_IO: |
| 645 | status = acpi_os_write_port(address, val, reg->bit_width); | 663 | status = acpi_os_write_port(address, val, access_bit_width); |
| 646 | if (ACPI_FAILURE(status)) | 664 | if (ACPI_FAILURE(status)) |
| 647 | return -EIO; | 665 | return -EIO; |
| 648 | break; | 666 | break; |
| @@ -661,23 +679,24 @@ static int collect_res_callback(struct apei_exec_context *ctx, | |||
| 661 | struct apei_resources *resources = data; | 679 | struct apei_resources *resources = data; |
| 662 | struct acpi_generic_address *reg = &entry->register_region; | 680 | struct acpi_generic_address *reg = &entry->register_region; |
| 663 | u8 ins = entry->instruction; | 681 | u8 ins = entry->instruction; |
| 682 | u32 access_bit_width; | ||
| 664 | u64 paddr; | 683 | u64 paddr; |
| 665 | int rc; | 684 | int rc; |
| 666 | 685 | ||
| 667 | if (!(ctx->ins_table[ins].flags & APEI_EXEC_INS_ACCESS_REGISTER)) | 686 | if (!(ctx->ins_table[ins].flags & APEI_EXEC_INS_ACCESS_REGISTER)) |
| 668 | return 0; | 687 | return 0; |
| 669 | 688 | ||
| 670 | rc = apei_check_gar(reg, &paddr); | 689 | rc = apei_check_gar(reg, &paddr, &access_bit_width); |
| 671 | if (rc) | 690 | if (rc) |
| 672 | return rc; | 691 | return rc; |
| 673 | 692 | ||
| 674 | switch (reg->space_id) { | 693 | switch (reg->space_id) { |
| 675 | case ACPI_ADR_SPACE_SYSTEM_MEMORY: | 694 | case ACPI_ADR_SPACE_SYSTEM_MEMORY: |
| 676 | return apei_res_add(&resources->iomem, paddr, | 695 | return apei_res_add(&resources->iomem, paddr, |
| 677 | reg->bit_width / 8); | 696 | access_bit_width / 8); |
| 678 | case ACPI_ADR_SPACE_SYSTEM_IO: | 697 | case ACPI_ADR_SPACE_SYSTEM_IO: |
| 679 | return apei_res_add(&resources->ioport, paddr, | 698 | return apei_res_add(&resources->ioport, paddr, |
| 680 | reg->bit_width / 8); | 699 | access_bit_width / 8); |
| 681 | default: | 700 | default: |
| 682 | return -EINVAL; | 701 | return -EINVAL; |
| 683 | } | 702 | } |
diff --git a/drivers/acpi/apei/cper.c b/drivers/acpi/apei/cper.c index 5d4189464d63..e6defd86b424 100644 --- a/drivers/acpi/apei/cper.c +++ b/drivers/acpi/apei/cper.c | |||
| @@ -362,6 +362,7 @@ void apei_estatus_print(const char *pfx, | |||
| 362 | gedata_len = gdata->error_data_length; | 362 | gedata_len = gdata->error_data_length; |
| 363 | apei_estatus_print_section(pfx, gdata, sec_no); | 363 | apei_estatus_print_section(pfx, gdata, sec_no); |
| 364 | data_len -= gedata_len + sizeof(*gdata); | 364 | data_len -= gedata_len + sizeof(*gdata); |
| 365 | gdata = (void *)(gdata + 1) + gedata_len; | ||
| 365 | sec_no++; | 366 | sec_no++; |
| 366 | } | 367 | } |
| 367 | } | 368 | } |
| @@ -396,6 +397,7 @@ int apei_estatus_check(const struct acpi_hest_generic_status *estatus) | |||
| 396 | if (gedata_len > data_len - sizeof(*gdata)) | 397 | if (gedata_len > data_len - sizeof(*gdata)) |
| 397 | return -EINVAL; | 398 | return -EINVAL; |
| 398 | data_len -= gedata_len + sizeof(*gdata); | 399 | data_len -= gedata_len + sizeof(*gdata); |
| 400 | gdata = (void *)(gdata + 1) + gedata_len; | ||
| 399 | } | 401 | } |
| 400 | if (data_len) | 402 | if (data_len) |
| 401 | return -EINVAL; | 403 | return -EINVAL; |
diff --git a/drivers/acpi/apei/einj.c b/drivers/acpi/apei/einj.c index 4ca087dd5f4f..8e1793649ec0 100644 --- a/drivers/acpi/apei/einj.c +++ b/drivers/acpi/apei/einj.c | |||
| @@ -74,6 +74,8 @@ struct vendor_error_type_extension { | |||
| 74 | u8 reserved[3]; | 74 | u8 reserved[3]; |
| 75 | }; | 75 | }; |
| 76 | 76 | ||
| 77 | static u32 notrigger; | ||
| 78 | |||
| 77 | static u32 vendor_flags; | 79 | static u32 vendor_flags; |
| 78 | static struct debugfs_blob_wrapper vendor_blob; | 80 | static struct debugfs_blob_wrapper vendor_blob; |
| 79 | static char vendor_dev[64]; | 81 | static char vendor_dev[64]; |
| @@ -238,7 +240,7 @@ static void *einj_get_parameter_address(void) | |||
| 238 | return v5param; | 240 | return v5param; |
| 239 | } | 241 | } |
| 240 | } | 242 | } |
| 241 | if (paddrv4) { | 243 | if (param_extension && paddrv4) { |
| 242 | struct einj_parameter *v4param; | 244 | struct einj_parameter *v4param; |
| 243 | 245 | ||
| 244 | v4param = acpi_os_map_memory(paddrv4, sizeof(*v4param)); | 246 | v4param = acpi_os_map_memory(paddrv4, sizeof(*v4param)); |
| @@ -496,9 +498,11 @@ static int __einj_error_inject(u32 type, u64 param1, u64 param2) | |||
| 496 | if (rc) | 498 | if (rc) |
| 497 | return rc; | 499 | return rc; |
| 498 | trigger_paddr = apei_exec_ctx_get_output(&ctx); | 500 | trigger_paddr = apei_exec_ctx_get_output(&ctx); |
| 499 | rc = __einj_error_trigger(trigger_paddr, type, param1, param2); | 501 | if (notrigger == 0) { |
| 500 | if (rc) | 502 | rc = __einj_error_trigger(trigger_paddr, type, param1, param2); |
| 501 | return rc; | 503 | if (rc) |
| 504 | return rc; | ||
| 505 | } | ||
| 502 | rc = apei_exec_run_optional(&ctx, ACPI_EINJ_END_OPERATION); | 506 | rc = apei_exec_run_optional(&ctx, ACPI_EINJ_END_OPERATION); |
| 503 | 507 | ||
| 504 | return rc; | 508 | return rc; |
| @@ -700,6 +704,11 @@ static int __init einj_init(void) | |||
| 700 | einj_debug_dir, &error_param2); | 704 | einj_debug_dir, &error_param2); |
| 701 | if (!fentry) | 705 | if (!fentry) |
| 702 | goto err_unmap; | 706 | goto err_unmap; |
| 707 | |||
| 708 | fentry = debugfs_create_x32("notrigger", S_IRUSR | S_IWUSR, | ||
| 709 | einj_debug_dir, ¬rigger); | ||
| 710 | if (!fentry) | ||
| 711 | goto err_unmap; | ||
| 703 | } | 712 | } |
| 704 | 713 | ||
| 705 | if (vendor_dev[0]) { | 714 | if (vendor_dev[0]) { |
diff --git a/drivers/acpi/apei/erst.c b/drivers/acpi/apei/erst.c index eb9fab5b96e4..e4d9d24eb73d 100644 --- a/drivers/acpi/apei/erst.c +++ b/drivers/acpi/apei/erst.c | |||
| @@ -917,7 +917,7 @@ static int erst_check_table(struct acpi_table_erst *erst_tab) | |||
| 917 | { | 917 | { |
| 918 | if ((erst_tab->header_length != | 918 | if ((erst_tab->header_length != |
| 919 | (sizeof(struct acpi_table_erst) - sizeof(erst_tab->header))) | 919 | (sizeof(struct acpi_table_erst) - sizeof(erst_tab->header))) |
| 920 | && (erst_tab->header_length != sizeof(struct acpi_table_einj))) | 920 | && (erst_tab->header_length != sizeof(struct acpi_table_erst))) |
| 921 | return -EINVAL; | 921 | return -EINVAL; |
| 922 | if (erst_tab->header.length < sizeof(struct acpi_table_erst)) | 922 | if (erst_tab->header.length < sizeof(struct acpi_table_erst)) |
| 923 | return -EINVAL; | 923 | return -EINVAL; |
