284 lines
12 KiB
C
284 lines
12 KiB
C
//
|
|
// Created by rick on 22-08-21.
|
|
//
|
|
|
|
#include <string.h>
|
|
#include <stdio.h>
|
|
#include <stdbool.h>
|
|
|
|
#include <lai/core.h>
|
|
#include <lai/helpers/pc-bios.h>
|
|
|
|
#include <myke/acpi/acpi.h>
|
|
#include <myke/acpi/structures.h>
|
|
#include <myke/cpu/lapic.h>
|
|
#include <myke/libk/libk.h>
|
|
#include <myke/util/init.h>
|
|
|
|
void acpi_handle_facp(const struct acpi_sdt_header *addr);
|
|
|
|
void acpi_handle_apic(const struct acpi_sdt_header *addr);
|
|
|
|
struct acpi_type acpi_types[] = {
|
|
{"APIC", "Multiple APIC Description Table (MADT)", acpi_handle_apic},
|
|
{"BGRT", "Boot Graphics Resource Table (BGRT; only supported on UEFI systems)", NULL},
|
|
{"BERT", "Boot Error Record Table (BERT)", NULL},
|
|
{"CPEP", "Corrected Platform Error Polling Table (CPEP)", NULL},
|
|
{"DSDT", "Differentiated System Description Table (DSDT)", NULL},
|
|
{"ECDT", "Embedded Controller Boot Resources Table (ECDT)", NULL},
|
|
{"EINJ", "Error Injection Table (EINJ)", NULL},
|
|
{"ERST", "Error Record Serialization Table (ERST)", NULL},
|
|
{"FACP", "Fixed ACPI Description Table (FADT)", acpi_handle_facp},
|
|
{"FACS", "Firmware ACPPI Control Structure (FACS)", NULL},
|
|
{"HEST", "Hardware Error Source Table (HEST)", NULL},
|
|
{"MSCT", "Maximum System Characteristics Table (MSCT)", NULL},
|
|
{"MPST", "Memory Power State Table (MPST)", NULL},
|
|
// {"OEMx", "OEM Specific Information Tables (Any table with a signature beginning with \"OEM\" falls into this definition)", NULL},
|
|
{"PMTT", "latform Memory Topology Table (PMTT)", NULL},
|
|
{"PSDT", "Persistent System Description Table (PSDT)", NULL},
|
|
{"RASF", "ACPI RAS FeatureTable (RASF)", NULL},
|
|
{"RSDT", "Root System Description Table (RSDT; 32-bit version of the XSDT)", NULL},
|
|
{"SBST", "Smart Battery Specification Table (SBST)", NULL},
|
|
{"SLIT", "System Locality System Information Table (SLIT)", NULL},
|
|
{"SRAT", "System Resource Affinity Table (SRAT)", NULL},
|
|
{"SSDT", "Secondary System Description Table (SSDT)", NULL},
|
|
{"XSDT", "Extended System Description Table (This wiki page; included for completeness)", NULL},
|
|
{"\0\0\0\0", NULL, NULL},
|
|
};
|
|
|
|
bool acpi_checksum(const void *table_addr, const size_t table_size) {
|
|
if (table_size < 1) {
|
|
return false; // invalid input
|
|
}
|
|
const uint8_t *table = table_addr;
|
|
uint8_t val = 0;
|
|
for (size_t i = 0; i < table_size; ++i) {
|
|
val += table[i];
|
|
}
|
|
return val == 0;
|
|
}
|
|
|
|
const struct acpi_rsdt *rsdt_table = NULL;
|
|
const struct acpi_xsdt *xsdt_table = NULL;
|
|
|
|
const struct acpi_fadt_t *fadt;
|
|
const struct acpi_sdt_header *dsdt;
|
|
|
|
void acpi_handle_facp(const struct acpi_sdt_header *addr) {
|
|
// parse the FACP/FADT
|
|
fadt = (const struct acpi_fadt_t *) addr;
|
|
if (fadt->header.revision > 5) {
|
|
// minor since 5.1
|
|
printf("ACPI Revision: %i.%i %.6s\n", fadt->header.revision, fadt->minor_version, fadt->header.oem);
|
|
} else {
|
|
printf("ACPI Revision: %i.0 %.6s\n", fadt->header.revision, fadt->header.oem);
|
|
}
|
|
if (fadt->x_dsdt != 0 && fadt->x_dsdt < UINT32_MAX) {
|
|
dsdt = (struct acpi_sdt_header *) fadt->x_dsdt;
|
|
} else if (fadt->dsdt != 0) {
|
|
dsdt = (struct acpi_sdt_header *) fadt->dsdt;
|
|
} else {
|
|
k_panics("No DSDT");
|
|
}
|
|
if (!acpi_checksum(dsdt, dsdt->length)) {
|
|
k_panics("DSDT is not valid");
|
|
}
|
|
}
|
|
|
|
void acpi_handle_apic(const struct acpi_sdt_header *addr) {
|
|
const struct acpi_madt *madt = (const struct acpi_madt *) addr;
|
|
printf("Local apic: %p %i\n", madt->local_apic_addr, madt->flags_int);
|
|
lapic_set_addr(madt->local_apic_addr);
|
|
const uint8_t *current = madt->entries;
|
|
const uint8_t *end = (const uint8_t *) (madt) + madt->header.length;
|
|
while (current < end) {
|
|
struct acpi_madt_entry_header *current_header = (struct acpi_madt_entry_header *) current;
|
|
switch (current_header->type) {
|
|
case MADT_TYPE_PROCESSOR_LOCAL_APIC: {
|
|
const struct acpi_madt_local_apic *la = (const struct acpi_madt_local_apic *) current_header;
|
|
printf("Local APIC %x for %x %x\n", la->apic_id, la->processor_id, la->flags);
|
|
break;
|
|
}
|
|
case MADT_TYPE_IO_APIC: {
|
|
const struct acpi_madt_io_apic *ia = (const struct acpi_madt_io_apic *) current_header;
|
|
printf("IO APIC %x at %x with %x\n", ia->apic_id, ia->io_apic_address,
|
|
ia->global_system_interrupt_base);
|
|
break;
|
|
}
|
|
case MADT_TYPE_IO_APIC_INTERRUPT_SOURCE_OVERRIDE: {
|
|
const struct acpi_madt_io_apic_interrupt_source_override *override = (const struct acpi_madt_io_apic_interrupt_source_override *) current_header;
|
|
printf("IO APIC Interrupt source override %x:%x -> %x %x\n", override->bus_source, override->irq_source,
|
|
override->global_system_interrupt, override->flags);
|
|
break;
|
|
}
|
|
case MADT_TYPE_IO_APIC_NON_MASKABLE_INTERRUPT_SOURCE: {
|
|
const struct acpi_madt_io_apic_non_maskable_interrupt *nmi = (const struct acpi_madt_io_apic_non_maskable_interrupt *) current_header;
|
|
printf("IO APIC NMI source %x %x %x\n", nmi->nmi_source, nmi->flags, nmi->global_system_interrupt);
|
|
break;
|
|
}
|
|
case MADT_TYPE_LOCAL_APIC_NON_MASKABLE_INTERRUPTS: {
|
|
const struct acpi_madt_local_apic_non_maskable_interrupts *nmi = (const struct acpi_madt_local_apic_non_maskable_interrupts *) current_header;
|
|
printf("Local APIC NMI CPU %x %x %x\n", nmi->acpi_processor_id, nmi->flags, nmi->lint_no);
|
|
break;
|
|
}
|
|
case MADT_TYPE_LOCAL_APIC_ADDRESS_OVERRIDE: {
|
|
const struct acpi_madt_local_apic_address_override *ao = (const struct acpi_madt_local_apic_address_override *) current_header;
|
|
printf("Local APIC addr override %x%x\n", ao->physical_address_local_apic >> 32,
|
|
ao->physical_address_local_apic & UINT32_MAX);
|
|
if (ao->physical_address_local_apic > UINT32_MAX) {
|
|
printf("Override above 4GB!");
|
|
} else {
|
|
lapic_set_addr(ao->physical_address_local_apic & UINT32_MAX);
|
|
}
|
|
break;
|
|
}
|
|
case MADT_TYPE_PROCESSOR_LOCAL_X2_APIC: {
|
|
const struct acpi_madt_processor_local_x2_apic *x2 = (const struct acpi_madt_processor_local_x2_apic *) current_header;
|
|
printf("Local x2APIC %x %x %x\n", x2->processors_local_x2_apic_id, x2->flags, x2->acpi_id);
|
|
break;
|
|
}
|
|
default:
|
|
printf("Unknown MADT entry %x", current_header->type);
|
|
break;
|
|
}
|
|
current += current_header->length;
|
|
}
|
|
printf("Lapic id %x v%x\n", lapic_get_id(), lapic_get_version());
|
|
}
|
|
|
|
void acpi_parse_table(uint32_t table_addr, uint32_t index) {
|
|
struct acpi_sdt_header *s_header = (struct acpi_sdt_header *) table_addr;
|
|
if (!acpi_checksum(s_header, s_header->length)) {
|
|
printf("%i: invalid acpi entry\n", index);
|
|
return;
|
|
}
|
|
struct acpi_type *c = &acpi_types[0];
|
|
while (c->name[0] != '\0') {
|
|
if (strncmp(s_header->signature, c->name, 4) == 0) {
|
|
printf("%i: %.4s %s\n", index, c->name, c->description);
|
|
if (c->handle_table != NULL) {
|
|
c->handle_table(s_header);
|
|
}
|
|
break;
|
|
}
|
|
c += 1;
|
|
}
|
|
if (c->name[0] == '\0') {
|
|
printf("%i: %.4s unknown\n", index, s_header->signature);
|
|
}
|
|
|
|
}
|
|
|
|
void acpi_parse_rsdt(uint32_t address) {
|
|
printf("Parsing RSDT\n");
|
|
rsdt_table = (const struct acpi_rsdt *) address;
|
|
if (!acpi_checksum(rsdt_table, rsdt_table->header.length)) {
|
|
k_panics("Malformed RSDT");
|
|
}
|
|
if (strncmp(rsdt_table->header.signature, "RSDT", 4) != 0) {
|
|
k_panics("RSDT pointer is not RSDT");
|
|
}
|
|
uint32_t entries = (rsdt_table->header.length - sizeof(struct acpi_sdt_header)) / sizeof(uint32_t);
|
|
for (uint32_t i = 0; i < entries; ++i) {
|
|
acpi_parse_table(rsdt_table->pointer_to_other_sdt[i], i);
|
|
}
|
|
}
|
|
|
|
void acpi_parse_xsdt(uint64_t address) {
|
|
printf("Parsing XSDT\n");
|
|
if (address > UINT32_MAX) {
|
|
k_panics("acpi table above 4GB");
|
|
}
|
|
xsdt_table = (const struct acpi_xsdt *) (uint32_t) address;
|
|
if (!acpi_checksum(xsdt_table, xsdt_table->header.length)) {
|
|
k_panics("Malformed XSDT");
|
|
}
|
|
if (strncmp(xsdt_table->header.signature, "XSDT", 4) != 0) {
|
|
k_panics("XSDT pointer is not XSDT");
|
|
}
|
|
uint32_t entries = (xsdt_table->header.length - sizeof(struct acpi_sdt_header)) / sizeof(uint64_t);
|
|
for (uint32_t i = 0; i < entries; ++i) {
|
|
const uint64_t entry_addr = xsdt_table->pointer_to_other_sdt[i];
|
|
if (entry_addr > UINT32_MAX) {
|
|
printf("%i: Above 4GB\n", i);
|
|
continue;
|
|
}
|
|
acpi_parse_table(xsdt_table->pointer_to_other_sdt[i], i);
|
|
}
|
|
}
|
|
|
|
void *acpi_find_table_by_name(const char *name, int skip) {
|
|
if (xsdt_table != NULL) {
|
|
uint32_t entries = (xsdt_table->header.length - sizeof(struct acpi_sdt_header)) / sizeof(uint64_t);
|
|
for (uint32_t i = 0; i < entries; ++i) {
|
|
const uint64_t entry_addr = xsdt_table->pointer_to_other_sdt[i];
|
|
if (entry_addr > UINT32_MAX) {
|
|
printf("%i: Above 4GB\n", i);
|
|
continue;
|
|
}
|
|
struct acpi_sdt_header *header = (struct acpi_sdt_header *) xsdt_table->pointer_to_other_sdt[i];
|
|
if (!acpi_checksum(header, header->length)) {
|
|
printf("Invalid header %d", i);
|
|
continue;
|
|
}
|
|
if (strncmp(header->signature, name, 4) == 0) {
|
|
if (skip > 0) {
|
|
skip--;
|
|
continue;
|
|
}
|
|
return header;
|
|
}
|
|
}
|
|
}
|
|
if (rsdt_table != NULL) {
|
|
uint32_t entries = (rsdt_table->header.length - sizeof(struct acpi_sdt_header)) / sizeof(uint32_t);
|
|
for (uint32_t i = 0; i < entries; ++i) {
|
|
struct acpi_sdt_header *header = (struct acpi_sdt_header *) rsdt_table->pointer_to_other_sdt[i];
|
|
if (!acpi_checksum(header, header->length)) {
|
|
printf("Invalid header %d", i);
|
|
continue;
|
|
}
|
|
if (strncmp(header->signature, name, 4) == 0) {
|
|
if (skip > 0) {
|
|
skip--;
|
|
continue;
|
|
}
|
|
return header;
|
|
}
|
|
}
|
|
}
|
|
// check if DSDT is requested, but was not present as normal table
|
|
if (strncmp(name, "DSDT", 4) == 0) {
|
|
return (void *) dsdt;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
void acpi_parse() {
|
|
struct lai_rsdp_info lai_rsdp_info;
|
|
lai_api_error_t result = lai_bios_detect_rsdp(&lai_rsdp_info);
|
|
if (result != LAI_ERROR_NONE) {
|
|
printf("LAI: could not find rsdp: %s\n", lai_api_error_to_string(result));
|
|
k_panics("Could not init ACPI");
|
|
}
|
|
lai_set_acpi_revision(lai_rsdp_info.acpi_version);
|
|
|
|
if (lai_rsdp_info.xsdt_address != 0) {
|
|
acpi_parse_xsdt(lai_rsdp_info.xsdt_address);
|
|
} else if (lai_rsdp_info.rsdt_address != 0) {
|
|
acpi_parse_rsdt(lai_rsdp_info.rsdt_address);
|
|
} else {
|
|
k_panics("No rsdt nor xsdt");
|
|
}
|
|
}
|
|
|
|
void acpi_init() {
|
|
acpi_parse();
|
|
lai_create_namespace();
|
|
}
|
|
|
|
INIT_FUNCTION(100) = {
|
|
.name = "acpi",
|
|
.stage = INIT_STAGE_EARLY_BOOT_1,
|
|
.init = acpi_init,
|
|
}; |