aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGary Hade <garyhade@us.ibm.com>2012-03-21 18:28:50 -0400
committerLen Brown <len.brown@intel.com>2012-03-30 03:30:19 -0400
commit15afae604651d4e17652d2ffb56f5e36f991cfef (patch)
tree3386a102666376d1d987549d866f8232ec1aa8b1
parent6ef19ab7fa1535d35006535dba6c407dad2d845c (diff)
ACPI, APEI: Fix incorrect APEI register bit width check and usage
The current code incorrectly assumes that (1) the APEI register bit width is always 8, 16, 32, or 64 and (2) the APEI register bit width is always equal to the APEI register access width. ERST serialization instructions entries such as: [030h 0048 1] Action : 00 [Begin Write Operation] [031h 0049 1] Instruction : 03 [Write Register Value] [032h 0050 1] Flags (decoded below) : 01 Preserve Register Bits : 1 [033h 0051 1] Reserved : 00 [034h 0052 12] Register Region : [Generic Address Structure] [034h 0052 1] Space ID : 00 [SystemMemory] [035h 0053 1] Bit Width : 03 [036h 0054 1] Bit Offset : 00 [037h 0055 1] Encoded Access Width : 03 [DWord Access:32] [038h 0056 8] Address : 000000007F2D7038 [040h 0064 8] Value : 0000000000000001 [048h 0072 8] Mask : 0000000000000007 break this assumption by yielding: [Firmware Bug]: APEI: Invalid bit width in GAR [0x7f2d7038/3/0] I have found no ACPI specification requirements corresponding with the above assumptions. There is even a good example in the Serialization Instruction Entries section (ACPI 4.0 section 17.4,1.2, ACPI 4.0a section 2.5.1.2, ACPI 5.0 section 18.5.1.2) that mentions a serialization instruction with a bit range of [6:2] which is 5 bits wide, _not_ 8, 16, 32, or 64 bits wide. Compile and boot tested with 3.3.0-rc7 on a IBM HX5. Signed-off-by: Gary Hade <garyhade@us.ibm.com> Signed-off-by: Len Brown <len.brown@intel.com>
-rw-r--r--drivers/acpi/apei/apei-base.c61
1 files changed, 40 insertions, 21 deletions
diff --git a/drivers/acpi/apei/apei-base.c b/drivers/acpi/apei/apei-base.c
index e5d53b7ddc7e..1d3656f6919f 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}
559EXPORT_SYMBOL_GPL(apei_resources_release); 559EXPORT_SYMBOL_GPL(apei_resources_release);
560 560
561static int apei_check_gar(struct acpi_generic_address *reg, u64 *paddr) 561static 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, &reg->address, sizeof(*paddr)); 571 memcpy(paddr, &reg->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)
595int apei_read(u64 *val, struct acpi_generic_address *reg) 610int 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_memory64((acpi_physical_address) 624 status = acpi_os_read_memory64((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);
627int apei_write(u64 val, struct acpi_generic_address *reg) 644int 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_memory64((acpi_physical_address) 657 status = acpi_os_write_memory64((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 }