diff --git a/zion/boot/acpi.cpp b/zion/boot/acpi.cpp index 9c450db..e909762 100644 --- a/zion/boot/acpi.cpp +++ b/zion/boot/acpi.cpp @@ -12,6 +12,9 @@ namespace { static uint64_t gPcieEcBase = 0x0; static uint64_t gPcieEcSize = 0x0; +static uint64_t gLApicBase = 0x0; +static uint64_t gIOApicBase = 0x0; + struct RsdpDescriptor { char signature[8]; uint8_t checksum; @@ -136,12 +139,12 @@ void ParseMcfg(SdtHeader* rsdt) { } void ParseMadt(SdtHeader* rsdt) { -#if K_ACPI_DEBUG dbgsz(rsdt->signature, 4); uint64_t max_addr = reinterpret_cast(rsdt) + rsdt->length; MadtHeader* header = reinterpret_cast(rsdt); dbgln("Local APIC %x", header->local_apic_address); + gLApicBase = header->local_apic_address; dbgln("Flags: %x", header->flags); MadtEntry* entry = &header->first_entry; @@ -158,6 +161,10 @@ void ParseMadt(SdtHeader* rsdt) { MadtIoApic* io = reinterpret_cast(entry); dbgln("IO Apic (id, addr, gsi base): %x, %x, %x", io->io_apic_id, io->io_apic_address, io->global_system_interrupt_base); + if (gIOApicBase != 0) { + dbgln("More than one IOApic, unhandled"); + } + gIOApicBase = io->io_apic_address; break; } case 2: { @@ -181,7 +188,6 @@ void ParseMadt(SdtHeader* rsdt) { entry = reinterpret_cast(reinterpret_cast(entry) + entry->length); } -#endif } void ParseSdt(SdtHeader* rsdt) { @@ -262,3 +268,11 @@ glcr::ErrorOr GetPciExtendedConfiguration() { return PcieConfiguration{gPcieEcBase, gPcieEcSize}; } + +glcr::ErrorOr GetApicConfiguration() { + if (gLApicBase == 0 || gIOApicBase == 0) { + return glcr::NOT_FOUND; + } + + return ApicConfiguration{gLApicBase, gIOApicBase}; +} diff --git a/zion/boot/acpi.h b/zion/boot/acpi.h index 1216540..f122d48 100644 --- a/zion/boot/acpi.h +++ b/zion/boot/acpi.h @@ -12,3 +12,9 @@ struct PcieConfiguration { uint64_t offset; }; glcr::ErrorOr GetPciExtendedConfiguration(); + +struct ApicConfiguration { + uint64_t lapic_base; + uint64_t ioapic_base; +}; +glcr::ErrorOr GetApicConfiguration(); diff --git a/zion/interrupt/apic.cpp b/zion/interrupt/apic.cpp index cce117f..0191cd8 100644 --- a/zion/interrupt/apic.cpp +++ b/zion/interrupt/apic.cpp @@ -16,51 +16,22 @@ namespace { #define IA32_APIC_BASE_MSR_BSP 0x100 // Processor is a BSP #define IA32_APIC_BASE_MSR_ENABLE 0x800 -const uint64_t kEoiOffset = 0xB0; -const uint64_t kLvtTimerOffset = 0x320; -const uint64_t kTimerInitOffset = 0x380; -const uint64_t kTimerCurrOffset = 0x390; -const uint64_t kTimerDivOffset = 0x3E0; +#define LAPIC_TIMER_ONESHOT 0 +#define LAPIC_TIMER_PERIODIC 1 << 17 -// FIXME: parse these from madt. -constexpr uint64_t kLApicBase = 0xFEE0'0000; -constexpr uint64_t kIoApicAddr = 0xFEC0'0000; -constexpr uint64_t kIoApicData = 0xFEC0'0010; +#define APIC_MASK 0x10000 + +const uint16_t kEoiOffset = 0xB0; +const uint16_t kLvtTimerOffset = 0x320; +const uint16_t kTimerInitOffset = 0x380; +const uint16_t kTimerCurrOffset = 0x390; +const uint16_t kTimerDivOffset = 0x3E0; 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 @@ -86,7 +57,17 @@ void MaskPic() { } // namespace -void InspectApic() { +Apic* gApic = nullptr; + +void Apic::Init() { + auto config_or = GetApicConfiguration(); + if (!config_or.ok()) { + panic("Error fetching APIC info from ACPI: %x", config_or.error()); + } + gApic = new Apic(config_or.value()); +} + +void Apic::DumpInfo() { #if APIC_DEBUG dbgln("APIC:"); dbgln("ID: %x", GetLocalReg(0x20)); @@ -118,36 +99,85 @@ void InspectApic() { #endif } -// For now set these based on the presets in the following spec. -// FIXME: However in the future we should likely use the MADT for legacy -// interrupts and AML for PCI etc. -// -// http://web.archive.org/web/20161130153145/http://download.intel.com/design/chipsets/datashts/29056601.pdf -void EnableApic() { - MaskPic(); - // Map Timer. - SetIoEntry(0x14, 0x10020); +uint32_t Apic::GetLocalTimerValue() { return GetLocalReg(kTimerCurrOffset); } +void Apic::InitializeLocalTimer(uint32_t init_cnt, TimerMode mode) { + // FIXME: Don't hardcode this. + uint32_t vector = 0x21; + switch (mode) { + case ONESHOT: + vector |= LAPIC_TIMER_ONESHOT; + break; + case PERIODIC: + vector |= LAPIC_TIMER_PERIODIC; + break; + default: + panic("Unhandled timer mode."); + } + SetLocalReg(kLvtTimerOffset, vector); + SetLocalReg(kTimerInitOffset, init_cnt); +} + +void Apic::SignalEOI() { SetLocalReg(kEoiOffset, 0x0); } + +void Apic::UnmaskPit() { + SetIoDoubleReg(0x14, GetIoDoubleReg(0x14) & ~(APIC_MASK)); +} +void Apic::MaskPit() { SetIoDoubleReg(0x14, GetIoDoubleReg(0x14) | APIC_MASK); } + +Apic::Apic(const ApicConfiguration& config) + : l_apic_base_(config.lapic_base), + io_apic_addr_( + reinterpret_cast(GetPhys(config.ioapic_base))), + io_apic_data_(GetPhys(config.ioapic_base, 0x10)) { + MaskPic(); + + // Map Timer. + // FIXME: Get this offset from ACPI. + SetIoDoubleReg(0x14, 0x20 | APIC_MASK); + + // For now set these based on the presets in the following spec. + // http://web.archive.org/web/20161130153145/http://download.intel.com/design/chipsets/datashts/29056601.pdf + // FIXME: However in the future we should likely use the MADT for legacy + // interrupts and AML for PCI etc. // PCI Line 1-4 // FIXME: These should be level triggered according to spec I believe // but because we handle the interrupt outside of the kernel it is tricky // to wait to send the end of interrupt message. // Because of this leave them as edge triggered and send EOI immediately. - SetIoEntry(0x30, 0x30); - SetIoEntry(0x32, 0x31); - SetIoEntry(0x34, 0x32); - SetIoEntry(0x36, 0x33); + SetIoDoubleReg(0x30, 0x30); + SetIoDoubleReg(0x32, 0x31); + SetIoDoubleReg(0x34, 0x32); + SetIoDoubleReg(0x36, 0x33); - InspectApic(); + DumpInfo(); } -void SetLocalTimer(uint32_t init_cnt, uint64_t mode) { - WriteLocalReg(kTimerInitOffset, init_cnt); - WriteLocalReg(kLvtTimerOffset, mode | 0x21); +uint32_t Apic::GetLocalReg(uint16_t offset) { + uint32_t volatile* reg = GetPhys(l_apic_base_, offset); + return *reg; } -uint32_t GetLocalTimer() { return GetLocalReg(kTimerCurrOffset); } -void UnmaskPit() { SetIoEntry(0x14, GetIoEntry(0x14) & ~(0x10000)); } -void MaskPit() { SetIoEntry(0x14, GetIoEntry(0x14) | 0x10000); } +void Apic::SetLocalReg(uint16_t offset, uint32_t value) { + *GetPhys(l_apic_base_, offset) = value; +} -void SignalEOI() { WriteLocalReg(kEoiOffset, 0x0); } +uint32_t Apic::GetIoReg(uint8_t offset) { + *io_apic_addr_ = offset; + return *io_apic_data_; +} + +uint64_t Apic::GetIoDoubleReg(uint8_t offset) { + *io_apic_addr_ = offset; + uint64_t entry = *io_apic_data_; + *io_apic_addr_ = offset + 1; + entry |= ((uint64_t)*io_apic_data_) << 32; + return entry; +} + +void Apic::SetIoDoubleReg(uint8_t offset, uint64_t value) { + *io_apic_addr_ = offset; + *io_apic_data_ = value & 0xFFFFFFFF; + *io_apic_addr_ = offset + 1; + *io_apic_data_ = value >> 32; +} diff --git a/zion/interrupt/apic.h b/zion/interrupt/apic.h index 696e900..7503326 100644 --- a/zion/interrupt/apic.h +++ b/zion/interrupt/apic.h @@ -2,17 +2,39 @@ #include -void InspectApic(); +#include "boot/acpi.h" -void EnableApic(); +class Apic { + public: + static void Init(); + static void DumpInfo(); -#define LAPIC_TIMER_ONESHOT 0 -#define LAPIC_TIMER_PERIODIC 1 << 17 + uint32_t GetLocalTimerValue(); + enum TimerMode { + ONESHOT, + PERIODIC, + }; + void InitializeLocalTimer(uint32_t init_cnt, TimerMode mode); -void SetLocalTimer(uint32_t init_cnt, uint64_t mode); -uint32_t GetLocalTimer(); + void SignalEOI(); -void UnmaskPit(); -void MaskPit(); + void UnmaskPit(); + void MaskPit(); -void SignalEOI(); + private: + uint64_t l_apic_base_; + volatile uint8_t* io_apic_addr_; + volatile uint32_t* io_apic_data_; + + Apic(const ApicConfiguration& config); + + uint32_t GetLocalReg(uint16_t offset); + void SetLocalReg(uint16_t offset, uint32_t value); + + uint64_t GetIoDoubleReg(uint8_t offset); + void SetIoDoubleReg(uint8_t offset, uint64_t value); + + uint32_t GetIoReg(uint8_t offset); +}; + +extern Apic* gApic; diff --git a/zion/interrupt/apic_timer.cpp b/zion/interrupt/apic_timer.cpp index d686cfb..9fbbaf6 100644 --- a/zion/interrupt/apic_timer.cpp +++ b/zion/interrupt/apic_timer.cpp @@ -17,26 +17,26 @@ void ApicTimer::Init() { void ApicTimer::StartCalibration() { SetFrequency(100); - SetLocalTimer(0xFFFFFFFF, 0); - UnmaskPit(); + gApic->InitializeLocalTimer(0xFFFFFFFF, Apic::ONESHOT); + gApic->UnmaskPit(); } void ApicTimer::Calibrate() { if (calibration_.initial_measurement == 0) { - calibration_.initial_measurement = GetLocalTimer(); + calibration_.initial_measurement = gApic->GetLocalTimerValue(); return; } calibration_.tick_count++; if (calibration_.tick_count == 10) { calculated_frequency_ = - 10 * (calibration_.initial_measurement - GetLocalTimer()); + 10 * (calibration_.initial_measurement - gApic->GetLocalTimerValue()); FinishCalibration(); } } void ApicTimer::FinishCalibration() { - MaskPit(); - SetLocalTimer(calculated_frequency_ / kScheduleFrequency, - LAPIC_TIMER_PERIODIC); + gApic->MaskPit(); + gApic->InitializeLocalTimer(calculated_frequency_ / kScheduleFrequency, + Apic::PERIODIC); } diff --git a/zion/interrupt/interrupt.cpp b/zion/interrupt/interrupt.cpp index 871a2cc..b1ac511 100644 --- a/zion/interrupt/interrupt.cpp +++ b/zion/interrupt/interrupt.cpp @@ -128,7 +128,7 @@ extern "C" void interrupt_page_fault(InterruptFrame* frame) { extern "C" void isr_timer(); extern "C" void interrupt_timer(InterruptFrame*) { gApicTimer->Calibrate(); - SignalEOI(); + gApic->SignalEOI(); } uint64_t cnt = 0; @@ -141,7 +141,7 @@ extern "C" void interrupt_apic_timer(InterruptFrame*) { } dbgln("timer: %us", cnt * 50 / 1000); } - SignalEOI(); + gApic->SignalEOI(); gScheduler->Preempt(); } @@ -150,25 +150,25 @@ extern "C" void isr_pci1(); extern "C" void interrupt_pci1(InterruptFrame*) { dbgln("Interrupt PCI line 1"); pci1_port->Send(0, nullptr, 0, nullptr); - SignalEOI(); + gApic->SignalEOI(); } extern "C" void isr_pci2(); extern "C" void interrupt_pci2(InterruptFrame*) { dbgln("Interrupt PCI line 2"); - SignalEOI(); + gApic->SignalEOI(); } extern "C" void isr_pci3(); extern "C" void interrupt_pci3(InterruptFrame*) { dbgln("Interrupt PCI line 3"); - SignalEOI(); + gApic->SignalEOI(); } extern "C" void isr_pci4(); extern "C" void interrupt_pci4(InterruptFrame*) { dbgln("Interrupt PCI line 4"); - SignalEOI(); + gApic->SignalEOI(); } void InitIdt() { @@ -189,8 +189,6 @@ void InitIdt() { .base = reinterpret_cast(gIdt), }; asm volatile("lidt %0" ::"m"(idtp)); - - EnableApic(); } void RegisterPciPort(const glcr::RefPtr& port) { pci1_port = port; } diff --git a/zion/zion.cpp b/zion/zion.cpp index 9e223b8..ff72cbb 100644 --- a/zion/zion.cpp +++ b/zion/zion.cpp @@ -3,6 +3,7 @@ #include "boot/acpi.h" #include "common/gdt.h" #include "debug/debug.h" +#include "interrupt/apic.h" #include "interrupt/apic_timer.h" #include "interrupt/interrupt.h" #include "interrupt/timer.h" @@ -30,6 +31,10 @@ extern "C" void zion() { dbgln("[boot] Probing Hardware"); ProbeRsdp(); + // These two need to occur after memory allocation is available. + Apic::Init(); + ApicTimer::Init(); + dbgln("[boot] Init Kernel Stack Manager."); KernelStackManager::Init(); @@ -39,7 +44,6 @@ extern "C" void zion() { dbgln("[boot] Init scheduler."); ProcessManager::Init(); Scheduler::Init(); - ApicTimer::Init(); dbgln("[boot] Loading sys init program."); LoadInitProgram();