diff --git a/kernel/Makefile b/kernel/Makefile index 3a224328..4151de2e 100644 --- a/kernel/Makefile +++ b/kernel/Makefile @@ -29,24 +29,26 @@ LIBS:=$(LIBS) $(KERNEL_ARCH_LIBS) BUILDDIR=$(abspath build) -KERNEL_OBJS= \ -$(KERNEL_ARCH_OBJS) \ -kernel/build_libc.o \ -kernel/CPUID.o \ -kernel/font.o \ -kernel/Input.o \ -kernel/kernel.o \ -kernel/kmalloc.o \ -kernel/Panic.o \ -kernel/PIC.o \ -kernel/PIT.o \ -kernel/RTC.o \ -kernel/Serial.o \ -kernel/Shell.o \ -kernel/SSP.o \ -kernel/TTY.o \ -kernel/VesaTerminalDriver.o \ -icxxabi.o \ +KERNEL_OBJS= \ +$(KERNEL_ARCH_OBJS) \ +kernel/APIC.o \ +kernel/build_libc.o \ +kernel/CPUID.o \ +kernel/font.o \ +kernel/Input.o \ +kernel/InterruptController.o \ +kernel/kernel.o \ +kernel/kmalloc.o \ +kernel/Panic.o \ +kernel/PIC.o \ +kernel/PIT.o \ +kernel/RTC.o \ +kernel/Serial.o \ +kernel/Shell.o \ +kernel/SSP.o \ +kernel/TTY.o \ +kernel/VesaTerminalDriver.o \ +icxxabi.o \ OBJS= \ $(ARCHDIR)/crti.o \ diff --git a/kernel/arch/i386/APIC.cpp b/kernel/arch/i386/APIC.cpp deleted file mode 100644 index f60c481c..00000000 --- a/kernel/arch/i386/APIC.cpp +++ /dev/null @@ -1,440 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#define APIC_DEBUG_PRINT 0 - -#define IA32_APIC_BASE 0x1B -#define IA32_APIC_BASE_ENABLE (1 << 11) - -namespace APIC -{ - - static bool s_using_fallback_pic = false; - - static uint8_t s_processor_ids[0x100] { }; - static uint8_t s_lapic_ids[0x100] { }; - static uint8_t s_lapic_count = 0; - - 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 acpi_processor_id; - uint8_t apic_id; - uint32_t flags; - } __attribute__((packed)) entry0; - struct - { - uint8_t ioapic_id; - uint8_t reserved; - uint32_t ioapic_address; - uint32_t gsi_base; - } __attribute__((packed)) entry1; - struct - { - uint8_t bus_source; - uint8_t irq_source; - uint32_t gsi; - uint16_t flags; - } __attribute__((packed)) entry2; - struct - { - uint8_t nmi_source; - uint8_t reserved; - uint16_t flags; - uint32_t gsi; - } __attribute__((packed)) entry3; - struct - { - uint8_t acpi_processor_id; - uint16_t flags; - uint8_t lint; - } __attribute__((packed)) entry4; - struct - { - uint16_t reserved; - uint64_t address; - } __attribute__((packed)) entry5; - struct - { - uint16_t reserved; - uint32_t local_x2acpi_id; - uint32_t flags; - uint32_t acpi_id; - } __attribute__((packed)) entry9; - }; - } __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 void SetMSR(uint32_t msr, uint32_t lo, uint32_t hi); - - - 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; - } - - static void ParseMADT(RSDPDescriptor* rsdp) - { - RSDT* const root = (RSDT*)(rsdp->revision == 2 ? ((RSDPDescriptor20*)rsdp)->xsdt_address : rsdp->rsdt_address); - MMU::Get().AllocatePage((uintptr_t)root); - uint32_t sdt_entry_count = (root->header.length - sizeof(root->header)) / (rsdp->revision == 2 ? 8 : 4); - - for (uint32_t i = 0; i < sdt_entry_count; i++) - { - ACPISDTHeader* header = (ACPISDTHeader*)(rsdp->revision == 2 ? ((uint64_t*)root->sdt_pointer)[i] : 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_ids[s_lapic_count] = entry->entry0.acpi_processor_id; - s_lapic_ids[s_lapic_count] = entry->entry0.apic_id; - s_lapic_count++; -#if APIC_DEBUG_PRINT >= 2 - dprintln("Entry0, processor id {}, apic id {}, flags 0b{32b}", - entry->entry0.acpi_processor_id, - entry->entry0.apic_id, - entry->entry0.flags - ); -#endif - break; - case 1: - if (s_io_apic == 0) - s_io_apic = entry->entry1.ioapic_address; -#if APIC_DEBUG_PRINT - dprintln("Entry1, io apic id {}, io apic address 0x{4H}, gsi base {}", - entry->entry1.ioapic_id, - entry->entry1.ioapic_address, - entry->entry1.gsi_base - ); -#endif - break; - case 2: - s_overrides[entry->entry2.irq_source] = entry->entry2.gsi; -#if APIC_DEBUG_PRINT - dprintln("Entry2, bus source {}, irq source {}, gsi {}, flags 0b{16b}", - entry->entry2.bus_source, - entry->entry2.irq_source, - entry->entry2.gsi, - entry->entry2.flags - ); -#endif - break; - case 3: -#if APIC_DEBUG_PRINT - dprintln("Entry3, nmi source {}, flags 0b{16b}, gsi {}", - entry->entry3.nmi_source, - entry->entry3.flags, - entry->entry3.gsi - ); -#endif - break; - case 4: -#if APIC_DEBUG_PRINT - dprintln("Entry4, acpi processor id 0x{2H}, flags 0b{16b}, lint{}", - entry->entry4.acpi_processor_id, - entry->entry4.flags, - entry->entry4.lint - ); -#endif - break; - case 5: - s_local_apic = entry->entry5.address; -#if APIC_DEBUG_PRINT - dprintln("Entry5, address 0x{4H}", - entry->entry5.address - ); -#endif - break; - case 9: -#if APIC_DEBUG_PRINT - dprintln("Entry9, x2 acpi id {}, flags 0b{32b}, acpi id {}", - entry->entry9.local_x2acpi_id, - entry->entry9.flags, - entry->entry9.acpi_id - ); -#endif - break; - default: - break; - } - entry_addr += entry->length; - } - } - } - MMU::Get().UnAllocatePage((uintptr_t)root); - } - - 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 void SetMSR(uint32_t msr, uint32_t lo, uint32_t hi) - { - asm volatile("wrmsr" : : "a"(lo), "d"(hi), "c"(msr)); - } - - static bool InitializeAPIC() - { - uint32_t ecx, edx; - CPUID::GetFeatures(ecx, edx); - if (!(edx & CPUID::Features::EDX_APIC)) - { - dprintln("Local APIC not available"); - return false; - } - if (!(edx & CPUID::Features::EDX_MSR)) - { - dprintln("MSR not available"); - return false; - } - - RSDPDescriptor* rsdp = LocateRSDP(); - if (rsdp == nullptr) - { - dprintln("Could not locate RSDP"); - return false; - } - - ParseMADT(rsdp); - - if (s_local_apic == 0 || s_io_apic == 0) - return false; - - MMU::Get().AllocatePage(s_io_apic); - MMU::Get().AllocatePage(s_local_apic); - - // Enable Local APIC - SetMSR(IA32_APIC_BASE, (s_local_apic & 0xFFFFF000) | IA32_APIC_BASE_ENABLE, 0); - - uint32_t sivr = ReadLocalAPIC(0xF0); - WriteLocalAPIC(0xF0, sivr | 0x1FF); - - return true; - } - - void Initialize(bool force_pic) - { - for (uint32_t i = 0x00; i <= 0xFF; i++) - s_overrides[i] = i; - - PIC::MaskAll(); - PIC::Remap(); - - if (force_pic) - { - dprintln("Using PIC instead of APIC"); - s_using_fallback_pic = true; - } - else if (!InitializeAPIC()) - { - dprintln("Could not initialize APIC. Using PIC as fallback"); - s_using_fallback_pic = true; - } - } - - void EOI(uint8_t irq) - { - if (s_using_fallback_pic) - return PIC::EOI(irq); - 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 = s_lapic_ids[0]; - - 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 7806dce8..35e91eff 100644 --- a/kernel/arch/i386/IDT.cpp +++ b/kernel/arch/i386/IDT.cpp @@ -1,5 +1,6 @@ -#include +#include #include +#include #include #include @@ -70,7 +71,7 @@ namespace IDT } __attribute((packed)); static IDTR s_idtr; - static GateDescriptor* s_idt; + static GateDescriptor* s_idt = nullptr; static void (**s_irq_handlers)(); @@ -110,7 +111,7 @@ namespace IDT extern "C" void handle_irq() { uint32_t isr[8]; - APIC::GetISR(isr); + InterruptController::Get().GetISR(isr); uint8_t irq = 0; for (uint8_t i = 0; i < 8; i++) @@ -137,7 +138,7 @@ namespace IDT else Kernel::Panic("no handler for irq 0x{2H}\n", irq); - APIC::EOI(irq); + InterruptController::Get().EOI(irq); } extern "C" void handle_irq_common(); @@ -192,7 +193,7 @@ namespace IDT s_idtr.offset = s_idt; s_idtr.size = 0x100 * sizeof(GateDescriptor) - 1; - for (uint8_t i = 0xFF; i > IRQ_VECTOR_BASE; i--) + for (uint8_t i = 0x00; i <= 0xFF - IRQ_VECTOR_BASE; i++) register_irq_handler(i, nullptr); REGISTER_HANDLER(0x00); diff --git a/kernel/arch/i386/make.config b/kernel/arch/i386/make.config index 7e789dc9..ff9eac99 100644 --- a/kernel/arch/i386/make.config +++ b/kernel/arch/i386/make.config @@ -4,7 +4,6 @@ KERNEL_ARCH_LDFLAGS= KERNEL_ARCH_LIBS= KERNEL_ARCH_OBJS= \ -$(ARCHDIR)/APIC.o \ $(ARCHDIR)/boot.o \ $(ARCHDIR)/IDT.o \ $(ARCHDIR)/MMU.o \ diff --git a/kernel/include/kernel/APIC.h b/kernel/include/kernel/APIC.h index 6c59ca2c..17b65c07 100644 --- a/kernel/include/kernel/APIC.h +++ b/kernel/include/kernel/APIC.h @@ -1,13 +1,50 @@ #pragma once -#include +#include +#include -namespace APIC +class APIC final : public InterruptController { +public: + virtual void EOI(uint8_t) override; + virtual void EnableIrq(uint8_t) override; + virtual void GetISR(uint32_t[8]) override; - void Initialize(bool force_pic = false); - void EOI(uint8_t irq); - void GetISR(uint32_t[8]); - void EnableIRQ(uint8_t irq); +private: + uint32_t ReadFromLocalAPIC(ptrdiff_t); + void WriteToLocalAPIC(ptrdiff_t, uint32_t); -} \ No newline at end of file +private: + static APIC* Create(); + friend class InterruptController; + +private: + struct Processor + { + enum Flags : uint8_t + { + Enabled = 1, + OnlineCapable = 2, + }; + uint8_t processor_id; + uint8_t apic_id; + uint8_t flags; + }; + + struct IOAPIC + { + uint8_t id; + uintptr_t address; + uint32_t gsi_base; + uint8_t max_redirs; + + uint32_t Read(uint8_t offset); + void Write(uint8_t offset, uint32_t data); + }; + +private: + BAN::Vector m_processors; + uintptr_t m_local_apic = 0; + BAN::Vector m_io_apics; + uint8_t m_irq_overrides[0x100] {}; +}; \ No newline at end of file diff --git a/kernel/include/kernel/InterruptController.h b/kernel/include/kernel/InterruptController.h new file mode 100644 index 00000000..328482a3 --- /dev/null +++ b/kernel/include/kernel/InterruptController.h @@ -0,0 +1,16 @@ +#pragma once + +#include + +class InterruptController +{ +public: + virtual ~InterruptController() {} + + virtual void EOI(uint8_t) = 0; + virtual void EnableIrq(uint8_t) = 0; + virtual void GetISR(uint32_t[8]) = 0; + + static void Initialize(bool force_pic); + static InterruptController& Get(); +}; \ No newline at end of file diff --git a/kernel/include/kernel/PIC.h b/kernel/include/kernel/PIC.h index 5b71a378..92574ca1 100644 --- a/kernel/include/kernel/PIC.h +++ b/kernel/include/kernel/PIC.h @@ -1,16 +1,18 @@ #pragma once -#include +#include -namespace PIC +class PIC final : public InterruptController { +public: + virtual void EOI(uint8_t) override; + virtual void EnableIrq(uint8_t) override; + virtual void GetISR(uint32_t[8]) override; - void Remap(); - void MaskAll(); - void EOI(uint8_t); - void Unmask(uint8_t); - void Mask(uint8_t); + static void Remap(); + static void MaskAll(); - uint16_t GetISR(); - -} \ No newline at end of file +private: + static PIC* Create(); + friend class InterruptController; +}; \ No newline at end of file diff --git a/kernel/kernel/APIC.cpp b/kernel/kernel/APIC.cpp new file mode 100644 index 00000000..b1415579 --- /dev/null +++ b/kernel/kernel/APIC.cpp @@ -0,0 +1,365 @@ +#include +#include +#include +#include +#include +#include +#include + +#include + +#define LAPIC_EIO_REG 0xB0 +#define LAPIC_SIV_REG 0xF0 +#define LAPIC_IS_REG 0x100 + +#define IOAPIC_MAX_REDIRS 0x01 +#define IOAPIC_REDIRS 0x10 + + +#define PIC1 0x20 +#define PIC2 0xA0 +#define PIC1_COMMAND PIC1 +#define PIC1_DATA (PIC1+1) +#define PIC2_COMMAND PIC2 +#define PIC2_DATA (PIC2+1) +#define ICW1_ICW4 0x01 +#define ICW1_INIT 0x10 +#define ICW4_8086 0x01 + + +// https://uefi.org/specs/ACPI/6.5/05_ACPI_Software_Programming_Model.html#multiple-apic-description-table-madt-format + +struct RSDPDescriptor +{ + char signature[8]; + uint8_t checksum; + char OEMID[6]; + uint8_t revision; + uint32_t rsdt_address; +} __attribute__ ((packed)); + +struct RSDPDescriptor20 +{ + RSDPDescriptor first_part; + 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 revision; + 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 acpi_processor_id; + uint8_t apic_id; + uint32_t flags; + } __attribute__((packed)) entry0; + struct + { + uint8_t ioapic_id; + uint8_t reserved; + uint32_t ioapic_address; + uint32_t gsi_base; + } __attribute__((packed)) entry1; + struct + { + uint8_t bus_source; + uint8_t irq_source; + uint32_t gsi; + uint16_t flags; + } __attribute__((packed)) entry2; + struct + { + uint16_t reserved; + uint64_t address; + } __attribute__((packed)) entry5; + }; +} __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 bool IsRSDP(const 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 (uintptr_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; +} + +static MADT* LocateMADT(const RSDPDescriptor* rsdp) +{ + uintptr_t root_addr = 0; + uint32_t entry_count = 0; + if (rsdp->revision == 2) + { + const XSDT* root = (const XSDT*)((RSDPDescriptor20*)rsdp)->xsdt_address; + MMU::Get().AllocatePage((uintptr_t)root); + entry_count = (root->header.length - sizeof(root->header)) / sizeof(*root->sdt_pointer); + root_addr = (uintptr_t)root; + dprintln("XSDT"); + } + else + { + const RSDT* root = (const RSDT*)(uintptr_t)rsdp->rsdt_address; + MMU::Get().AllocatePage((uintptr_t)root); + entry_count = (root->header.length - sizeof(root->header)) / sizeof(*root->sdt_pointer); + root_addr = (uintptr_t)root; + dprintln("RSDT"); + } + + BAN::ScopeGuard guard([root_addr]() { MMU::Get().UnAllocatePage(root_addr); }); + + for (uint32_t i = 0; i < entry_count; i++) + { + ACPISDTHeader* header = nullptr; + if (rsdp->revision == 2) + header = (ACPISDTHeader*)((const XSDT*)root_addr)->sdt_pointer[i]; + else + header = (ACPISDTHeader*)(uintptr_t)((const RSDT*)root_addr)->sdt_pointer[i]; + if (memcmp(header->signature, "APIC", 4) == 0 && IsValidACPISDTHeader(header)) + return (MADT*)header; + } + + return nullptr; +} + +APIC* APIC::Create() +{ + uint32_t ecx, edx; + CPUID::GetFeatures(ecx, edx); + if (!(edx & CPUID::Features::EDX_APIC)) + { + dprintln("Local APIC is not available"); + return nullptr; + } + + RSDPDescriptor* rsdp = LocateRSDP(); + if (rsdp == nullptr) + { + dprintln("Could not locate RSDP"); + return nullptr; + } + + MADT* madt = LocateMADT(rsdp); + if (madt == nullptr) + { + dprintln("Could not find MADT in RSDP"); + return nullptr; + } + + MMU::Get().AllocatePage((uintptr_t)madt); + + APIC* apic = new APIC; + apic->m_local_apic = madt->local_apic; + for (uint32_t i = 0x00; i <= 0xFF; i++) + apic->m_irq_overrides[i] = i; + + uintptr_t madt_entry_addr = (uintptr_t)madt + sizeof(MADT); + while (madt_entry_addr < (uintptr_t)madt + madt->header.length) + { + MADTEntry* entry = (MADTEntry*)madt_entry_addr; + switch (entry->type) + { + case 0: + Processor processor; + processor.processor_id = entry->entry0.acpi_processor_id; + processor.apic_id = entry->entry0.apic_id; + processor.flags = entry->entry0.flags & 0x03; + MUST(apic->m_processors.PushBack(processor)); + break; + case 1: + IOAPIC ioapic; + ioapic.id = entry->entry1.ioapic_id; + ioapic.address = entry->entry1.ioapic_address; + ioapic.gsi_base = entry->entry1.gsi_base; + ioapic.max_redirs = 0; + MUST(apic->m_io_apics.PushBack(ioapic)); + break; + case 2: + apic->m_irq_overrides[entry->entry2.irq_source] = entry->entry2.gsi; + break; + case 5: + apic->m_local_apic = entry->entry5.address; + break; + default: + dprintln("Unhandled madt entry, type {}", entry->type); + break; + } + madt_entry_addr += entry->length; + } + MMU::Get().UnAllocatePage((uintptr_t)madt); + + if (apic->m_local_apic == 0 || apic->m_io_apics.Empty()) + { + dprintln("MADT did not provide necessary information"); + delete apic; + return nullptr; + } + + MMU::Get().AllocatePage(apic->m_local_apic); + for (auto& io_apic : apic->m_io_apics) + { + MMU::Get().AllocatePage(io_apic.address); + io_apic.max_redirs = io_apic.Read(IOAPIC_MAX_REDIRS); + } + + // Mask all interrupts + uint32_t sivr = apic->ReadFromLocalAPIC(LAPIC_SIV_REG); + apic->WriteToLocalAPIC(LAPIC_SIV_REG, sivr | 0x1FF); + + return apic; +} + +uint32_t APIC::ReadFromLocalAPIC(ptrdiff_t offset) +{ + return *(uint32_t*)(m_local_apic + offset); +} + +void APIC::WriteToLocalAPIC(ptrdiff_t offset, uint32_t data) +{ + *(uint32_t*)(m_local_apic + offset) = data; +} + +uint32_t APIC::IOAPIC::Read(uint8_t offset) +{ + volatile uint32_t* ioapic = (volatile uint32_t*)address; + ioapic[0] = offset; + return ioapic[4]; +} + +void APIC::IOAPIC::Write(uint8_t offset, uint32_t data) +{ + volatile uint32_t* ioapic = (volatile uint32_t*)address; + ioapic[0] = offset; + ioapic[4] = data; +} + +void APIC::EOI(uint8_t) +{ + WriteToLocalAPIC(LAPIC_EIO_REG, 0); +} + +void APIC::EnableIrq(uint8_t irq) +{ + uint32_t gsi = m_irq_overrides[irq]; + + IOAPIC* ioapic = nullptr; + for (IOAPIC& io : m_io_apics) + { + if (io.gsi_base <= gsi && gsi <= io.gsi_base + io.max_redirs) + { + ioapic = &io; + break; + } + } + ASSERT(ioapic); + + RedirectionEntry redir; + redir.lo_dword = ioapic->Read(IOAPIC_REDIRS + gsi * 2); + redir.hi_dword = ioapic->Read(IOAPIC_REDIRS + gsi * 2 + 1); + + redir.vector = IRQ_VECTOR_BASE + irq; + redir.mask = 0; + redir.destination = m_processors.Front().apic_id; + + ioapic->Write(IOAPIC_REDIRS + gsi * 2, redir.lo_dword); + ioapic->Write(IOAPIC_REDIRS + gsi * 2 + 1, redir.hi_dword); +} + +void APIC::GetISR(uint32_t out[8]) +{ + for (uint32_t i = 0; i < 8; i++) + out[i] = ReadFromLocalAPIC(LAPIC_IS_REG + i * 0x10); +} \ No newline at end of file diff --git a/kernel/kernel/Input.cpp b/kernel/kernel/Input.cpp index 34c4342f..0e982c9d 100644 --- a/kernel/kernel/Input.cpp +++ b/kernel/kernel/Input.cpp @@ -1,7 +1,7 @@ #include -#include #include #include +#include #include #include #include @@ -540,7 +540,7 @@ namespace Input { // Register callback and IRQ IDT::register_irq_handler(KEYBOARD_IRQ, keyboard_irq_handler); - APIC::EnableIRQ(KEYBOARD_IRQ); + InterruptController::Get().EnableIrq(KEYBOARD_IRQ); i8042_controller_command(I8042_ENABLE_FIRST_PORT); MUST(s_command_queue.Push({ @@ -570,7 +570,7 @@ namespace Input { // Register callback and IRQ IDT::register_irq_handler(MOUSE_IRQ, mouse_irq_handler); - APIC::EnableIRQ(MOUSE_IRQ); + InterruptController::Get().EnableIrq(MOUSE_IRQ); i8042_controller_command(I8042_ENABLE_SECOND_PORT); MUST(s_command_queue.Push({ diff --git a/kernel/kernel/InterruptController.cpp b/kernel/kernel/InterruptController.cpp new file mode 100644 index 00000000..079be619 --- /dev/null +++ b/kernel/kernel/InterruptController.cpp @@ -0,0 +1,27 @@ +#include +#include +#include +#include + +static InterruptController* s_instance = nullptr; + +InterruptController& InterruptController::Get() +{ + ASSERT(s_instance); + return *s_instance; +} + +void InterruptController::Initialize(bool force_pic) +{ + ASSERT(s_instance == nullptr); + + PIC::MaskAll(); + PIC::Remap(); + + if (!force_pic) + s_instance = APIC::Create(); + if (s_instance) + return; + dprintln("Using PIC instead of APIC"); + s_instance = PIC::Create(); +} \ No newline at end of file diff --git a/kernel/kernel/PIC.cpp b/kernel/kernel/PIC.cpp index e6c2ed6d..f0c34d80 100644 --- a/kernel/kernel/PIC.cpp +++ b/kernel/kernel/PIC.cpp @@ -2,6 +2,8 @@ #include #include +#include + #define PIC1 0x20 /* IO base address for master PIC */ #define PIC2 0xA0 /* IO base address for slave PIC */ #define PIC1_COMMAND PIC1 @@ -23,92 +25,86 @@ #define ICW4_BUF_MASTER 0x0C /* Buffered mode/master */ #define ICW4_SFNM 0x10 /* Special fully nested (not) */ - -namespace PIC +PIC* PIC::Create() { + MaskAll(); + Remap(); + return new PIC; +} - void Remap() +void PIC::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(); + IO::outb(PIC2_COMMAND, ICW1_INIT | ICW1_ICW4); + IO::io_wait(); + + // ICW2 + IO::outb(PIC1_DATA, IRQ_VECTOR_BASE); + IO::io_wait(); + IO::outb(PIC2_DATA, IRQ_VECTOR_BASE + 0x08); + IO::io_wait(); + + // ICW3 + IO::outb(PIC1_DATA, 4); + IO::io_wait(); + IO::outb(PIC2_DATA, 2); + IO::io_wait(); + + // ICW4 + IO::outb(PIC1_DATA, ICW4_8086); + IO::io_wait(); + IO::outb(PIC2_DATA, ICW4_8086); + IO::io_wait(); + + // Restore original masks + IO::outb(PIC1_DATA, a1); + IO::outb(PIC2_DATA, a2); +} + +void PIC::MaskAll() +{ + IO::outb(PIC1_DATA, 0xFF); + IO::outb(PIC2_DATA, 0xFF); +} + +void PIC::EOI(uint8_t irq) +{ + if (irq >= 8) + IO::outb(PIC2_COMMAND, PIC_EOI); + IO::outb(PIC1_COMMAND, PIC_EOI); +} + +void PIC::EnableIrq(uint8_t irq) +{ + uint16_t port; + uint8_t value; + + if(irq < 8) { - 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(); - IO::outb(PIC2_COMMAND, ICW1_INIT | ICW1_ICW4); - IO::io_wait(); - - // ICW2 - IO::outb(PIC1_DATA, IRQ_VECTOR_BASE); - IO::io_wait(); - IO::outb(PIC2_DATA, IRQ_VECTOR_BASE + 0x08); - IO::io_wait(); - - // ICW3 - IO::outb(PIC1_DATA, 4); - IO::io_wait(); - IO::outb(PIC2_DATA, 2); - IO::io_wait(); - - // ICW4 - IO::outb(PIC1_DATA, ICW4_8086); - IO::io_wait(); - IO::outb(PIC2_DATA, ICW4_8086); - IO::io_wait(); - - // Restore original masks - IO::outb(PIC1_DATA, a1); - IO::outb(PIC2_DATA, a2); + port = PIC1_DATA; } - - void MaskAll() + else { - IO::outb(PIC1_DATA, 0xff); - IO::outb(PIC2_DATA, 0xff); + port = PIC2_DATA; + irq -= 8; } + value = IO::inb(port) & ~(1 << irq); + IO::outb(port, value); +} - 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) { - uint16_t port; - uint8_t value; - - if(irq < 8) { - port = PIC1_DATA; - } else { - port = PIC2_DATA; - irq -= 8; - } - value = IO::inb(port) | (1 << irq); - IO::outb(port, value); - } - - void Unmask(uint8_t irq) { - uint16_t port; - uint8_t value; - - if(irq < 8) { - port = PIC1_DATA; - } else { - port = PIC2_DATA; - irq -= 8; - } - value = IO::inb(port) & ~(1 << irq); - IO::outb(port, value); - } - - uint16_t GetISR() - { - IO::outb(PIC1_COMMAND, 0x0b); - IO::outb(PIC2_COMMAND, 0x0b); - uint8_t isr0 = IO::inb(PIC1_COMMAND); - uint8_t isr1 = IO::inb(PIC2_COMMAND); - return (isr1 << 8) | isr0; - } +void PIC::GetISR(uint32_t out[8]) +{ + memset(out, 0, 8 * sizeof(uint32_t)); + IO::outb(PIC1_COMMAND, 0x0b); + IO::outb(PIC2_COMMAND, 0x0b); + uint16_t isr0 = IO::inb(PIC1_COMMAND); + uint16_t isr1 = IO::inb(PIC2_COMMAND); + uintptr_t addr = (uintptr_t)out + IRQ_VECTOR_BASE / 8; + *(uint16_t*)addr = (isr1 << 8) | isr0; } \ No newline at end of file diff --git a/kernel/kernel/PIT.cpp b/kernel/kernel/PIT.cpp index e91ca26d..35d8f423 100644 --- a/kernel/kernel/PIT.cpp +++ b/kernel/kernel/PIT.cpp @@ -1,7 +1,7 @@ #include +#include #include #include -#include #define IRQ_TIMER 0 @@ -47,7 +47,7 @@ namespace PIT IDT::register_irq_handler(IRQ_TIMER, clock_handle); - APIC::EnableIRQ(IRQ_TIMER); + InterruptController::Get().EnableIrq(IRQ_TIMER); } } diff --git a/kernel/kernel/kernel.cpp b/kernel/kernel/kernel.cpp index 09eeb489..a884034a 100644 --- a/kernel/kernel/kernel.cpp +++ b/kernel/kernel/kernel.cpp @@ -1,15 +1,12 @@ -#include -#include -#include #include #include +#include #include #include #include #include #include #include -#include #include #include #include @@ -81,8 +78,9 @@ extern "C" void kernel_main() dprintln("VESA initialized"); TTY* tty1 = new TTY(terminal_driver); - APIC::Initialize(cmdline.force_pic); - dprintln("APIC initialized"); + InterruptController::Initialize(cmdline.force_pic); + dprintln("Interrupt controller initialized"); + PIT::initialize(); dprintln("PIT initialized"); if (!Input::initialize())