From a72352d1633263bdd4e41cfce5cdb6f6467ce5d6 Mon Sep 17 00:00:00 2001 From: "K. Lange" Date: Wed, 9 Feb 2022 21:02:00 +0900 Subject: [PATCH] aarch64: start work on interrupt dispatch --- kernel/arch/aarch64/main.c | 128 +++++++++++++++++++++++------------ kernel/arch/aarch64/virtio.c | 123 ++++++++++++++++++++++++++++++--- 2 files changed, 197 insertions(+), 54 deletions(-) diff --git a/kernel/arch/aarch64/main.c b/kernel/arch/aarch64/main.c index 6a68a08d..c7bfa417 100644 --- a/kernel/arch/aarch64/main.c +++ b/kernel/arch/aarch64/main.c @@ -152,25 +152,31 @@ void timer_start(void) { /* mask irqs */ asm volatile ("msr DAIFSet, #0b1111"); - /* Enable one of the timers. */ + /* Enable the local timer */ set_tick(); + asm volatile ( "mov x0, 1\n" "msr CNTV_CTL_EL0, x0\n" :::"x0"); - /* enable */ + /* This is global, we only need to do this once... */ gic_regs[0] = 1; + + /* This is specific to this CPU */ gicc_regs[0] = 1; + gicc_regs[1] = 0x1ff; - /* priority mask */ - gicc_regs[1] = 0xff; + /* Timer interrupts are private peripherals, so each CPU gets one */ + gic_regs[64] = 0xFFFFffff; //(1 << TIMER_IRQ); + gic_regs[160] = 0xFFFFffff; //(1 << TIMER_IRQ); - /* enable interrupts */ - gic_regs[64] = (1 << TIMER_IRQ); + /* These are shared? */ + gic_regs[65] = 0xFFFFFFFF; - /* clear this one */ - gic_regs[160] = (1 << TIMER_IRQ); + for (int i = 520; i <= 521; ++i) { + gic_regs[i] |= 0x07070707; + } } static volatile uint64_t time_slice_basis = 0; /**< When the last clock update happened */ @@ -307,36 +313,82 @@ void aarch64_sync_enter(struct regs * r) { } /* Unexpected fault, eg. page fault. */ - printf("In process %d (%s)\n", this_core->current_process->id, this_core->current_process->name); - printf("ESR: %#zx FAR: %#zx ELR: %#zx SPSR: %#zx\n", esr, far, elr, spsr); + dprintf("In process %d (%s)\n", this_core->current_process->id, this_core->current_process->name); + dprintf("ESR: %#zx FAR: %#zx ELR: %#zx SPSR: %#zx\n", esr, far, elr, spsr); aarch64_regs(r); uint64_t tpidr_el0; asm volatile ("mrs %0, TPIDR_EL0" : "=r"(tpidr_el0)); - printf(" TPIDR_EL0=%#zx\n", tpidr_el0); + dprintf(" TPIDR_EL0=%#zx\n", tpidr_el0); send_signal(this_core->current_process->id, SIGSEGV, 1); } -void aarch64_irq_enter(struct regs * r) { - uint32_t pending = gic_regs[160]; +char _ret_from_preempt_source[1]; +struct irq_callback { + void (*callback)(process_t * this, int irq, void *data); + process_t * owner; + void * data; +}; + +extern struct irq_callback irq_callbacks[]; + + +#define EOI(x) do { gicc_regs[4] = (x); } while (0) +static void aarch64_interrupt_dispatch(int from_wfi) { + uint32_t iar = gicc_regs[3]; + uint32_t irq = iar & 0x3FF; + //uint32_t cpu = (iar >> 10) & 0x7; + + switch (irq) { + case TIMER_IRQ: + update_clock(); + set_tick(); + EOI(iar); + if (from_wfi) { + switch_next(); + } else { + switch_task(1); + } + return; + + case 32: + case 33: + case 34: + case 35: + case 36: + case 37: + case 38: + case 39: + case 40: + case 41: + case 42: + { + struct irq_callback * cb = &irq_callbacks[irq-32]; + if (cb->owner) { + cb->callback(cb->owner, irq-32, cb->data); + } + EOI(iar); + return; + } + + case 1022: + case 1023: + return; + + default: + dprintf("gic: Unhandled interrupt: %d\n", irq); + EOI(iar); + return; + } +} + +void aarch64_irq_enter(struct regs * r) { if (this_core->current_process) { this_core->current_process->time_switch = arch_perf_timer(); } - /** - * TODO port the interrupt management system from x86. - */ - if (pending & (1 << TIMER_IRQ)) { - /* Timer interrupt fired in EL0. Update the global clock and reschedule */ - update_clock(); - set_tick(); - gic_regs[160] &= (1 << TIMER_IRQ); - switch_task(1); - } else if (pending) { - printf("Unexpected interrupt = %#x\n", pending); - arch_fatal(); - } + aarch64_interrupt_dispatch(0); } /** @@ -349,14 +401,14 @@ void aarch64_fault_enter(struct regs * r) { asm volatile ("mrs %0, ELR_EL1" : "=r"(elr)); asm volatile ("mrs %0, SPSR_EL1" : "=r"(spsr)); - printf("EL1-EL1 fault handler, core %d\n", this_core->cpu_id); - printf("In process %d (%s)\n", this_core->current_process->id, this_core->current_process->name); - printf("ESR: %#zx FAR: %#zx ELR: %#zx SPSR: %#zx\n", esr, far, elr, spsr); + dprintf("EL1-EL1 fault handler, core %d\n", this_core->cpu_id); + dprintf("In process %d (%s)\n", this_core->current_process->id, this_core->current_process->name); + dprintf("ESR: %#zx FAR: %#zx ELR: %#zx SPSR: %#zx\n", esr, far, elr, spsr); aarch64_regs(r); uint64_t tpidr_el0; asm volatile ("mrs %0, TPIDR_EL0" : "=r"(tpidr_el0)); - printf(" TPIDR_EL0=%#zx\n", tpidr_el0); + dprintf(" TPIDR_EL0=%#zx\n", tpidr_el0); extern void aarch64_safe_dump_traceback(uintptr_t elr, struct regs * r); aarch64_safe_dump_traceback(elr, r); @@ -387,21 +439,7 @@ void arch_pause(void) { * the interrupt function won't be called, so we'll need to change * it once we start getting actual hardware interrupts. */ asm volatile ("wfi"); - - /* Update the clock and reset the timer */ - update_clock(); - set_tick(); - - /* This shouldn't happen now, but this symbol needs to be present - * somewhere or we'll fail to link. Keeping it as the asm stub - * for future use... */ - asm volatile ( - ".globl _ret_from_preempt_source\n" - "_ret_from_preempt_source:" - ); - - /* Force reschedule */ - switch_next(); + aarch64_interrupt_dispatch(1); } /** diff --git a/kernel/arch/aarch64/virtio.c b/kernel/arch/aarch64/virtio.c index 4e041329..f6941547 100644 --- a/kernel/arch/aarch64/virtio.c +++ b/kernel/arch/aarch64/virtio.c @@ -18,6 +18,17 @@ #include #include +#include + +static uint32_t swizzle(uint32_t from) { + uint8_t a = from >> 24; + uint8_t b = from >> 16; + uint8_t c = from >> 8; + uint8_t d = from; + return (d << 24) | (c << 16) | (b << 8) | (a); +} + + static fs_node_t * mouse_pipe; static fs_node_t * vmmouse_pipe; static fs_node_t * keyboard_pipe; @@ -99,18 +110,112 @@ struct virtio_input_event { uint32_t value; }; +struct irq_callback { + void (*callback)(process_t * this, int irq, void *data); + process_t * owner; + void * data; +}; + +struct irq_callback irq_callbacks[256]; + +static void tablet_responder(process_t * this, int irq, void * data) { + uint8_t cause = *(volatile uint8_t *)data; + if (cause == 1) { + make_process_ready(this); + } +} + +static void keyboard_responder(process_t * this, int irq, void * data) { + uint8_t cause = *(volatile uint8_t *)data; + if (cause == 1) { + make_process_ready(this); + } +} + +static void map_interrupt(const char * name, uint32_t device, int * int_out, void (*callback)(process_t*,int,void*), void * isr_addr) { + uint32_t phys_hi = (pci_extract_bus(device) << 16) | (pci_extract_slot(device) << 11); + uint32_t pin = pci_read_field(device, PCI_INTERRUPT_PIN, 1); + dprintf("%s: device %#x, slot = %d (0x%04x), irq pin = %d\n", name, device, pci_extract_slot(device), + phys_hi, pin); + + uint32_t * pcie_dtb = dtb_find_node_prefix("pcie@"); + if (!pcie_dtb) { + dprintf("%s: can't find dtb entry\n", name); + return; + } + + uint32_t * intMask = dtb_node_find_property(pcie_dtb, "interrupt-map-mask"); + if (!intMask) { + dprintf("%s: can't find property 'interrupt-map-mask'\n", name); + return; + } + + uint32_t * intMap = dtb_node_find_property(pcie_dtb, "interrupt-map"); + + if (!intMap) { + dprintf("%s: can't find property 'interrupt-map'\n", name); + return; + } + + for (int i = 0; i < swizzle(intMap[0])/4; i += 10) { + if (swizzle(intMap[i+2]) == (swizzle(intMask[2]) & phys_hi)) { + if (swizzle(intMap[i+5]) == (swizzle(intMask[5]) & pin)) { + dprintf("%s: %#x %#x %#x %#x\n", name, + swizzle(intMap[i+2]), swizzle(intMap[i+3]), swizzle(intMap[i+4]), swizzle(intMap[i+5])); + dprintf("%s: Matching device and pin, Interrupt maps to %d\n", name, swizzle(intMap[i+10])); + *int_out = swizzle(intMap[i+10]); + irq_callbacks[*int_out].callback = callback; + irq_callbacks[*int_out].owner = this_core->current_process; + irq_callbacks[*int_out].data = isr_addr; + return; + } + } + } + +} + static void virtio_tablet_thread(void * data) { while (this_core->cpu_id != 0) switch_task(1); + uint32_t device = (uintptr_t)data; uintptr_t t = 0x12000000; pci_write_field(device, PCI_BAR4, 4, t|8); pci_write_field(device, PCI_COMMAND, 2, 4|2|1); + + uint8_t caps = pci_read_field(device, 0x34, 1) & 0xFC; + dprintf("virtio: capabilities at 0x%02x\n", caps); + + if (caps) { + uint8_t next = caps; + do { + uint8_t cap_id = pci_read_field(device, next, 1); + dprintf("@%d cap %d\n", next, cap_id); + if (cap_id == 9) { + /* vendor cap */ + uint8_t len = pci_read_field(device, next+2, 1); + uint8_t type = pci_read_field(device, next+3, 1); + uint8_t bar = pci_read_field(device, next+4, 1); + uint32_t off = pci_read_field(device, next+8, 4); + + dprintf("len=%d type=%d bar=%d off=%#x\n", + len, type, bar, off); + + } + next = pci_read_field(device, next+1, 1); + } while (next); + } + struct virtio_device_cfg * cfg = (void*)((char*)mmu_map_mmio_region(t + 0x2000, 0x1000)); cfg->select = 1; /* ask for name */ cfg->subsel = 0; asm volatile ("isb" ::: "memory"); dprintf("virtio: found '%s'\n", cfg->data.str); + volatile char * irq_region = (char*)mmu_map_mmio_region(t + 0x1000, 0x1000); + int irq; + map_interrupt("virtio-tablet", device, &irq, tablet_responder, irq_region); + dprintf("virtio-tablet: irq is %d\n", irq); + /* figure out range values */ cfg->select = 0x12; cfg->subsel = 0; /* X */ @@ -184,9 +289,6 @@ static void virtio_tablet_thread(void * data) { while (1) { /* Inform the device we have room */ while (queue->used.index == index) { - unsigned long s, ss; - relative_time(0, 100, &s, &ss); - sleep_until((process_t *)this_core->current_process, s, ss); switch_task(0); } @@ -265,8 +367,9 @@ static const uint8_t ext_key_map[256] = { static void virtio_keyboard_thread(void * data) { while (this_core->cpu_id != 0) switch_task(1); + uint32_t device = (uintptr_t)data; - uintptr_t t = 0x12000000; + uintptr_t t = 0x12100000; pci_write_field(device, PCI_BAR4, 4, t|8); pci_write_field(device, PCI_COMMAND, 2, 4|2|1); struct virtio_device_cfg * cfg = (void*)((char*)mmu_map_mmio_region(t + 0x2000, 0x1000)); @@ -275,6 +378,11 @@ static void virtio_keyboard_thread(void * data) { asm volatile ("isb" ::: "memory"); dprintf("virtio: found '%s'\n", cfg->data.str); + volatile char * irq_region = (char*)mmu_map_mmio_region(t + 0x1000, 0x1000); + int irq; + map_interrupt("virtio-keyboard", device, &irq, keyboard_responder, irq_region); + dprintf("virtio-keyboard: irq is %d\n", irq); + cfg->select = 0; cfg->subsel = 0; asm volatile ("isb" ::: "memory"); @@ -327,9 +435,6 @@ static void virtio_keyboard_thread(void * data) { while (1) { /* Inform the device we have room */ while (queue->used.index == index) { - unsigned long s, ss; - relative_time(0, 100, &s, &ss); - sleep_until((process_t *)this_core->current_process, s, ss); switch_task(0); } @@ -394,11 +499,11 @@ void virtio_input(void) { vmmouse_pipe->flags = FS_CHARDEVICE; vfs_mount("/dev/vmmouse", vmmouse_pipe); - pci_scan(virtio_input_maybe, -1, NULL); - keyboard_pipe = make_pipe(128); keyboard_pipe->flags = FS_CHARDEVICE; vfs_mount("/dev/kbd", keyboard_pipe); + + pci_scan(virtio_input_maybe, -1, NULL); }