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
+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;
}