diff --git a/kernel/arch/i386/APIC.cpp b/kernel/arch/i386/APIC.cpp new file mode 100644 index 000000000..9d078593b --- /dev/null +++ b/kernel/arch/i386/APIC.cpp @@ -0,0 +1,350 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace APIC +{ + + static bool s_using_fallback_pic = false; + + static uint32_t s_local_apic = 0; + static uint32_t s_io_apic = 0; + + static uint8_t s_overrides[0x100]; + + struct RSDPDescriptor + { + char signature[8]; + uint8_t checksum; + char OEMID[6]; + uint8_t revision; + uint32_t rsdt_address; + } __attribute__ ((packed)); + + struct RSDPDescriptor20 + { + RSDPDescriptor firstPart; + uint32_t length; + uint64_t xsdt_address; + uint8_t extended_checksum; + uint8_t reserved[3]; + } __attribute__((packed)); + + struct ACPISDTHeader + { + char signature[4]; + uint32_t length; + uint8_t eevision; + uint8_t checksum; + char OEMID[6]; + char OEM_table_id[8]; + uint32_t OEM_revision; + uint32_t creator_id; + uint32_t creator_revision; + } __attribute__((packed)); + + struct RSDT + { + ACPISDTHeader header; + uint32_t sdt_pointer[0]; + }; + + struct XSDT + { + ACPISDTHeader header; + uint64_t sdt_pointer[0]; + }; + + struct MADT + { + ACPISDTHeader header; + uint32_t local_apic; + uint32_t flags; + } __attribute__((packed)); + + struct MADTEntry + { + uint8_t type; + uint8_t length; + union + { + struct + { + uint8_t processor_id; + uint8_t apic_id; + uint32_t flags; + } __attribute__((packed)) lapic; + struct + { + uint8_t ioapic_id; + uint8_t reserved; + uint32_t ioapic_address; + uint32_t gsi_base; + } __attribute__((packed)) ioapic; + struct + { + uint8_t bus_source; + uint8_t irq_source; + uint32_t gsi; + uint16_t flags; + } __attribute__((packed)) interrupt_source_override; + }; + } __attribute__((packed)); + + union RedirectionEntry + { + struct + { + uint64_t vector : 8; + uint64_t delivery_mode : 3; + uint64_t destination_mode : 1; + uint64_t delivery_status : 1; + uint64_t pin_polarity : 1; + uint64_t remote_irr : 1; + uint64_t trigger_mode : 1; + uint64_t mask : 1; + uint64_t reserved : 39; + uint64_t destination : 8; + }; + struct + { + uint32_t lo_dword; + uint32_t hi_dword; + }; + }; + + + static uint32_t ReadLocalAPIC(uint32_t offset); + static void WriteLocalAPIC(uint32_t offset, uint32_t value); + static uint32_t ReadIOAPIC(uint8_t reg); + static void WriteIOAPIC(uint8_t reg, uint32_t value); + + + static bool IsRSDP(RSDPDescriptor* rsdp) + { + if (memcmp(rsdp->signature, "RSD PTR ", 8) != 0) + return false; + + { + uint8_t checksum = 0; + for (uint32_t i = 0; i < sizeof(RSDPDescriptor); i++) + checksum += ((uint8_t*)rsdp)[i]; + if (checksum != 0) + return false; + } + + if (rsdp->revision == 2) + { + RSDPDescriptor20* rsdp20 = (RSDPDescriptor20*)rsdp; + uint8_t checksum = 0; + for (uint32_t i = 0; i < sizeof(RSDPDescriptor20); i++) + checksum += ((uint8_t*)rsdp20)[i]; + if (checksum != 0) + return false; + } + + return true; + } + + static RSDPDescriptor* LocateRSDP() + { + // Look in main BIOS area below 1 MB + for (uint32_t addr = 0x000E0000; addr < 0x000FFFFF; addr += 16) + if (IsRSDP((RSDPDescriptor*)addr)) + return (RSDPDescriptor*)addr; + + return nullptr; + } + + static bool IsValidACPISDTHeader(ACPISDTHeader* header) + { + uint8_t sum = 0; + for (uint32_t i = 0; i < header->length; i++) + sum += ((uint8_t*)header)[i]; + return sum == 0; + } + + template + static void GetAPIC(SDT* root) + { + uint32_t sdt_entry_count = (root->header.length - sizeof(root->header)) / sizeof(*(root->sdt_pointer)); + for (uint32_t i = 0; i < sdt_entry_count; i++) + { + ACPISDTHeader* header = (ACPISDTHeader*)root->sdt_pointer[i]; + if (!IsValidACPISDTHeader(header)) + continue; + if (memcmp(header->signature, "APIC", 4) == 0) + { + MADT* madt = (MADT*)header; + s_local_apic = madt->local_apic; + uint32_t entry_addr = (uint32_t)madt + sizeof(MADT); + while (entry_addr < (uint32_t)madt + madt->header.length) + { + MADTEntry* entry = (MADTEntry*)entry_addr; + + switch (entry->type) + { + case 0: + //s_processor_id = entry->lapic.processor_id; + kprintln("Processor {} ({})", entry->lapic.processor_id, entry->lapic.apic_id); + break; + case 1: + if (s_io_apic == 0) + s_io_apic = entry->ioapic.ioapic_address; + kprintln("IOAPIC #{}", entry->ioapic.ioapic_id); + kprintln(" addr: {}", (void*)entry->ioapic.ioapic_address); + kprintln(" gsi base: {} ({})", entry->ioapic.gsi_base, (uint8_t)((ReadIOAPIC(0x01) >> 16) + 1)); + break; + case 2: + s_overrides[entry->interrupt_source_override.irq_source] = entry->interrupt_source_override.gsi; + break; + default: + break; + } + entry_addr += entry->length; + } + } + } + } + + + static uint32_t ReadLocalAPIC(uint32_t offset) + { + return *(uint32_t*)(s_local_apic + offset); + } + + static void WriteLocalAPIC(uint32_t offset, uint32_t value) + { +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Warray-bounds" + *(uint32_t*)(s_local_apic + offset) = value; +#pragma GCC diagnostic pop + } + + static uint32_t ReadIOAPIC(uint8_t reg) + { + volatile uint32_t* ioapic = (volatile uint32_t*)s_io_apic; + ioapic[0] = reg; + return ioapic[4]; + } + + static void WriteIOAPIC(uint8_t reg, uint32_t value) + { + volatile uint32_t* ioapic = (volatile uint32_t*)s_io_apic; + ioapic[0] = reg; + ioapic[4] = value; + } + + static bool InitializeAPIC() + { + if (!CPUID::IsAvailable()) + { + kprintln("CPUID not available"); + return false; + } + + uint32_t ecx, edx; + CPUID::GetFeatures(ecx, edx); + if (!(edx & CPUID::Features::EDX_APIC)) + { + kprintln("Local APIC not available"); + return false; + } + if (!(edx & CPUID::Features::EDX_MSR)) + { + kprintln("MSR not available"); + return false; + } + + RSDPDescriptor* rsdp = LocateRSDP(); + if (rsdp == nullptr) + { + kprintln("Could not locate RSDP"); + return false; + } + + if (rsdp->revision == 2) + { + GetAPIC((XSDT*)((RSDPDescriptor20*)rsdp)->xsdt_address); + } + else + { + GetAPIC((RSDT*)rsdp->rsdt_address); + } + + if (s_local_apic == 0 || s_io_apic == 0) + return false; + + // Enable Local APIC + uint32_t sipi = ReadLocalAPIC(0xF0); + WriteIOAPIC(0xF0, sipi | 0x1FF); + + return true; + } + + void Initialize() + { + for (uint32_t i = 0x00; i <= 0xFF; i++) + s_overrides[i] = i; + + PIC::MaskAll(); + PIC::Remap(); + + if (!InitializeAPIC()) + { + kprintln("Could not initialize APIC. Using PIC as fallback"); + s_using_fallback_pic = true; + } + } + + void EOI() + { + if (s_using_fallback_pic) + return PIC::EOI(0); + WriteLocalAPIC(0xB0, 0); + } + + void GetISR(uint32_t out[8]) + { + if (s_using_fallback_pic) + { + memset(out, 0, sizeof(uint32_t) * 8); + uint32_t addr = (uint32_t)out + IRQ_VECTOR_BASE / 8; + *(uint16_t*)addr = PIC::GetISR(); + return; + } + + for (uint32_t i = 0; i < 8; i++) + out[i] = ReadLocalAPIC(0x100 + i * 0x10); + } + + void EnableIRQ(uint8_t irq) + { + if (s_using_fallback_pic) + return PIC::Unmask(irq); + + uint32_t gsi = s_overrides[irq]; + + RedirectionEntry redir; + redir.lo_dword = ReadIOAPIC(0x10 + gsi * 2); + redir.hi_dword = ReadIOAPIC(0x10 + gsi * 2 + 1); + + redir.vector = IRQ_VECTOR_BASE + irq; + redir.mask = 0; + redir.destination = ReadLocalAPIC(0x20); + + kprintln("register irq {2} -> {2} @ apic {}", irq, gsi, redir.destination); + + WriteIOAPIC(0x10 + gsi * 2, redir.lo_dword); + WriteIOAPIC(0x10 + gsi * 2 + 1, redir.hi_dword); + } + +} \ No newline at end of file diff --git a/kernel/arch/i386/IDT.cpp b/kernel/arch/i386/IDT.cpp index 9a7ce49e1..674eaeade 100644 --- a/kernel/arch/i386/IDT.cpp +++ b/kernel/arch/i386/IDT.cpp @@ -1,8 +1,9 @@ +#include #include #include #include -#include #include +#include union GateDescriptor { @@ -33,12 +34,9 @@ struct IDTR } __attribute((packed)); static IDTR s_idtr; -static GateDescriptor* s_idt; +static GateDescriptor s_idt[0x100]; -static void (*s_irq_handlers[16])(); - -extern "C" void handle_irq(); -extern "C" void handle_irq_common(); +static void (*s_irq_handlers[0xFF])() { nullptr }; #define INTERRUPT_HANDLER(i, msg) \ static void interrupt ## i () \ @@ -119,21 +117,17 @@ namespace IDT void register_irq_handler(uint8_t irq, void (*f)()) { - s_irq_handlers[irq] = f; + s_irq_handlers[IRQ_VECTOR_BASE + irq] = f; register_interrupt_handler(IRQ_VECTOR_BASE + irq, handle_irq_common); } void initialize() { - constexpr size_t idt_size = 256; - - s_idt = new GateDescriptor[idt_size]; - s_idtr.offset = s_idt; - s_idtr.size = idt_size * 8; + s_idtr.size = sizeof(s_idt); - for (uint8_t i = 0xff; i > 0x10; i--) - register_interrupt_handler(i, unimplemented_trap); + for (uint8_t i = 0xFF; i > IRQ_VECTOR_BASE; i--) + register_irq_handler(i, nullptr); REGISTER_HANDLER(0x00); REGISTER_HANDLER(0x01); @@ -145,16 +139,28 @@ namespace IDT REGISTER_HANDLER(0x07); REGISTER_HANDLER(0x08); REGISTER_HANDLER(0x09); - REGISTER_HANDLER(0x0a); - REGISTER_HANDLER(0x0b); - REGISTER_HANDLER(0x0c); - REGISTER_HANDLER(0x0d); - REGISTER_HANDLER(0x0e); - REGISTER_HANDLER(0x0f); + REGISTER_HANDLER(0x0A); + REGISTER_HANDLER(0x0B); + REGISTER_HANDLER(0x0C); + REGISTER_HANDLER(0x0D); + REGISTER_HANDLER(0x0E); + REGISTER_HANDLER(0x0F); REGISTER_HANDLER(0x10); - - for (uint8_t i = 0; i < 16; i++) - register_irq_handler(i, nullptr); + REGISTER_HANDLER(0x11); + REGISTER_HANDLER(0x12); + REGISTER_HANDLER(0x13); + REGISTER_HANDLER(0x14); + REGISTER_HANDLER(0x15); + REGISTER_HANDLER(0x16); + REGISTER_HANDLER(0x17); + REGISTER_HANDLER(0x18); + REGISTER_HANDLER(0x19); + REGISTER_HANDLER(0x1A); + REGISTER_HANDLER(0x1B); + REGISTER_HANDLER(0x1C); + REGISTER_HANDLER(0x1D); + REGISTER_HANDLER(0x1E); + REGISTER_HANDLER(0x1F); flush_idt(); } diff --git a/kernel/arch/i386/make.config b/kernel/arch/i386/make.config index a1ad4764f..cb3b248a3 100644 --- a/kernel/arch/i386/make.config +++ b/kernel/arch/i386/make.config @@ -4,6 +4,7 @@ KERNEL_ARCH_LDFLAGS= KERNEL_ARCH_LIBS= KERNEL_ARCH_OBJS= \ +$(ARCHDIR)/APIC.o \ $(ARCHDIR)/boot.o \ $(ARCHDIR)/CPUID.o \ $(ARCHDIR)/font.o \ diff --git a/kernel/include/kernel/APIC.h b/kernel/include/kernel/APIC.h new file mode 100644 index 000000000..8b21f0d2d --- /dev/null +++ b/kernel/include/kernel/APIC.h @@ -0,0 +1,13 @@ +#pragma once + +#include + +namespace APIC +{ + + void Initialize(); + void EOI(); + void GetISR(uint32_t[8]); + void EnableIRQ(uint8_t irq); + +} \ No newline at end of file diff --git a/kernel/include/kernel/IDT.h b/kernel/include/kernel/IDT.h index ecc0a3517..946d956e8 100644 --- a/kernel/include/kernel/IDT.h +++ b/kernel/include/kernel/IDT.h @@ -2,7 +2,7 @@ #include -constexpr uint8_t IRQ_VECTOR_BASE = 0x50; +constexpr uint8_t IRQ_VECTOR_BASE = 0x20; namespace IDT { diff --git a/kernel/include/kernel/PIC.h b/kernel/include/kernel/PIC.h index c4bf9440d..5b71a3789 100644 --- a/kernel/include/kernel/PIC.h +++ b/kernel/include/kernel/PIC.h @@ -5,11 +5,12 @@ namespace PIC { - void initialize(); - void eoi(uint8_t); - void unmask(uint8_t); - void mask(uint8_t); + void Remap(); + void MaskAll(); + void EOI(uint8_t); + void Unmask(uint8_t); + void Mask(uint8_t); - uint16_t get_isr(); + uint16_t GetISR(); } \ No newline at end of file diff --git a/kernel/kernel/Keyboard.cpp b/kernel/kernel/Keyboard.cpp index 3e2994b39..644033e4f 100644 --- a/kernel/kernel/Keyboard.cpp +++ b/kernel/kernel/Keyboard.cpp @@ -1,9 +1,9 @@ #include +#include #include #include #include #include -#include #include #include @@ -449,7 +449,7 @@ namespace Keyboard // Register callback and IRQ IDT::register_irq_handler(KEYBOARD_IRQ, keyboard_irq_handler); - PIC::unmask(KEYBOARD_IRQ); + APIC::EnableIRQ(KEYBOARD_IRQ); return true; } diff --git a/kernel/kernel/PIC.cpp b/kernel/kernel/PIC.cpp index f9e280235..e6c2ed6d6 100644 --- a/kernel/kernel/PIC.cpp +++ b/kernel/kernel/PIC.cpp @@ -27,8 +27,11 @@ namespace PIC { - void initialize() + void Remap() { + uint8_t a1 = IO::inb(PIC1_DATA); + uint8_t a2 = IO::inb(PIC2_DATA); + // Start the initialization sequence (in cascade mode) IO::outb(PIC1_COMMAND, ICW1_INIT | ICW1_ICW4); IO::io_wait(); @@ -53,19 +56,25 @@ namespace PIC IO::outb(PIC2_DATA, ICW4_8086); IO::io_wait(); - // Mask everything + // Restore original masks + IO::outb(PIC1_DATA, a1); + IO::outb(PIC2_DATA, a2); + } + + void MaskAll() + { IO::outb(PIC1_DATA, 0xff); IO::outb(PIC2_DATA, 0xff); } - void eoi(uint8_t irq) + void EOI(uint8_t irq) { if (irq >= 8) IO::outb(PIC2_COMMAND, PIC_EOI); IO::outb(PIC1_COMMAND, PIC_EOI); } - void mask(uint8_t irq) { + void Mask(uint8_t irq) { uint16_t port; uint8_t value; @@ -79,7 +88,7 @@ namespace PIC IO::outb(port, value); } - void unmask(uint8_t irq) { + void Unmask(uint8_t irq) { uint16_t port; uint8_t value; @@ -93,7 +102,7 @@ namespace PIC IO::outb(port, value); } - uint16_t get_isr() + uint16_t GetISR() { IO::outb(PIC1_COMMAND, 0x0b); IO::outb(PIC2_COMMAND, 0x0b); diff --git a/kernel/kernel/PIT.cpp b/kernel/kernel/PIT.cpp index ab4d15f33..e91ca26d8 100644 --- a/kernel/kernel/PIT.cpp +++ b/kernel/kernel/PIT.cpp @@ -1,7 +1,7 @@ #include #include #include -#include +#include #define IRQ_TIMER 0 @@ -28,7 +28,7 @@ namespace PIT void clock_handle() { - PIT::s_system_time++; + s_system_time++; } uint64_t ms_since_boot() @@ -47,7 +47,7 @@ namespace PIT IDT::register_irq_handler(IRQ_TIMER, clock_handle); - PIC::unmask(IRQ_TIMER); + APIC::EnableIRQ(IRQ_TIMER); } } diff --git a/kernel/kernel/kernel.cpp b/kernel/kernel/kernel.cpp index 0c820316f..8f22715f5 100644 --- a/kernel/kernel/kernel.cpp +++ b/kernel/kernel/kernel.cpp @@ -1,3 +1,4 @@ +#include #include #include #include @@ -40,11 +41,9 @@ void kernel_main(multiboot_info_t* mbi, uint32_t magic) } TTY::initialize(); - dprintln("{}", mbi->framebuffer.type); - kmalloc_initialize(); - PIC::initialize(); + APIC::Initialize(); gdt_initialize(); IDT::initialize(); @@ -52,11 +51,12 @@ void kernel_main(multiboot_info_t* mbi, uint32_t magic) if (!Keyboard::initialize()) return; - kprintln("Hello from the kernel!"); - ENABLE_INTERRUPTS(); + kprintln("Hello from the kernel!"); + auto& shell = Kernel::Shell::Get(); + shell.Run(); for (;;) diff --git a/qemu.sh b/qemu.sh index fcb3c7f58..c32ecfa7f 100755 --- a/qemu.sh +++ b/qemu.sh @@ -2,8 +2,9 @@ set -e . ./iso.sh -qemu-system-$(./target-triplet-to-arch.sh $HOST) \ +qemu-system-$(./target-triplet-to-arch.sh $HOST) \ -m 128 \ + -smp 2 \ -drive format=raw,media=cdrom,file=banan-os.iso \ -serial stdio \ \ No newline at end of file