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
+8
View File
@@ -2,6 +2,9 @@
#pragma once
#include <zinnia/errno.h>
#include <zinnia/initgraph.h>
#include <zinnia/irq.h>
#include <zinnia/mmio.h>
#include <stdint.h>
@@ -81,3 +84,8 @@ void lapic_send_ipi(
);
void pic_disable();
errno_t apic_alloc_vector(struct irq_line* line, uint8_t* out_vector, uint32_t* out_lapic);
void apic_free_vector(uint8_t vector);
INIT_EXTERN(ioapic_stage);
+281
View File
@@ -0,0 +1,281 @@
// SPDX-License-Identifier: ISC
#define pr_fmt(fmt) "ioapic: " fmt
#include <zinnia/acpi.h>
#include <zinnia/alloc.h>
#include <zinnia/initgraph.h>
#include <zinnia/irq.h>
#include <zinnia/mmio.h>
#include <zinnia/percpu.h>
#include <zinnia/pmap.h>
#include <zinnia/print.h>
#include <zinnia/spin.h>
#include <zinnia/utils.h>
#include <zinnia/virt.h>
#include <zinnia/vma.h>
#include <arch/irq.h>
#include <arch/mem.h>
#include <uacpi/acpi.h>
#include <uacpi/status.h>
#include <uacpi/tables.h>
#include <x86_64/apic.h>
#include <x86_64/percpu.h>
constexpr size_t ISA_IRQS = 16;
constexpr mmio_reg32 IOAPIC_REG_SELECT = {.offset = 0x00};
constexpr mmio_reg32 IOAPIC_REG_WINDOW = {.offset = 0x10};
constexpr uint32_t IOAPIC_VERSION = 0x01;
constexpr uint32_t IOAPIC_REDIRECT = 0x10;
constexpr uint64_t IOAPIC_RTE_POLARITY_LOW = 1 << 13;
constexpr uint64_t IOAPIC_RTE_TRIGGER_LEVEL = 1 << 15;
constexpr uint64_t IOAPIC_RTE_MASKED = 1 << 16;
struct ioapic_line {
struct irq_line line;
struct ioapic* ioapic;
uint32_t pin;
uint32_t lapic;
uint8_t vector;
};
struct ioapic {
void* regs;
struct ioapic_line** lines;
struct spinlock lock;
uint32_t gsi_base;
uint32_t num_pins;
};
struct isa_override {
uint32_t gsi;
uint16_t flags;
bool present;
};
static struct ioapic* ioapics;
static size_t num_ioapics;
static struct isa_override isa_overrides[ISA_IRQS];
static struct spinlock lines_lock;
static uint32_t ioapic_read(struct ioapic* ioapic, uint32_t reg) {
mmio_reg_write(ioapic->regs, IOAPIC_REG_SELECT, reg);
return mmio_reg_read(ioapic->regs, IOAPIC_REG_WINDOW);
}
static void ioapic_write(struct ioapic* ioapic, uint32_t reg, uint32_t value) {
mmio_reg_write(ioapic->regs, IOAPIC_REG_SELECT, reg);
mmio_reg_write(ioapic->regs, IOAPIC_REG_WINDOW, value);
}
static void write_rte(struct ioapic* ioapic, uint32_t pin, uint64_t rte) {
ioapic_write(ioapic, IOAPIC_REDIRECT + pin * 2, IOAPIC_RTE_MASKED);
ioapic_write(ioapic, IOAPIC_REDIRECT + pin * 2 + 1, rte >> 32);
ioapic_write(ioapic, IOAPIC_REDIRECT + pin * 2, rte);
}
static void update_mask(struct ioapic_line* line, bool masked) {
struct ioapic* ioapic = line->ioapic;
spin_lock(&ioapic->lock);
uint32_t low = ioapic_read(ioapic, IOAPIC_REDIRECT + line->pin * 2);
low = masked ? low | IOAPIC_RTE_MASKED : low & ~IOAPIC_RTE_MASKED;
ioapic_write(ioapic, IOAPIC_REDIRECT + line->pin * 2, low);
spin_unlock(&ioapic->lock);
}
static enum irq_mode ioapic_set_config(
struct irq_line* self,
enum irq_trigger_mode trigger,
enum irq_polarity polarity
) {
struct ioapic_line* line = containerof(self, struct ioapic_line, line);
uint64_t rte = line->vector | ((uint64_t)line->lapic << 56);
if (polarity == IRQ_POLARITY_LOW)
rte |= IOAPIC_RTE_POLARITY_LOW;
if (trigger == IRQ_TRIGGER_LEVEL)
rte |= IOAPIC_RTE_TRIGGER_LEVEL;
spin_lock(&line->ioapic->lock);
write_rte(line->ioapic, line->pin, rte);
spin_unlock(&line->ioapic->lock);
return IRQ_MODE_EOI | IRQ_MODE_MASKABLE;
}
static void ioapic_mask(struct irq_line* self) {
update_mask(containerof(self, struct ioapic_line, line), true);
}
static void ioapic_unmask(struct irq_line* self) {
update_mask(containerof(self, struct ioapic_line, line), false);
}
static void ioapic_eoi(struct irq_line* self) {
lapic_eoi(&percpu_ref(arch_data)->lapic);
}
errno_t arch_irq_get_gsi(uint32_t gsi, struct irq_line** out) {
// Find the IOAPIC that handles this GSI.
struct ioapic* ioapic = nullptr;
for (size_t i = 0; i < num_ioapics; i++) {
if (gsi >= ioapics[i].gsi_base && gsi < ioapics[i].gsi_base + ioapics[i].num_pins) {
ioapic = &ioapics[i];
break;
}
}
if (!ioapic)
return ENOENT;
const uint32_t pin = gsi - ioapic->gsi_base;
spin_lock(&lines_lock);
struct ioapic_line* line = ioapic->lines[pin];
if (line) {
spin_unlock(&lines_lock);
*out = &line->line;
return 0;
}
spin_unlock(&lines_lock);
line = mem_alloc(sizeof(*line), 0);
if (!line)
return ENOMEM;
line->line.set_config = ioapic_set_config;
line->line.mask = ioapic_mask;
line->line.unmask = ioapic_unmask;
line->line.eoi = ioapic_eoi;
line->ioapic = ioapic;
line->pin = pin;
errno_t status = apic_alloc_vector(&line->line, &line->vector, &line->lapic);
if (status) {
mem_free(line);
return status;
}
spin_lock(&lines_lock);
if (ioapic->lines[pin]) {
spin_unlock(&lines_lock);
apic_free_vector(line->vector);
mem_free(line);
return arch_irq_get_gsi(gsi, out);
}
ioapic->lines[pin] = line;
spin_unlock(&lines_lock);
*out = &line->line;
return 0;
}
bool arch_irq_isa_override(
uint8_t isa_irq,
uint32_t* out_gsi,
enum irq_trigger_mode* out_trigger,
enum irq_polarity* out_polarity
) {
if (isa_irq >= ISA_IRQS || !isa_overrides[isa_irq].present)
return false;
const struct isa_override* iso = &isa_overrides[isa_irq];
*out_gsi = iso->gsi;
switch (iso->flags & ACPI_MADT_POLARITY_MASK) {
case ACPI_MADT_POLARITY_ACTIVE_HIGH:
*out_polarity = IRQ_POLARITY_HIGH;
break;
case ACPI_MADT_POLARITY_ACTIVE_LOW:
*out_polarity = IRQ_POLARITY_LOW;
break;
}
switch (iso->flags & ACPI_MADT_TRIGGERING_MASK) {
case ACPI_MADT_TRIGGERING_EDGE:
*out_trigger = IRQ_TRIGGER_EDGE;
break;
case ACPI_MADT_TRIGGERING_LEVEL:
*out_trigger = IRQ_TRIGGER_LEVEL;
break;
}
return true;
}
static uacpi_iteration_decision count_ioapics(uacpi_handle user, struct acpi_entry_hdr* hdr) {
size_t* count = user;
if (hdr->type == ACPI_MADT_ENTRY_TYPE_IOAPIC)
(*count)++;
return UACPI_ITERATION_DECISION_CONTINUE;
}
static uacpi_iteration_decision parse_madt_entry(uacpi_handle, struct acpi_entry_hdr* hdr) {
switch (hdr->type) {
case ACPI_MADT_ENTRY_TYPE_IOAPIC: {
// Map the IOAPIC and mask all of its pins.
const struct acpi_madt_ioapic* entry = (const struct acpi_madt_ioapic*)hdr;
uintptr_t vaddr = vm_alloc(arch_mem_page_size());
if (!vaddr)
return UACPI_ITERATION_DECISION_CONTINUE;
pmap_map(&kernel_vma.pmap, vaddr, entry->address, PTE_READ | PTE_WRITE, CACHE_MMIO);
struct ioapic* ioapic = &ioapics[num_ioapics];
ioapic->regs = (void*)vaddr;
ioapic->gsi_base = entry->gsi_base;
ioapic->num_pins = ((ioapic_read(ioapic, IOAPIC_VERSION) >> 16) & 0xFF) + 1;
spin_init(&ioapic->lock);
ioapic->lines = mem_alloc(ioapic->num_pins * sizeof(*ioapic->lines), 0);
if (!ioapic->lines) {
pmap_unmap(&kernel_vma.pmap, vaddr);
vm_free(vaddr, arch_mem_page_size());
return UACPI_ITERATION_DECISION_CONTINUE;
}
for (uint32_t pin = 0; pin < ioapic->num_pins; pin++)
write_rte(ioapic, pin, IOAPIC_RTE_MASKED);
num_ioapics++;
log("IOAPIC %u at %#x handles GSIs %u-%u\n",
entry->id,
entry->address,
ioapic->gsi_base,
ioapic->gsi_base + ioapic->num_pins - 1);
break;
}
case ACPI_MADT_ENTRY_TYPE_INTERRUPT_SOURCE_OVERRIDE:
auto iso = (const struct acpi_madt_interrupt_source_override*)hdr;
if (iso->bus == 0 && iso->source < ISA_IRQS) {
isa_overrides[iso->source] = (struct isa_override){
.gsi = iso->gsi,
.flags = iso->flags,
.present = true,
};
}
break;
}
return UACPI_ITERATION_DECISION_CONTINUE;
}
static void ioapic_init(struct boot_info*) {
uacpi_table table;
if (uacpi_table_find_by_signature(ACPI_MADT_SIGNATURE, &table) != UACPI_STATUS_OK) {
log_warn("No MADT found\n");
return;
}
size_t capacity = 0;
uacpi_status status = uacpi_for_each_subtable(table.hdr, sizeof(struct acpi_madt), count_ioapics, &capacity);
if (status != UACPI_STATUS_OK)
log_warn("Failed to parse the MADT: %s\n", uacpi_status_to_string(status));
if (capacity) {
ioapics = mem_alloc(capacity * sizeof(*ioapics), 0);
if (!ioapics) {
log_err("Unable to allocate %zu controllers\n", capacity);
uacpi_table_unref(&table);
return;
}
}
uacpi_for_each_subtable(table.hdr, sizeof(struct acpi_madt), parse_madt_entry, nullptr);
uacpi_table_unref(&table);
}
INIT_TASK(ioapic_stage, "arch.x86_64.ioapic", ioapic_init);
INIT_DEPENDS(ioapic_stage, acpi_stage, percpu_stage);
INIT_ENTAILS(ioapic_stage, acpi_namespace_stage);
+25 -10
View File
@@ -61,11 +61,7 @@ static void apic_msi_eoi(struct irq_line* self) {
lapic_eoi(&percpu_ref(arch_data)->lapic);
}
errno_t arch_irq_alloc_msi(struct msi_line** out) {
struct apic_msi_line* line = mem_alloc(sizeof(*line), 0);
if (!line)
return ENOMEM;
errno_t apic_alloc_vector(struct irq_line* line, uint8_t* out_vector, uint32_t* out_lapic) {
irq_lock();
struct arch_percpu* arch_cpu = percpu_ref(arch_data);
size_t slot;
@@ -75,20 +71,39 @@ errno_t arch_irq_alloc_msi(struct msi_line** out) {
}
if (slot == ARRAY_SIZE(arch_cpu->irq_lines)) {
irq_unlock();
mem_free(line);
return ENOSPC;
}
*out_vector = slot + 0x20;
*out_lapic = arch_cpu->lapic.id;
arch_cpu->irq_lines[slot] = line;
irq_unlock();
return 0;
}
void apic_free_vector(uint8_t vector) {
irq_lock();
percpu_ref(arch_data)->irq_lines[vector - 0x20] = nullptr;
irq_unlock();
}
errno_t arch_irq_alloc_msi(struct msi_line** out) {
struct apic_msi_line* line = mem_alloc(sizeof(*line), 0);
if (!line)
return ENOMEM;
line->msi.line.set_config = apic_msi_set_config;
line->msi.line.mask = apic_msi_mask;
line->msi.line.unmask = apic_msi_unmask;
line->msi.line.eoi = apic_msi_eoi;
line->msi.msg_addr = apic_msi_msg_addr;
line->msi.msg_data = apic_msi_msg_data;
line->vector = slot + 0x20;
line->lapic = arch_cpu->lapic.id;
arch_cpu->irq_lines[slot] = &line->msi.line;
irq_unlock();
errno_t status = apic_alloc_vector(&line->msi.line, &line->vector, &line->lapic);
if (status) {
mem_free(line);
return status;
}
*out = &line->msi;
return 0;
+1
View File
@@ -10,6 +10,7 @@ kernel_sources += files(
'gdt.c',
'hpet.c',
'idt.c',
'ioapic.c',
'irq.c',
'mem.c',
'percpu.c',
+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;
}
+7
View File
@@ -8,3 +8,10 @@ bool arch_irq_set_state(bool state);
bool arch_irq_get_state();
void arch_irq_wait();
errno_t arch_irq_alloc_msi(struct msi_line** out);
errno_t arch_irq_get_gsi(uint32_t gsi, struct irq_line** out);
bool arch_irq_isa_override(
uint8_t isa_irq,
uint32_t* out_gsi,
enum irq_trigger_mode* out_trigger,
enum irq_polarity* out_polarity
);
+1
View File
@@ -61,6 +61,7 @@ extern struct irq_percpu irq_data;
void irq_line_program(struct irq_line* line, enum irq_trigger_mode trigger, enum irq_polarity polarity);
void irq_line_raise(struct irq_line* line);
void irq_line_attach(struct irq_line* line, struct irq_handler* handler);
void irq_line_detach(struct irq_line* line, struct irq_handler* handler);
void irq_lock();
void irq_unlock();
+14
View File
@@ -5,6 +5,7 @@
#include <zinnia/list.h>
#include <zinnia/percpu.h>
#include <zinnia/print.h>
#include <arch/cpu.h>
#include <arch/irq.h>
[[__percpu]]
@@ -69,3 +70,16 @@ void irq_line_attach(struct irq_line* line, struct irq_handler* handler) {
list_insert_head(&line->handlers, handler, next);
irq_unlock();
}
void irq_line_detach(struct irq_line* line, struct irq_handler* handler) {
irq_lock();
struct irq_handler** link = &list_first(&line->handlers);
while (*link && *link != handler)
link = &list_next(*link, next);
if (*link)
*link = list_next(handler, next);
irq_unlock();
while (__atomic_load_n(&line->is_busy, __ATOMIC_ACQUIRE))
arch_cpu_pause();
}