Files
my-kern/kernel/acpi/acpi.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,
};