diff --git a/arch/x86_64/include/x86_64/apic.h b/arch/x86_64/include/x86_64/apic.h index f6f0d14..79252ea 100644 --- a/arch/x86_64/include/x86_64/apic.h +++ b/arch/x86_64/include/x86_64/apic.h @@ -2,6 +2,9 @@ #pragma once +#include +#include +#include #include #include @@ -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); diff --git a/arch/x86_64/ioapic.c b/arch/x86_64/ioapic.c new file mode 100644 index 0000000..7d9216e --- /dev/null +++ b/arch/x86_64/ioapic.c @@ -0,0 +1,281 @@ +// SPDX-License-Identifier: ISC + +#define pr_fmt(fmt) "ioapic: " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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); diff --git a/arch/x86_64/irq.c b/arch/x86_64/irq.c index 97c95b9..6e97907 100644 --- a/arch/x86_64/irq.c +++ b/arch/x86_64/irq.c @@ -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; diff --git a/arch/x86_64/meson.build b/arch/x86_64/meson.build index 5a34550..c869a09 100644 --- a/arch/x86_64/meson.build +++ b/arch/x86_64/meson.build @@ -10,6 +10,7 @@ kernel_sources += files( 'gdt.c', 'hpet.c', 'idt.c', + 'ioapic.c', 'irq.c', 'mem.c', 'percpu.c', diff --git a/dev/acpi/include/zinnia/acpi.h b/dev/acpi/include/zinnia/acpi.h index 0f37767..b68c2c3 100644 --- a/dev/acpi/include/zinnia/acpi.h +++ b/dev/acpi/include/zinnia/acpi.h @@ -2,14 +2,21 @@ #pragma once +#include #include #include #include +#include #include 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; diff --git a/dev/acpi/init.c b/dev/acpi/init.c index e2f7824..be46d39 100644 --- a/dev/acpi/init.c +++ b/dev/acpi/init.c @@ -5,14 +5,21 @@ #include #include #include +#include +#include #include #include +#include #include +#include +#include +#include #include #include #include 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; +} diff --git a/dev/acpi/meson.build b/dev/acpi/meson.build index b6aab99..f1f1487 100644 --- a/dev/acpi/meson.build +++ b/dev/acpi/meson.build @@ -15,6 +15,5 @@ kernel_includes += include_directories('include') kernel_c_args += [ '-DUACPI_FORMATTED_LOGGING', - '-DUACPI_BAREBONES_MODE', # TODO: Remove '-DUACPI_OVERRIDE_TYPES', ] diff --git a/dev/acpi/uacpi_glue.c b/dev/acpi/uacpi_glue.c index c3ad765..fd7e36d 100644 --- a/dev/acpi/uacpi_glue.c +++ b/dev/acpi/uacpi_glue.c @@ -2,14 +2,23 @@ #include #include +#include #include #include +#include #include +#include +#include #include #include +#include +#include +#include #include #include #include +#include +#include #include #include #include @@ -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; +} diff --git a/include/arch/irq.h b/include/arch/irq.h index f95adb0..39c68ca 100644 --- a/include/arch/irq.h +++ b/include/arch/irq.h @@ -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 +); diff --git a/include/zinnia/irq.h b/include/zinnia/irq.h index cd93147..e2badae 100644 --- a/include/zinnia/irq.h +++ b/include/zinnia/irq.h @@ -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(); diff --git a/lib/irq.c b/lib/irq.c index 1b16360..a459a0b 100644 --- a/lib/irq.c +++ b/lib/irq.c @@ -5,6 +5,7 @@ #include #include #include +#include #include [[__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(); +}