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