dev/acpi: Implement uACPI kernel APIs

This commit is contained in:
2026-10-28 22:07:39 +01:00
parent 8f30525cab
commit b52a3949a5
11 changed files with 672 additions and 11 deletions
+7
View File
@@ -2,14 +2,21 @@
#pragma once
#include <zinnia/errno.h>
#include <zinnia/init.h>
#include <zinnia/initgraph.h>
#include <zinnia/types.h>
#include <zinnia/workqueue.h>
#include <stdint.h>
INIT_EXTERN(acpi_stage);
INIT_EXTERN(acpi_namespace_stage);
extern phys_t acpi_rsdp_addr;
extern struct workqueue* acpi_workqueue;
errno_t acpi_reboot();
errno_t acpi_power_off();
struct acpi_match {
const char* hid;
+70
View File
@@ -5,14 +5,21 @@
#include <zinnia/acpi.h>
#include <zinnia/alloc.h>
#include <zinnia/cmdline.h>
#include <zinnia/irq.h>
#include <zinnia/pci.h>
#include <zinnia/percpu.h>
#include <zinnia/print.h>
#include <zinnia/sched.h>
#include <zinnia/types.h>
#include <zinnia/workqueue.h>
#include <uacpi/event.h>
#include <uacpi/sleep.h>
#include <uacpi/status.h>
#include <uacpi/tables.h>
#include <uacpi/uacpi.h>
phys_t acpi_rsdp_addr = 0;
struct workqueue* acpi_workqueue;
static char table_buf[0x1000];
@@ -45,3 +52,66 @@ static bool acpi_stage_init(struct boot_info* info) {
INIT_TASK(acpi_stage, "dev.acpi", acpi_stage_init);
INIT_DEPENDS(acpi_stage, mem_stage);
INIT_ENTAILS(acpi_stage, percpu_stage);
static bool namespace_done;
[[__init]]
static void acpi_namespace_init(struct boot_info*) {
errno_t error = workqueue_create("acpi", SCHED_PRIO_WORK_HIGH, &acpi_workqueue);
if (error) {
log_err("Failed to create the ACPI workqueue (%i)\n", error);
return;
}
uacpi_status status = uacpi_initialize(0);
if (status != UACPI_STATUS_OK) {
log_err("Initialization failed with error \"%s\"!\n", uacpi_status_to_string(status));
return;
}
status = uacpi_namespace_load();
if (status != UACPI_STATUS_OK) {
log_err("Namespace loading failed with error \"%s\"!\n", uacpi_status_to_string(status));
return;
}
status = uacpi_namespace_initialize();
if (status != UACPI_STATUS_OK) {
log_err("Namespace initialization failed with error \"%s\"!\n", uacpi_status_to_string(status));
return;
}
status = uacpi_finalize_gpe_initialization();
if (status != UACPI_STATUS_OK)
log_warn("GPE initialization failed with error \"%s\"!\n", uacpi_status_to_string(status));
namespace_done = true;
}
INIT_TASK(acpi_namespace_stage, "dev.acpi.namespace", acpi_namespace_init);
INIT_DEPENDS(acpi_namespace_stage, acpi_stage, workqueue_stage);
#if CONFIG_PCI
INIT_DEPENDS(acpi_namespace_stage, pci_stage);
#endif
errno_t acpi_reboot() {
if (!namespace_done)
return ENOSYS;
uacpi_status status = uacpi_reboot();
log_err("Failed to reset the system: %s\n", uacpi_status_to_string(status));
return EIO;
}
errno_t acpi_power_off() {
if (!namespace_done)
return ENOSYS;
uacpi_status status = uacpi_prepare_for_sleep_state(UACPI_SLEEP_STATE_S5);
if (status == UACPI_STATUS_OK) {
irq_lock();
status = uacpi_enter_sleep_state(UACPI_SLEEP_STATE_S5);
irq_unlock();
}
log_err("Failed to power off the system: %s\n", uacpi_status_to_string(status));
return EIO;
}
-1
View File
@@ -15,6 +15,5 @@ kernel_includes += include_directories('include')
kernel_c_args += [
'-DUACPI_FORMATTED_LOGGING',
'-DUACPI_BAREBONES_MODE', # TODO: Remove
'-DUACPI_OVERRIDE_TYPES',
]
+258
View File
@@ -2,14 +2,23 @@
#include <zinnia/acpi.h>
#include <zinnia/alloc.h>
#include <zinnia/clock.h>
#include <zinnia/console.h>
#include <zinnia/init.h>
#include <zinnia/irq.h>
#include <zinnia/mmio.h>
#include <zinnia/pci.h>
#include <zinnia/percpu.h>
#include <zinnia/pmap.h>
#include <zinnia/print.h>
#include <zinnia/sched.h>
#include <zinnia/semaphore.h>
#include <zinnia/spin.h>
#include <zinnia/utils.h>
#include <zinnia/virt.h>
#include <zinnia/vma.h>
#include <zinnia/workqueue.h>
#include <arch/irq.h>
#include <arch/mem.h>
#include <stdarg.h>
#include <uacpi/kernel_api.h>
@@ -73,3 +82,252 @@ void uacpi_kernel_log(uacpi_log_level lvl, const uacpi_char* msg, ...) {
void uacpi_kernel_vlog(uacpi_log_level lvl, const uacpi_char* msg, uacpi_va_list args) {
log_vwrite_prefixed(acpi_log_level(lvl), "acpi: ", msg, args);
}
uacpi_status uacpi_kernel_pci_device_open(uacpi_pci_address address, uacpi_handle* out_handle) {
#if CONFIG_PCI
struct pci_address addr = {
.domain = address.segment,
.bus = address.bus,
.device = address.device,
.function = address.function,
};
struct pci_access* access = pci_access_find(&addr);
if (!access)
return UACPI_STATUS_NOT_FOUND;
struct pci_device* dev = mem_alloc(sizeof(*dev), 0);
if (!dev)
return UACPI_STATUS_OUT_OF_MEMORY;
dev->address = addr;
dev->access = access;
*out_handle = dev;
return UACPI_STATUS_OK;
#else
return UACPI_STATUS_NOT_FOUND;
#endif
}
void uacpi_kernel_pci_device_close(uacpi_handle handle) {
mem_free(handle);
}
#if CONFIG_PCI
#define PCI_READ(width) \
uacpi_status uacpi_kernel_pci_read##width(uacpi_handle device, uacpi_size offset, uacpi_u##width* value) { \
*value = pci_read##width(device, offset); \
return UACPI_STATUS_OK; \
}
#define PCI_WRITE(width) \
uacpi_status uacpi_kernel_pci_write##width(uacpi_handle device, uacpi_size offset, uacpi_u##width value) { \
pci_write##width(device, offset, value); \
return UACPI_STATUS_OK; \
}
#else
#define PCI_READ(width) \
uacpi_status uacpi_kernel_pci_read##width(uacpi_handle device, uacpi_size offset, uacpi_u##width* value) { \
return UACPI_STATUS_UNIMPLEMENTED; \
}
#define PCI_WRITE(width) \
uacpi_status uacpi_kernel_pci_write##width(uacpi_handle device, uacpi_size offset, uacpi_u##width value) { \
return UACPI_STATUS_UNIMPLEMENTED; \
}
#endif
PCI_READ(8)
PCI_READ(16)
PCI_READ(32)
PCI_WRITE(8)
PCI_WRITE(16)
PCI_WRITE(32)
void* uacpi_kernel_alloc(uacpi_size size) {
return mem_alloc(size, ALLOC_NOZERO);
}
void uacpi_kernel_free(void* mem) {
if (mem)
mem_free(mem);
}
uacpi_u64 uacpi_kernel_get_nanoseconds_since_boot() {
return clock_get_elapsed_ns();
}
void uacpi_kernel_stall(uacpi_u8 usec) {
clock_spin_ns(usec * 1000ull);
}
void uacpi_kernel_sleep(uacpi_u64 msec) {
sched_sleep_until(clock_get_elapsed_ns() + msec * 1'000'000);
}
uacpi_handle uacpi_kernel_create_semaphore(uacpi_u32 initial_units) {
sem_t* sem = mem_alloc(sizeof(*sem), 0);
if (sem)
sem_init(sem, initial_units);
return sem;
}
void uacpi_kernel_free_semaphore(uacpi_handle handle) {
mem_free(handle);
}
uacpi_status uacpi_kernel_wait_for_semaphore(uacpi_handle handle, uacpi_u16 timeout) {
sem_t* sem = handle;
if (timeout == 0)
return sem_trywait(sem) ? UACPI_STATUS_OK : UACPI_STATUS_TIMEOUT;
if (timeout == 0xFFFF) {
sem_wait(sem);
return UACPI_STATUS_OK;
}
const uint64_t deadline = clock_get_elapsed_ns() + timeout * 1'000'000ull;
return sem_timedwait(sem, &deadline) ? UACPI_STATUS_OK : UACPI_STATUS_TIMEOUT;
}
void uacpi_kernel_signal_semaphore(uacpi_handle handle) {
sem_post(handle);
}
uacpi_thread_id uacpi_kernel_get_thread_id() {
return percpu_get()->sched.current;
}
uacpi_interrupt_state uacpi_kernel_disable_interrupts() {
irq_lock();
return 0;
}
void uacpi_kernel_restore_interrupts(uacpi_interrupt_state) {
irq_unlock();
}
uacpi_status uacpi_kernel_handle_firmware_request(uacpi_firmware_request* req) {
switch (req->type) {
case UACPI_FIRMWARE_REQUEST_TYPE_BREAKPOINT:
log_warn("acpi: Ignoring AML breakpoint\n");
break;
case UACPI_FIRMWARE_REQUEST_TYPE_FATAL:
log_err(
"acpi: Fatal firmware error: type %u, code %#x, arg %#llx\n",
req->fatal.type,
req->fatal.code,
(unsigned long long)req->fatal.arg
);
break;
}
return UACPI_STATUS_OK;
}
struct acpi_irq {
struct irq_handler handler;
struct irq_line* line;
uacpi_interrupt_handler func;
uacpi_handle ctx;
};
static enum irq_status acpi_irq_raise(struct irq_handler* self) {
struct acpi_irq* irq = containerof(self, struct acpi_irq, handler);
return irq->func(irq->ctx) == UACPI_INTERRUPT_HANDLED ? IRQ_STATUS_HANDLED : IRQ_STATUS_IGNORED;
}
uacpi_status uacpi_kernel_install_interrupt_handler(
uacpi_u32 irq,
uacpi_interrupt_handler handler,
uacpi_handle ctx,
uacpi_handle* out_irq_handle
) {
uint32_t gsi = irq;
enum irq_trigger_mode trigger = IRQ_TRIGGER_LEVEL;
enum irq_polarity polarity = IRQ_POLARITY_LOW;
if (irq <= UINT8_MAX)
arch_irq_isa_override(irq, &gsi, &trigger, &polarity);
struct irq_line* line;
errno_t status = arch_irq_get_gsi(gsi, &line);
if (status) {
log_err("acpi: Unable to get GSI %u for IRQ %u (%i)\n", gsi, irq, status);
return UACPI_STATUS_UNIMPLEMENTED;
}
struct acpi_irq* acpi_irq = mem_alloc(sizeof(*acpi_irq), 0);
if (!acpi_irq)
return UACPI_STATUS_OUT_OF_MEMORY;
acpi_irq->handler.raise = acpi_irq_raise;
acpi_irq->line = line;
acpi_irq->func = handler;
acpi_irq->ctx = ctx;
irq_line_attach(line, &acpi_irq->handler);
irq_line_program(line, trigger, polarity);
log("acpi: Installed handler for IRQ %u on GSI %u\n", irq, gsi);
*out_irq_handle = acpi_irq;
return UACPI_STATUS_OK;
}
uacpi_status uacpi_kernel_uninstall_interrupt_handler(uacpi_interrupt_handler handler, uacpi_handle irq_handle) {
struct acpi_irq* acpi_irq = irq_handle;
struct irq_line* line = acpi_irq->line;
irq_line_detach(line, &acpi_irq->handler);
if (list_is_empty(&line->handlers) && line->mask)
line->mask(line);
mem_free(acpi_irq);
return UACPI_STATUS_OK;
}
uacpi_handle uacpi_kernel_create_spinlock() {
struct spinlock* lock = mem_alloc(sizeof(*lock), 0);
if (lock)
spin_init(lock);
return lock;
}
void uacpi_kernel_free_spinlock(uacpi_handle handle) {
mem_free(handle);
}
uacpi_cpu_flags uacpi_kernel_lock_spinlock(uacpi_handle handle) {
spin_lock(handle);
return 0;
}
void uacpi_kernel_unlock_spinlock(uacpi_handle handle, uacpi_cpu_flags) {
spin_unlock(handle);
}
struct acpi_work {
struct work work;
uacpi_work_handler handler;
uacpi_handle ctx;
};
static void acpi_work_fn(struct work* work) {
struct acpi_work* item = containerof(work, struct acpi_work, work);
const uacpi_work_handler handler = item->handler;
const uacpi_handle ctx = item->ctx;
handler(ctx);
}
uacpi_handle uacpi_kernel_create_work_item() {
struct acpi_work* item = mem_alloc(sizeof(*item), 0);
if (item)
work_init(&item->work, acpi_work_fn);
return item;
}
void uacpi_kernel_free_work_item(uacpi_handle handle) {
mem_free(handle);
}
void uacpi_kernel_schedule_work(uacpi_work_type, uacpi_handle work_item, uacpi_work_handler handler, uacpi_handle ctx) {
struct acpi_work* item = work_item;
item->handler = handler;
item->ctx = ctx;
work_queue(acpi_workqueue, &item->work);
}
uacpi_status uacpi_kernel_wait_for_work_completion() {
workqueue_flush(acpi_workqueue);
return UACPI_STATUS_OK;
}