dev/acpi: Implement uACPI kernel APIs
This commit is contained in:
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -15,6 +15,5 @@ kernel_includes += include_directories('include')
|
||||
|
||||
kernel_c_args += [
|
||||
'-DUACPI_FORMATTED_LOGGING',
|
||||
'-DUACPI_BAREBONES_MODE', # TODO: Remove
|
||||
'-DUACPI_OVERRIDE_TYPES',
|
||||
]
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user