Improve and fix scanning for ACPI tables.

This commit is contained in:
Martin Whitaker 2020-12-11 13:27:05 +00:00
parent 27c5fe363f
commit 1d2bf57403
1 changed files with 43 additions and 28 deletions

View File

@ -89,7 +89,8 @@
#define MPCSignature ('P' | ('C' << 8) | ('M' << 16) | ('P' << 24)) #define MPCSignature ('P' | ('C' << 8) | ('M' << 16) | ('P' << 24))
#define RSDPSignature ('R' | ('S' << 8) | ('D' << 16) | (' ' << 24)) #define RSDPSignature1 ('R' | ('S' << 8) | ('D' << 16) | (' ' << 24))
#define RSDPSignature2 ('P' | ('T' << 8) | ('R' << 16) | (' ' << 24))
#define RSDTSignature ('R' | ('S' << 8) | ('D' << 16) | ('T' << 24)) #define RSDTSignature ('R' | ('S' << 8) | ('D' << 16) | ('T' << 24))
@ -207,7 +208,7 @@ typedef struct {
} rsdp_t; } rsdp_t;
typedef struct { typedef struct {
char signature[4]; // "RSDT" char signature[4]; // "RSDT" or "XSDT"
uint32_t length; uint32_t length;
uint8_t revision; uint8_t revision;
uint8_t checksum; uint8_t checksum;
@ -216,7 +217,7 @@ typedef struct {
char oem_revision[4]; char oem_revision[4];
char creator_id[4]; char creator_id[4];
char creator_revision[4]; char creator_revision[4];
} rsdt_t; } rsdt_header_t;
typedef struct { typedef struct {
uint8_t type; uint8_t type;
@ -382,7 +383,7 @@ static bool find_cpus_in_floating_mp_struct(void)
fp = scan_for_floating_ptr_struct(0xf0000, 0x10000); fp = scan_for_floating_ptr_struct(0xf0000, 0x10000);
} }
if (fp == NULL) { if (fp == NULL) {
// Search the BIOS ESDS area. // Search the BIOS EBDA area.
uintptr_t address = *(uint16_t *)0x40E << 4; uintptr_t address = *(uint16_t *)0x40E << 4;
if (address) { if (address) {
fp = scan_for_floating_ptr_struct(address, 0x400); fp = scan_for_floating_ptr_struct(address, 0x400);
@ -420,10 +421,12 @@ static rsdp_t *scan_for_rsdp(uintptr_t addr, int length)
while (ptr < end) { while (ptr < end) {
rsdp_t *rp = (rsdp_t *)ptr; rsdp_t *rp = (rsdp_t *)ptr;
if (*ptr == RSDPSignature && checksum(ptr, rp->length) == 0) { if (*ptr == RSDPSignature1 && *(ptr+1) == RSDPSignature2 && checksum(ptr, 20) == 0) {
return rp; if (rp->revision < 2 || (rp->length < 1024 && checksum(ptr, rp->length) == 0)) {
return rp;
}
} }
ptr++; ptr += 4;
} }
return NULL; return NULL;
} }
@ -516,25 +519,25 @@ static bool find_cpus_in_rsdp(void)
} }
#endif #endif
if (rp == NULL) { if (rp == NULL) {
// Search the BIOS reserved area. // Search the BIOS EBDA area.
rp = scan_for_rsdp(0xE0000, 0x20000);
}
if (rp == NULL) {
// Search the BIOS ESDS area.
uintptr_t address = *(uint16_t *)0x40E << 4; uintptr_t address = *(uint16_t *)0x40E << 4;
if (address) { if (address) {
rp = scan_for_rsdp(address, 0x400); rp = scan_for_rsdp(address, 0x400);
} }
} }
if (rp == NULL) {
// Search the BIOS reserved area.
rp = scan_for_rsdp(0xE0000, 0x20000);
}
if (rp == NULL) { if (rp == NULL) {
// RSDP not found, give up. // RSDP not found, give up.
return false; return false;
} }
// Found the RSDP, now get either the RSDT or XSDT. // Found the RSDP, now get either the RSDT or XSDT and scan it for a pointer to the MADT.
rsdt_t *rt; rsdt_header_t *rt;
if (rp->revision >= 2) { if (rp->revision >= 2) {
rt = (rsdt_t *)((uintptr_t)rp->xsdt_addr); rt = (rsdt_header_t *)((uintptr_t)rp->xsdt_addr);
if (rt == 0) { if (rt == 0) {
return false; return false;
} }
@ -542,11 +545,24 @@ static bool find_cpus_in_rsdp(void)
if (*(uint32_t *)rt != XSDTSignature) { if (*(uint32_t *)rt != XSDTSignature) {
return false; return false;
} }
if (checksum((uint8_t *)rt, rt->length) != 0) { if (checksum(rt, rt->length) != 0) {
return false; return false;
} }
// Scan the XSDT for a pointer to the MADT.
uint64_t *tab_ptr = (uint64_t *)((uint8_t *)rt + sizeof(rsdt_header_t));
uint64_t *tab_end = (uint64_t *)((uint8_t *)rt + rt->length);
while (tab_ptr < tab_end) {
uint32_t *ptr = (uint32_t *)((uintptr_t)(*tab_ptr++)); // read the next table entry
if (ptr && *ptr == MADTSignature) {
if (parse_madt(ptr)) {
return true;
}
}
}
} else { } else {
rt = (rsdt_t *)((uintptr_t)rp->rsdt_addr); rt = (rsdt_header_t *)((uintptr_t)rp->rsdt_addr);
if (rt == 0) { if (rt == 0) {
return false; return false;
} }
@ -554,21 +570,20 @@ static bool find_cpus_in_rsdp(void)
if (*(uint32_t *)rt != RSDTSignature) { if (*(uint32_t *)rt != RSDTSignature) {
return false; return false;
} }
if (checksum((uint8_t *)rt, rt->length) != 0) { if (checksum(rt, rt->length) != 0) {
return false; return false;
} }
} // Scan the RSDT for a pointer to the MADT.
uint32_t *tab_ptr = (uint32_t *)((uint8_t *)rt + sizeof(rsdt_header_t));
uint32_t *tab_end = (uint32_t *)((uint8_t *)rt + rt->length);
// Scan the RSDT or XSDT for a pointer to the MADT. while (tab_ptr < tab_end) {
uint32_t *tab_ptr = (uint32_t *)(rt + 1); // immediately follows the RSDT/XSDT uint32_t *ptr = (uint32_t *)((uintptr_t)(*tab_ptr++)); // read the next table entry
uint32_t *tab_end = tab_ptr + (rt->length / sizeof(uint32_t));
while (tab_ptr < tab_end) { if (ptr && *ptr == MADTSignature) {
uint32_t *ptr = (uint32_t *)((uintptr_t)(*tab_ptr++)); // read the next table entry if (parse_madt(ptr)) {
return true;
if (ptr && *ptr == MADTSignature) { }
if (parse_madt(ptr)) {
return true;
} }
} }
} }