From 75d48ffdbcb6a00c1542850baa225220e8422314 Mon Sep 17 00:00:00 2001 From: Mark Date: Thu, 10 Oct 2019 16:40:19 +0300 Subject: [PATCH] Add support for I/O APIC with testing kb mapping --- include/sys/amd64/asm/asm_irq.h | 4 ++ include/sys/amd64/hw/ioapic.h | 36 +++++++++++ sys/amd64/conf.mk | 4 +- sys/amd64/hw/apic.c | 26 ++++++++ sys/amd64/hw/idt.c | 3 +- sys/amd64/hw/ioapic.c | 104 ++++++++++++++++++++++++++++++++ sys/amd64/hw/irq0.S | 20 ------ sys/amd64/hw/irqs_s.S | 47 +++++++++++++++ 8 files changed, 222 insertions(+), 22 deletions(-) create mode 100644 include/sys/amd64/hw/ioapic.h create mode 100644 sys/amd64/hw/ioapic.c create mode 100644 sys/amd64/hw/irqs_s.S diff --git a/include/sys/amd64/asm/asm_irq.h b/include/sys/amd64/asm/asm_irq.h index 23dc121..bb0c7e6 100644 --- a/include/sys/amd64/asm/asm_irq.h +++ b/include/sys/amd64/asm/asm_irq.h @@ -94,4 +94,8 @@ movq $AMD64_LAPIC_REG_EOI_ABS, %rax movl $0, (%rax) .endm +#else +// Externs for C code +extern void amd64_irq0(); +extern void amd64_irq1(); #endif diff --git a/include/sys/amd64/hw/ioapic.h b/include/sys/amd64/hw/ioapic.h new file mode 100644 index 0000000..9eff293 --- /dev/null +++ b/include/sys/amd64/hw/ioapic.h @@ -0,0 +1,36 @@ +#pragma once +#include "sys/types.h" + +#define IOAPIC_REG_ID 0x00 +#define IOAPIC_REG_VER 0x01 +#define IOAPIC_REG_REDIR 0x10 + +#define IOAPIC_REDIR_DEL_NOPRI (0x0 << 8) +#define IOAPIC_REDIR_DEL_LOPRI (0x1 << 8) +#define IOAPIC_REDIR_DEL_SMI (0x2 << 8) +#define IOAPIC_REDIR_DEL_NMI (0x4 << 8) +#define IOAPIC_REDIR_DEL_INIT (0x5 << 8) +#define IOAPIC_REDIR_DEL_EXTI (0x7 << 8) + +#define IOAPIC_REDIR_DST_LOG (1 << 11) + +#define IOAPIC_REDIR_POL_LOW (1 << 13) + +#define IOAPIC_REDIR_TRG_LEVEL (1 << 15) + +#define IOAPIC_REDIR_MSK (1 << 16) + +#define IOAPIC_REDIR_DST_SET(x) (((x) & 0xFF) << 24) +#define IOAPIC_REDIR_DST_GET(x) (((uint32_t) x) >> 24) + +// Base address of the two-register structure +// 0 means "Not available" +// [Virtual] +extern uintptr_t amd64_ioapic_base; + +uint32_t amd64_ioapic_read(uint8_t reg); +void amd64_ioapic_write(uint8_t reg, uint32_t v); + +// Set from reading the MADT +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); diff --git a/sys/amd64/conf.mk b/sys/amd64/conf.mk index 85340a5..743b5fc 100644 --- a/sys/amd64/conf.mk +++ b/sys/amd64/conf.mk @@ -18,7 +18,9 @@ OBJS+=$(O)/sys/amd64/hw/rs232.o \ $(O)/sys/amd64/hw/irq0.o \ $(O)/sys/amd64/hw/ap_code_blob.o \ $(O)/sys/amd64/hw/con.o \ - $(O)/sys/amd64/hw/timer.o + $(O)/sys/amd64/hw/timer.o \ + $(O)/sys/amd64/hw/ioapic.o \ + $(O)/sys/amd64/hw/irqs_s.o ### From config ifdef AMD64_TRACE_IRQ diff --git a/sys/amd64/hw/apic.c b/sys/amd64/hw/apic.c index 02553d2..3d67d32 100644 --- a/sys/amd64/hw/apic.c +++ b/sys/amd64/hw/apic.c @@ -1,4 +1,5 @@ #include "sys/amd64/hw/apic.h" +#include "sys/amd64/hw/ioapic.h" #include "sys/amd64/hw/timer.h" #include "sys/amd64/hw/gdt.h" #include "sys/amd64/hw/idt.h" @@ -50,6 +51,15 @@ struct acpi_ioapic_entry { uint32_t gsi_base; } __attribute__((packed)); +struct acpi_int_src_override_entry { + struct acpi_apic_field_type hdr; + + uint8_t bus_src; + uint8_t irq_src; + uint32_t gsi; + uint16_t flags; +} __attribute__((packed)); + ///// // LAPIC ID of the BSP CPU @@ -195,6 +205,18 @@ void amd64_apic_init(struct acpi_madt *madt) { // Get other LAPICs from MADT size_t offset = 0; + while (offset < madt->hdr.length - sizeof(struct acpi_madt)) { + struct acpi_apic_field_type *ent_hdr = (struct acpi_apic_field_type *) &madt->entry[offset]; + if (ent_hdr->type == 1) { + // Found I/O APIC + struct acpi_ioapic_entry *ent = (struct acpi_ioapic_entry *) ent_hdr; + amd64_ioapic_set(ent->ioapic_addr + 0xFFFFFF0000000000); + } + + offset += ent_hdr->length; + } + + offset = 0; while (offset < madt->hdr.length - sizeof(struct acpi_madt)) { struct acpi_apic_field_type *ent_hdr = (struct acpi_apic_field_type *) &madt->entry[offset]; @@ -207,7 +229,11 @@ void amd64_apic_init(struct acpi_madt *madt) { // Initiate wakeup sequence amd64_core_wakeup(ent->apic_id); } + } else if (ent_hdr->type == 2) { + struct acpi_int_src_override_entry *ent = (struct acpi_int_src_override_entry *) ent_hdr; + amd64_ioapic_int_src_override(ent->bus_src, ent->irq_src, ent->gsi, ent->flags); } + offset += ent_hdr->length; } diff --git a/sys/amd64/hw/idt.c b/sys/amd64/hw/idt.c index f9a7790..c543a75 100644 --- a/sys/amd64/hw/idt.c +++ b/sys/amd64/hw/idt.c @@ -1,4 +1,5 @@ #include "sys/amd64/hw/idt.h" +#include "asm/asm_irq.h" #include "sys/string.h" #include "sys/types.h" @@ -15,7 +16,6 @@ #define IDT_FLG_P (1 << 7) extern uintptr_t amd64_exception_vectors[32]; -extern void amd64_irq0(void); static struct amd64_idt_entry { uint16_t base_lo; @@ -48,6 +48,7 @@ void amd64_idt_init(void) { 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); asm volatile ("lidt amd64_idtr(%rip)"); } diff --git a/sys/amd64/hw/ioapic.c b/sys/amd64/hw/ioapic.c new file mode 100644 index 0000000..322e0bb --- /dev/null +++ b/sys/amd64/hw/ioapic.c @@ -0,0 +1,104 @@ +#include "sys/amd64/hw/ioapic.h" +#include "sys/debug.h" + +uintptr_t amd64_ioapic_base = 0; + +// Legacy PIC -> I/O APIC routing table +static uint32_t ioapic_leg_rt[16] = { 0xFFFFFFFF }; + +uint32_t amd64_ioapic_read(uint8_t reg) { + if (!amd64_ioapic_base) { + // TODO: panic + kdebug("Reading non-existent I/O APIC\n"); + while (1); + } + uint32_t volatile *ioapic = (uint32_t volatile *) amd64_ioapic_base; + ioapic[0] = (reg & 0xFF); + return ioapic[4]; +} + +void amd64_ioapic_write(uint8_t reg, uint32_t v) { + if (!amd64_ioapic_base) { + // TODO: panic + kdebug("Writing non-existent I/O APIC\n"); + while (1); + } + uint32_t volatile *ioapic = (uint32_t volatile *) amd64_ioapic_base; + ioapic[0] = reg & 0xFF; + ioapic[4] = v; +} + +void amd64_ioapic_int_src_override(uint8_t bus_src, uint8_t irq_src, uint32_t no, uint16_t flags) { + kdebug("IRQ Override: %02x:%02x -> %d\n", bus_src, irq_src, no); + if (bus_src == 0 && irq_src < 16) { + // Totally a legacy IRQ + ioapic_leg_rt[irq_src] = no; + } + + // Setup flags properly + uint32_t low = amd64_ioapic_read(0x10 + no * 2); + + if (flags & (1 << 1)) { + // Low-active IRQ + low |= IOAPIC_REDIR_POL_LOW; + } else { + low &= ~IOAPIC_REDIR_POL_LOW; + } + + if (flags & (1 << 3)) { + low |= IOAPIC_REDIR_TRG_LEVEL; + } else { + low &= ~IOAPIC_REDIR_TRG_LEVEL; + } + + amd64_ioapic_write(0x10 + no * 2, low); +} + +void amd64_ioapic_set(uintptr_t addr) { + if (amd64_ioapic_base != 0) { + // TODO: panic + kdebug("I/O APIC base already set\n"); + while (1); + } + amd64_ioapic_base = addr; + + kdebug("I/O APIC @ %p\n", addr); + + // Dump the entries of I/O APIC + + for (uint8_t i = 0x10; i < 0x3F; i += 2) { + uint32_t low = amd64_ioapic_read(i + 0); + uint32_t high = amd64_ioapic_read(i + 1); + + static const char *del_type[] = { + "Normal", + "Low priority", + "SMI", + "???", + "NMI", + "INIT", + "???", + "EXTI" + }; + + kdebug("[%d] %s, %s, DST 0x%02x:0x%02x\n", + i, + (low & IOAPIC_REDIR_MSK) ? "Masked" : "", + del_type[(low >> 8) & 0x7], + IOAPIC_REDIR_DST_GET(high), + 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); + + 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); +} diff --git a/sys/amd64/hw/irq0.S b/sys/amd64/hw/irq0.S index 5b25788..a3bac3b 100644 --- a/sys/amd64/hw/irq0.S +++ b/sys/amd64/hw/irq0.S @@ -22,23 +22,3 @@ amd64_irq0: irq_pop_ctx iretq - -#if defined(AMD64_STACK_CTX_CANARY) -amd64_kstack_canary_invalid: - // This means we somehow managed to fuck up - // Context's kernel stack and were going to - // pop nonsense and iret would be fatal. - - // TODO: guess it would be just better to panic - // than to halt one CPU - xorq %rdi, %rdi - leaq _msg0(%rip), %rsi - call debugs -1: - cli - hlt - jmp 1b - -_msg0: - .string "Stack fuckup detected: halting\n" -#endif diff --git a/sys/amd64/hw/irqs_s.S b/sys/amd64/hw/irqs_s.S new file mode 100644 index 0000000..04afc9b --- /dev/null +++ b/sys/amd64/hw/irqs_s.S @@ -0,0 +1,47 @@ +// Temporary file where I play with +// Generic IRQ handlers +#include "asm/asm_irq.h" + +.section .text + +// This is keyboard +// Can be either entered from a legacy PIT IRQ +// Or is mapped in I/O APIC +.global amd64_irq1 +amd64_irq1: + cli + + irq_push_ctx + movq %rsp, %rbp + + irq_trace 1 + + // Read from keyboard buffer + inb $0x60, %al + + irq_eoi_lapic 1 + + irq_pop_ctx + + iretq + +.global amd64_kstack_canary_invalid +#if defined(AMD64_STACK_CTX_CANARY) +amd64_kstack_canary_invalid: + // This means we somehow managed to fuck up + // Context's kernel stack and were going to + // pop nonsense and iret would be fatal. + + // TODO: guess it would be just better to panic + // than to halt one CPU + xorq %rdi, %rdi + leaq _msg0(%rip), %rsi + call debugs +1: + cli + hlt + jmp 1b + +_msg0: + .string "Stack fuckup detected: halting\n" +#endif