diff --git a/AML/uACPI b/AML/uACPI new file mode 160000 index 0000000..f5f6cc2 --- /dev/null +++ b/AML/uACPI @@ -0,0 +1 @@ +Subproject commit f5f6cc29f85dd5db0dd79c47700c8f3ebce18be4 diff --git a/Makefile b/Makefile index fa9d0ca..aa0e11b 100644 --- a/Makefile +++ b/Makefile @@ -7,14 +7,16 @@ KERNEL_SRC ?= $(abspath $(ACPI_DIR)/../src) CFLAGS := -std=c11 -Wall -Wextra -O2 -ffreestanding -fno-stack-protector \ -fno-pic -m64 -mno-red-zone -mcmodel=kernel -mgeneral-regs-only \ - -I$(ACPI_DIR)/include -I$(KERNEL_SRC) + -I$(ACPI_DIR)/include -I$(KERNEL_SRC) -I$(ACPI_DIR)/AML/uACPI/include BIN_DIR ?= $(ACPI_DIR)/build MODULE_DIR := $(BIN_DIR)/modules BUILD := $(ACPI_DIR)/build/obj -SOURCES := src/acpi.c src/acpi_s5.c src/acpi_power.c -MOD_OBJS := $(SOURCES:src/%.c=$(BUILD)/%.k.o) +UACPI_SOURCES := $(wildcard $(ACPI_DIR)/AML/uACPI/source/*.c) +UACPI_SOURCES := $(UACPI_SOURCES:$(ACPI_DIR)/%=%) +SOURCES := src/acpi.c src/acpi_s5.c src/acpi_power.c src/uacpi_glue.c src/uacpi_heap.c src/acpi_ec.c src/acpi_battery.c $(UACPI_SOURCES) +MOD_OBJS := $(SOURCES:%.c=$(BUILD)/%.k.o) MOD_ELF := $(MODULE_DIR)/acpi.elf MOD_KO := $(MODULE_DIR)/acpi.ko @@ -30,7 +32,7 @@ check: module: $(MOD_ELF) $(MOD_KO) -$(BUILD)/%.k.o: $(ACPI_DIR)/src/%.c +$(BUILD)/%.k.o: $(ACPI_DIR)/%.c @mkdir -p $(@D) $(CC) $(CFLAGS) -MMD -MP -c $< -o $@ diff --git a/include/acpi/acpi.h b/include/acpi/acpi.h index 9eab44e..ef574ad 100644 --- a/include/acpi/acpi.h +++ b/include/acpi/acpi.h @@ -6,6 +6,49 @@ int acpi_init(void *rsdp_address, uint64_t hhdm_offset); bool acpi_is_ready(void); void acpi_dump_tables(void); void *acpi_find_table(const char *signature); +typedef void (*acpi_table_visitor)(const char *sig, void *table, uint32_t len, void *ctx); +void acpi_for_each_table(const char *signature, acpi_table_visitor visitor, void *ctx); +#define ACPI_MADT_MAX_CPUS 16 +struct acpi_madt_info { + bool present; + uint32_t lapic_base; + uint32_t enabled_cpus; + uint32_t total_cpus; + uint32_t ioapic_count; + uint32_t iso_count; + uint32_t nmi_count; + uint32_t lapic_nmi_count; + uint32_t override_count; + uint32_t ioapic_first_addr; + uint8_t lapic_ids[ACPI_MADT_MAX_CPUS]; +}; +bool acpi_get_madt(struct acpi_madt_info *out); +bool acpi_get_sci_irq(uint16_t *out); +void uacpi_glue_set_rsdp(void *rsdp_virt, uint64_t hhdm); +int acpi_uacpi_early_init(void); +int acpi_uacpi_full_init(void); +bool acpi_uacpi_tables_ready(void); +bool acpi_uacpi_full_ready(void); +bool acpi_uacpi_find_table(const char *sig, void **out_ptr, uint32_t *out_len); +void acpi_uacpi_dump(void); +void acpi_uacpi_rescan_tables(void); +void acpi_scan_blob(const uint8_t *data, uint32_t len, const char *tname); +void acpi_ec_init(void); +bool acpi_ec_ready(void); +bool acpi_ec_read(uint8_t offset, uint8_t *out); +bool acpi_ec_write(uint8_t offset, uint8_t value); +struct acpi_battery_live { + bool valid; + bool present; + uint32_t percent; + uint32_t charging; + uint32_t voltage_mv; + uint32_t current_ma; + uint32_t remaining_min; + char model[16]; +}; +bool acpi_battery_refresh(struct acpi_battery_live *out); +bool acpi_battery_present_in_namespace(void); bool acpi_get_slp_typ(uint16_t *slp_typa, uint16_t *slp_typb); bool acpi_has_battery(void); int acpi_battery_count(void); diff --git a/include/acpi/acpi_priv.h b/include/acpi/acpi_priv.h index 06416ba..ddeda47 100644 --- a/include/acpi/acpi_priv.h +++ b/include/acpi/acpi_priv.h @@ -27,6 +27,8 @@ struct acpi_fadt_cache { uint8_t reset_value; bool has_reset_reg; int reset_reg_off; + uint16_t sci_int; + bool has_sci_int; }; extern struct acpi_fadt_cache g_acpi_fadt; diff --git a/src/acpi.c b/src/acpi.c index 7d56c7f..493dcdb 100644 --- a/src/acpi.c +++ b/src/acpi.c @@ -49,6 +49,7 @@ struct sdt_header { #define FADT_DSDT 40u #define FADT_X_DSDT 140u +#define FADT_SCI_INT 46u #define FADT_SMI_CMD 48u #define FADT_ACPI_ENABLE 52u #define FADT_ACPI_DISABLE 53u @@ -251,6 +252,115 @@ void *acpi_get_dsdt(void) { return acpi_find_table("DSDT"); } +void acpi_for_each_table(const char *signature, acpi_table_visitor visitor, void *ctx) { + if (!visitor) + return; + struct sdt_header *xsdt = NULL, *rsdt = NULL; + acpi_root_tables(&xsdt, &rsdt); + struct sdt_header *roots[2] = {xsdt, rsdt}; + for (int t = 0; t < 2; t++) { + struct sdt_header *root = roots[t]; + if (!root || root->length < sizeof(struct sdt_header)) + continue; + bool wide = (root == xsdt); + uint32_t stride = wide ? 8u : 4u; + uint32_t entries = (root->length - sizeof(struct sdt_header)) / stride; + const uint8_t *base = (const uint8_t *)root + sizeof(struct sdt_header); + for (uint32_t i = 0; i < entries; i++) { + uint64_t addr = 0; + if (wide) { + for (int b = 7; b >= 0; b--) + addr = (addr << 8) | base[i * 8 + (uint32_t)b]; + } else { + for (int b = 3; b >= 0; b--) + addr = (addr << 8) | base[i * 4 + (uint32_t)b]; + } + if (!addr) + continue; + struct sdt_header *tbl = (struct sdt_header *)acpi_map_phys(addr); + if (!tbl || tbl->length < sizeof(struct sdt_header)) + continue; + if (!acpi_table_valid(tbl, tbl->length)) + continue; + if (signature && memcmp(tbl->signature, signature, 4) != 0) + continue; + visitor(tbl->signature, tbl, tbl->length, ctx); + } + } +} + +static struct acpi_madt_info g_madt_cache; +static bool g_madt_parsed = false; + +static void madt_parse_into(struct acpi_madt_info *out) { + memset(out, 0, sizeof(*out)); + struct sdt_header *madt = (struct sdt_header *)acpi_find_table("APIC"); + if (!madt || madt->length < 44) + return; + const uint8_t *m = (const uint8_t *)madt; + out->lapic_base = (uint32_t)m[36] | ((uint32_t)m[37] << 8) | + ((uint32_t)m[38] << 16) | ((uint32_t)m[39] << 24); + out->present = true; + uint32_t off = 44; + while (off + 2 <= madt->length) { + uint8_t type = m[off], len = m[off + 1]; + if (len < 2 || off + len > madt->length) + break; + switch (type) { + case 0: + out->total_cpus++; + if (len >= 8 && (m[off + 4] & 0x01)) { + if (out->enabled_cpus < ACPI_MADT_MAX_CPUS) + out->lapic_ids[out->enabled_cpus] = m[off + 3]; + out->enabled_cpus++; + } + break; + case 1: + if (len >= 12) { + uint32_t addr = (uint32_t)m[off + 4] | ((uint32_t)m[off + 5] << 8) | + ((uint32_t)m[off + 6] << 16) | ((uint32_t)m[off + 7] << 24); + if (out->ioapic_count == 0) + out->ioapic_first_addr = addr; + out->ioapic_count++; + } + break; + case 2: + out->iso_count++; + break; + case 3: + out->nmi_count++; + break; + case 4: + out->lapic_nmi_count++; + break; + case 5: + out->override_count++; + break; + default: + break; + } + off += len; + } +} + +bool acpi_get_sci_irq(uint16_t *out) { + if (!out || !g_acpi_fadt.has_sci_int) + return false; + *out = g_acpi_fadt.sci_int; + return true; +} + +bool acpi_get_madt(struct acpi_madt_info *out) { + if (!out) + return false; + if (!g_madt_parsed) { + madt_parse_into(&g_madt_cache); + g_madt_parsed = true; + } + *out = g_madt_cache; + return g_madt_cache.present; +} + static void fadt_parse(struct sdt_header *fadt) { memset(&g_acpi_fadt, 0, sizeof(g_acpi_fadt)); g_acpi_fadt.reset_reg_off = -1; @@ -264,6 +374,12 @@ static void fadt_parse(struct sdt_header *fadt) { g_acpi_fadt.dsdt = fadt_u32(f, FADT_DSDT); if (len >= FADT_X_DSDT + 8) g_acpi_fadt.x_dsdt = fadt_u64(f, FADT_X_DSDT); + if (len > FADT_SCI_INT + 1) { + g_acpi_fadt.sci_int = + (uint16_t)((uint16_t)f[FADT_SCI_INT] | + ((uint16_t)f[FADT_SCI_INT + 1] << 8)); + g_acpi_fadt.has_sci_int = true; + } if (len > FADT_SMI_CMD) g_acpi_fadt.smi_cmd = fadt_u32(f, FADT_SMI_CMD); if (len > FADT_ACPI_ENABLE) @@ -341,11 +457,19 @@ static bool mem_has_str(const uint8_t *data, uint32_t len, const char *s) { return false; } +static void scan_aml_blob(const uint8_t *data, uint32_t len, + const char *tname); + +void acpi_scan_blob(const uint8_t *data, uint32_t len, const char *tname) { + scan_aml_blob(data, len, tname); +} + static void scan_aml_blob(const uint8_t *data, uint32_t len, const char *tname) { if (!data || len < 36) return; - static const char *bat_names[] = {"BAT0", "BAT1", "BAT2", "BAT3"}; - for (unsigned b = 0; b < 4; b++) { + static const char *bat_names[] = {"BAT0", "BAT1", "BAT2", "BAT3", + "BATT", "BATC"}; + for (unsigned b = 0; b < 6; b++) { if (mem_has_str(data, len, bat_names[b])) { if (!(g_battery_seen & (uint8_t)(1u << b))) { g_battery_seen |= (uint8_t)(1u << b); @@ -484,6 +608,8 @@ int acpi_init(void *rsdp_address, uint64_t hhdm_offset) { g_acpi_rsdp = rsdp_address; g_acpi_hhdm = hhdm_offset; g_ready = false; + g_madt_parsed = false; + memset(&g_madt_cache, 0, sizeof(g_madt_cache)); memset(&g_acpi_fadt, 0, sizeof(g_acpi_fadt)); g_acpi_fadt.reset_reg_off = -1; @@ -520,9 +646,10 @@ int acpi_init(void *rsdp_address, uint64_t hhdm_offset) { return -3; } fadt_parse(fadt); - klogf(KLOG_INFO, "acpi: FADT rev=%u len=%u dsdt=0x%x pm1a_cnt=0x%x pm1b_cnt=0x%x", + klogf(KLOG_INFO, "acpi: FADT rev=%u len=%u dsdt=0x%x pm1a_cnt=0x%x pm1b_cnt=0x%x sci=%u", g_acpi_fadt.rev, fadt->length, g_acpi_fadt.dsdt, - g_acpi_fadt.pm1a_cnt, g_acpi_fadt.pm1b_cnt); + g_acpi_fadt.pm1a_cnt, g_acpi_fadt.pm1b_cnt, + g_acpi_fadt.has_sci_int ? g_acpi_fadt.sci_int : 0xFFFF); if (g_acpi_fadt.has_reset_reg) klogf(KLOG_INFO, "acpi: ResetReg validated at FADT+%d space=%u width=%u addr=0x%llx val=0x%x", g_acpi_fadt.reset_reg_off, g_acpi_fadt.reset_reg.space, @@ -535,6 +662,25 @@ int acpi_init(void *rsdp_address, uint64_t hhdm_offset) { acpi_s5_parse(); battery_probe(); + uacpi_glue_set_rsdp(g_acpi_rsdp, g_acpi_hhdm); + if (acpi_uacpi_early_init() == 0) { + if (acpi_uacpi_full_init() == 0) { + klog(KLOG_OK, "acpi: uACPI full mode active"); + acpi_ec_init(); + acpi_uacpi_rescan_tables(); + if (!g_battery_present && + acpi_battery_present_in_namespace()) { + g_battery_present = true; + if (!g_battery_name[0] || g_battery_name[0] == '-') + memcpy(g_battery_name, "BAT", 3); + klog(KLOG_INFO, "acpi: battery found via namespace walk"); + } + } else + klog(KLOG_WARN, "acpi: uACPI full init failed, tables only"); + acpi_uacpi_dump(); + } else + klog(KLOG_WARN, "acpi: uACPI early table access failed, using manual parser"); + g_ready = true; klog(KLOG_OK, "acpi: subsystem ready"); return 0; @@ -616,26 +762,10 @@ void acpi_dump_tables(void) { } } } - struct sdt_header *madt = (struct sdt_header *)acpi_find_table("APIC"); - if (madt && madt->length >= 44) { - const uint8_t *m = (const uint8_t *)madt; - uint32_t lapic = (uint32_t)m[36] | ((uint32_t)m[37] << 8) | - ((uint32_t)m[38] << 16) | ((uint32_t)m[39] << 24); - uint32_t counts[6] = {0, 0, 0, 0, 0, 0}; - uint32_t off = 44; - uint32_t cpus = 0; - while (off + 2 <= madt->length) { - uint8_t type = m[off], len = m[off + 1]; - if (len < 2 || off + len > madt->length) - break; - if (type < 6) - counts[type]++; - if (type == 0 && len >= 8 && (m[off + 4] & 0x01)) - cpus++; - off += len; - } - klogf(KLOG_INFO, "acpi: MADT lapic_base=0x%x enabled_cpus=%u ioapic=%u iso=%u nmi=%u lapic_nmi=%u lapic_override=%u", - lapic, cpus, counts[1], counts[2], counts[3], counts[4], counts[5]); + struct acpi_madt_info mi; + if (acpi_get_madt(&mi)) { + klogf(KLOG_INFO, "acpi: MADT lapic_base=0x%x enabled_cpus=%u total_cpus=%u ioapic=%u iso=%u nmi=%u lapic_nmi=%u lapic_override=%u ioapic_addr=0x%x", + mi.lapic_base, mi.enabled_cpus, mi.total_cpus, mi.ioapic_count, mi.iso_count, mi.nmi_count, mi.lapic_nmi_count, mi.override_count, mi.ioapic_first_addr); } else { klog(KLOG_WARN, "acpi: MADT (APIC) not found"); } diff --git a/src/acpi_battery.c b/src/acpi_battery.c new file mode 100644 index 0000000..322e91a --- /dev/null +++ b/src/acpi_battery.c @@ -0,0 +1,193 @@ +#include + +#include "kernel/diagnostics/klog.h" +#include "lib/string.h" + +#include +#include +#include +#include +#include + +static bool pkg_u64(uacpi_object_array *arr, uint32_t idx, uint64_t *out) { + if (!arr || idx >= arr->count || !out) + return false; + uacpi_object *o = arr->objects[idx]; + if (!o || !uacpi_object_is(o, UACPI_OBJECT_INTEGER)) + return false; + return uacpi_object_get_integer(o, out) == UACPI_STATUS_OK; +} + +static bool pkg_str(uacpi_object_array *arr, uint32_t idx, char *dst, + uint32_t cap) { + if (!arr || idx >= arr->count || !dst || cap == 0) + return false; + uacpi_object *o = arr->objects[idx]; + if (!o || !uacpi_object_is(o, UACPI_OBJECT_STRING)) + return false; + uacpi_data_view v; + if (uacpi_object_get_string(o, &v) != UACPI_STATUS_OK) + return false; + if (!v.text || v.length == 0) + return false; + uint32_t n = (uint32_t)v.length; + if (n >= cap) + n = cap - 1; + memcpy(dst, v.text, n); + dst[n] = '\0'; + return true; +} + +struct bat_find_ctx { + uacpi_namespace_node *hid_match; + uacpi_namespace_node *any_bif; +}; + +static uacpi_iteration_decision bat_find_cb(void *ctx, + uacpi_namespace_node *node, + uacpi_u32 depth) { + (void)depth; + struct bat_find_ctx *c = (struct bat_find_ctx *)ctx; + uacpi_id_string *hid = NULL; + if (uacpi_eval_hid(node, &hid) == UACPI_STATUS_OK) { + if (hid->value && + __builtin_strcmp(hid->value, "PNP0C0A") == 0) { + uacpi_free_id_string(hid); + c->hid_match = node; + return UACPI_ITERATION_DECISION_BREAK; + } + uacpi_free_id_string(hid); + } + if (!c->any_bif) { + uacpi_object *pkg = NULL; + if (uacpi_eval_simple_package(node, "_BIF", &pkg) == + UACPI_STATUS_OK || + uacpi_eval_simple_package(node, "_BIX", &pkg) == + UACPI_STATUS_OK) { + uacpi_object_unref(pkg); + c->any_bif = node; + } + } + return UACPI_ITERATION_DECISION_CONTINUE; +} + +static uacpi_namespace_node *battery_find_node(void) { + struct bat_find_ctx ctx = {0}; + uacpi_status st = uacpi_namespace_for_each_child( + uacpi_namespace_root(), bat_find_cb, NULL, UACPI_OBJECT_DEVICE_BIT, + UACPI_MAX_DEPTH_ANY, &ctx); + if (st != UACPI_STATUS_OK) + return NULL; + return ctx.hid_match ? ctx.hid_match : ctx.any_bif; +} + +bool acpi_battery_present_in_namespace(void) { + if (!acpi_uacpi_full_ready()) + return false; + return battery_find_node() != NULL; +} + +bool acpi_battery_refresh(struct acpi_battery_live *out) { + if (out) + memset(out, 0, sizeof(*out)); + if (!out || !acpi_uacpi_full_ready()) + return false; + + uacpi_namespace_node *bat = battery_find_node(); + if (!bat) + return false; + +bool acpi_battery_refresh(struct acpi_battery_live *out) { + if (out) + memset(out, 0, sizeof(*out)); + if (!out || !acpi_uacpi_full_ready()) + return false; + for (int pass = 0; pass < 8; pass++) { + uacpi_namespace_node *bat = battery_find_node_pass(pass); + if (!bat) { + if (pass == 0) + return false; + break; + } + + uint64_t sta = 0; + bool have_sta = uacpi_eval_simple_integer(bat, "_STA", &sta) == + UACPI_STATUS_OK; + if (have_info_sta_absent(have_sta_ok, sta)) + continue; + + return battery_read_live(bat, out); + } + return false; +} + + uint64_t unit = 0, design = 0, full = 0, voltage = 0; + bool have_info = false; + uacpi_object *pkg = NULL; + if (uacpi_eval_simple_package(bat, "_BIF", &pkg) == UACPI_STATUS_OK) { + uacpi_object_array arr; + if (uacpi_object_get_package(pkg, &arr) == UACPI_STATUS_OK && + arr.count >= 13) { + have_info = pkg_u64(&arr, 0, &unit) && + pkg_u64(&arr, 1, &design) && + pkg_u64(&arr, 2, &full) && + pkg_u64(&arr, 4, &voltage); + pkg_str(&arr, 9, out->model, sizeof(out->model)); + } + uacpi_object_unref(pkg); + pkg = NULL; + } + if (!have_info && + uacpi_eval_simple_package(bat, "_BIX", &pkg) == UACPI_STATUS_OK) { + uacpi_object_array arr; + if (uacpi_object_get_package(pkg, &arr) == UACPI_STATUS_OK && + arr.count >= 17) { + have_info = pkg_u64(&arr, 1, &unit) && + pkg_u64(&arr, 2, &design) && + pkg_u64(&arr, 3, &full) && + pkg_u64(&arr, 5, &voltage); + pkg_str(&arr, 16, out->model, sizeof(out->model)); + } + uacpi_object_unref(pkg); + pkg = NULL; + } + if (!have_info || full == 0) + return false; + + uint64_t state = 0, rate = 0, remaining = 0, present_volt = 0; + if (uacpi_eval_simple_package(bat, "_BST", &pkg) != UACPI_STATUS_OK) + return false; + bool ok = false; + uacpi_object_array bst; + if (uacpi_object_get_package(pkg, &bst) == UACPI_STATUS_OK && + bst.count >= 4) { + ok = pkg_u64(&bst, 0, &state) && pkg_u64(&bst, 1, &rate) && + pkg_u64(&bst, 2, &remaining) && pkg_u64(&bst, 3, &present_volt); + } + uacpi_object_unref(pkg); + if (!ok) + return false; + + uint64_t pct = (remaining * 100) / full; + if (pct > 100) + pct = 100; + out->percent = (uint32_t)pct; + out->charging = (state & 0x02) ? 1 : 0; + out->voltage_mv = (uint32_t)present_volt; + if (unit != 0) { + out->current_ma = (uint32_t)rate; + if (rate > 0 && remaining > 0) + out->remaining_min = (uint32_t)((remaining * 60) / rate); + } else { + out->current_ma = 0; + if (rate > 0 && remaining > 0) + out->remaining_min = (uint32_t)((remaining * 60) / rate); + } + (void)design; + (void)voltage; + out->valid = true; + klogf(KLOG_INFO, "acpi: battery %u%% %s %umV%s%s", out->percent, + out->charging ? "charging" : "discharging", out->voltage_mv, + out->model[0] ? " " : "", out->model); + return true; +} diff --git a/src/acpi_ec.c b/src/acpi_ec.c new file mode 100644 index 0000000..cd7a645 --- /dev/null +++ b/src/acpi_ec.c @@ -0,0 +1,199 @@ +#include + +#include "kernel/diagnostics/klog.h" +#include "drivers/interrupts/timer.h" + +#include +#include +#include +#include +#include +#include + +void *acpi_map_phys(unsigned long long phys); + +#define EC_STS_IBF (1u << 0) +#define EC_STS_OBF (1u << 1) + +#define EC_CMD_READ 0x80 +#define EC_CMD_WRITE 0x81 + +static uint16_t g_ec_cmd = 0x66; +static uint16_t g_ec_data = 0x62; +static bool g_ec_ports_known = false; +static bool g_ec_installed = false; + +static inline uint8_t ec_inb(uint16_t port) { + uint8_t v; + __asm__ volatile("inb %1,%0" : "=a"(v) : "Nd"(port)); + return v; +} + +static inline void ec_outb(uint16_t port, uint8_t v) { + __asm__ volatile("outb %0,%1" ::"a"(v), "Nd"(port)); +} + +static bool ec_wait_ibf_clear(uint32_t timeout_ms) { + uint64_t start = timer_ticks(); + while (ec_inb(g_ec_cmd) & EC_STS_IBF) { + if (timer_ticks() - start >= timeout_ms) + return false; + __asm__ volatile("pause"); + } + return true; +} + +static bool ec_wait_obf_set(uint32_t timeout_ms) { + uint64_t start = timer_ticks(); + while (!(ec_inb(g_ec_cmd) & EC_STS_OBF)) { + if (timer_ticks() - start >= timeout_ms) + return false; + __asm__ volatile("pause"); + } + return true; +} + +bool acpi_ec_read(uint8_t offset, uint8_t *out) { + if (!out) + return false; + if (!ec_wait_ibf_clear(100)) + return false; + ec_outb(g_ec_cmd, EC_CMD_READ); + if (!ec_wait_ibf_clear(100)) + return false; + ec_outb(g_ec_data, offset); + if (!ec_wait_obf_set(100)) + return false; + *out = ec_inb(g_ec_data); + return true; +} + +bool acpi_ec_write(uint8_t offset, uint8_t value) { + if (!ec_wait_ibf_clear(100)) + return false; + ec_outb(g_ec_cmd, EC_CMD_WRITE); + if (!ec_wait_ibf_clear(100)) + return false; + ec_outb(g_ec_data, offset); + if (!ec_wait_ibf_clear(100)) + return false; + ec_outb(g_ec_data, value); + if (!ec_wait_ibf_clear(100)) + return false; + return true; +} + +static void ec_parse_ecdt(void) { + void *ptr = acpi_find_table("ECDT"); + if (!ptr) + return; + const uint8_t *t = (const uint8_t *)ptr; + uint32_t len = (uint32_t)t[4] | ((uint32_t)t[5] << 8) | + ((uint32_t)t[6] << 16) | ((uint32_t)t[7] << 24); + if (len < 60) + return; + uint8_t ctl_space = t[36]; + uint64_t ctl_addr = 0; + for (int i = 7; i >= 0; i--) + ctl_addr = (ctl_addr << 8) | t[36 + 4 + i]; + uint8_t data_space = t[48]; + uint64_t data_addr = 0; + for (int i = 7; i >= 0; i--) + data_addr = (data_addr << 8) | t[48 + 4 + i]; + if (ctl_space == 1 && data_space == 1 && ctl_addr <= 0xFFFF && + data_addr <= 0xFFFF && ctl_addr != 0 && data_addr != 0) { + g_ec_cmd = (uint16_t)ctl_addr; + g_ec_data = (uint16_t)data_addr; + g_ec_ports_known = true; + klogf(KLOG_INFO, "acpi: ECDT ec cmd=0x%x data=0x%x", g_ec_cmd, + g_ec_data); + } +} + +static uacpi_status ec_region_handler(uacpi_region_op op, + uacpi_handle op_data) { + if (op != UACPI_REGION_OP_READ && op != UACPI_REGION_OP_WRITE) + return UACPI_STATUS_OK; + uacpi_region_rw_data *rw = (uacpi_region_rw_data *)op_data; + if (!rw || rw->byte_width == 0 || rw->byte_width > 4) + return UACPI_STATUS_INVALID_ARGUMENT; + if (rw->offset > 0xFF) + return UACPI_STATUS_INVALID_ARGUMENT; + if (op == UACPI_REGION_OP_READ) { + uint64_t val = 0; + for (uint8_t i = 0; i < rw->byte_width; i++) { + uint8_t b = 0; + if (!acpi_ec_read((uint8_t)(rw->offset + i), &b)) + return UACPI_STATUS_HARDWARE_TIMEOUT; + val |= (uint64_t)b << (i * 8); + } + rw->value = val; + return UACPI_STATUS_OK; + } + for (uint8_t i = 0; i < rw->byte_width; i++) { + uint8_t b = (uint8_t)((rw->value >> (i * 8)) & 0xFF); + if (!acpi_ec_write((uint8_t)(rw->offset + i), b)) + return UACPI_STATUS_HARDWARE_TIMEOUT; + } + return UACPI_STATUS_OK; +} + +struct ec_find_ctx { + uacpi_namespace_node *node; +}; + +static uacpi_iteration_decision ec_find_cb(void *ctx, + uacpi_namespace_node *node, + uacpi_u32 depth) { + (void)depth; + struct ec_find_ctx *c = (struct ec_find_ctx *)ctx; + uacpi_id_string *hid = NULL; + if (uacpi_eval_hid(node, &hid) != UACPI_STATUS_OK) + return UACPI_ITERATION_DECISION_CONTINUE; + bool match = hid->value && hid->value[0] == 'P' && + __builtin_strcmp(hid->value, "PNP0C09") == 0; + uacpi_free_id_string(hid); + if (match) { + c->node = node; + return UACPI_ITERATION_DECISION_BREAK; + } + return UACPI_ITERATION_DECISION_CONTINUE; +} + +void acpi_ec_init(void) { + ec_parse_ecdt(); + if (!acpi_uacpi_full_ready()) { + klog(KLOG_DEBUG, "acpi: EC install deferred (uACPI not full yet)"); + return; + } + if (g_ec_installed) + return; + struct ec_find_ctx ctx = {0}; + uacpi_status st = uacpi_namespace_for_each_child( + uacpi_namespace_root(), ec_find_cb, NULL, UACPI_OBJECT_DEVICE_BIT, + UACPI_MAX_DEPTH_ANY, &ctx); + if (st != UACPI_STATUS_OK || !ctx.node) { + klog(KLOG_DEBUG, "acpi: no PNP0C09 EC device, EC handler skipped"); + return; + } + st = uacpi_install_address_space_handler( + ctx.node, UACPI_ADDRESS_SPACE_EMBEDDED_CONTROLLER, + ec_region_handler, NULL); + if (st != UACPI_STATUS_OK) { + klogf(KLOG_WARN, "acpi: EC handler install failed: %s", + uacpi_status_to_string(st)); + return; + } + st = uacpi_reg_all_opregions(ctx.node, + UACPI_ADDRESS_SPACE_EMBEDDED_CONTROLLER); + if (st != UACPI_STATUS_OK) + klogf(KLOG_DEBUG, "acpi: EC _REG connect returned %s", + uacpi_status_to_string(st)); + g_ec_installed = true; + klogf(KLOG_OK, "acpi: EC handler ready (cmd=0x%x data=0x%x)", + g_ec_cmd, g_ec_data); +} + +bool acpi_ec_ready(void) { + return g_ec_installed; +} diff --git a/src/uacpi_glue.c b/src/uacpi_glue.c new file mode 100644 index 0000000..08f82f8 --- /dev/null +++ b/src/uacpi_glue.c @@ -0,0 +1,655 @@ +#include +void *acpi_map_phys(unsigned long long phys); + +#include "kernel/diagnostics/klog.h" +#include "lib/string.h" +#include "drivers/interrupts/timer.h" +#include "kernel/process/scheduler.h" +#include "drivers/pci/pci.h" +#include "arch/x86_64/idt/include/idt.h" + +#include +#include +#include +#include +#include +#include +#include + +void *uheap_alloc(uint64_t size); +void *uheap_alloc_zeroed(uint64_t size); +void uheap_free(void *ptr); + +static uint64_t g_uacpi_rsdp_phys = 0; + +void uacpi_glue_set_rsdp(void *rsdp_virt, uint64_t hhdm) { + if (!rsdp_virt) { + g_uacpi_rsdp_phys = 0; + return; + } + uint64_t v = (uint64_t)(uintptr_t)rsdp_virt; + if (hhdm != 0 && v >= hhdm) + g_uacpi_rsdp_phys = v - hhdm; + else + g_uacpi_rsdp_phys = v; +} + +uacpi_status uacpi_kernel_get_rsdp(uacpi_phys_addr *out_rsdp_address) { + if (!out_rsdp_address) + return UACPI_STATUS_INVALID_ARGUMENT; + if (g_uacpi_rsdp_phys == 0) + return UACPI_STATUS_NOT_FOUND; + *out_rsdp_address = g_uacpi_rsdp_phys; + return UACPI_STATUS_OK; +} + +void *uacpi_kernel_map(uacpi_phys_addr addr, uacpi_size len) { + (void)len; + if (addr == 0) + return UACPI_MAP_FAILED; + return acpi_map_phys((uint64_t)addr); +} + +void uacpi_kernel_unmap(void *addr, uacpi_size len) { + (void)addr; + (void)len; +} + +void uacpi_kernel_log(uacpi_log_level lvl, const uacpi_char *text) { + if (!text) + return; + switch (lvl) { + case UACPI_LOG_DEBUG: + case UACPI_LOG_TRACE: + klog(KLOG_DEBUG, text); + break; + case UACPI_LOG_INFO: + klog(KLOG_INFO, text); + break; + case UACPI_LOG_WARN: + klog(KLOG_WARN, text); + break; + case UACPI_LOG_ERROR: + klog(KLOG_ERROR, text); + break; + default: + klog(KLOG_INFO, text); + break; + } +} + +void *uacpi_kernel_alloc(uacpi_size size) { + return uheap_alloc((uint64_t)size); +} + +void *uacpi_kernel_alloc_zeroed(uacpi_size size) { + return uheap_alloc_zeroed((uint64_t)size); +} + +void uacpi_kernel_free(void *mem) { + uheap_free(mem); +} + +uacpi_u64 uacpi_kernel_get_nanoseconds_since_boot(void) { + return (uacpi_u64)timer_ticks() * 1000000ULL; +} + +void uacpi_kernel_stall(uacpi_u8 usec) { + for (volatile uint32_t i = 0; i < (uint32_t)usec * 100u; i++) + __asm__ volatile("pause"); +} + +void uacpi_kernel_sleep(uacpi_u64 msec) { + if (msec > 0xFFFFFFFFULL) + msec = 0xFFFFFFFFULL; + timer_sleep((uint32_t)msec); +} + +struct uacpi_mutex { + volatile int locked; +}; + +uacpi_handle uacpi_kernel_create_mutex(void) { + struct uacpi_mutex *m = uheap_alloc(sizeof(*m)); + if (!m) + return NULL; + m->locked = 0; + return (uacpi_handle)m; +} + +void uacpi_kernel_free_mutex(uacpi_handle h) { + uheap_free(h); +} + +uacpi_status uacpi_kernel_acquire_mutex(uacpi_handle h, uacpi_u16 timeout) { + struct uacpi_mutex *m = (struct uacpi_mutex *)h; + if (!m) + return UACPI_STATUS_INVALID_ARGUMENT; + if (timeout == 0) { + int expected = 0; + if (__atomic_compare_exchange_n(&m->locked, &expected, 1, false, + __ATOMIC_ACQUIRE, __ATOMIC_RELAXED)) + return UACPI_STATUS_OK; + return UACPI_STATUS_TIMEOUT; + } + uint64_t start = timer_ticks(); + uint64_t limit = (timeout == 0xFFFF) ? UINT64_MAX : (uint64_t)timeout; + for (;;) { + int expected = 0; + if (__atomic_compare_exchange_n(&m->locked, &expected, 1, false, + __ATOMIC_ACQUIRE, __ATOMIC_RELAXED)) + return UACPI_STATUS_OK; + if (timeout != 0xFFFF && timer_ticks() - start >= limit) + return UACPI_STATUS_TIMEOUT; + scheduler_yield(); + __asm__ volatile("pause"); + } +} + +void uacpi_kernel_release_mutex(uacpi_handle h) { + struct uacpi_mutex *m = (struct uacpi_mutex *)h; + if (!m) + return; + __atomic_store_n(&m->locked, 0, __ATOMIC_RELEASE); +} + +struct uacpi_event { + volatile uint64_t count; +}; + +uacpi_handle uacpi_kernel_create_event(void) { + struct uacpi_event *e = uheap_alloc_zeroed(sizeof(*e)); + return (uacpi_handle)e; +} + +void uacpi_kernel_free_event(uacpi_handle h) { + uheap_free(h); +} + +uacpi_bool uacpi_kernel_wait_for_event(uacpi_handle h, uacpi_u16 timeout) { + struct uacpi_event *e = (struct uacpi_event *)h; + if (!e) + return UACPI_FALSE; + uint64_t start = timer_ticks(); + uint64_t limit = (timeout == 0xFFFF) ? UINT64_MAX : (uint64_t)timeout; + for (;;) { + uint64_t c = __atomic_load_n(&e->count, __ATOMIC_ACQUIRE); + if (c > 0) { + uint64_t exp = c; + if (__atomic_compare_exchange_n(&e->count, &exp, c - 1, false, + __ATOMIC_ACQ_REL, __ATOMIC_RELAXED)) + return UACPI_TRUE; + continue; + } + if (timeout != 0xFFFF && timer_ticks() - start >= limit) + return UACPI_FALSE; + scheduler_yield(); + __asm__ volatile("pause"); + } +} + +void uacpi_kernel_signal_event(uacpi_handle h) { + struct uacpi_event *e = (struct uacpi_event *)h; + if (!e) + return; + __atomic_add_fetch(&e->count, 1, __ATOMIC_RELEASE); +} + +void uacpi_kernel_reset_event(uacpi_handle h) { + struct uacpi_event *e = (struct uacpi_event *)h; + if (!e) + return; + __atomic_store_n(&e->count, 0, __ATOMIC_RELEASE); +} + +uacpi_thread_id uacpi_kernel_get_thread_id(void) { + int tid = scheduler_current_tid(); + if (tid < 0) + return (uacpi_thread_id)(uintptr_t)1; + return (uacpi_thread_id)(uintptr_t)(tid + 1); +} + +uacpi_interrupt_state uacpi_kernel_disable_interrupts(void) { + uint64_t f; + __asm__ volatile("pushfq; pop %0; cli" : "=r"(f) :: "memory"); + return (uacpi_interrupt_state)f; +} + +void uacpi_kernel_restore_interrupts(uacpi_interrupt_state state) { + if (state & (1UL << 9)) + __asm__ volatile("sti" ::: "memory"); +} + +struct uacpi_spinlock { + volatile int locked; +}; + +uacpi_handle uacpi_kernel_create_spinlock(void) { + struct uacpi_spinlock *s = uheap_alloc(sizeof(*s)); + if (!s) + return NULL; + s->locked = 0; + return (uacpi_handle)s; +} + +void uacpi_kernel_free_spinlock(uacpi_handle h) { + uheap_free(h); +} + +uacpi_cpu_flags uacpi_kernel_lock_spinlock(uacpi_handle h) { + struct uacpi_spinlock *s = (struct uacpi_spinlock *)h; + uint64_t f; + __asm__ volatile("pushfq; pop %0; cli" : "=r"(f) :: "memory"); + if (s) { + while (__atomic_test_and_set(&s->locked, __ATOMIC_ACQUIRE)) + __asm__ volatile("pause"); + } + return (uacpi_cpu_flags)f; +} + +void uacpi_kernel_unlock_spinlock(uacpi_handle h, uacpi_cpu_flags flags) { + struct uacpi_spinlock *s = (struct uacpi_spinlock *)h; + if (s) + __atomic_clear(&s->locked, __ATOMIC_RELEASE); + if (flags & (1UL << 9)) + __asm__ volatile("sti" ::: "memory"); +} + +uacpi_status uacpi_kernel_handle_firmware_request(uacpi_firmware_request *req) { + if (!req) + return UACPI_STATUS_INVALID_ARGUMENT; + klogf(KLOG_WARN, "acpi: firmware request type=%d", (int)req->type); + return UACPI_STATUS_OK; +} + +struct uacpi_irq_handle { + uint32_t irq; + uacpi_interrupt_handler handler; + uacpi_handle ctx; +}; + +uacpi_status uacpi_kernel_install_interrupt_handler( + uacpi_u32 irq, uacpi_interrupt_handler handler, uacpi_handle ctx, + uacpi_handle *out_irq_handle) { + if (!handler || !out_irq_handle) + return UACPI_STATUS_INVALID_ARGUMENT; + if (irq >= 16) { + klogf(KLOG_WARN, "acpi: SCI irq %u needs IOAPIC, not supported", + (unsigned)irq); + return UACPI_STATUS_UNIMPLEMENTED; + } + struct uacpi_irq_handle *h = uheap_alloc(sizeof(*h)); + if (!h) + return UACPI_STATUS_OUT_OF_MEMORY; + h->irq = irq; + h->handler = handler; + h->ctx = ctx; + idt_set_irq_handler((uint8_t)irq, (void *)handler, ctx); + idt_unmask_irq((uint8_t)irq); + *out_irq_handle = (uacpi_handle)h; + klogf(KLOG_OK, "acpi: IRQ %u handler installed", (unsigned)irq); + return UACPI_STATUS_OK; +} + +uacpi_status uacpi_kernel_uninstall_interrupt_handler( + uacpi_interrupt_handler handler, uacpi_handle irq_handle) { + struct uacpi_irq_handle *h = (struct uacpi_irq_handle *)irq_handle; + if (!h) + return UACPI_STATUS_INVALID_ARGUMENT; + if (h->irq < 16) { + idt_mask_irq((uint8_t)h->irq); + idt_clear_irq_handler((uint8_t)h->irq); + } + (void)handler; + uheap_free(h); + return UACPI_STATUS_OK; +} + +uacpi_status uacpi_kernel_schedule_work( + uacpi_work_type type, uacpi_work_handler handler, uacpi_handle ctx) { + (void)type; + if (!handler) + return UACPI_STATUS_INVALID_ARGUMENT; + handler(ctx); + return UACPI_STATUS_OK; +} + +uacpi_status uacpi_kernel_wait_for_work_completion(void) { + return UACPI_STATUS_OK; +} + +struct uacpi_pci_handle { + uint8_t bus; + uint8_t dev; + uint8_t func; +}; + +uacpi_status uacpi_kernel_pci_device_open( + uacpi_pci_address address, uacpi_handle *out_handle) { + if (!out_handle) + return UACPI_STATUS_INVALID_ARGUMENT; + if (address.device > 31 || address.function > 7) + return UACPI_STATUS_INVALID_ARGUMENT; + uint32_t id = pci_read_config32((uint8_t)address.bus, + (uint8_t)address.device, + (uint8_t)address.function, 0x00); + if ((uint16_t)id == 0xFFFF) + return UACPI_STATUS_NOT_FOUND; + struct uacpi_pci_handle *h = uheap_alloc(sizeof(*h)); + if (!h) + return UACPI_STATUS_OUT_OF_MEMORY; + h->bus = (uint8_t)address.bus; + h->dev = (uint8_t)address.device; + h->func = (uint8_t)address.function; + *out_handle = (uacpi_handle)h; + return UACPI_STATUS_OK; +} + +void uacpi_kernel_pci_device_close(uacpi_handle h) { + uheap_free(h); +} + +static struct uacpi_pci_handle *pci_h(uacpi_handle h) { + return (struct uacpi_pci_handle *)h; +} + +uacpi_status uacpi_kernel_pci_read8( + uacpi_handle dev, uacpi_size off, uacpi_u8 *v) { + if (!dev || !v || off > 255) + return UACPI_STATUS_INVALID_ARGUMENT; + struct uacpi_pci_handle *h = pci_h(dev); + uint32_t w = pci_read_config32(h->bus, h->dev, h->func, (uint8_t)(off & ~3u)); + *v = (uacpi_u8)((w >> ((off & 3) * 8)) & 0xFF); + return UACPI_STATUS_OK; +} + +uacpi_status uacpi_kernel_pci_read16( + uacpi_handle dev, uacpi_size off, uacpi_u16 *v) { + if (!dev || !v || off > 254 || (off & 1)) + return UACPI_STATUS_INVALID_ARGUMENT; + struct uacpi_pci_handle *h = pci_h(dev); + uint32_t w = pci_read_config32(h->bus, h->dev, h->func, (uint8_t)(off & ~3u)); + *v = (uacpi_u16)((w >> ((off & 3) * 8)) & 0xFFFF); + return UACPI_STATUS_OK; +} + +uacpi_status uacpi_kernel_pci_read32( + uacpi_handle dev, uacpi_size off, uacpi_u32 *v) { + if (!dev || !v || off > 252 || (off & 3)) + return UACPI_STATUS_INVALID_ARGUMENT; + struct uacpi_pci_handle *h = pci_h(dev); + *v = pci_read_config32(h->bus, h->dev, h->func, (uint8_t)off); + return UACPI_STATUS_OK; +} + +uacpi_status uacpi_kernel_pci_write8( + uacpi_handle dev, uacpi_size off, uacpi_u8 v) { + if (!dev || off > 255) + return UACPI_STATUS_INVALID_ARGUMENT; + struct uacpi_pci_handle *h = pci_h(dev); + uint8_t a = (uint8_t)(off & ~3u); + uint32_t w = pci_read_config32(h->bus, h->dev, h->func, a); + uint32_t sh = (uint32_t)(off & 3) * 8; + w = (w & ~(0xFFu << sh)) | ((uint32_t)v << sh); + pci_write_config32(h->bus, h->dev, h->func, a, w); + return UACPI_STATUS_OK; +} + +uacpi_status uacpi_kernel_pci_write16( + uacpi_handle dev, uacpi_size off, uacpi_u16 v) { + if (!dev || off > 254 || (off & 1)) + return UACPI_STATUS_INVALID_ARGUMENT; + struct uacpi_pci_handle *h = pci_h(dev); + uint8_t a = (uint8_t)(off & ~3u); + uint32_t w = pci_read_config32(h->bus, h->dev, h->func, a); + uint32_t sh = (uint32_t)(off & 3) * 8; + w = (w & ~(0xFFFFu << sh)) | ((uint32_t)v << sh); + pci_write_config32(h->bus, h->dev, h->func, a, w); + return UACPI_STATUS_OK; +} + +uacpi_status uacpi_kernel_pci_write32( + uacpi_handle dev, uacpi_size off, uacpi_u32 v) { + if (!dev || off > 252 || (off & 3)) + return UACPI_STATUS_INVALID_ARGUMENT; + struct uacpi_pci_handle *h = pci_h(dev); + pci_write_config32(h->bus, h->dev, h->func, (uint8_t)off, v); + return UACPI_STATUS_OK; +} + +struct uacpi_io_handle { + uint16_t base; + uint64_t len; +}; + +static inline uint8_t io_inb(uint16_t p) { + uint8_t v; + __asm__ volatile("inb %1,%0" : "=a"(v) : "Nd"(p)); + return v; +} +static inline uint16_t io_inw(uint16_t p) { + uint16_t v; + __asm__ volatile("inw %1,%0" : "=a"(v) : "Nd"(p)); + return v; +} +static inline uint32_t io_inl(uint16_t p) { + uint32_t v; + __asm__ volatile("inl %1,%0" : "=a"(v) : "Nd"(p)); + return v; +} +static inline void io_outb(uint16_t p, uint8_t v) { + __asm__ volatile("outb %0,%1" ::"a"(v), "Nd"(p)); +} +static inline void io_outw(uint16_t p, uint16_t v) { + __asm__ volatile("outw %0,%1" ::"a"(v), "Nd"(p)); +} +static inline void io_outl(uint16_t p, uint32_t v) { + __asm__ volatile("outl %0,%1" ::"a"(v), "Nd"(p)); +} + +uacpi_status uacpi_kernel_io_map( + uacpi_io_addr base, uacpi_size len, uacpi_handle *out_handle) { + if (!out_handle || len == 0 || base > 0xFFFF) + return UACPI_STATUS_INVALID_ARGUMENT; + struct uacpi_io_handle *h = uheap_alloc(sizeof(*h)); + if (!h) + return UACPI_STATUS_OUT_OF_MEMORY; + h->base = (uint16_t)base; + h->len = (uint64_t)len; + *out_handle = (uacpi_handle)h; + return UACPI_STATUS_OK; +} + +void uacpi_kernel_io_unmap(uacpi_handle h) { + uheap_free(h); +} + +uacpi_status uacpi_kernel_io_read8( + uacpi_handle h, uacpi_size off, uacpi_u8 *v) { + struct uacpi_io_handle *io = h; + if (!io || !v || off >= io->len) + return UACPI_STATUS_INVALID_ARGUMENT; + *v = io_inb((uint16_t)(io->base + off)); + return UACPI_STATUS_OK; +} + +uacpi_status uacpi_kernel_io_read16( + uacpi_handle h, uacpi_size off, uacpi_u16 *v) { + struct uacpi_io_handle *io = h; + if (!io || !v || off + 1 >= io->len) + return UACPI_STATUS_INVALID_ARGUMENT; + *v = io_inw((uint16_t)(io->base + off)); + return UACPI_STATUS_OK; +} + +uacpi_status uacpi_kernel_io_read32( + uacpi_handle h, uacpi_size off, uacpi_u32 *v) { + struct uacpi_io_handle *io = h; + if (!io || !v || off + 3 >= io->len) + return UACPI_STATUS_INVALID_ARGUMENT; + *v = io_inl((uint16_t)(io->base + off)); + return UACPI_STATUS_OK; +} + +uacpi_status uacpi_kernel_io_write8( + uacpi_handle h, uacpi_size off, uacpi_u8 v) { + struct uacpi_io_handle *io = h; + if (!io || off >= io->len) + return UACPI_STATUS_INVALID_ARGUMENT; + io_outb((uint16_t)(io->base + off), v); + return UACPI_STATUS_OK; +} + +uacpi_status uacpi_kernel_io_write16( + uacpi_handle h, uacpi_size off, uacpi_u16 v) { + struct uacpi_io_handle *io = h; + if (!io || off + 1 >= io->len) + return UACPI_STATUS_INVALID_ARGUMENT; + io_outw((uint16_t)(io->base + off), v); + return UACPI_STATUS_OK; +} + +uacpi_status uacpi_kernel_io_write32( + uacpi_handle h, uacpi_size off, uacpi_u32 v) { + struct uacpi_io_handle *io = h; + if (!io || off + 3 >= io->len) + return UACPI_STATUS_INVALID_ARGUMENT; + io_outl((uint16_t)(io->base + off), v); + return UACPI_STATUS_OK; +} + +static uint8_t g_uacpi_early_table_buf[4096] __attribute__((aligned(8))); +static bool g_uacpi_tables_ready = false; +static bool g_uacpi_full_ready = false; + +bool acpi_uacpi_tables_ready(void) { + return g_uacpi_tables_ready; +} + +bool acpi_uacpi_full_ready(void) { + return g_uacpi_full_ready; +} + +int acpi_uacpi_early_init(void) { + if (g_uacpi_rsdp_phys == 0) + return -1; + uacpi_status st = uacpi_setup_early_table_access( + g_uacpi_early_table_buf, sizeof(g_uacpi_early_table_buf)); + if (st != UACPI_STATUS_OK) { + g_uacpi_tables_ready = false; + return -2; + } + g_uacpi_tables_ready = true; + klogf(KLOG_OK, "acpi: uACPI early table access ready (tables=%u)", + (unsigned)uacpi_table_count()); + return 0; +} + +int acpi_uacpi_full_init(void) { + if (!g_uacpi_tables_ready) + return -1; + uacpi_status st = uacpi_initialize(0); + if (st != UACPI_STATUS_OK) { + klogf(KLOG_ERROR, "acpi: uACPI initialize failed: %s", + uacpi_status_to_string(st)); + return -2; + } + st = uacpi_namespace_load(); + if (st != UACPI_STATUS_OK) { + klogf(KLOG_ERROR, "acpi: uACPI namespace_load failed: %s", + uacpi_status_to_string(st)); + return -3; + } + st = uacpi_namespace_initialize(); + if (st != UACPI_STATUS_OK) { + klogf(KLOG_ERROR, "acpi: uACPI namespace_initialize failed: %s", + uacpi_status_to_string(st)); + return -4; + } + g_uacpi_full_ready = true; + klog(KLOG_OK, "acpi: uACPI full AML namespace ready"); + return 0; +} + +bool acpi_uacpi_find_table(const char *sig, void **out_ptr, uint32_t *out_len) { + if (!sig || !out_ptr) + return false; + if (!g_uacpi_tables_ready) + return false; + uacpi_table tbl; + uacpi_status st = uacpi_table_find_by_signature(sig, &tbl); + if (st != UACPI_STATUS_OK) + return false; + struct { + char signature[4]; + uint32_t length; + } __attribute__((packed)) *hdr = tbl.ptr; + if (out_len) + *out_len = hdr->length; + *out_ptr = tbl.ptr; + uacpi_table_unref(&tbl); + return true; +} + +static uacpi_iteration_decision uacpi_dump_dev( + void *ctx, uacpi_namespace_node *node, uacpi_u32 depth) { + (void)ctx; + (void)depth; + uacpi_id_string *hid = NULL; + if (uacpi_eval_hid(node, &hid) != UACPI_STATUS_OK) + return UACPI_ITERATION_DECISION_CONTINUE; + klogf(KLOG_INFO, "acpi: dev %s", hid->value ? hid->value : "?"); + uacpi_free_id_string(hid); + return UACPI_ITERATION_DECISION_CONTINUE; +} + +void acpi_uacpi_rescan_tables(void) { + if (!g_uacpi_tables_ready) + return; + uacpi_size n = uacpi_table_count(); + for (uacpi_size i = 0; i < n; i++) { + uacpi_table tbl; + if (uacpi_table_get_by_index(i, &tbl) != UACPI_STATUS_OK) + continue; + const char *sig = tbl.hdr->signature; + char s[5]; + s[0] = sig[0]; + s[1] = sig[1]; + s[2] = sig[2]; + s[3] = sig[3]; + s[4] = '\0'; + if ((s[0] == 'D' && s[1] == 'S' && s[2] == 'D' && s[3] == 'T') || + (s[0] == 'S' && s[1] == 'S' && s[2] == 'D' && s[3] == 'T')) + acpi_scan_blob((const uint8_t *)tbl.ptr, tbl.hdr->length, s); + uacpi_table_unref(&tbl); + } +} + +void acpi_uacpi_dump(void) { + if (!g_uacpi_tables_ready) { + klog(KLOG_WARN, "acpi: uACPI tables not ready"); + return; + } + uacpi_size n = uacpi_table_count(); + klogf(KLOG_INFO, "acpi: uACPI tables=%u full=%d", (unsigned)n, + g_uacpi_full_ready ? 1 : 0); + uacpi_size show = n > 12 ? 12 : n; + for (uacpi_size i = 0; i < show; i++) { + uacpi_table tbl; + if (uacpi_table_get_by_index(i, &tbl) != UACPI_STATUS_OK) + continue; + const char *sig = tbl.hdr->signature; + char s[5]; + s[0] = sig[0]; + s[1] = sig[1]; + s[2] = sig[2]; + s[3] = sig[3]; + s[4] = '\0'; + klogf(KLOG_INFO, "acpi: uACPI [%u] %s len=%u", (unsigned)i, s, + (unsigned)tbl.hdr->length); + uacpi_table_unref(&tbl); + } + if (g_uacpi_full_ready) + uacpi_namespace_for_each_child(uacpi_namespace_root(), uacpi_dump_dev, + NULL, UACPI_OBJECT_DEVICE_BIT, UACPI_MAX_DEPTH_ANY, NULL); +} diff --git a/src/uacpi_heap.c b/src/uacpi_heap.c new file mode 100644 index 0000000..7d26c8d --- /dev/null +++ b/src/uacpi_heap.c @@ -0,0 +1,138 @@ +#include +#include +#include + +#include "mm/pmm.h" +#include "lib/string.h" + +#define UHEAP_MAGIC 0x55484541u +#define UHEAP_ALIGN 8u + +struct uheap_block { + uint32_t magic; + uint32_t free; + uint64_t size; + struct uheap_block *next; +}; + +static struct uheap_block *g_head = NULL; + +static inline uint64_t uheap_irq_save(void) { + uint64_t f; + __asm__ volatile("pushfq; pop %0; cli" : "=r"(f) :: "memory"); + return f; +} + +static inline void uheap_irq_restore(uint64_t f) { + if (f & (1ULL << 9)) + __asm__ volatile("sti" ::: "memory"); +} + +static uint64_t uheap_align_up(uint64_t v) { + return (v + (UHEAP_ALIGN - 1)) & ~(uint64_t)(UHEAP_ALIGN - 1); +} + +static void uheap_coalesce(void) { + struct uheap_block *b = g_head; + while (b && b->next) { + uint8_t *bend = + (uint8_t *)b + sizeof(struct uheap_block) + b->size; + if (b->free && b->next->free && + bend == (uint8_t *)b->next) { + b->size += sizeof(struct uheap_block) + b->next->size; + b->next = b->next->next; + continue; + } + b = b->next; + } +} + +static void uheap_insert_sorted(struct uheap_block *nb) { + nb->next = NULL; + if (!g_head || (uintptr_t)nb < (uintptr_t)g_head) { + nb->next = g_head; + g_head = nb; + return; + } + struct uheap_block *b = g_head; + while (b->next && (uintptr_t)b->next < (uintptr_t)nb) + b = b->next; + nb->next = b->next; + b->next = nb; +} + +void *uheap_alloc(uint64_t size) { + if (size == 0) + return NULL; + uint64_t need = uheap_align_up(size); + uint64_t flags = uheap_irq_save(); + for (int attempt = 0; attempt < 2; attempt++) { + struct uheap_block *b = g_head; + while (b) { + if (b->free && b->magic == UHEAP_MAGIC && b->size >= need) { + uint64_t leftover = b->size - need; + if (leftover >= sizeof(struct uheap_block) + UHEAP_ALIGN) { + struct uheap_block *split = + (struct uheap_block *)((uint8_t *)(b + 1) + need); + split->magic = UHEAP_MAGIC; + split->free = 1; + split->size = leftover - sizeof(struct uheap_block); + split->next = b->next; + b->size = need; + b->next = split; + } + b->free = 0; + uheap_irq_restore(flags); + return (void *)(b + 1); + } + b = b->next; + } + if (attempt == 0) { + uint64_t total = need + sizeof(struct uheap_block); + uint64_t pages = (total + 4095) / 4096; + if (pages == 0) + pages = 1; + uint64_t phys = pmm_allocate_contiguous(pages); + if (phys == 0) { + uheap_irq_restore(flags); + return NULL; + } + void *virt = pmm_physical_to_virtual(phys); + if (!virt) { + pmm_free_contiguous(phys, pages); + uheap_irq_restore(flags); + return NULL; + } + struct uheap_block *nb = (struct uheap_block *)virt; + nb->magic = UHEAP_MAGIC; + nb->free = 1; + nb->size = pages * 4096 - sizeof(struct uheap_block); + nb->next = NULL; + uheap_insert_sorted(nb); + uheap_coalesce(); + } + } + uheap_irq_restore(flags); + return NULL; +} + +void *uheap_alloc_zeroed(uint64_t size) { + void *p = uheap_alloc(size); + if (p) + memset(p, 0, (size_t)size); + return p; +} + +void uheap_free(void *ptr) { + if (!ptr) + return; + uint64_t flags = uheap_irq_save(); + struct uheap_block *b = ((struct uheap_block *)ptr) - 1; + if (b->magic != UHEAP_MAGIC) { + uheap_irq_restore(flags); + return; + } + b->free = 1; + uheap_coalesce(); + uheap_irq_restore(flags); +}