#include #include #include #include #include #include #include #include #include #include #define DEGUB_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* root = (RSDT*)(rsdp->revision == 2 ? ((RSDPDescriptor20*)rsdp)->xsdt_address : rsdp->rsdt_address); 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 DEBUG_PRINT //kprintln("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 DEBUG_PRINT kprintln("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 DEBUG_PRINT kprintln("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 DEBUG_PRINT kprintln("Entry3, nmi source {}, flags 0b{16b}, gsi {}", entry->entry3.nmi_source, entry->entry3.flags, entry->entry3.gsi ); #endif break; case 4: #if DEBUG_PRINT kprintln("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 DEBUG_PRINT kprintln("Entry5, address 0x{4H}", entry->entry5.address ); #endif break; case 9: #if DEBUG_PRINT kprintln("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; } } } } 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() { 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; } ParseMADT(rsdp); if (s_local_apic == 0 || s_io_apic == 0) return false; // Enable Local APIC SetMSR(IA32_APIC_BASE, (s_local_apic & 0xFFFFF000) | IA32_APIC_BASE_ENABLE, 0); uint32_t sipi = ReadLocalAPIC(0xF0); WriteIOAPIC(0xF0, sipi | 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) { kprintln("Using PIC instead of APIC"); s_using_fallback_pic = true; } else 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 = s_lapic_ids[0]; WriteIOAPIC(0x10 + gsi * 2, redir.lo_dword); WriteIOAPIC(0x10 + gsi * 2 + 1, redir.hi_dword); } }