mirror of
https://github.com/BoredDevNL/BoredOS.git
synced 2026-05-15 10:48:38 +00:00
220 lines
6.3 KiB
C
220 lines
6.3 KiB
C
#include <stdint.h>
|
|
#include <stddef.h>
|
|
|
|
#include "acpi_structures.h"
|
|
#include "acpi.h"
|
|
#include "../sys/idt.h"
|
|
#include "../core/limine.h"
|
|
#include "../core/panic.h"
|
|
#include "../core/platform.h"
|
|
#include "../core/kconsole.h"
|
|
|
|
#define MAX_ISO 16
|
|
|
|
static struct acpi_rsdp *acpi_rsdp = NULL;
|
|
static struct acpi_madt *acpi_madt = NULL;
|
|
|
|
static fadt_t *acpi_fadt = NULL; // my header file sucks ill make this nicer but its fine
|
|
|
|
static struct {
|
|
uint8_t source;
|
|
uint32_t gsi;
|
|
uint16_t flags;
|
|
} iso_table[MAX_ISO];
|
|
static uint8_t iso_count = 0;
|
|
|
|
/*
|
|
Each ACPI table contains an 8-bit checksum field.
|
|
When all bytes in the table are added together (including the checksum byte),
|
|
the lower 8 bits of the total sum must be zero for it to pass.
|
|
*/
|
|
static int acpi_checksum(void *ptr, size_t len) {
|
|
uint8_t sum = 0;
|
|
uint8_t *p = ptr;
|
|
for (size_t i = 0; i < len; i++)
|
|
sum += p[i];
|
|
return sum == 0;
|
|
}
|
|
|
|
static void *acpi_get_rsdp(void){
|
|
extern volatile struct limine_rsdp_request acpi_rsdp_request;
|
|
|
|
if (!acpi_rsdp_request.response)
|
|
kernel_panic(NULL, "ACPI RSDP not provided by the Bootloader");
|
|
if (!acpi_rsdp_request.response->address)
|
|
kernel_panic(NULL, "ACPI Invalid RSDP address provided by Bootloader");
|
|
|
|
return acpi_rsdp_request.response->address;
|
|
}
|
|
|
|
static struct acpi_sdt *acpi_get_sdt(const char signature[4]) {
|
|
if (acpi_rsdp->revision >= 2 && acpi_rsdp->xsdt_address) {
|
|
struct acpi_xsdt *acpi_xsdt = (struct acpi_xsdt *)p2v(acpi_rsdp->xsdt_address);
|
|
if (acpi_checksum(acpi_xsdt, acpi_xsdt->header.length)) {
|
|
size_t entries = (acpi_xsdt->header.length - sizeof(struct acpi_sdt)) / 8;
|
|
for (size_t i = 0; i < entries; i++) {
|
|
struct acpi_sdt *tbl = (struct acpi_sdt *)p2v(acpi_xsdt->tables[i]);
|
|
if (!tbl) continue;
|
|
if (!memcmp(tbl->signature, signature, 4))
|
|
return tbl;
|
|
}
|
|
}
|
|
}
|
|
|
|
// RSDT fallback
|
|
if (!acpi_rsdp->rsdt_address)
|
|
return NULL;
|
|
|
|
struct acpi_sdt *acpi_rsdt = (struct acpi_sdt *)p2v(acpi_rsdp->rsdt_address);
|
|
if (!acpi_checksum(acpi_rsdt, acpi_rsdt->length))
|
|
return NULL;
|
|
|
|
uint32_t *tables = (uint32_t *)((uint8_t *)acpi_rsdt + sizeof(struct acpi_sdt));
|
|
size_t entries = (acpi_rsdt->length - sizeof(struct acpi_sdt)) / 4;
|
|
for (size_t i = 0; i < entries; i++) {
|
|
struct acpi_sdt *tbl = (struct acpi_sdt *)p2v(tables[i]);
|
|
if (!tbl) continue;
|
|
if (!memcmp(tbl->signature, signature, 4))
|
|
return tbl;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static uint16_t SLP_TYPa = 0;
|
|
static uint16_t SLP_TYPb = 0;
|
|
|
|
void acpi_parse_s5(void) {
|
|
if (!acpi_fadt || !acpi_fadt->dsdt) return;
|
|
|
|
char *dsdt = (char *)p2v((uintptr_t)acpi_fadt->dsdt);
|
|
|
|
if (memcmp(dsdt, "DSDT", 4) != 0) return;
|
|
|
|
uint32_t dsdt_len = *(uint32_t*)(dsdt + 4);
|
|
char *ptr = dsdt + 36;
|
|
char *end = dsdt + dsdt_len;
|
|
|
|
while (ptr < end) {
|
|
if (memcmp(ptr, "_S5_", 4) == 0) {
|
|
ptr += 4;
|
|
if (*ptr == 0x12) {
|
|
ptr += 3;
|
|
|
|
if (*ptr == 0x0A) ptr++;
|
|
SLP_TYPa = (*(uint8_t*)ptr) << 10;
|
|
ptr++;
|
|
|
|
if (*ptr == 0x0A) ptr++;
|
|
SLP_TYPb = (*(uint8_t*)ptr) << 10;
|
|
|
|
return;
|
|
}
|
|
}
|
|
ptr++;
|
|
}
|
|
}
|
|
|
|
__attribute__((noreturn))
|
|
void acpi_shutdown(void) {
|
|
if (SLP_TYPa == 0) acpi_parse_s5();
|
|
if (SLP_TYPa == 0) SLP_TYPa = (5 << 10);
|
|
|
|
outw((uint16_t)acpi_fadt->pm1a_cnt_blk, SLP_TYPa | SLP_EN);
|
|
|
|
if (acpi_fadt->pm1b_cnt_blk != 0) {
|
|
outw((uint16_t)acpi_fadt->pm1b_cnt_blk, SLP_TYPb | SLP_EN);
|
|
}
|
|
|
|
//virtulizers last just incase these have some sort of unintended effect on real hw
|
|
outw(0xB004, 0x2000); // bochs
|
|
outw(0x4004, 0x3400); // vbox
|
|
outw(0x604, 0x2000); // QEMU
|
|
outw(0x600, 0x34); // Cloud Hypervisor
|
|
|
|
asm volatile("cli");
|
|
for(;;) asm volatile("hlt");
|
|
}
|
|
|
|
int acpi_init(void){
|
|
acpi_rsdp = acpi_get_rsdp();
|
|
if (!acpi_rsdp)
|
|
kernel_panic(NULL, "ACPI does not provide a required RSDP");
|
|
|
|
size_t rsdp_len = acpi_rsdp->revision >= 2 ? acpi_rsdp->length : 20;
|
|
if (!acpi_checksum(acpi_rsdp, rsdp_len))
|
|
kernel_panic(NULL, "bad RSDP checksum");
|
|
|
|
acpi_fadt = (struct acpi_fadt *)acpi_get_sdt("FACP");
|
|
if (!acpi_fadt)
|
|
kernel_panic(NULL, "FADT not found");
|
|
|
|
if (!acpi_checksum(acpi_fadt, acpi_fadt->header.length))
|
|
kernel_panic(NULL, "bad FADT checksum");
|
|
|
|
if (acpi_fadt->smi_cmd && acpi_fadt->acpi_enable) {
|
|
outb(acpi_fadt->smi_cmd, acpi_fadt->acpi_enable);
|
|
|
|
int timeout = 1000000;
|
|
while (!(inw(acpi_fadt->pm1a_cnt_blk) & 1) && timeout-- > 0)
|
|
asm("pause");
|
|
if (timeout <= 0) {
|
|
kernel_panic(NULL, "Enable timeout");
|
|
}
|
|
}
|
|
|
|
acpi_madt = (struct acpi_madt *)acpi_get_sdt("APIC");
|
|
if (!acpi_madt)
|
|
kernel_panic(NULL, "MADT not found");
|
|
|
|
if (!acpi_checksum(acpi_madt, acpi_madt->header.length))
|
|
kernel_panic(NULL, "bad MADT checksum");
|
|
|
|
uint8_t *ptr = acpi_madt->entries;
|
|
uint8_t *end = (uint8_t *)acpi_madt + acpi_madt->header.length;
|
|
|
|
while (ptr < end) {
|
|
struct madt_entry_header *h = (void *)ptr;
|
|
if (h->length < sizeof(struct madt_entry_header))
|
|
break;
|
|
if (ptr + h->length > end)
|
|
break;
|
|
|
|
switch (h->type) {
|
|
case 2: {
|
|
if (iso_count < MAX_ISO) {
|
|
struct madt_iso *iso = (void *)ptr;
|
|
iso_table[iso_count].source = iso->source;
|
|
iso_table[iso_count].gsi = iso->gsi;
|
|
iso_table[iso_count].flags = iso->flags;
|
|
iso_count++;
|
|
}
|
|
break;
|
|
}
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
ptr += h->length;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
uint32_t acpi_irq_to_gsi(uint32_t irq) {
|
|
for (size_t i = 0; i < iso_count; i++) {
|
|
if (iso_table[i].source == irq)
|
|
return iso_table[i].gsi;
|
|
}
|
|
return irq;
|
|
}
|
|
|
|
uint16_t acpi_irq_flags(uint32_t irq) {
|
|
for (size_t i = 0; i < iso_count; i++) {
|
|
if (iso_table[i].source == irq)
|
|
return iso_table[i].flags;
|
|
}
|
|
return 0;
|
|
}
|
|
|