feat: added basic ACPI support using LAI
This commit is contained in:
276
kernel/acpi/acpi.c
Normal file
276
kernel/acpi/acpi.c
Normal file
@@ -0,0 +1,276 @@
|
||||
//
|
||||
// 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>
|
||||
|
||||
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() {
|
||||
lai_create_namespace();
|
||||
}
|
||||
80
kernel/acpi/lailayer.c
Normal file
80
kernel/acpi/lailayer.c
Normal file
@@ -0,0 +1,80 @@
|
||||
//
|
||||
// Created by rick on 26-08-21.
|
||||
//
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <lai/host.h>
|
||||
#include <myke/acpi/acpi.h>
|
||||
#include <myke/drivers/ports.h>
|
||||
#include <myke/libk/libk.h>
|
||||
|
||||
const char *lai_loglevel_str(int level) {
|
||||
switch (level) {
|
||||
case LAI_DEBUG_LOG:
|
||||
return "debug ";
|
||||
case LAI_WARN_LOG:
|
||||
return "warn ";
|
||||
default:
|
||||
return "unknown";
|
||||
}
|
||||
}
|
||||
|
||||
void laihost_log(int level, const char *msg) {
|
||||
printf("lai [%s]: %s\n", lai_loglevel_str(level), msg);
|
||||
}
|
||||
|
||||
void laihost_panic(const char *msg) {
|
||||
k_panics(msg);
|
||||
}
|
||||
|
||||
void *laihost_malloc(size_t size) {
|
||||
return malloc(size);
|
||||
}
|
||||
|
||||
void *laihost_realloc(void *data, size_t size, size_t old_size) {
|
||||
return realloc(data, size);
|
||||
}
|
||||
|
||||
void laihost_free(void *data, size_t size) {
|
||||
free(data);
|
||||
}
|
||||
|
||||
void *laihost_map(size_t address, size_t size) {
|
||||
return (void *) address;
|
||||
}
|
||||
|
||||
void laihost_unmap(void *pointer, size_t count) {
|
||||
}
|
||||
|
||||
void *laihost_scan(const char *sig, size_t index) {
|
||||
return acpi_find_table_by_name(sig, (int)index);
|
||||
}
|
||||
|
||||
void laihost_outb(uint16_t port, uint8_t val) {
|
||||
port_byte_out(port, val);
|
||||
}
|
||||
|
||||
void laihost_outw(uint16_t port, uint16_t val) {
|
||||
port_word_out(port, val);
|
||||
}
|
||||
|
||||
void laihost_outd(uint16_t port, uint32_t val) {
|
||||
port_double_word_out(port, val);
|
||||
}
|
||||
|
||||
uint8_t laihost_inb(uint16_t port) {
|
||||
return port_byte_in(port);
|
||||
}
|
||||
|
||||
uint16_t laihost_inw(uint16_t port) {
|
||||
return port_word_in(port);
|
||||
}
|
||||
|
||||
uint32_t laihost_ind(uint16_t port) {
|
||||
return port_double_word_in(port);
|
||||
}
|
||||
|
||||
void laihost_sleep(uint64_t ms) {
|
||||
return; // todo implement for real system
|
||||
}
|
||||
Reference in New Issue
Block a user