From 73df2f2086aab13793b46594bd0c89ad6a00d96f Mon Sep 17 00:00:00 2001 From: Rene Gollent Date: Sun, 12 Jun 2011 22:01:43 +0000 Subject: [PATCH] * Add validation of the RSDP checksums. * If we detect ACPI 2.0 or higher, the spec says we should use the XSDT rather than the RSDT. Attempt to do so, falling back to the RSDT if the former fails to be mapped/validated. * Refactored acpi_find_table into a templated version to account for the fact that the XSDT exports different pointer widths for its links to other tables than the RSDT. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@42133 a95241bf-73f2-0310-859d-f6bbb57e9c96 --- headers/private/kernel/arch/x86/arch_acpi.h | 15 +- src/system/boot/platform/bios_ia32/acpi.cpp | 146 +++++++++++++++----- 2 files changed, 125 insertions(+), 36 deletions(-) diff --git a/headers/private/kernel/arch/x86/arch_acpi.h b/headers/private/kernel/arch/x86/arch_acpi.h index 0f1592cbf9..0e1ae043df 100644 --- a/headers/private/kernel/arch/x86/arch_acpi.h +++ b/headers/private/kernel/arch/x86/arch_acpi.h @@ -8,11 +8,20 @@ #define ACPI_RSDP_SIGNATURE "RSD PTR " #define ACPI_RSDT_SIGNATURE "RSDT" +#define ACPI_XSDT_SIGNATURE "XSDT" #define ACPI_MADT_SIGNATURE "APIC" #define ACPI_LOCAL_APIC_ENABLED 0x01 -typedef struct acpi_rsdp { +typedef struct acpi_rsdp_legacy { + char signature[8]; /* "RSD PTR " including blank */ + uint8 checksum; /* checksum of bytes 0-19 (per ACPI 1.0) */ + char oem_id[6]; /* not null terminated */ + uint8 revision; /* 0 = ACPI 1.0, 2 = ACPI 3.0 */ + uint32 rsdt_address; /* physical memory address of RSDT */ +} _PACKED acpi_rsdp_legacy; + +typedef struct acpi_rsdp_extended { char signature[8]; /* "RSD PTR " including blank */ uint8 checksum; /* checksum of bytes 0-19 (per ACPI 1.0) */ char oem_id[6]; /* not null terminated */ @@ -22,7 +31,9 @@ typedef struct acpi_rsdp { uint64 xsdt_address; /* 64bit physical memory address of XSDT */ uint8 extended_checksum; /* including entire table */ uint8 reserved[3]; -} _PACKED acpi_rsdp; +} _PACKED acpi_rsdp_extended; + +typedef acpi_rsdp_extended acpi_rsdp; typedef struct acpi_descriptor_header { char signature[4]; /* table identifier as ASCII string */ diff --git a/src/system/boot/platform/bios_ia32/acpi.cpp b/src/system/boot/platform/bios_ia32/acpi.cpp index f58ac13c3f..d9207fd38c 100644 --- a/src/system/boot/platform/bios_ia32/acpi.cpp +++ b/src/system/boot/platform/bios_ia32/acpi.cpp @@ -1,4 +1,5 @@ /* + * Copyright 2011, Rene Gollent, rene@gollent.com. * Copyright 2008, Dustin Howett, dustin.howett@gmail.com. All rights reserved. * Copyright 2007, Michael Lotz, mmlr@mlotz.ch * Copyright 2004-2005, Axel Dörfler, axeld@pinc-software.de. @@ -33,9 +34,40 @@ static struct scan_spots_struct acpi_scan_spots[] = { }; static acpi_descriptor_header* sAcpiRsdt; // System Description Table +static acpi_descriptor_header* sAcpiXsdt; // Extended System Description Table static int32 sNumEntries = -1; +static status_t +acpi_validate_rsdp(acpi_rsdp* rsdp) +{ + const char* data = (const char*)rsdp; + unsigned char checksum = 0; + for (uint32 i = 0; i < sizeof(acpi_rsdp_legacy); i++) + checksum += data[i]; + + if ((checksum & 0xff) != 0) { + TRACE(("acpi: rsdp failed basic checksum\n")); + return B_BAD_DATA; + } + + // for ACPI 2.0+ we need to also validate the extended checksum + if (rsdp->revision > 0) { + for (uint32 i = sizeof(acpi_rsdp_legacy); + i < sizeof(acpi_rsdp_extended); i++) { + checksum += data[i]; + } + + if ((checksum & 0xff) != 0) { + TRACE(("acpi: rsdp failed extended checksum\n")); + return B_BAD_DATA; + } + } + + return B_OK; +} + + static status_t acpi_validate_rsdt(acpi_descriptor_header* rsdt) { @@ -51,38 +83,67 @@ acpi_validate_rsdt(acpi_descriptor_header* rsdt) static status_t acpi_check_rsdt(acpi_rsdp* rsdp) { + if (acpi_validate_rsdp(rsdp) != B_OK) + return B_BAD_DATA; + + bool usingXsdt = false; + TRACE(("acpi: found rsdp at %p oem id: %.6s, rev %d\n", rsdp, rsdp->oem_id, rsdp->revision)); TRACE(("acpi: rsdp points to rsdt at 0x%lx\n", rsdp->rsdt_address)); - // map and validate the root system description table - acpi_descriptor_header* rsdt - = (acpi_descriptor_header*)mmu_map_physical_memory( - rsdp->rsdt_address, sizeof(acpi_descriptor_header), - kDefaultPageFlags); - if (rsdt == NULL - || strncmp(rsdt->signature, ACPI_RSDT_SIGNATURE, 4) != 0) { - if (rsdt != NULL) - mmu_free(rsdt, sizeof(acpi_descriptor_header)); - TRACE(("acpi: invalid root system description table\n")); - return B_ERROR; + uint32 length = 0; + acpi_descriptor_header* rsdt = NULL; + if (rsdp->revision > 0) { + length = rsdp->xsdt_length; + rsdt = (acpi_descriptor_header*)mmu_map_physical_memory( + (uint32)rsdp->xsdt_address, rsdp->xsdt_length, kDefaultPageFlags); + if (rsdt != NULL + && strncmp(rsdt->signature, ACPI_XSDT_SIGNATURE, 4) != 0) { + mmu_free(rsdt, rsdp->xsdt_length); + rsdt = NULL; + TRACE(("acpi: invalid extended system description table\n")); + } else + usingXsdt = true; } - // Map the whole table, not just the header - uint32 length = rsdt->length; - TRACE(("acpi: rsdt length: %lu\n", length)); - mmu_free(rsdt, sizeof(acpi_descriptor_header)); - - sAcpiRsdt = (acpi_descriptor_header*)mmu_map_physical_memory(rsdp->rsdt_address, - length, kDefaultPageFlags); - - if (sAcpiRsdt != NULL) { - if (acpi_validate_rsdt(sAcpiRsdt) != B_OK) { - TRACE(("acpi: rsdt failed validation\n")); - mmu_free(sAcpiRsdt, length); - sAcpiRsdt = NULL; + // if we're ACPI v1 or we fail to map the XSDT for some reason, + // attempt to use the RSDT instead. + if (rsdt == NULL) { + // map and validate the root system description table + rsdt = (acpi_descriptor_header*)mmu_map_physical_memory( + rsdp->rsdt_address, sizeof(acpi_descriptor_header), + kDefaultPageFlags); + if (rsdt != NULL + && strncmp(rsdt->signature, ACPI_RSDT_SIGNATURE, 4) != 0) { + mmu_free(rsdt, sizeof(acpi_descriptor_header)); + rsdt = NULL; + TRACE(("acpi: invalid root system description table\n")); return B_ERROR; } + + length = rsdt->length; + // Map the whole table, not just the header + TRACE(("acpi: rsdt length: %lu\n", length)); + mmu_free(rsdt, sizeof(acpi_descriptor_header)); + rsdt = (acpi_descriptor_header*)mmu_map_physical_memory( + rsdp->rsdt_address, length, kDefaultPageFlags); + } + + if (rsdt != NULL) { + if (acpi_validate_rsdt(rsdt) != B_OK) { + TRACE(("acpi: rsdt failed checksum validation\n")); + mmu_free(rsdt, length); + return B_ERROR; + } else { + if (usingXsdt) + sAcpiXsdt = rsdt; + else + sAcpiRsdt = rsdt; + TRACE(("acpi: found valid %s at %p\n", + usingXsdt ? ACPI_XSDT_SIGNATURE : ACPI_RSDT_SIGNATURE, + rsdt)); + } } else return B_ERROR; @@ -90,15 +151,18 @@ acpi_check_rsdt(acpi_rsdp* rsdp) } +template acpi_descriptor_header* -acpi_find_table(const char* signature) +acpi_find_table_generic(const char* signature, acpi_descriptor_header* acpiSdt) { - if (sAcpiRsdt == NULL) + if (acpiSdt == NULL) return NULL; if (sNumEntries == -1) { - sNumEntries = (sAcpiRsdt->length - - sizeof(acpi_descriptor_header)) / 4; + // if using the xsdt, our entries are 64 bits wide. + sNumEntries = (acpiSdt->length + - sizeof(acpi_descriptor_header)) + / sizeof(PointerType); } if (sNumEntries <= 0) { @@ -109,14 +173,15 @@ acpi_find_table(const char* signature) TRACE(("acpi: searching %ld entries for table '%.4s'\n", sNumEntries, signature)); - uint32* pointer = (uint32*)((uint8*)sAcpiRsdt + PointerType* pointer = (PointerType*)((uint8*)acpiSdt + sizeof(acpi_descriptor_header)); acpi_descriptor_header* header = NULL; for (int32 j = 0; j < sNumEntries; j++, pointer++) { header = (acpi_descriptor_header*) - mmu_map_physical_memory(*pointer, - sizeof(acpi_descriptor_header), kDefaultPageFlags); + mmu_map_physical_memory((uint32)*pointer, + sizeof(acpi_descriptor_header), kDefaultPageFlags); + if (header == NULL || strncmp(header->signature, signature, 4) != 0) { // not interesting for us @@ -127,6 +192,7 @@ acpi_find_table(const char* signature) mmu_free(header, sizeof(acpi_descriptor_header)); header = NULL; } + continue; } @@ -134,7 +200,7 @@ acpi_find_table(const char* signature) break; } - + if (header == NULL) return NULL; @@ -142,8 +208,20 @@ acpi_find_table(const char* signature) uint32 length = header->length; mmu_free(header, sizeof(acpi_descriptor_header)); - return (acpi_descriptor_header*)mmu_map_physical_memory(*pointer, - length, kDefaultPageFlags); + return (acpi_descriptor_header*)mmu_map_physical_memory( + (uint32)*pointer, length, kDefaultPageFlags); +} + + +acpi_descriptor_header* +acpi_find_table(const char* signature) +{ + if (sAcpiRsdt != NULL) + return acpi_find_table_generic(signature, sAcpiRsdt); + else if (sAcpiXsdt != NULL) + return acpi_find_table_generic(signature, sAcpiXsdt); + + return NULL; }