diff --git a/src/kernel/apic.cpp b/src/kernel/apic.cpp index 729a527..285824a 100644 --- a/src/kernel/apic.cpp +++ b/src/kernel/apic.cpp @@ -6,17 +6,30 @@ static uint32_t -apic_read(uint32_t *apic, uint16_t offset) +apic_read(uint32_t volatile *apic, uint16_t offset) { return *(apic + offset/sizeof(uint32_t)); } static void -apic_write(uint32_t *apic, uint16_t offset, uint32_t value) +apic_write(uint32_t volatile *apic, uint16_t offset, uint32_t value) { *(apic + offset/sizeof(uint32_t)) = value; } +static uint32_t +ioapic_read(uint32_t volatile *base, uint8_t reg) +{ + *base = reg; + return *(base + 4); +} + +static void +ioapic_write(uint32_t volatile *base, uint8_t reg, uint32_t value) +{ + *base = reg; + *(base + 4) = value; +} apic::apic(uint32_t *base) : m_base(base) @@ -69,7 +82,7 @@ lapic::enable_lint(uint8_t num, isr vector, bool nmi, uint16_t flags) uint16_t polarity = flags & 0x3; if (polarity == 3) - lvte |= (1 << 13); + lvte |= (1 << 13); uint16_t trigger = (flags >> 2) & 0x3; if (trigger == 3) @@ -99,8 +112,83 @@ lapic::disable() } -ioapic::ioapic(uint32_t *base, uint32_t base_gsr) : +ioapic::ioapic(uint32_t *base, uint32_t base_gsi) : apic(base), - m_base_gsr(base_gsr) + m_base_gsi(base_gsi) { + uint32_t id = ioapic_read(m_base, 0); + uint32_t version = ioapic_read(m_base, 1); + + m_id = (id >> 24) & 0xff; + m_version = version & 0xff; + m_num_gsi = (version >> 16) & 0xff; + log::debug(logs::interrupt, "IOAPIC %d loaded, version %d, GSIs %d-%d", + m_id, m_version, base_gsi, base_gsi + (m_num_gsi - 1)); + + for (uint8_t i = 0; i < m_num_gsi; ++i) { + uint16_t flags = (i < 0x10) ? 0 : 0xf; + isr vector = isr::irq0 + i; + redirect(i, vector, flags, true); + } +} + +void +ioapic::redirect(uint8_t irq, isr vector, uint16_t flags, bool masked) +{ + uint64_t entry = static_cast(vector); + + uint16_t polarity = flags & 0x3; + if (polarity == 3) + entry |= (1 << 13); + + uint16_t trigger = (flags >> 2) & 0x3; + if (trigger == 3) + entry |= (1 << 15); + + if (masked) + entry |= (1 << 16); + + ioapic_write(m_base, (2 * irq) + 0x10, static_cast(entry & 0xffffffff)); + ioapic_write(m_base, (2 * irq) + 0x11, static_cast(entry >> 32)); +} + +void +ioapic::mask(uint8_t irq, bool masked) +{ + uint32_t entry = ioapic_read(m_base, (2 * irq) + 0x10); + if (masked) + entry |= (1 << 16); + else + entry &= ~(1 << 16); + + ioapic_write(m_base, (2 * irq) + 0x10, entry); +} + +void +ioapic::dump_redirs() const +{ + log::debug(logs::interrupt, "IOAPIC %d redirections:", m_id); + + for (uint8_t i = 0; i < m_num_gsi; ++i) { + uint64_t low = ioapic_read(m_base, 0x10 + (2 *i)); + uint64_t high = ioapic_read(m_base, 0x10 + (2 *i)); + uint64_t redir = low | (high << 32); + if (redir == 0) continue; + + uint8_t vector = redir & 0xff; + uint8_t mode = (redir >> 8) & 0x7; + uint8_t dest_mode = (redir >> 11) & 0x1; + uint8_t polarity = (redir >> 13) & 0x1; + uint8_t trigger = (redir >> 15) & 0x1; + uint8_t mask = (redir >> 16) & 0x1; + uint8_t dest = (redir >> 56) & 0xff; + + log::debug(logs::interrupt, " %2d: vec %3d %s active, %s-triggered %s dest %d: %x", + m_base_gsi + i, vector, + polarity ? "low" : "high", + trigger ? "level" : "edge", + mask ? "masked" : "", + dest_mode, + dest); + } } diff --git a/src/kernel/apic.h b/src/kernel/apic.h index 02fc60e..7b6ae59 100644 --- a/src/kernel/apic.h +++ b/src/kernel/apic.h @@ -56,14 +56,37 @@ class ioapic : public: /// Constructor /// \arg base Base virtual address of the APIC's MMIO registers - /// \arg base_gsr Starting global system interrupt number of this IOAPIC - ioapic(uint32_t *base, uint32_t base_gsr); + /// \arg base_gsi Starting global system interrupt number of this IOAPIC + ioapic(uint32_t *base, uint32_t base_gsi); - uint32_t get_base_gsr() const { return m_base_gsr; } - uint32_t get_num_gsr() const { return m_num_gsr; } + uint32_t get_base_gsi() const { return m_base_gsi; } + uint32_t get_num_gsi() const { return m_num_gsi; } + + /// Set a redirection entry. + /// TODO: pick CPU + /// \arg source Source interrupt number + /// \arg vector Interrupt vector that should be used + /// \arg flags Flags for mode/polarity (ACPI MPS INTI flags) + /// \arg masked Whether the iterrupt should be suppressed + void redirect(uint8_t irq, isr vector, uint16_t flags, bool masked); + + /// Mask or unmask an interrupt to stop/start having it sent to the CPU + /// \arg irq The IOAPIC-local irq number + /// \arg masked Whether to suppress this interrupt + void mask(uint8_t irq, bool masked); + + /// Mask all interrupts on this IOAPIC. + void mask_all() { for(int i=0; i(root_table); @@ -83,7 +84,7 @@ device_manager::device_manager(const void *root_table) : ioapic * device_manager::get_ioapic(int i) { - return (i < m_num_ioapics) ? m_ioapics + i : nullptr; + return (i < m_num_ioapics) ? m_ioapics[i] : nullptr; } static void @@ -128,36 +129,48 @@ device_manager::load_apic(const acpi_apic *apic) { uint32_t *local = reinterpret_cast(apic->local_address); - m_lapic = new (kalloc(sizeof(lapic))) lapic(local, isr::isrSpurious); - m_lapic->enable_lint(0, isr::isrLINT0, false, 0); - m_lapic->enable_lint(1, isr::isrLINT1, false, 0); + m_lapic = new lapic(local, isr::isrSpurious); + size_t count = acpi_table_entries(apic, 1); uint8_t const *p = apic->controller_data; - uint8_t const *end = p + acpi_table_entries(apic, 1); + uint8_t const *end = p + count; + // Pass one: set up IOAPIC objcts + while (p < end) { + const uint8_t type = p[0]; + const uint8_t length = p[1]; + if (type == 1) { + uint32_t *base = reinterpret_cast(kutil::read_from(p+4)); + uint32_t base_gsr = kutil::read_from(p+8); + m_ioapics[m_num_ioapics++] = new ioapic(base, base_gsr); + } + p += length; + } + + // Pass two: configure APIC objects + p = apic->controller_data; while (p < end) { const uint8_t type = p[0]; const uint8_t length = p[1]; switch (type) { case 0: // Local APIC + case 1: // I/O APIC break; - case 1: { // I/O APIC - uint32_t *io_apic = reinterpret_cast(kutil::read_from(p+4)); - uint32_t base = kutil::read_from(p+8); - log::info(logs::devices, " IO APIC address %lx base %d", io_apic, base); + case 2: { // Interrupt source override + uint8_t source = kutil::read_from(p+3); + isr gsi = isr::irq0 + kutil::read_from(p+4); + uint16_t flags = kutil::read_from(p+8); + + log::info(logs::devices, " Intr source override IRQ %d -> %d Pol %d Tri %d", + source, gsi, (flags & 0x3), ((flags >> 2) & 0x3)); + + // TODO: in a multiple-IOAPIC system this might be elsewhere + m_ioapics[0]->redirect(source, static_cast(gsi), flags, true); } break; - case 2: // Interrupt source override - log::info(logs::devices, " Intr source override IRQ %d -> %d Pol %d Tri %d", - kutil::read_from(p+3), - kutil::read_from(p+4), - kutil::read_from(p+8) & 0x3, - (kutil::read_from(p+8) >> 2) & 0x3); - break; - case 4: {// LAPIC NMI uint8_t cpu = kutil::read_from(p + 2); uint8_t num = kutil::read_from(p + 5); @@ -181,5 +194,14 @@ device_manager::load_apic(const acpi_apic *apic) } // m_lapic->enable_timer(isr::isrTimer, 128, 3000000); + + for (uint8_t i = 0; i < m_ioapics[0]->get_num_gsi(); ++i) { + switch (i) { + case 2: break; + default: m_ioapics[0]->mask(i, false); + } + } + + m_ioapics[0]->dump_redirs(); m_lapic->enable(); } diff --git a/src/kernel/device_manager.h b/src/kernel/device_manager.h index ad76b95..8f7703e 100644 --- a/src/kernel/device_manager.h +++ b/src/kernel/device_manager.h @@ -18,8 +18,10 @@ public: ioapic * get_ioapic(int i); private: + static const int max_ioapics = 16; + lapic *m_lapic; - ioapic *m_ioapics; + ioapic *m_ioapics[16]; int m_num_ioapics; void load_xsdt(const acpi_xsdt *xsdt); diff --git a/src/kernel/interrupt_isrs.inc b/src/kernel/interrupt_isrs.inc index 7a830f5..b9c3dd5 100644 --- a/src/kernel/interrupt_isrs.inc +++ b/src/kernel/interrupt_isrs.inc @@ -47,8 +47,33 @@ IRQ (76, 12, irq12) IRQ (77, 13, irq13) IRQ (78, 14, irq14) IRQ (79, 15, irq15) +IRQ (80, 16, irq16) +IRQ (81, 17, irq17) +IRQ (82, 18, irq18) +IRQ (83, 19, irq19) +IRQ (84, 20, irq20) +IRQ (85, 21, irq21) +IRQ (86, 22, irq22) ISR (0x7c, isrTimer) ISR (0x7d, isrLINT0) ISR (0x7e, isrLINT1) ISR (0x7f, isrSpurious) + +ISR (0xf0, isrIgnore0) +ISR (0xf1, isrIgnore1) +ISR (0xf2, isrIgnore2) +ISR (0xf3, isrIgnore3) +ISR (0xf4, isrIgnore4) +ISR (0xf5, isrIgnore5) +ISR (0xf6, isrIgnore6) +ISR (0xf7, isrIgnore7) + +ISR (0xf8, isrIgnore8) +ISR (0xf9, isrIgnore9) +ISR (0xfa, isrIgnoreA) +ISR (0xfb, isrIgnoreB) +ISR (0xfc, isrIgnoreC) +ISR (0xfd, isrIgnoreD) +ISR (0xfe, isrIgnoreE) +ISR (0xff, isrIgnoreF) diff --git a/src/kernel/interrupts.cpp b/src/kernel/interrupts.cpp index 87fe876..d1ae33c 100644 --- a/src/kernel/interrupts.cpp +++ b/src/kernel/interrupts.cpp @@ -1,9 +1,11 @@ #include #include +#include "kutil/enum_bitfields.h" #include "kutil/memory.h" #include "console.h" #include "interrupts.h" +#include "io.h" #include "log.h" #include "memory_pages.h" @@ -20,13 +22,7 @@ enum class gdt_flags : uint8_t res_1 = 0x10 }; - -inline gdt_flags -operator | (gdt_flags lhs, gdt_flags rhs) -{ - return static_cast( - static_cast(lhs) | static_cast(rhs)); -} +IS_BITFIELD(gdt_flags); struct gdt_descriptor { @@ -84,6 +80,29 @@ extern "C" { void idt_dump(const table_ptr &table); void gdt_dump(const table_ptr &table); +isr +operator+(const isr &lhs, int rhs) +{ + using under_t = std::underlying_type::type; + return static_cast(static_cast(lhs) + rhs); +} + +uint8_t +get_irq(unsigned vector) +{ + switch (vector) { +#define ISR(i, name) +#define EISR(i, name) +#define IRQ(i, q, name) case i : return q; +#include "interrupt_isrs.inc" +#undef IRQ +#undef EISR +#undef ISR + + default: return 0xff; + } +} + void set_gdt_entry(uint8_t i, uint32_t base, uint32_t limit, bool is64, gdt_flags flags) { @@ -111,6 +130,30 @@ set_idt_entry(uint8_t i, uint64_t addr, uint16_t selector, uint8_t flags) g_idt_table[i].reserved = 0; } +void +disable_legacy_pic() +{ + + static const uint16_t PIC1 = 0x20; + static const uint16_t PIC2 = 0xa0; + + // Mask all interrupts + outb(0xa1, 0xff); + outb(0x21, 0xff); + + // Start initialization sequence + outb(PIC1, 0x11); io_wait(); + outb(PIC2, 0x11); io_wait(); + + // Remap into ignore ISRs + outb(PIC1+1, static_cast(isr::isrIgnore0)); io_wait(); + outb(PIC2+1, static_cast(isr::isrIgnore8)); io_wait(); + + // Tell PICs about each other + outb(PIC1+1, 0x04); io_wait(); + outb(PIC2+1, 0x02); io_wait(); +} + void interrupts_init() { @@ -142,6 +185,8 @@ interrupts_init() #undef ISR idt_write(); + disable_legacy_pic(); + log::info(logs::interrupt, "Interrupts enabled."); } @@ -173,6 +218,28 @@ isr_handler(registers regs) cons->puts("\nLINT1\n"); break; + case isr::isrIgnore0: + case isr::isrIgnore1: + case isr::isrIgnore2: + case isr::isrIgnore3: + case isr::isrIgnore4: + case isr::isrIgnore5: + case isr::isrIgnore6: + case isr::isrIgnore7: + case isr::isrIgnore8: + case isr::isrIgnore9: + case isr::isrIgnoreA: + case isr::isrIgnoreB: + case isr::isrIgnoreC: + case isr::isrIgnoreD: + case isr::isrIgnoreE: + case isr::isrIgnoreF: + /* + cons->printf("\nIGNORED PIC INTERRUPT %d\n", + (regs.interrupt % 0xff) - 0xf0); + */ + break; + default: cons->set_color(9); cons->puts("\nReceived ISR interrupt:\n"); @@ -212,32 +279,41 @@ void irq_handler(registers regs) { console *cons = console::get(); + uint8_t irq = get_irq(regs.interrupt); - cons->set_color(9); - cons->puts("\nReceived IRQ interrupt:\n"); - cons->set_color(); + switch (irq) { + case 2: + cons->set_color(11); + cons->puts("."); + cons->set_color(); + break; - print_reg("ISR", regs.interrupt); - print_reg("ERR", regs.errorcode); - cons->puts("\n"); + default: + cons->set_color(11); + cons->printf("\nReceived IRQ interrupt: %d (vec %d)\n", + irq, regs.interrupt); + cons->set_color(); - print_reg(" ds", regs.ds); - print_reg("rdi", regs.rdi); - print_reg("rsi", regs.rsi); - print_reg("rbp", regs.rbp); - print_reg("rsp", regs.rsp); - print_reg("rbx", regs.rbx); - print_reg("rdx", regs.rdx); - print_reg("rcx", regs.rcx); - print_reg("rax", regs.rax); - cons->puts("\n"); + print_reg(" ds", regs.ds); + print_reg("rdi", regs.rdi); + print_reg("rsi", regs.rsi); + print_reg("rbp", regs.rbp); + print_reg("rsp", regs.rsp); + print_reg("rbx", regs.rbx); + print_reg("rdx", regs.rdx); + print_reg("rcx", regs.rcx); + print_reg("rax", regs.rax); + cons->puts("\n"); - print_reg("rip", regs.rip); - print_reg(" cs", regs.cs); - print_reg(" ef", regs.eflags); - print_reg("esp", regs.user_esp); - print_reg(" ss", regs.ss); - while(1) asm("hlt"); + print_reg("rip", regs.rip); + print_reg(" cs", regs.cs); + print_reg(" ef", regs.eflags); + print_reg("esp", regs.user_esp); + print_reg(" ss", regs.ss); + while(1) asm("hlt"); + } + + *reinterpret_cast(0xffffff80fee000b0) = 0; } diff --git a/src/kernel/interrupts.h b/src/kernel/interrupts.h index c9f5794..fe033fe 100644 --- a/src/kernel/interrupts.h +++ b/src/kernel/interrupts.h @@ -14,9 +14,11 @@ enum class isr : uint8_t #undef EISR #undef ISR - max + _zero = 0 }; +isr operator+(const isr &lhs, int rhs); + extern "C" { void interrupts_enable(); void interrupts_disable(); diff --git a/src/kernel/io.cpp b/src/kernel/io.cpp index ac3cb59..6065c9a 100644 --- a/src/kernel/io.cpp +++ b/src/kernel/io.cpp @@ -30,3 +30,8 @@ wrmsr(uint64_t addr, uint64_t value) __asm__ __volatile__ ("wrmsr" :: "c"(addr), "a"(low), "d"(high)); } +void +io_wait() +{ + outb(0x80, 0); +} diff --git a/src/kernel/io.h b/src/kernel/io.h index 6e77085..dbd9f67 100644 --- a/src/kernel/io.h +++ b/src/kernel/io.h @@ -24,6 +24,9 @@ uint64_t rdmsr(uint64_t addr); /// \arg value The value to write void wrmsr(uint64_t addr, uint64_t value); +/// Pause briefly by doing IO to port 0x80 +void io_wait(); + } const uint16_t COM1 = 0x03f8; diff --git a/src/kernel/main.cpp b/src/kernel/main.cpp index 5d13659..d4a36cd 100644 --- a/src/kernel/main.cpp +++ b/src/kernel/main.cpp @@ -72,9 +72,9 @@ kernel_main(popcorn_data *header) pager->map_offset_pointer(&header->frame_buffer, header->frame_buffer_length); interrupts_init(); - interrupts_enable(); device_manager devices(header->acpi_table); + interrupts_enable(); // int x = 1 / 0; // __asm__ __volatile__("int $15"); diff --git a/src/kernel/memory.h b/src/kernel/memory.h index 7a94286..a3a69a9 100644 --- a/src/kernel/memory.h +++ b/src/kernel/memory.h @@ -44,3 +44,12 @@ void memory_initialize_managers(const void *memory_map, size_t map_length, size_ /// \returns A pointer to the allocated memory, or nullptr if /// allocation failed. inline void * kalloc(size_t length) { return g_kernel_memory_manager.allocate(length); } + +/// Free kernel space memory. +/// \arg p The pointer to free +inline void kfree(void *p) { g_kernel_memory_manager.free(p); } + +inline void * operator new (size_t n) { return g_kernel_memory_manager.allocate(n); } +inline void * operator new[] (size_t n) { return g_kernel_memory_manager.allocate(n); } +inline void operator delete (void *p) { return g_kernel_memory_manager.free(p); } +inline void operator delete[] (void *p){ return g_kernel_memory_manager.free(p); }