From d99624daf659feea758be802d7aef3d670ac7bd4 Mon Sep 17 00:00:00 2001 From: Drew Galbraith Date: Tue, 1 Aug 2023 20:18:47 -0700 Subject: [PATCH] [zion] Move to using the LAPIC timer over the PIT. --- zion/CMakeLists.txt | 1 + zion/interrupt/apic.cpp | 16 +++++++++++++- zion/interrupt/apic.h | 11 ++++++++++ zion/interrupt/apic_timer.cpp | 37 ++++++++++++++++++++++++++++++++ zion/interrupt/apic_timer.h | 34 +++++++++++++++++++++++++++++ zion/interrupt/interrupt.cpp | 10 ++++++++- zion/interrupt/interrupt_enter.s | 1 + zion/zion.cpp | 6 +++++- 8 files changed, 113 insertions(+), 3 deletions(-) create mode 100644 zion/interrupt/apic_timer.cpp create mode 100644 zion/interrupt/apic_timer.h diff --git a/zion/CMakeLists.txt b/zion/CMakeLists.txt index 4e6fe25..9c4147a 100644 --- a/zion/CMakeLists.txt +++ b/zion/CMakeLists.txt @@ -7,6 +7,7 @@ add_executable(zion common/msr.cpp debug/debug.cpp interrupt/apic.cpp + interrupt/apic_timer.cpp interrupt/interrupt.cpp interrupt/interrupt_enter.s interrupt/timer.cpp diff --git a/zion/interrupt/apic.cpp b/zion/interrupt/apic.cpp index 0202470..cce117f 100644 --- a/zion/interrupt/apic.cpp +++ b/zion/interrupt/apic.cpp @@ -6,6 +6,7 @@ #include "common/msr.h" #include "common/port.h" #include "debug/debug.h" +#include "interrupt/apic_timer.h" #define APIC_DEBUG 0 @@ -16,6 +17,10 @@ namespace { #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; // FIXME: parse these from madt. constexpr uint64_t kLApicBase = 0xFEE0'0000; @@ -121,7 +126,7 @@ void InspectApic() { void EnableApic() { MaskPic(); // Map Timer. - SetIoEntry(0x14, 0x20); + SetIoEntry(0x14, 0x10020); // PCI Line 1-4 // FIXME: These should be level triggered according to spec I believe @@ -136,4 +141,13 @@ void EnableApic() { InspectApic(); } +void SetLocalTimer(uint32_t init_cnt, uint64_t mode) { + WriteLocalReg(kTimerInitOffset, init_cnt); + WriteLocalReg(kLvtTimerOffset, mode | 0x21); +} +uint32_t GetLocalTimer() { return GetLocalReg(kTimerCurrOffset); } + +void UnmaskPit() { SetIoEntry(0x14, GetIoEntry(0x14) & ~(0x10000)); } +void MaskPit() { SetIoEntry(0x14, GetIoEntry(0x14) | 0x10000); } + void SignalEOI() { WriteLocalReg(kEoiOffset, 0x0); } diff --git a/zion/interrupt/apic.h b/zion/interrupt/apic.h index b3a259e..696e900 100644 --- a/zion/interrupt/apic.h +++ b/zion/interrupt/apic.h @@ -1,7 +1,18 @@ #pragma once +#include + void InspectApic(); void EnableApic(); +#define LAPIC_TIMER_ONESHOT 0 +#define LAPIC_TIMER_PERIODIC 1 << 17 + +void SetLocalTimer(uint32_t init_cnt, uint64_t mode); +uint32_t GetLocalTimer(); + +void UnmaskPit(); +void MaskPit(); + void SignalEOI(); diff --git a/zion/interrupt/apic_timer.cpp b/zion/interrupt/apic_timer.cpp new file mode 100644 index 0000000..53c98d4 --- /dev/null +++ b/zion/interrupt/apic_timer.cpp @@ -0,0 +1,37 @@ +#include "interrupt/apic_timer.h" + +#include "debug/debug.h" +#include "interrupt/apic.h" +#include "interrupt/timer.h" + +ApicTimer* gApicTimer = nullptr; + +void ApicTimer::Init() { + gApicTimer = new ApicTimer(); + gApicTimer->StartCalibration(); +} + +void ApicTimer::StartCalibration() { + SetFrequency(100); + SetLocalTimer(0xFFFFFFFF, 0); + UnmaskPit(); +} + +void ApicTimer::Calibrate() { + if (calibration_.initial_measurement == 0) { + calibration_.initial_measurement = GetLocalTimer(); + return; + } + calibration_.tick_count++; + + if (calibration_.tick_count == 10) { + calculated_frequency_ = + 10 * (calibration_.initial_measurement - GetLocalTimer()); + FinishCalibration(); + } +} + +void ApicTimer::FinishCalibration() { + MaskPit(); + SetLocalTimer(calculated_frequency_ / 20, LAPIC_TIMER_PERIODIC); +} diff --git a/zion/interrupt/apic_timer.h b/zion/interrupt/apic_timer.h new file mode 100644 index 0000000..7036566 --- /dev/null +++ b/zion/interrupt/apic_timer.h @@ -0,0 +1,34 @@ +#pragma once + +#include + +class ApicTimer { + public: + static void Init(); + + // FIXME: The calibration is not currently very accurate due to time taken + // handling the PIT interrupts. It would probably be good to revisit this + // after implementing HPET. + void Calibrate(); + + void WaitCalibration() { + while (calculated_frequency_ == 0) { + asm("hlt;"); + } + } + + private: + struct Calibration { + uint32_t initial_measurement = 0; + uint32_t tick_count = 0; + }; + Calibration calibration_; + + uint64_t calculated_frequency_ = 0; + + ApicTimer() {} + void StartCalibration(); + void FinishCalibration(); +}; + +extern ApicTimer* gApicTimer; diff --git a/zion/interrupt/interrupt.cpp b/zion/interrupt/interrupt.cpp index 7988065..871a2cc 100644 --- a/zion/interrupt/interrupt.cpp +++ b/zion/interrupt/interrupt.cpp @@ -5,6 +5,7 @@ #include "common/port.h" #include "debug/debug.h" #include "interrupt/apic.h" +#include "interrupt/apic_timer.h" #include "memory/kernel_heap.h" #include "scheduler/scheduler.h" @@ -124,9 +125,15 @@ extern "C" void interrupt_page_fault(InterruptFrame* frame) { panic("PF"); } -uint64_t cnt = 0; extern "C" void isr_timer(); extern "C" void interrupt_timer(InterruptFrame*) { + gApicTimer->Calibrate(); + SignalEOI(); +} + +uint64_t cnt = 0; +extern "C" void isr_apic_timer(); +extern "C" void interrupt_apic_timer(InterruptFrame*) { cnt++; if (cnt % 20 == 0) { if (cnt == 20) { @@ -170,6 +177,7 @@ void InitIdt() { gIdt[14] = CreateDescriptor(isr_page_fault); gIdt[0x20] = CreateDescriptor(isr_timer); + gIdt[0x21] = CreateDescriptor(isr_apic_timer); gIdt[0x30] = CreateDescriptor(isr_pci1); gIdt[0x31] = CreateDescriptor(isr_pci2); diff --git a/zion/interrupt/interrupt_enter.s b/zion/interrupt/interrupt_enter.s index 82eefb0..cd425dc 100644 --- a/zion/interrupt/interrupt_enter.s +++ b/zion/interrupt/interrupt_enter.s @@ -60,6 +60,7 @@ isr_handler protection_fault,1 isr_handler page_fault,1 isr_handler timer +isr_handler apic_timer isr_handler pci1 isr_handler pci2 diff --git a/zion/zion.cpp b/zion/zion.cpp index 62808c2..7402787 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_timer.h" #include "interrupt/interrupt.h" #include "interrupt/timer.h" #include "loader/init_loader.h" @@ -39,7 +40,10 @@ extern "C" void zion() { ProcessManager::Init(); Scheduler::Init(); // Schedule every 50ms. - SetFrequency(/* hertz= */ 20); + ApicTimer::Init(); + asm("sti;"); + gApicTimer->WaitCalibration(); + asm("cli;"); dbgln("[boot] Loading sys init program."); LoadInitProgram();