aarch64: start work on interrupt dispatch

This commit is contained in:
K. Lange 2022-02-09 21:02:00 +09:00
parent 8c7e6209f4
commit a72352d163
2 changed files with 197 additions and 54 deletions

View File

@ -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);
}
/**

View File

@ -18,6 +18,17 @@
#include <kernel/mouse.h>
#include <kernel/time.h>
#include <kernel/arch/aarch64/dtb.h>
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);
}