mirror of
https://github.com/memtest86plus/memtest86plus.git
synced 2024-11-23 08:26:23 -06:00
345 lines
12 KiB
C
345 lines
12 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
// Copyright (C) 2020-2022 Martin Whitaker.
|
|
// Copyright (C) 2004-2022 Sam Demeulemeester.
|
|
|
|
#include "boot.h"
|
|
#include "bootparams.h"
|
|
#include "efi.h"
|
|
|
|
#include "pmem.h"
|
|
#include "string.h"
|
|
#include "unistd.h"
|
|
#include "vmem.h"
|
|
|
|
#include "acpi.h"
|
|
|
|
//------------------------------------------------------------------------------
|
|
// Constants
|
|
//------------------------------------------------------------------------------
|
|
|
|
// Table signatures
|
|
|
|
#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)) // Root System Description Table
|
|
|
|
#define XSDTSignature ('X' | ('S' << 8) | ('D' << 16) | ('T' << 24)) // Extended System Description Table
|
|
|
|
#define MADTSignature ('A' | ('P' << 8) | ('I' << 16) | ('C' << 24)) // Multiple APIC Description Table
|
|
|
|
#define FADTSignature ('F' | ('A' << 8) | ('C' << 16) | ('P' << 24)) // Fixed ACPI Description Table
|
|
|
|
#define HPETSignature ('H' | ('P' << 8) | ('E' << 16) | ('T' << 24)) // High Precision Event Timer
|
|
|
|
#define EINJSignature ('E' | ('I' << 8) | ('N' << 16) | ('J' << 24)) // Error Injection Table
|
|
#define ERSTSignature ('E' | ('R' << 8) | ('S' << 16) | ('T' << 24)) // Error Record Serialization Table
|
|
#define CPEPSignature ('C' | ('P' << 8) | ('E' << 16) | ('P' << 24)) // Corrected Platform Error Polling Table
|
|
#define HESTSignature ('H' | ('E' << 8) | ('S' << 16) | ('T' << 24)) // Hardware Error Source Table
|
|
|
|
#define SLITSignature ('S' | ('L' << 8) | ('I' << 16) | ('T' << 24)) // System Locality Information Table (NUMA)
|
|
#define SRATSignature ('S' | ('R' << 8) | ('A' << 16) | ('T' << 24)) // System Resource Affinity Table (NUMA)
|
|
|
|
//------------------------------------------------------------------------------
|
|
// Types
|
|
//------------------------------------------------------------------------------
|
|
|
|
typedef struct __attribute__ ((packed)) {
|
|
uint8_t address_space;
|
|
uint8_t bit_width;
|
|
uint8_t bit_offset;
|
|
uint8_t access_size;
|
|
uint64_t address;
|
|
} acpi_gen_addr_struct;
|
|
|
|
typedef struct {
|
|
char signature[8]; // "RSD PTR "
|
|
uint8_t checksum;
|
|
char oem_id[6];
|
|
uint8_t revision;
|
|
uint32_t rsdt_addr;
|
|
uint32_t length;
|
|
uint64_t xsdt_addr;
|
|
uint8_t xchecksum;
|
|
uint8_t reserved[3];
|
|
} rsdp_t;
|
|
|
|
typedef struct {
|
|
char signature[4]; // "RSDT" or "XSDT"
|
|
uint32_t length;
|
|
uint8_t revision;
|
|
uint8_t checksum;
|
|
char oem_id[6];
|
|
char oem_table_id[8];
|
|
char oem_revision[4];
|
|
char creator_id[4];
|
|
char creator_revision[4];
|
|
} rsdt_header_t;
|
|
|
|
//------------------------------------------------------------------------------
|
|
// Private Variables
|
|
//------------------------------------------------------------------------------
|
|
|
|
static const efi_guid_t EFI_ACPI_1_RDSP_GUID = { 0xeb9d2d30, 0x2d88, 0x11d3, {0x9a, 0x16, 0x00, 0x90, 0x27, 0x3f, 0xc1, 0x4d} };
|
|
static const efi_guid_t EFI_ACPI_2_RDSP_GUID = { 0x8868e871, 0xe4f1, 0x11d3, {0xbc, 0x22, 0x00, 0x80, 0xc7, 0x3c, 0x88, 0x81} };
|
|
|
|
//------------------------------------------------------------------------------
|
|
// Variables
|
|
//------------------------------------------------------------------------------
|
|
|
|
const char *rsdp_source = "";
|
|
|
|
acpi_t acpi_config = {0, 0, 0, 0, 0, 0, 0, false};
|
|
|
|
//------------------------------------------------------------------------------
|
|
// Private Functions
|
|
//------------------------------------------------------------------------------
|
|
|
|
static rsdp_t *scan_for_rsdp(uintptr_t addr, int length)
|
|
{
|
|
uint32_t *ptr = (uint32_t *)addr;
|
|
uint32_t *end = ptr + length / sizeof(uint32_t);
|
|
|
|
while (ptr < end) {
|
|
rsdp_t *rp = (rsdp_t *)ptr;
|
|
if (*ptr == RSDPSignature1 && *(ptr+1) == RSDPSignature2 && acpi_checksum(ptr, 20) == 0) {
|
|
if (rp->revision < 2 || (rp->length < 1024 && acpi_checksum(ptr, rp->length) == 0)) {
|
|
return rp;
|
|
}
|
|
}
|
|
ptr += 4;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
#ifdef __x86_64__
|
|
static rsdp_t *find_rsdp_in_efi64_system_table(efi64_system_table_t *system_table)
|
|
{
|
|
efi64_config_table_t *config_tables = (efi64_config_table_t *)map_region(system_table->config_tables,
|
|
system_table->num_config_tables * sizeof(efi64_config_table_t),
|
|
true);
|
|
if (config_tables == NULL) return NULL;
|
|
|
|
uintptr_t table_addr = 0;
|
|
for (uint32_t i = 0; i < system_table->num_config_tables; i++) {
|
|
if (memcmp(&config_tables[i].guid, &EFI_ACPI_2_RDSP_GUID, sizeof(efi_guid_t)) == 0) {
|
|
table_addr = config_tables[i].table;
|
|
break;
|
|
}
|
|
if (memcmp(&config_tables[i].guid, &EFI_ACPI_1_RDSP_GUID, sizeof(efi_guid_t)) == 0) {
|
|
table_addr = config_tables[i].table;
|
|
}
|
|
}
|
|
return (rsdp_t *)table_addr;
|
|
}
|
|
#else
|
|
static rsdp_t *find_rsdp_in_efi32_system_table(efi32_system_table_t *system_table)
|
|
{
|
|
efi32_config_table_t *config_tables = (efi32_config_table_t *)map_region(system_table->config_tables,
|
|
system_table->num_config_tables * sizeof(efi32_config_table_t),
|
|
true);
|
|
if (config_tables == NULL) return NULL;
|
|
|
|
uintptr_t table_addr = 0;
|
|
for (uint32_t i = 0; i < system_table->num_config_tables; i++) {
|
|
if (memcmp(&config_tables[i].guid, &EFI_ACPI_2_RDSP_GUID, sizeof(efi_guid_t)) == 0) {
|
|
table_addr = config_tables[i].table;
|
|
break;
|
|
}
|
|
if (memcmp(&config_tables[i].guid, &EFI_ACPI_1_RDSP_GUID, sizeof(efi_guid_t)) == 0) {
|
|
table_addr = config_tables[i].table;
|
|
}
|
|
}
|
|
return (rsdp_t *)table_addr;
|
|
}
|
|
#endif
|
|
|
|
static uintptr_t find_rsdp(void)
|
|
{
|
|
const boot_params_t *boot_params = (boot_params_t *)boot_params_addr;
|
|
|
|
const efi_info_t *efi_info = &boot_params->efi_info;
|
|
|
|
// Search for the RSDP
|
|
rsdp_t *rp = NULL;
|
|
#ifdef __x86_64__
|
|
if (efi_info->loader_signature == EFI64_LOADER_SIGNATURE) {
|
|
uintptr_t system_table_addr = (uintptr_t)efi_info->sys_tab_hi << 32 | (uintptr_t)efi_info->sys_tab;
|
|
system_table_addr = map_region(system_table_addr, sizeof(efi64_system_table_t), true);
|
|
if (system_table_addr != 0) {
|
|
rp = find_rsdp_in_efi64_system_table((efi64_system_table_t *)system_table_addr);
|
|
if (rp) rsdp_source = "EFI64 system table";
|
|
}
|
|
}
|
|
#else
|
|
if (efi_info->loader_signature == EFI32_LOADER_SIGNATURE) {
|
|
uintptr_t system_table_addr = map_region(efi_info->sys_tab, sizeof(efi32_system_table_t), true);
|
|
if (system_table_addr != 0) {
|
|
rp = find_rsdp_in_efi32_system_table((efi32_system_table_t *)system_table_addr);
|
|
if (rp) rsdp_source = "EFI32 system table";
|
|
}
|
|
}
|
|
#endif
|
|
if (rp == NULL) {
|
|
// Search the BIOS EBDA area.
|
|
uintptr_t address = *(uint16_t *)0x40E << 4;
|
|
if (address) {
|
|
rp = scan_for_rsdp(address, 0x400);
|
|
if (rp) rsdp_source = "BIOS EBDA";
|
|
}
|
|
}
|
|
if (rp == NULL) {
|
|
// Search the BIOS reserved area.
|
|
rp = scan_for_rsdp(0xE0000, 0x20000);
|
|
if (rp) rsdp_source = "BIOS reserved area";
|
|
}
|
|
if (rp == NULL) {
|
|
// RSDP not found, give up.
|
|
return 0;
|
|
}
|
|
return (uintptr_t)rp;
|
|
}
|
|
|
|
static uintptr_t find_acpi_table(uint32_t table_signature)
|
|
{
|
|
rsdp_t *rp = (rsdp_t *)acpi_config.rsdp_addr;
|
|
|
|
// Found the RSDP, now get either the RSDT or XSDT
|
|
// and scan it for a pointer to the table we're looking for
|
|
rsdt_header_t *rt;
|
|
|
|
if (acpi_config.ver_maj < rp->revision) {
|
|
acpi_config.ver_maj = rp->revision;
|
|
}
|
|
|
|
if (rp->revision >= 2) {
|
|
rt = (rsdt_header_t *)map_region(rp->xsdt_addr, sizeof(rsdt_header_t), true);
|
|
if (rt == NULL) {
|
|
return 0;
|
|
}
|
|
// Validate the XSDT.
|
|
if (*(uint32_t *)rt != XSDTSignature) {
|
|
return 0;
|
|
}
|
|
rt = (rsdt_header_t *)map_region(rp->xsdt_addr, rt->length, true);
|
|
if (rt == NULL || acpi_checksum(rt, rt->length) != 0) {
|
|
return 0;
|
|
}
|
|
// Scan the XSDT for a pointer to the table we're looking for.
|
|
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) {
|
|
uintptr_t addr = *tab_ptr++; // read the next table entry
|
|
uint32_t *ptr = (uint32_t *)map_region(addr, sizeof(uint32_t), true);
|
|
|
|
if (ptr && *ptr == table_signature) {
|
|
return addr;
|
|
}
|
|
}
|
|
} else {
|
|
rt = (rsdt_header_t *)map_region(rp->rsdt_addr, sizeof(rsdt_header_t), true);
|
|
if (rt == NULL) {
|
|
return 0;
|
|
}
|
|
// Validate the RSDT.
|
|
if (*(uint32_t *)rt != RSDTSignature) {
|
|
return 0;
|
|
}
|
|
rt = (rsdt_header_t *)map_region(rp->rsdt_addr, rt->length, true);
|
|
if (rt == NULL || acpi_checksum(rt, rt->length) != 0) {
|
|
return 0;
|
|
}
|
|
// Scan the RSDT for a pointer to the table we're looking for.
|
|
uint32_t *tab_ptr = (uint32_t *)((uint8_t *)rt + sizeof(rsdt_header_t));
|
|
uint32_t *tab_end = (uint32_t *)((uint8_t *)rt + rt->length);
|
|
|
|
while (tab_ptr < tab_end) {
|
|
uintptr_t addr = *tab_ptr++; // read the next table entry
|
|
uint32_t *ptr = (uint32_t *)map_region(addr, sizeof(uint32_t), true);
|
|
|
|
if (ptr && *ptr == table_signature) {
|
|
return addr;
|
|
}
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static bool parse_fadt(uintptr_t fadt_addr)
|
|
{
|
|
// FADT is a very big & complex table and we only need a few data.
|
|
// We use byte offset instead of a complete struct.
|
|
|
|
// FADT Header is identical to RSDP Header
|
|
rsdt_header_t *fadt = (rsdt_header_t *)fadt_addr;
|
|
|
|
// Validate FADT
|
|
if (fadt == NULL || acpi_checksum(fadt, fadt->length) != 0) {
|
|
return false;
|
|
}
|
|
|
|
// Get ACPI Version
|
|
acpi_config.ver_maj = fadt->revision;
|
|
|
|
if (fadt->length > FADT_MINOR_REV_OFFSET) {
|
|
acpi_config.ver_min = *(uint8_t *)(fadt_addr+FADT_MINOR_REV_OFFSET) & 0xF;
|
|
}
|
|
|
|
// Get Old PM Base Address (32bit IO)
|
|
acpi_config.pm_addr = *(uint32_t *)(fadt_addr+FADT_PM_TMR_BLK_OFFSET);
|
|
acpi_config.pm_is_io = true;
|
|
|
|
#ifdef __x86_64__
|
|
acpi_gen_addr_struct *rt;
|
|
|
|
// Get APIC Timer Address
|
|
if (fadt->length > FADT_X_PM_TMR_BLK_OFFSET) {
|
|
rt = (acpi_gen_addr_struct *)map_region(fadt_addr+FADT_X_PM_TMR_BLK_OFFSET, sizeof(acpi_gen_addr_struct), true);
|
|
|
|
acpi_config.pm_is_io = (rt->address_space == 1) ? true : false;
|
|
|
|
if (rt->address != 0) {
|
|
acpi_config.pm_addr = rt->address;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
return true;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
// Public Functions
|
|
//------------------------------------------------------------------------------
|
|
|
|
int acpi_checksum(const void *data, int length)
|
|
{
|
|
uint8_t sum = 0;
|
|
|
|
uint8_t *ptr = (uint8_t *)data;
|
|
while (length--) {
|
|
sum += *ptr++;
|
|
}
|
|
return sum;
|
|
}
|
|
|
|
void acpi_init(void)
|
|
{
|
|
acpi_config.rsdp_addr = find_rsdp();
|
|
|
|
if (acpi_config.rsdp_addr == 0) {
|
|
return;
|
|
}
|
|
|
|
acpi_config.madt_addr = find_acpi_table(MADTSignature);
|
|
|
|
acpi_config.fadt_addr = find_acpi_table(FADTSignature);
|
|
|
|
if (acpi_config.fadt_addr) {
|
|
parse_fadt(acpi_config.fadt_addr);
|
|
}
|
|
|
|
acpi_config.hpet_addr = find_acpi_table(HPETSignature);
|
|
}
|