Skip to contentSkip to conversation

acpi_scan_rsdp() in arch/i386/kernel/acpi/boot.c

PATCH Jon TURNEY 1 participant 1 message Feb 19, 2004 ACPI

lore
Share & tools
lore

Jon TURNEY

author
#0 lore raw

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
diff -Naur -X exclude linux-2.6.3/arch/i386/kernel/acpi/boot.c linux-2.6.3-acpi-table-fix/arch/i386/kernel/acpi/boot.c
@@ -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
diff -Naur -X exclude linux-2.6.3/drivers/acpi/tables.c linux-2.6.3-acpi-table-fix/drivers/acpi/tables.c
@@ -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
diff -Naur -X exclude linux-2.6.3/include/linux/acpi.h linux-2.6.3-acpi-table-fix/include/linux/acpi.h
@@ -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);