Better IRQ handling

This commit is contained in:
Mark
2019-10-23 15:40:32 +03:00
parent 1af3d0dd78
commit 6798480797
14 changed files with 298 additions and 103 deletions
+23
View File
@@ -0,0 +1,23 @@
Full IRQ routing?
=================
External IRQ sources:
* Legacy IRQ lines
* PCI IRQ pins (INTA#/INTB#/INTC#/INTD#)
Each of these is routed to a single GSI (IRQ of I/O APIC),
which in turn is mapped to a single interrupt vector in any of the Local APICs
Mapping legacy IRQ to a handler:
If I/O APIC is available
1. Get 8259 IRQ -> I/O APIC Line mapping
2. Allocate an interrupt vector/reuse shared vector
3. Add a mapping in I/O APIC
4. Add entry to IRQ handlers list
5. Unmask the GSI
Otherwise:
1. Add entry `irq_number` to IRQ handlers list
I/O APIC init:
1. Assign I/O APIC address variable
2. Relocate existing legacy handlers to I/O APIC
+2
View File
@@ -39,6 +39,8 @@ OBJS+=$(O)/sys/amd64/hw/rs232.o \
$(O)/sys/amd64/hw/ide/ahci.o \
$(O)/sys/amd64/hw/ide/ide.o \
$(O)/sys/amd64/hw/pci/pcidb.o \
$(O)/sys/amd64/hw/ps2.o \
$(O)/sys/amd64/hw/irq.o \
$(ACPICA_OBJS) \
$(O)/sys/amd64/acpi_osl_mem.o \
$(O)/sys/amd64/acpi_osl_printf.o \
+32
View File
@@ -6,6 +6,24 @@
#if defined(__ASM__)
.extern local_apic
.extern irq_handle
// TODO: make this use less memory
.macro irq_entry, n
.global amd64_irq\n
amd64_irq\n:
cli
irq_push_ctx
movq $\n, %rdi
call irq_handle
irq_eoi_lapic 0
irq_pop_ctx
iretq
.endm
.macro irq_trace, n
#if defined(AMD64_TRACE_IRQ)
@@ -101,4 +119,18 @@
// Externs for C code
extern void amd64_irq0();
extern void amd64_irq1();
extern void amd64_irq2();
extern void amd64_irq3();
extern void amd64_irq4();
extern void amd64_irq5();
extern void amd64_irq6();
extern void amd64_irq7();
extern void amd64_irq8();
extern void amd64_irq9();
extern void amd64_irq10();
extern void amd64_irq11();
extern void amd64_irq12();
extern void amd64_irq13();
extern void amd64_irq14();
extern void amd64_irq15();
#endif
+14
View File
@@ -1,6 +1,18 @@
#pragma once
#include "sys/types.h"
#define IDT_FLG_TASK32 0x5
#define IDT_FLG_INT16 0x6
#define IDT_FLG_TRAP16 0x7
#define IDT_FLG_INT32 0xE
#define IDT_FLG_TRAP32 0xF
#define IDT_FLG_SS (1 << 4)
#define IDT_FLG_R0 (0 << 5)
#define IDT_FLG_R1 (1 << 5)
#define IDT_FLG_R2 (2 << 5)
#define IDT_FLG_R3 (3 << 5)
#define IDT_FLG_P (1 << 7)
struct amd64_idtr {
uint16_t size;
uintptr_t offset;
@@ -8,4 +20,6 @@ struct amd64_idtr {
extern const struct amd64_idtr amd64_idtr;
void amd64_idt_set(int idx, uintptr_t base, uint16_t selector, uint8_t flags);
void amd64_idt_init(void);
+3
View File
@@ -37,6 +37,9 @@ void amd64_ioapic_write(uint8_t reg, uint32_t v);
void amd64_ioapic_set(uintptr_t addr);
void amd64_ioapic_int_src_override(uint8_t bus, uint8_t src, uint32_t no, uint16_t flags);
void amd64_ioapic_map_gsi(uint8_t gsi, uint8_t lapic, uint8_t vector);
void amd64_ioapic_unmask(uint8_t gsi);
//// Generic IRQ stuff
// A route exists (LNKx) but is not mapped to any IRQ at the moment
#define PCI_IRQ_NO_ROUTE 0xFFFFFFFF
+13
View File
@@ -0,0 +1,13 @@
#pragma once
#include "sys/types.h"
#define IRQ_LEG_KEYBOARD 1
#define IRQ_LEG_COM1_3 4
typedef int (*irq_handler_t) (void);
int irq_add_handler(uint8_t gsi, irq_handler_t handler);
int irq_add_leg_handler(uint8_t leg_irq, irq_handler_t handler);
void irq_enable_ioapic_mode(void);
void irq_init(void);
+3
View File
@@ -0,0 +1,3 @@
#pragma once
void ps2_init(void);
+4 -16
View File
@@ -1,20 +1,8 @@
#include "sys/amd64/hw/idt.h"
#include "sys/amd64/asm/asm_irq.h"
#include "sys/amd64/hw/irq.h"
#include "sys/string.h"
#include "sys/types.h"
#define IDT_FLG_TASK32 0x5
#define IDT_FLG_INT16 0x6
#define IDT_FLG_TRAP16 0x7
#define IDT_FLG_INT32 0xE
#define IDT_FLG_TRAP32 0xF
#define IDT_FLG_SS (1 << 4)
#define IDT_FLG_R0 (0 << 5)
#define IDT_FLG_R1 (1 << 5)
#define IDT_FLG_R2 (2 << 5)
#define IDT_FLG_R3 (3 << 5)
#define IDT_FLG_P (1 << 7)
extern uintptr_t amd64_exception_vectors[32];
static struct amd64_idt_entry {
@@ -32,7 +20,7 @@ const struct amd64_idtr amd64_idtr = {
.offset = (uintptr_t) idt
};
static void amd64_idt_set(int idx, uintptr_t base, uint16_t selector, uint8_t flags) {
void amd64_idt_set(int idx, uintptr_t base, uint16_t selector, uint8_t flags) {
idt[idx].base_lo = base & 0xFFFF;
idt[idx].base_hi = (base >> 16) & 0xFFFF;
idt[idx].base_ex = (base >> 32) & 0xFFFFFFFF;
@@ -47,8 +35,8 @@ void amd64_idt_init(void) {
for (size_t i = 0; i < 32; ++i) {
amd64_idt_set(i, amd64_exception_vectors[i], 0x08, IDT_FLG_P | IDT_FLG_R0 | IDT_FLG_INT32);
}
amd64_idt_set(32, (uintptr_t) amd64_irq0, 0x08, IDT_FLG_P | IDT_FLG_R0 | IDT_FLG_INT32);
amd64_idt_set(33, (uintptr_t) amd64_irq1, 0x08, IDT_FLG_P | IDT_FLG_R0 | IDT_FLG_INT32);
irq_init();
asm volatile ("lidt amd64_idtr(%rip)");
}
+20 -24
View File
@@ -1,4 +1,5 @@
#include "sys/amd64/hw/ioapic.h"
#include "sys/amd64/hw/irq.h"
#include "sys/string.h"
#include "sys/mm.h"
#include "sys/debug.h"
@@ -95,33 +96,28 @@ void amd64_ioapic_set(uintptr_t addr) {
// low & 0xF);
}
// Test: set IRQ1 -> LAPIC 0:1 and unmask it
uint32_t low = amd64_ioapic_read(0x10 + 1 * 2);
uint32_t high = amd64_ioapic_read(0x11 + 1 * 2);
irq_enable_ioapic_mode();
}
void amd64_ioapic_map_gsi(uint8_t gsi, uint8_t lapic, uint8_t vector) {
uint32_t low = amd64_ioapic_read((gsi * 2) + 0x10);
uint32_t high = amd64_ioapic_read((gsi * 2) + 0x11);
low &= ~0xFF;
low |= vector;
high = IOAPIC_REDIR_DST_SET(lapic);
amd64_ioapic_write((gsi * 2) + 0x10, low);
amd64_ioapic_write((gsi * 2) + 0x11, high);
}
void amd64_ioapic_unmask(uint8_t gsi) {
uint32_t low = amd64_ioapic_read((gsi * 2) + 0x10);
low &= ~0x7;
low &= ~IOAPIC_REDIR_MSK;
low |= 0x1 + 0x20;
high = IOAPIC_REDIR_DST_SET(0);
amd64_ioapic_write(0x10 + 1 * 2, low);
amd64_ioapic_write(0x11 + 1 * 2, high);
// TODO: find IRQ mappings corresponding to legacy irq 1/4
low = amd64_ioapic_read(0x10 + 4 * 2);
high = amd64_ioapic_read(0x11 + 4 * 2);
low &= ~0x7;
low &= ~IOAPIC_REDIR_MSK;
low |= 0x1 + 0x20;
high = IOAPIC_REDIR_DST_SET(0);
amd64_ioapic_write(0x10 + 4 * 2, low);
amd64_ioapic_write(0x11 + 4 * 2, high);
// TODO: unconfigured PCI IRQs may be mapped here
amd64_ioapic_write((gsi * 2) + 0x10, low);
}
#define PCI_LINK_MAX_POSSIBLE_IRQS 16
+128
View File
@@ -0,0 +1,128 @@
#include "sys/amd64/asm/asm_irq.h"
#include "sys/amd64/hw/ioapic.h"
#include "sys/amd64/hw/irq.h"
#include "sys/amd64/hw/idt.h"
#include "sys/string.h"
#include "sys/assert.h"
#include "sys/panic.h"
#include "sys/debug.h"
#define IRQ_MAX 16 // Maximum assignable IRQ vectors
#define IRQ_MAX_HANDLERS 4 // Maximum handlers per IRQ vector (sharing)
static irq_handler_t handlers[IRQ_MAX * IRQ_MAX_HANDLERS] = {0};
static uint64_t vector_bitmap[4] = {0};
static int ioapic_available = 0;
static int irq_alloc_vector(uint8_t *vector) {
for (size_t i = 1; i < IRQ_MAX; ++i) {
size_t idx = i / 64;
size_t bit = 1ULL << (i % 64);
if (!(vector_bitmap[idx] & bit)) {
*vector = i;
vector_bitmap[idx] |= bit;
return 0;
}
}
return -1;
}
void irq_handle(uintptr_t n) {
_assert(n && n < IRQ_MAX);
irq_handler_t *list = &handlers[IRQ_MAX_HANDLERS * n];
for (size_t i = 0; i < IRQ_MAX_HANDLERS; ++i) {
if (list[i] && (list[i]() == 0)) {
return;
}
}
}
int irq_add_handler(uint8_t gsi, irq_handler_t handler) {
_assert(gsi != 0 && gsi < IRQ_MAX);
_assert(ioapic_available);
uint8_t vector;
if (irq_alloc_vector(&vector)) {
return -1;
}
kdebug("I/O APIC: Mapping GSI%u -> Vector %d:%d\n", gsi, 0, vector);
irq_handler_t *list = &handlers[IRQ_MAX_HANDLERS * vector];
for (size_t i = 0; i < IRQ_MAX_HANDLERS; ++i) {
if (!list[i]) {
list[i] = handler;
amd64_ioapic_map_gsi(gsi, 0, vector + 0x20);
amd64_ioapic_unmask(gsi);
return 0;
}
}
return -1;
}
int irq_add_leg_handler(uint8_t leg_irq, irq_handler_t handler) {
_assert(leg_irq != 0 && leg_irq < IRQ_MAX);
if (ioapic_available) {
// Find out the route (TODO)
uint8_t gsi = leg_irq;
return irq_add_handler(gsi, handler);
} else {
irq_handler_t *list = &handlers[IRQ_MAX_HANDLERS * leg_irq];
for (size_t i = 0; i < IRQ_MAX_HANDLERS; ++i) {
if (!list[i]) {
list[i] = handler;
return 0;
}
}
return -1;
}
}
void irq_enable_ioapic_mode(void) {
ioapic_available = 1;
// Copy current table
irq_handler_t handlers_old[16 * IRQ_MAX_HANDLERS];
memcpy(handlers_old, handlers, 16 * IRQ_MAX_HANDLERS * sizeof(irq_handler_t));
memset(handlers, 0, 16 * IRQ_MAX_HANDLERS * sizeof(irq_handler_t));
// All legacy IRQs
for (size_t i = 0; i < 16; ++i) {
irq_handler_t *handler_list = &handlers_old[i * IRQ_MAX_HANDLERS];
for (size_t j = 0; j < IRQ_MAX_HANDLERS; ++j) {
if (handler_list[j]) {
irq_add_leg_handler(i, handlers_old[i * IRQ_MAX_HANDLERS + j]);
}
}
}
}
void irq_init(void) {
amd64_idt_set(32, (uintptr_t) amd64_irq0, 0x08, IDT_FLG_P | IDT_FLG_R0 | IDT_FLG_INT32);
amd64_idt_set(33, (uintptr_t) amd64_irq1, 0x08, IDT_FLG_P | IDT_FLG_R0 | IDT_FLG_INT32);
amd64_idt_set(34, (uintptr_t) amd64_irq2, 0x08, IDT_FLG_P | IDT_FLG_R0 | IDT_FLG_INT32);
amd64_idt_set(35, (uintptr_t) amd64_irq3, 0x08, IDT_FLG_P | IDT_FLG_R0 | IDT_FLG_INT32);
amd64_idt_set(36, (uintptr_t) amd64_irq4, 0x08, IDT_FLG_P | IDT_FLG_R0 | IDT_FLG_INT32);
amd64_idt_set(37, (uintptr_t) amd64_irq5, 0x08, IDT_FLG_P | IDT_FLG_R0 | IDT_FLG_INT32);
amd64_idt_set(38, (uintptr_t) amd64_irq6, 0x08, IDT_FLG_P | IDT_FLG_R0 | IDT_FLG_INT32);
amd64_idt_set(39, (uintptr_t) amd64_irq7, 0x08, IDT_FLG_P | IDT_FLG_R0 | IDT_FLG_INT32);
amd64_idt_set(40, (uintptr_t) amd64_irq8, 0x08, IDT_FLG_P | IDT_FLG_R0 | IDT_FLG_INT32);
amd64_idt_set(41, (uintptr_t) amd64_irq9, 0x08, IDT_FLG_P | IDT_FLG_R0 | IDT_FLG_INT32);
amd64_idt_set(42, (uintptr_t) amd64_irq10, 0x08, IDT_FLG_P | IDT_FLG_R0 | IDT_FLG_INT32);
amd64_idt_set(43, (uintptr_t) amd64_irq11, 0x08, IDT_FLG_P | IDT_FLG_R0 | IDT_FLG_INT32);
amd64_idt_set(44, (uintptr_t) amd64_irq12, 0x08, IDT_FLG_P | IDT_FLG_R0 | IDT_FLG_INT32);
amd64_idt_set(45, (uintptr_t) amd64_irq13, 0x08, IDT_FLG_P | IDT_FLG_R0 | IDT_FLG_INT32);
amd64_idt_set(46, (uintptr_t) amd64_irq14, 0x08, IDT_FLG_P | IDT_FLG_R0 | IDT_FLG_INT32);
amd64_idt_set(47, (uintptr_t) amd64_irq15, 0x08, IDT_FLG_P | IDT_FLG_R0 | IDT_FLG_INT32);
}
+17 -46
View File
@@ -4,51 +4,22 @@
.section .text
// TODO: move all this to some kind of generic handler
// This is keyboard
// Can be either entered from a legacy PIC IRQ
// Or is mapped in I/O APIC
.global amd64_irq1
amd64_irq1:
cli
irq_push_ctx
movq %rsp, %rbp
// The ISR is shared between legacy IRQs 1 and 4, so
// check if it's keyboard or serial input on COM1
// Check PS/2 keyboard
inb $0x64, %al
test $(1 << 0), %al
jz 1f
// Read from keyboard buffer
inb $0x60, %al
movb %al, irq1_key(%rip)
jmp 2f
1:
movq $0x3F8, %rdi
call rs232_irq
test %rax, %rax
jz 2f
// Only trace IRQ if it's unhandled
irq_trace 1
2:
irq_eoi_lapic 1
irq_pop_ctx
iretq
_amd64_irq1_unhandled:
.string "Something unknown triggered ISR 0x21\n"
_amd64_serial:
.string "Serial status: %02x\n"
// 33
irq_entry 1
irq_entry 2
irq_entry 3
irq_entry 4
irq_entry 5
irq_entry 6
irq_entry 7
irq_entry 8
irq_entry 9
irq_entry 10
irq_entry 11
irq_entry 12
irq_entry 13
irq_entry 14
irq_entry 15
.global amd64_kstack_canary_invalid
#if defined(AMD64_STACK_CTX_CANARY)
@@ -68,5 +39,5 @@ amd64_kstack_canary_invalid:
jmp 1b
_msg0:
.string "Stack fuckup detected: halting\n"
.string "Stack fuckup detected: halting\n"
#endif
+19
View File
@@ -0,0 +1,19 @@
#include "sys/amd64/hw/ps2.h"
#include "sys/amd64/hw/irq.h"
#include "sys/amd64/hw/io.h"
int ps2_irq_keyboard(void) {
uint8_t st = inb(0x64);
if (!(st & 1)) {
return -1;
}
inb(0x60);
return 0;
}
void ps2_init(void) {
irq_add_leg_handler(IRQ_LEG_KEYBOARD, ps2_irq_keyboard);
}
+18 -15
View File
@@ -1,4 +1,5 @@
#include "sys/amd64/hw/rs232.h"
#include "sys/amd64/hw/irq.h"
#include "sys/amd64/hw/io.h"
#include "sys/debug.h"
@@ -31,6 +32,21 @@ void rs232_send(uint16_t port, char c) {
outb(port, c);
}
static int rs232_irq(void) {
int has_data = 0;
uint8_t s;
while ((s = inb(RS232_COM0 + RS232_LSR)) & RS232_LSR_DR) {
has_data = 1;
uint8_t c = inb(RS232_COM0);
// Act as echo for now
rs232_send(RS232_COM0, c);
}
return !has_data;
}
void rs232_init(uint16_t port) {
// Disable interrupts
outb(port + RS232_IER, 0x00);
@@ -48,19 +64,6 @@ void rs232_init(uint16_t port) {
// Enable receive interrupts
outb(port + RS232_IER, RS232_IER_RCE);
}
int rs232_irq(uint16_t port) {
int has_data = 0;
uint8_t s;
while ((s = inb(port + RS232_LSR)) & RS232_LSR_DR) {
has_data = 1;
uint8_t c = inb(port);
// Act as echo for now
rs232_send(port, c);
}
return !has_data;
irq_add_leg_handler(IRQ_LEG_COM1_3, rs232_irq);
}
+2 -2
View File
@@ -9,15 +9,15 @@
#include "sys/amd64/hw/apic.h"
#include "sys/amd64/hw/pci/pci.h"
#include "sys/amd64/hw/rs232.h"
#include "sys/amd64/hw/ps2.h"
#include "sys/panic.h"
#include "sys/assert.h"
#include "acpi.h"
static multiboot_info_t *multiboot_info;
void kernel_main(struct amd64_loader_data *data) {
// Reinitialize RS232 properly
ps2_init();
rs232_init(RS232_COM0);
data = (struct amd64_loader_data *) MM_VIRTUALIZE(data);