* 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
This commit is contained in:
Rene Gollent
2011-06-12 22:01:43 +00:00
parent 62e1d169f8
commit 73df2f2086
2 changed files with 125 additions and 36 deletions
+13 -2
View File
@@ -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 */
+112 -34
View File
@@ -1,4 +1,5 @@
/*
* Copyright 2011, Rene Gollent, [email protected].
* Copyright 2008, Dustin Howett, [email protected]. All rights reserved.
* Copyright 2007, Michael Lotz, [email protected]
* Copyright 2004-2005, Axel Dörfler, [email protected].
@@ -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<typename PointerType>
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<uint32>(signature, sAcpiRsdt);
else if (sAcpiXsdt != NULL)
return acpi_find_table_generic<uint64>(signature, sAcpiXsdt);
return NULL;
}