acpi_scan_rsdp() in arch/i386/kernel/acpi/boot.c
The implementation of acpi_scan_rsdp() in linux 2.6.3 does not correctly locate the RSDP on an Acer TravelMate C100 with BIOS versions R01-A4F or R01-A4K
BIOS version R01-A4K has an RSDP signature at 0xEC2D0, with zero checksum and RSDT pointer, and another RSDP signature at 0xFE030, with a valid checksum and RSDT pointer.
Currently acpi_scan_rsdp() in arch/i386/kernel/acpi/boot.c searches for a RSDP signature, and returns the first one it finds. If this does not have a valid checksum, acpi_table_init() will return an error (and since 2.6.2, this causes ACPI not to be initialised)
ACPI spec 2.0c, section 5.4.2.1 says:
"OSPM finds the Root System Description Pointer (RSDP) structure by searching physical memory ranges on 16-byte boundaries for a valid Root System Description Pointer structure signature and checksum match"
It's at least arguably correct to contine the search until a RSDP signature and valid checksum is found.
The attached patch against 2.6.3 demonstrates a (not-very-good) way of doing this.
(It also adds a printk which reports when ACPI is disabled because the RSDP is invalid)
extract from dmesg without patch:
ACPI: RSDP (v000 Acer ) @ 0x000ec2d0
Quoted 1 line
Quoted 1 line
Quoted 1 line
ERROR: Invalid checksum
extract from dmesg with patch:
ACPI: RSDP (v000 Acer ) @ 0x000fe030
ACPI: RSDT (v001 Acer TM100 0x00000001 MSFT 0x00000001) @ 0x0ffe0000
ACPI: FADT (v001 Acer TM100 0x00000001 MSFT 0x00000001) @ 0x0ffe0054
ACPI: BOOT (v001 Acer TM100 0x00000001 MSFT 0x00000001) @ 0x0ffe002c
ACPI: DSDT (v001 Acer AN100 0x00001000 MSFT 0x0100000e) @ 0x000000002
arch/i386/kernel/acpi/boot.c +8 -3
@@ -341,7 +341,10 @@for (offset =0 ; offset < length; offset +=16 ) {if (strncmp ((char *) (start + offset),"RSD PTR " , sig_len))continue ; -return (start + offset); + +/* Check for valid checksum */ +if (acpi_checksum_rsdp ((struct acpi_table_rsdp *)(start+offset)) ==0 ) +return (start + offset); }return 0 ;
@@ -388,8 +391,9 @@return __pa (efi.acpi); }/* -* Scan memory looking for the RSDP signature. First search EBDA (low -* memory) paragraphs and then search upper memory (E0000-FFFFF). +* Scan memory looking for the RSDP signature and valid checksum. +* First search EBDA (low memory) paragraphs and then search +* upper memory (E0000-FFFFF). */ rsdp_phys =acpi_scan_rsdp (0 ,0x400 );if (!rsdp_phys)
@@ -437,6 +441,7 @@*/ result =acpi_table_init ();if (result) { + printk(KERN_WARNING PREFIX "Malformed ACPI tables, disabling ACPI support\n"); acpi_disabled =1 ;return result; }
drivers/acpi/tables.c +13 -7
@@ -550,13 +550,24 @@return 0 ; } +int __init +acpi_checksum_rsdp (struct acpi_table_rsdp *rsdp) +{ +int result =0 ; + +if (rsdp->revision <2 ) + result =acpi_table_compute_checksum (rsdp,sizeof (struct acpi_table_rsdp )); +else + result =acpi_table_compute_checksum (rsdp, ((struct acpi20_table_rsdp *)rsdp)->length); + +return result; +}int __init acpi_table_init (void ) {struct acpi_table_rsdp *rsdp =NULL ;unsigned long rsdp_phys =0 ; -int result =0 ;/* Locate and map the Root System Description Table (RSDP) */
@@ -575,12 +586,7 @@ printk(KERN_INFO PREFIX "RSDP (v%3.3d %6.6s ) @ 0x%p\n", rsdp->revision, rsdp->oem_id, (void *) rsdp_phys); -if (rsdp->revision <2 ) - result =acpi_table_compute_checksum (rsdp,sizeof (struct acpi_table_rsdp )); -else - result =acpi_table_compute_checksum (rsdp, ((struct acpi20_table_rsdp *)rsdp)->length); - -if (result) { +if (acpi_checksum_rsdp (rsdp)) {printk (KERN_WARNING " >>> ERROR: Invalid checksum\n" );return -ENODEV ; }
include/linux/acpi.h +1 -0
@@ -349,6 +349,7 @@char *__acpi_map_table (unsigned long phys_addr,unsigned long size);unsigned long acpi_find_rsdp (void ); +int acpi_checksum_rsdp (struct acpi_table_rsdp *rsdp);int acpi_boot_init (void );int acpi_numa_init (void );