[zion] Move all APIC functions into a global class.

This commit is contained in:
Drew Galbraith 2023-08-01 22:26:29 -07:00
parent f8de60e2dd
commit 35b1844862
7 changed files with 161 additions and 87 deletions

View File

@ -12,6 +12,9 @@ namespace {
static uint64_t gPcieEcBase = 0x0; static uint64_t gPcieEcBase = 0x0;
static uint64_t gPcieEcSize = 0x0; static uint64_t gPcieEcSize = 0x0;
static uint64_t gLApicBase = 0x0;
static uint64_t gIOApicBase = 0x0;
struct RsdpDescriptor { struct RsdpDescriptor {
char signature[8]; char signature[8];
uint8_t checksum; uint8_t checksum;
@ -136,12 +139,12 @@ void ParseMcfg(SdtHeader* rsdt) {
} }
void ParseMadt(SdtHeader* rsdt) { void ParseMadt(SdtHeader* rsdt) {
#if K_ACPI_DEBUG
dbgsz(rsdt->signature, 4); dbgsz(rsdt->signature, 4);
uint64_t max_addr = reinterpret_cast<uint64_t>(rsdt) + rsdt->length; uint64_t max_addr = reinterpret_cast<uint64_t>(rsdt) + rsdt->length;
MadtHeader* header = reinterpret_cast<MadtHeader*>(rsdt); MadtHeader* header = reinterpret_cast<MadtHeader*>(rsdt);
dbgln("Local APIC %x", header->local_apic_address); dbgln("Local APIC %x", header->local_apic_address);
gLApicBase = header->local_apic_address;
dbgln("Flags: %x", header->flags); dbgln("Flags: %x", header->flags);
MadtEntry* entry = &header->first_entry; MadtEntry* entry = &header->first_entry;
@ -158,6 +161,10 @@ void ParseMadt(SdtHeader* rsdt) {
MadtIoApic* io = reinterpret_cast<MadtIoApic*>(entry); MadtIoApic* io = reinterpret_cast<MadtIoApic*>(entry);
dbgln("IO Apic (id, addr, gsi base): %x, %x, %x", io->io_apic_id, dbgln("IO Apic (id, addr, gsi base): %x, %x, %x", io->io_apic_id,
io->io_apic_address, io->global_system_interrupt_base); io->io_apic_address, io->global_system_interrupt_base);
if (gIOApicBase != 0) {
dbgln("More than one IOApic, unhandled");
}
gIOApicBase = io->io_apic_address;
break; break;
} }
case 2: { case 2: {
@ -181,7 +188,6 @@ void ParseMadt(SdtHeader* rsdt) {
entry = reinterpret_cast<MadtEntry*>(reinterpret_cast<uint64_t>(entry) + entry = reinterpret_cast<MadtEntry*>(reinterpret_cast<uint64_t>(entry) +
entry->length); entry->length);
} }
#endif
} }
void ParseSdt(SdtHeader* rsdt) { void ParseSdt(SdtHeader* rsdt) {
@ -262,3 +268,11 @@ glcr::ErrorOr<PcieConfiguration> GetPciExtendedConfiguration() {
return PcieConfiguration{gPcieEcBase, gPcieEcSize}; return PcieConfiguration{gPcieEcBase, gPcieEcSize};
} }
glcr::ErrorOr<ApicConfiguration> GetApicConfiguration() {
if (gLApicBase == 0 || gIOApicBase == 0) {
return glcr::NOT_FOUND;
}
return ApicConfiguration{gLApicBase, gIOApicBase};
}

View File

@ -12,3 +12,9 @@ struct PcieConfiguration {
uint64_t offset; uint64_t offset;
}; };
glcr::ErrorOr<PcieConfiguration> GetPciExtendedConfiguration(); glcr::ErrorOr<PcieConfiguration> GetPciExtendedConfiguration();
struct ApicConfiguration {
uint64_t lapic_base;
uint64_t ioapic_base;
};
glcr::ErrorOr<ApicConfiguration> GetApicConfiguration();

View File

@ -16,51 +16,22 @@ namespace {
#define IA32_APIC_BASE_MSR_BSP 0x100 // Processor is a BSP #define IA32_APIC_BASE_MSR_BSP 0x100 // Processor is a BSP
#define IA32_APIC_BASE_MSR_ENABLE 0x800 #define IA32_APIC_BASE_MSR_ENABLE 0x800
const uint64_t kEoiOffset = 0xB0; #define LAPIC_TIMER_ONESHOT 0
const uint64_t kLvtTimerOffset = 0x320; #define LAPIC_TIMER_PERIODIC 1 << 17
const uint64_t kTimerInitOffset = 0x380;
const uint64_t kTimerCurrOffset = 0x390;
const uint64_t kTimerDivOffset = 0x3E0;
// FIXME: parse these from madt. #define APIC_MASK 0x10000
constexpr uint64_t kLApicBase = 0xFEE0'0000;
constexpr uint64_t kIoApicAddr = 0xFEC0'0000; const uint16_t kEoiOffset = 0xB0;
constexpr uint64_t kIoApicData = 0xFEC0'0010; 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) { uint32_t volatile* GetPhys(uint64_t base, uint64_t offset = 0) {
return reinterpret_cast<uint32_t*>(boot::GetHigherHalfDirectMap() + base + return reinterpret_cast<uint32_t*>(boot::GetHigherHalfDirectMap() + base +
offset); 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 PIC1_COMMAND 0x20
#define PIC2_COMMAND 0xA0 #define PIC2_COMMAND 0xA0
#define PIC1_DATA 0x21 #define PIC1_DATA 0x21
@ -86,7 +57,17 @@ void MaskPic() {
} // namespace } // 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 #if APIC_DEBUG
dbgln("APIC:"); dbgln("APIC:");
dbgln("ID: %x", GetLocalReg(0x20)); dbgln("ID: %x", GetLocalReg(0x20));
@ -118,36 +99,85 @@ void InspectApic() {
#endif #endif
} }
// For now set these based on the presets in the following spec. uint32_t Apic::GetLocalTimerValue() { return GetLocalReg(kTimerCurrOffset); }
// FIXME: However in the future we should likely use the MADT for legacy void Apic::InitializeLocalTimer(uint32_t init_cnt, TimerMode mode) {
// interrupts and AML for PCI etc. // FIXME: Don't hardcode this.
// uint32_t vector = 0x21;
// http://web.archive.org/web/20161130153145/http://download.intel.com/design/chipsets/datashts/29056601.pdf switch (mode) {
void EnableApic() { case ONESHOT:
MaskPic(); vector |= LAPIC_TIMER_ONESHOT;
// Map Timer. break;
SetIoEntry(0x14, 0x10020); 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<volatile uint8_t*>(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 // 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
// but because we handle the interrupt outside of the kernel it is tricky // but because we handle the interrupt outside of the kernel it is tricky
// to wait to send the end of interrupt message. // to wait to send the end of interrupt message.
// Because of this leave them as edge triggered and send EOI immediately. // Because of this leave them as edge triggered and send EOI immediately.
SetIoEntry(0x30, 0x30); SetIoDoubleReg(0x30, 0x30);
SetIoEntry(0x32, 0x31); SetIoDoubleReg(0x32, 0x31);
SetIoEntry(0x34, 0x32); SetIoDoubleReg(0x34, 0x32);
SetIoEntry(0x36, 0x33); SetIoDoubleReg(0x36, 0x33);
InspectApic(); DumpInfo();
} }
void SetLocalTimer(uint32_t init_cnt, uint64_t mode) { uint32_t Apic::GetLocalReg(uint16_t offset) {
WriteLocalReg(kTimerInitOffset, init_cnt); uint32_t volatile* reg = GetPhys(l_apic_base_, offset);
WriteLocalReg(kLvtTimerOffset, mode | 0x21); return *reg;
} }
uint32_t GetLocalTimer() { return GetLocalReg(kTimerCurrOffset); }
void UnmaskPit() { SetIoEntry(0x14, GetIoEntry(0x14) & ~(0x10000)); } void Apic::SetLocalReg(uint16_t offset, uint32_t value) {
void MaskPit() { SetIoEntry(0x14, GetIoEntry(0x14) | 0x10000); } *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;
}

View File

@ -2,17 +2,39 @@
#include <stdint.h> #include <stdint.h>
void InspectApic(); #include "boot/acpi.h"
void EnableApic(); class Apic {
public:
static void Init();
static void DumpInfo();
#define LAPIC_TIMER_ONESHOT 0 uint32_t GetLocalTimerValue();
#define LAPIC_TIMER_PERIODIC 1 << 17 enum TimerMode {
ONESHOT,
PERIODIC,
};
void InitializeLocalTimer(uint32_t init_cnt, TimerMode mode);
void SetLocalTimer(uint32_t init_cnt, uint64_t mode); void SignalEOI();
uint32_t GetLocalTimer();
void UnmaskPit(); void UnmaskPit();
void MaskPit(); 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;

View File

@ -17,26 +17,26 @@ void ApicTimer::Init() {
void ApicTimer::StartCalibration() { void ApicTimer::StartCalibration() {
SetFrequency(100); SetFrequency(100);
SetLocalTimer(0xFFFFFFFF, 0); gApic->InitializeLocalTimer(0xFFFFFFFF, Apic::ONESHOT);
UnmaskPit(); gApic->UnmaskPit();
} }
void ApicTimer::Calibrate() { void ApicTimer::Calibrate() {
if (calibration_.initial_measurement == 0) { if (calibration_.initial_measurement == 0) {
calibration_.initial_measurement = GetLocalTimer(); calibration_.initial_measurement = gApic->GetLocalTimerValue();
return; return;
} }
calibration_.tick_count++; calibration_.tick_count++;
if (calibration_.tick_count == 10) { if (calibration_.tick_count == 10) {
calculated_frequency_ = calculated_frequency_ =
10 * (calibration_.initial_measurement - GetLocalTimer()); 10 * (calibration_.initial_measurement - gApic->GetLocalTimerValue());
FinishCalibration(); FinishCalibration();
} }
} }
void ApicTimer::FinishCalibration() { void ApicTimer::FinishCalibration() {
MaskPit(); gApic->MaskPit();
SetLocalTimer(calculated_frequency_ / kScheduleFrequency, gApic->InitializeLocalTimer(calculated_frequency_ / kScheduleFrequency,
LAPIC_TIMER_PERIODIC); Apic::PERIODIC);
} }

View File

@ -128,7 +128,7 @@ extern "C" void interrupt_page_fault(InterruptFrame* frame) {
extern "C" void isr_timer(); extern "C" void isr_timer();
extern "C" void interrupt_timer(InterruptFrame*) { extern "C" void interrupt_timer(InterruptFrame*) {
gApicTimer->Calibrate(); gApicTimer->Calibrate();
SignalEOI(); gApic->SignalEOI();
} }
uint64_t cnt = 0; uint64_t cnt = 0;
@ -141,7 +141,7 @@ extern "C" void interrupt_apic_timer(InterruptFrame*) {
} }
dbgln("timer: %us", cnt * 50 / 1000); dbgln("timer: %us", cnt * 50 / 1000);
} }
SignalEOI(); gApic->SignalEOI();
gScheduler->Preempt(); gScheduler->Preempt();
} }
@ -150,25 +150,25 @@ extern "C" void isr_pci1();
extern "C" void interrupt_pci1(InterruptFrame*) { extern "C" void interrupt_pci1(InterruptFrame*) {
dbgln("Interrupt PCI line 1"); dbgln("Interrupt PCI line 1");
pci1_port->Send(0, nullptr, 0, nullptr); pci1_port->Send(0, nullptr, 0, nullptr);
SignalEOI(); gApic->SignalEOI();
} }
extern "C" void isr_pci2(); extern "C" void isr_pci2();
extern "C" void interrupt_pci2(InterruptFrame*) { extern "C" void interrupt_pci2(InterruptFrame*) {
dbgln("Interrupt PCI line 2"); dbgln("Interrupt PCI line 2");
SignalEOI(); gApic->SignalEOI();
} }
extern "C" void isr_pci3(); extern "C" void isr_pci3();
extern "C" void interrupt_pci3(InterruptFrame*) { extern "C" void interrupt_pci3(InterruptFrame*) {
dbgln("Interrupt PCI line 3"); dbgln("Interrupt PCI line 3");
SignalEOI(); gApic->SignalEOI();
} }
extern "C" void isr_pci4(); extern "C" void isr_pci4();
extern "C" void interrupt_pci4(InterruptFrame*) { extern "C" void interrupt_pci4(InterruptFrame*) {
dbgln("Interrupt PCI line 4"); dbgln("Interrupt PCI line 4");
SignalEOI(); gApic->SignalEOI();
} }
void InitIdt() { void InitIdt() {
@ -189,8 +189,6 @@ void InitIdt() {
.base = reinterpret_cast<uint64_t>(gIdt), .base = reinterpret_cast<uint64_t>(gIdt),
}; };
asm volatile("lidt %0" ::"m"(idtp)); asm volatile("lidt %0" ::"m"(idtp));
EnableApic();
} }
void RegisterPciPort(const glcr::RefPtr<Port>& port) { pci1_port = port; } void RegisterPciPort(const glcr::RefPtr<Port>& port) { pci1_port = port; }

View File

@ -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.h"
#include "interrupt/apic_timer.h" #include "interrupt/apic_timer.h"
#include "interrupt/interrupt.h" #include "interrupt/interrupt.h"
#include "interrupt/timer.h" #include "interrupt/timer.h"
@ -30,6 +31,10 @@ extern "C" void zion() {
dbgln("[boot] Probing Hardware"); dbgln("[boot] Probing Hardware");
ProbeRsdp(); ProbeRsdp();
// These two need to occur after memory allocation is available.
Apic::Init();
ApicTimer::Init();
dbgln("[boot] Init Kernel Stack Manager."); dbgln("[boot] Init Kernel Stack Manager.");
KernelStackManager::Init(); KernelStackManager::Init();
@ -39,7 +44,6 @@ extern "C" void zion() {
dbgln("[boot] Init scheduler."); dbgln("[boot] Init scheduler.");
ProcessManager::Init(); ProcessManager::Init();
Scheduler::Init(); Scheduler::Init();
ApicTimer::Init();
dbgln("[boot] Loading sys init program."); dbgln("[boot] Loading sys init program.");
LoadInitProgram(); LoadInitProgram();