[zion] Move to using the LAPIC timer over the PIT.
This commit is contained in:
parent
f0add6e0c3
commit
d99624daf6
|
@ -7,6 +7,7 @@ add_executable(zion
|
||||||
common/msr.cpp
|
common/msr.cpp
|
||||||
debug/debug.cpp
|
debug/debug.cpp
|
||||||
interrupt/apic.cpp
|
interrupt/apic.cpp
|
||||||
|
interrupt/apic_timer.cpp
|
||||||
interrupt/interrupt.cpp
|
interrupt/interrupt.cpp
|
||||||
interrupt/interrupt_enter.s
|
interrupt/interrupt_enter.s
|
||||||
interrupt/timer.cpp
|
interrupt/timer.cpp
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
#include "common/msr.h"
|
#include "common/msr.h"
|
||||||
#include "common/port.h"
|
#include "common/port.h"
|
||||||
#include "debug/debug.h"
|
#include "debug/debug.h"
|
||||||
|
#include "interrupt/apic_timer.h"
|
||||||
|
|
||||||
#define APIC_DEBUG 0
|
#define APIC_DEBUG 0
|
||||||
|
|
||||||
|
@ -16,6 +17,10 @@ namespace {
|
||||||
#define IA32_APIC_BASE_MSR_ENABLE 0x800
|
#define IA32_APIC_BASE_MSR_ENABLE 0x800
|
||||||
|
|
||||||
const uint64_t kEoiOffset = 0xB0;
|
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.
|
// FIXME: parse these from madt.
|
||||||
constexpr uint64_t kLApicBase = 0xFEE0'0000;
|
constexpr uint64_t kLApicBase = 0xFEE0'0000;
|
||||||
|
@ -121,7 +126,7 @@ void InspectApic() {
|
||||||
void EnableApic() {
|
void EnableApic() {
|
||||||
MaskPic();
|
MaskPic();
|
||||||
// Map Timer.
|
// Map Timer.
|
||||||
SetIoEntry(0x14, 0x20);
|
SetIoEntry(0x14, 0x10020);
|
||||||
|
|
||||||
// PCI Line 1-4
|
// PCI Line 1-4
|
||||||
// FIXME: These should be level triggered according to spec I believe
|
// FIXME: These should be level triggered according to spec I believe
|
||||||
|
@ -136,4 +141,13 @@ void EnableApic() {
|
||||||
InspectApic();
|
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); }
|
void SignalEOI() { WriteLocalReg(kEoiOffset, 0x0); }
|
||||||
|
|
|
@ -1,7 +1,18 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
void InspectApic();
|
void InspectApic();
|
||||||
|
|
||||||
void EnableApic();
|
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();
|
void SignalEOI();
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
|
@ -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;
|
|
@ -5,6 +5,7 @@
|
||||||
#include "common/port.h"
|
#include "common/port.h"
|
||||||
#include "debug/debug.h"
|
#include "debug/debug.h"
|
||||||
#include "interrupt/apic.h"
|
#include "interrupt/apic.h"
|
||||||
|
#include "interrupt/apic_timer.h"
|
||||||
#include "memory/kernel_heap.h"
|
#include "memory/kernel_heap.h"
|
||||||
#include "scheduler/scheduler.h"
|
#include "scheduler/scheduler.h"
|
||||||
|
|
||||||
|
@ -124,9 +125,15 @@ extern "C" void interrupt_page_fault(InterruptFrame* frame) {
|
||||||
panic("PF");
|
panic("PF");
|
||||||
}
|
}
|
||||||
|
|
||||||
uint64_t cnt = 0;
|
|
||||||
extern "C" void isr_timer();
|
extern "C" void isr_timer();
|
||||||
extern "C" void interrupt_timer(InterruptFrame*) {
|
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++;
|
cnt++;
|
||||||
if (cnt % 20 == 0) {
|
if (cnt % 20 == 0) {
|
||||||
if (cnt == 20) {
|
if (cnt == 20) {
|
||||||
|
@ -170,6 +177,7 @@ void InitIdt() {
|
||||||
gIdt[14] = CreateDescriptor(isr_page_fault);
|
gIdt[14] = CreateDescriptor(isr_page_fault);
|
||||||
|
|
||||||
gIdt[0x20] = CreateDescriptor(isr_timer);
|
gIdt[0x20] = CreateDescriptor(isr_timer);
|
||||||
|
gIdt[0x21] = CreateDescriptor(isr_apic_timer);
|
||||||
|
|
||||||
gIdt[0x30] = CreateDescriptor(isr_pci1);
|
gIdt[0x30] = CreateDescriptor(isr_pci1);
|
||||||
gIdt[0x31] = CreateDescriptor(isr_pci2);
|
gIdt[0x31] = CreateDescriptor(isr_pci2);
|
||||||
|
|
|
@ -60,6 +60,7 @@ isr_handler protection_fault,1
|
||||||
isr_handler page_fault,1
|
isr_handler page_fault,1
|
||||||
|
|
||||||
isr_handler timer
|
isr_handler timer
|
||||||
|
isr_handler apic_timer
|
||||||
|
|
||||||
isr_handler pci1
|
isr_handler pci1
|
||||||
isr_handler pci2
|
isr_handler pci2
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
#include "boot/acpi.h"
|
#include "boot/acpi.h"
|
||||||
#include "common/gdt.h"
|
#include "common/gdt.h"
|
||||||
#include "debug/debug.h"
|
#include "debug/debug.h"
|
||||||
|
#include "interrupt/apic_timer.h"
|
||||||
#include "interrupt/interrupt.h"
|
#include "interrupt/interrupt.h"
|
||||||
#include "interrupt/timer.h"
|
#include "interrupt/timer.h"
|
||||||
#include "loader/init_loader.h"
|
#include "loader/init_loader.h"
|
||||||
|
@ -39,7 +40,10 @@ extern "C" void zion() {
|
||||||
ProcessManager::Init();
|
ProcessManager::Init();
|
||||||
Scheduler::Init();
|
Scheduler::Init();
|
||||||
// Schedule every 50ms.
|
// Schedule every 50ms.
|
||||||
SetFrequency(/* hertz= */ 20);
|
ApicTimer::Init();
|
||||||
|
asm("sti;");
|
||||||
|
gApicTimer->WaitCalibration();
|
||||||
|
asm("cli;");
|
||||||
|
|
||||||
dbgln("[boot] Loading sys init program.");
|
dbgln("[boot] Loading sys init program.");
|
||||||
LoadInitProgram();
|
LoadInitProgram();
|
||||||
|
|
Loading…
Reference in New Issue