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; }