From add533071bfa3d8ba3037b25f8f3fe0680fef5ea Mon Sep 17 00:00:00 2001 From: Drew Galbraith Date: Wed, 7 Jun 2023 13:40:36 -0700 Subject: [PATCH] Use APIC for interrupts rather than PIC. Still rely on the PIT for now rather than the local APIC timer. --- zion/CMakeLists.txt | 2 + zion/common/msr.cpp | 13 ++++ zion/common/msr.h | 6 ++ zion/interrupt/apic.cpp | 128 +++++++++++++++++++++++++++++++++++ zion/interrupt/apic.h | 7 ++ zion/interrupt/interrupt.cpp | 19 +----- zion/syscall/syscall.cpp | 17 +---- 7 files changed, 160 insertions(+), 32 deletions(-) create mode 100644 zion/common/msr.cpp create mode 100644 zion/common/msr.h create mode 100644 zion/interrupt/apic.cpp create mode 100644 zion/interrupt/apic.h diff --git a/zion/CMakeLists.txt b/zion/CMakeLists.txt index eaa3ce9..06f76a5 100644 --- a/zion/CMakeLists.txt +++ b/zion/CMakeLists.txt @@ -3,7 +3,9 @@ add_executable(zion capability/capability.cpp common/gdt.cpp common/load_gdt.s + common/msr.cpp debug/debug.cpp + interrupt/apic.cpp interrupt/interrupt.cpp interrupt/interrupt_enter.s interrupt/timer.cpp diff --git a/zion/common/msr.cpp b/zion/common/msr.cpp new file mode 100644 index 0000000..800f90b --- /dev/null +++ b/zion/common/msr.cpp @@ -0,0 +1,13 @@ +#include "common/msr.h" + +uint64_t GetMSR(uint32_t msr) { + uint32_t lo, hi; + asm("rdmsr" : "=a"(lo), "=d"(hi) : "c"(msr)); + return (static_cast(hi) << 32) | lo; +} + +void SetMSR(uint32_t msr, uint64_t val) { + uint32_t lo = static_cast(val); + uint32_t hi = val >> 32; + asm("wrmsr" ::"a"(lo), "d"(hi), "c"(msr)); +} diff --git a/zion/common/msr.h b/zion/common/msr.h new file mode 100644 index 0000000..ec76129 --- /dev/null +++ b/zion/common/msr.h @@ -0,0 +1,6 @@ +#pragma once + +#include + +uint64_t GetMSR(uint32_t msr); +void SetMSR(uint32_t msr, uint64_t val); diff --git a/zion/interrupt/apic.cpp b/zion/interrupt/apic.cpp new file mode 100644 index 0000000..9f6a248 --- /dev/null +++ b/zion/interrupt/apic.cpp @@ -0,0 +1,128 @@ +#include "interrupt/apic.h" + +#include + +#include "boot/boot_info.h" +#include "common/msr.h" +#include "common/port.h" +#include "debug/debug.h" + +#define APIC_DEBUG 0 + +namespace { + +#define IA32_APIC_BASE_MSR 0x1B +#define IA32_APIC_BASE_MSR_BSP 0x100 // Processor is a BSP +#define IA32_APIC_BASE_MSR_ENABLE 0x800 + +const uint64_t kEoiOffset = 0xB0; + +constexpr uint64_t kLApicBase = 0xFEE0'0000; +constexpr uint64_t kIoApicAddr = 0xFEC0'0000; +constexpr uint64_t kIoApicData = 0xFEC0'0010; + +uint32_t volatile* GetPhys(uint64_t base, uint64_t offset = 0) { + return reinterpret_cast(boot::GetHigherHalfDirectMap() + base + + offset); +} + +uint32_t GetLocalReg(uint64_t offset) { + uint32_t volatile* reg = GetPhys(kLApicBase, offset); + return *reg; +} + +void WriteLocalReg(uint64_t offset, uint32_t value) { + *GetPhys(kLApicBase, offset) = value; +} + +uint32_t GetIoReg(uint8_t reg) { + *GetPhys(kIoApicAddr) = reg; + return *GetPhys(kIoApicData); +} + +uint64_t GetIoEntry(uint8_t reg) { + *GetPhys(kIoApicAddr) = reg; + uint64_t entry = *GetPhys(kIoApicData); + *GetPhys(kIoApicAddr) = reg + 1; + entry |= ((uint64_t)*GetPhys(kIoApicData)) << 32; + return entry; +} + +void SetIoEntry(uint8_t reg, uint64_t value) { + *GetPhys(kIoApicAddr) = reg; + *GetPhys(kIoApicData) = value & 0xFFFFFFFF; + *GetPhys(kIoApicAddr) = reg + 1; + *GetPhys(kIoApicData) = value >> 32; +} + +#define PIC1_COMMAND 0x20 +#define PIC2_COMMAND 0xA0 +#define PIC1_DATA 0x21 +#define PIC2_DATA 0xA1 + +void MaskPic() { + outb(PIC1_DATA, 0xFF); + outb(PIC2_DATA, 0xFF); + outb(PIC1_COMMAND, 0x11); + outb(PIC2_COMMAND, 0x11); + // Potential spurious interrupts at 0x87 + outb(PIC1_DATA, 0x80); // PIC1 offset (high and dry). + outb(PIC2_DATA, 0x80); // PIC2 offset (high and dry). + outb(PIC1_DATA, 0x4); + outb(PIC2_DATA, 0x2); + outb(PIC1_DATA, 0x1); + outb(PIC2_DATA, 0x1); + + // Mask all. + outb(PIC1_DATA, 0xFF); + outb(PIC2_DATA, 0xFF); +} + +} // namespace + +void InspectApic() { +#if APIC_DEBUG + dbgln("APIC:"); + dbgln("ID: %x", GetLocalReg(0x20)); + dbgln("VER: %x", GetLocalReg(0x30)); + dbgln("TPR: %x", GetLocalReg(0x80)); + dbgln("APR: %x", GetLocalReg(0x90)); + dbgln("PPR: %x", GetLocalReg(0xA0)); + dbgln("RRD: %x", GetLocalReg(0xC0)); + dbgln("LDR: %x", GetLocalReg(0xD0)); + dbgln("DFR: %x", GetLocalReg(0xE0)); + dbgln("SIV: %x", GetLocalReg(0xF0)); + for (uint64_t i = 0; i < 8; i++) { + dbgln("ISR(%u): %x", i, GetLocalReg(0x100 + (0x10 * i))); + } + for (uint64_t i = 0; i < 8; i++) { + dbgln("TMR(%u): %x", i, GetLocalReg(0x180 + (0x10 * i))); + } + for (uint64_t i = 0; i < 8; i++) { + dbgln("IRR(%u): %x", i, GetLocalReg(0x200 + (0x10 * i))); + } + dbgln("ESR: %x", GetLocalReg(0x280)); + + dbgln("IO ID: %x", GetIoReg(0x0)); + dbgln("IO VER: %x", GetIoReg(0x1)); + dbgln("IO ARB: %x", GetIoReg(0x2)); + for (uint8_t i = 0x10; i < 0x3F; i += 2) { + dbgln("IO (%u): %x", i, GetIoEntry(i)); + } + dbgln("APIC MSR: %x", GetMSR(0x1B)); +#endif +} + +void EnableApic() { + MaskPic(); + // Map Timer. + SetIoEntry(0x14, 0x20); + // Skip Keyboard for now. + // SetIoEntry(0x12, 0x21); + InspectApic(); +} + +void SignalEOI() { + // Value doesn't matter. + WriteLocalReg(kEoiOffset, 0x1); +} diff --git a/zion/interrupt/apic.h b/zion/interrupt/apic.h new file mode 100644 index 0000000..b3a259e --- /dev/null +++ b/zion/interrupt/apic.h @@ -0,0 +1,7 @@ +#pragma once + +void InspectApic(); + +void EnableApic(); + +void SignalEOI(); diff --git a/zion/interrupt/interrupt.cpp b/zion/interrupt/interrupt.cpp index da41838..6ed22c9 100644 --- a/zion/interrupt/interrupt.cpp +++ b/zion/interrupt/interrupt.cpp @@ -4,6 +4,7 @@ #include "common/port.h" #include "debug/debug.h" +#include "interrupt/apic.h" #include "memory/kernel_heap.h" #include "scheduler/scheduler.h" @@ -124,10 +125,6 @@ extern "C" void interrupt_page_fault(InterruptFrame* frame) { panic("PF"); } -#define PIC1_COMMAND 0x20 -#define PIC1_DATA 0x21 -#define PIC_EOI 0x20 - uint64_t cnt = 0; extern "C" void isr_timer(); extern "C" void interrupt_timer(InterruptFrame*) { @@ -138,20 +135,10 @@ extern "C" void interrupt_timer(InterruptFrame*) { } dbgln("timer: %us", cnt * 50 / 1000); } - outb(PIC1_COMMAND, PIC_EOI); + SignalEOI(); gScheduler->Preempt(); } -void EnablePic() { - outb(PIC1_COMMAND, 0x11); - outb(PIC1_DATA, 0x20); // PIC1 offset. - outb(PIC1_DATA, 0x4); - outb(PIC1_DATA, 0x1); - - // Mask all except the timer. - outb(PIC1_DATA, 0xE); -} - void InitIdt() { gIdt[0] = CreateDescriptor(isr_divide_by_zero); gIdt[13] = CreateDescriptor(isr_protection_fault); @@ -163,5 +150,5 @@ void InitIdt() { }; asm volatile("lidt %0" ::"m"(idtp)); - EnablePic(); + EnableApic(); } diff --git a/zion/syscall/syscall.cpp b/zion/syscall/syscall.cpp index bee188f..8abaa7e 100644 --- a/zion/syscall/syscall.cpp +++ b/zion/syscall/syscall.cpp @@ -2,6 +2,7 @@ #include +#include "common/msr.h" #include "debug/debug.h" #include "include/zcall.h" #include "include/zerrors.h" @@ -15,24 +16,8 @@ #define STAR 0xC0000081 #define LSTAR 0xC0000082 -namespace { - -uint64_t GetMSR(uint32_t msr) { - uint32_t lo, hi; - asm("rdmsr" : "=a"(lo), "=d"(hi) : "c"(msr)); - return (static_cast(hi) << 32) | lo; -} - -void SetMSR(uint32_t msr, uint64_t val) { - uint32_t lo = static_cast(val); - uint32_t hi = val >> 32; - asm("wrmsr" ::"a"(lo), "d"(hi), "c"(msr)); -} - extern "C" void syscall_enter(); -} // namespace - // Used by syscall_enter.s extern "C" uint64_t GetKernelRsp() { return gScheduler->CurrentThread().Rsp0Start();