aarch64: interrupt mapping improvements, chained interrupt handlers

This commit is contained in:
K. Lange 2022-02-12 11:38:10 +09:00
parent b95f27dc60
commit f6d0206059
5 changed files with 130 additions and 82 deletions

View File

@ -0,0 +1,15 @@
#pragma once
#include <kernel/process.h>
struct irq_callback {
int (*callback)(process_t * this, int irq, void *data);
process_t * owner;
void * data;
struct irq_callback * next;
};
extern struct irq_callback * irq_callbacks[];
void gic_assign_interrupt(int irq, int (*callback)(process_t*,int,void*), void * data);
void gic_map_pci_interrupt(const char * name, uint32_t device, int * int_out, int (*callback)(process_t*,int,void*), void * isr_addr);

87
kernel/arch/aarch64/gic.c Normal file
View File

@ -0,0 +1,87 @@
/**
* @file kernel/arch/aarch64/virtio.c
* @brief Rudimentary, hacky implementations of virtio input devices.
*
* @copyright
* This file is part of ToaruOS and is released under the terms
* of the NCSA / University of Illinois License - see LICENSE.md
* Copyright (C) 2021-2022 K. Lange
*/
#include <stdint.h>
#include <kernel/printf.h>
#include <kernel/string.h>
#include <kernel/pci.h>
#include <kernel/process.h>
#include <kernel/spinlock.h>
#include <kernel/arch/aarch64/dtb.h>
#include <kernel/arch/aarch64/gic.h>
struct irq_callback * irq_callbacks[256] = {0};
static spin_lock_t irq_acquire;
void gic_assign_interrupt(int irq, int (*callback)(process_t*,int,void*), void * data) {
spin_lock(irq_acquire);
dprintf("gic: assign irq %d\n", irq);
struct irq_callback * cb = calloc(sizeof(struct irq_callback),1);
cb->callback = callback;
cb->owner = (process_t*)this_core->current_process;
cb->data = data;
cb->next = NULL;
if (irq_callbacks[irq]) {
dprintf("gic: irq %d has an assignment, finding end of chain\n", irq);
struct irq_callback * parent = irq_callbacks[irq];
while (parent->next) {
parent = parent->next;
}
parent->next = cb;
} else {
dprintf("gic: irq %d is new\n", irq);
irq_callbacks[irq] = cb;
}
spin_unlock(irq_acquire);
}
void gic_map_pci_interrupt(const char * name, uint32_t device, int * int_out, int (*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 < (int)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]);
gic_assign_interrupt(*int_out, callback, isr_addr);
return;
}
}
}
}

View File

@ -29,6 +29,7 @@
#include <kernel/arch/aarch64/regs.h>
#include <kernel/arch/aarch64/dtb.h>
#include <kernel/arch/aarch64/gic.h>
extern void fbterm_initialize(void);
extern void mmu_init(size_t memsize, size_t phys, uintptr_t firstFreePage, uintptr_t endOfInitrd);
@ -324,14 +325,6 @@ void aarch64_sync_enter(struct regs * r) {
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];
@ -362,9 +355,14 @@ static void aarch64_interrupt_dispatch(int from_wfi) {
case 41:
case 42:
{
struct irq_callback * cb = &irq_callbacks[irq-32];
if (cb->owner) {
cb->callback(cb->owner, irq-32, cb->data);
struct irq_callback * cb = irq_callbacks[irq-32];
if (cb) {
while (cb) {
int res = cb->callback(cb->owner, irq-32, cb->data);
if (res) break;
cb = cb->next;
}
/* Maybe warn? We have a lot of spurious irqs, though */
} else {
dprintf("irq: unhandled irq %d\n", irq);
}

View File

@ -15,23 +15,19 @@
#include <kernel/mmu.h>
#include <kernel/arch/aarch64/dtb.h>
#include <kernel/arch/aarch64/gic.h>
/* TODO interrupt handler installers */
struct irq_callback {
void (*callback)(process_t * this, int irq, void *data);
process_t * owner;
void * data;
};
extern struct irq_callback irq_callbacks[];
static void pl011_irq(process_t * this, int irq, void * data) {
static int pl011_irq(process_t * this, int irq, void * data) {
volatile uint32_t * uart_mapped = (volatile uint32_t *)data;
uint32_t mis = uart_mapped[16];
if (mis & (1 << 4)) {
make_process_ready(this);
if (mis) {
if (mis & (1 << 4)) {
make_process_ready(this);
}
uart_mapped[17] = mis;
return 1;
}
uart_mapped[17] = mis;
return 0;
}
static void pl011_fill_name(pty_t * pty, char * name) {
@ -54,9 +50,7 @@ static void pl011_thread(void * arg) {
vfs_mount("/dev/ttyS0", pty->slave);
/* Set up interrupt callback */
irq_callbacks[1].callback = pl011_irq;
irq_callbacks[1].owner = (process_t*)this_core->current_process;
irq_callbacks[1].data = (void *)uart_mapped;
gic_assign_interrupt(1, pl011_irq, (void*)uart_mapped);
/* Enable interrupts */
uart_mapped[12] = 0;

View File

@ -18,7 +18,7 @@
#include <kernel/mouse.h>
#include <kernel/time.h>
#include <kernel/arch/aarch64/dtb.h>
#include <kernel/arch/aarch64/gic.h>
static fs_node_t * mouse_pipe;
static fs_node_t * vmmouse_pipe;
@ -101,68 +101,22 @@ 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) {
static int tablet_responder(process_t * this, int irq, void * data) {
uint8_t cause = *(volatile uint8_t *)data;
if (cause == 1) {
make_process_ready(this);
return 1;
}
return 0;
}
static void keyboard_responder(process_t * this, int irq, void * data) {
static int keyboard_responder(process_t * this, int irq, void * data) {
uint8_t cause = *(volatile uint8_t *)data;
if (cause == 1) {
make_process_ready(this);
return 1;
}
}
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;
}
}
}
return 0;
}
static void virtio_tablet_thread(void * data) {
@ -204,7 +158,7 @@ static void virtio_tablet_thread(void * data) {
volatile char * irq_region = (char*)mmu_map_mmio_region(t + 0x1000, 0x1000);
int irq;
map_interrupt("virtio-tablet", device, &irq, tablet_responder, irq_region);
gic_map_pci_interrupt("virtio-tablet", device, &irq, tablet_responder, irq_region);
dprintf("virtio-tablet: irq is %d\n", irq);
/* figure out range values */
@ -371,7 +325,7 @@ static void virtio_keyboard_thread(void * data) {
volatile char * irq_region = (char*)mmu_map_mmio_region(t + 0x1000, 0x1000);
int irq;
map_interrupt("virtio-keyboard", device, &irq, keyboard_responder, irq_region);
gic_map_pci_interrupt("virtio-keyboard", device, &irq, keyboard_responder, irq_region);
dprintf("virtio-keyboard: irq is %d\n", irq);
cfg->select = 0;