[zion] Move to using the LAPIC timer over the PIT.

This commit is contained in:
Drew Galbraith 2023-08-01 20:18:47 -07:00
parent f0add6e0c3
commit d99624daf6
8 changed files with 113 additions and 3 deletions

View File

@ -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

View File

@ -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); }

View File

@ -1,7 +1,18 @@
#pragma once
#include <stdint.h>
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();

View File

@ -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);
}

View File

@ -0,0 +1,34 @@
#pragma once
#include <stdint.h>
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;

View File

@ -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);

View File

@ -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

View File

@ -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();