Cucumber/Kernel/src/Tier0/apic.c

130 lines
4.0 KiB
C
Executable File

#include "Tier0/apic.h"
#include "Tier0/system.h"
#include "Tier0/kstdio.h"
#include "Tier0/paging.h"
#include "Tier0/interrupts.h"
#include "Tier0/panic.h"
#define APIC_SET32(field, value) do { *((u32 *)(g_APIC.LAPIC + field)) = (value);} while(0)
#define APIC_GET32(field) (*((u32*)(g_APIC.LAPIC + field)))
#define APIC_SET32A(field, value) do { APIC_SET32(field, value); ASSERT(APIC_GET32(field) == (value));} while(0)
#define APIC_CALIBRATION_SAMPLES 8
struct {
void *LAPIC;
volatile u32 CalibrationCounter;
volatile u32 CalibrationSamples[APIC_CALIBRATION_SAMPLES];
u32 BusSpeed;
} g_APIC;
void apic_eoi(void)
{
APIC_SET32(APIC_EOI, 0xFFFFFFFF);
}
void apic_spurious_interrupt(T_ISR_REGISTERS Registers)
{
}
void apic_timer_interrupt(T_ISR_REGISTERS Registers)
{
kprintf("ohai\n");
apic_eoi();
}
void apic_calibration_interrupt(T_ISR_REGISTERS Registers)
{
u32 Sample = APIC_GET32(APIC_TimerCCR);
if (g_APIC.CalibrationCounter >= APIC_CALIBRATION_SAMPLES)
{
koutb(0x20, 0x20);
return;
}
g_APIC.CalibrationSamples[g_APIC.CalibrationCounter] = Sample;
g_APIC.CalibrationCounter++;
// is the APIC timer 0? well shit.
if (Sample == 0)
PANIC("APIC Calibration look reached null timer value. Too many saples?");
koutb(0x20, 0x20);
}
void apic_enable_lapic(void)
{
u64 APICMSR = system_msr_get(0x1B);
if (APICMSR & (1 << 11))
kprintf("[i] APIC enable flag set in APIC MSR.\n");
else
{
kprintf("[i] APIC enable flag not set in APIC MSR. Trying to set it...\n");
APICMSR |= (1 << 11);
system_msr_set(0x1B, APICMSR);
}
u64 Virtual = paging_minivmm_allocate();
kprintf("[i] LAPIC will be @0x%x.\n", Virtual);
paging_map_page(Virtual, 0xFEE00000);
// prepare interrupts ..
interrupts_setup_isr(39, (void *)apic_spurious_interrupt, E_INTERRUPTS_RING0);
interrupts_setup_isr(200, (void *)apic_timer_interrupt, E_INTERRUPTS_RING0);
g_APIC.LAPIC = (void *)Virtual;
// reset APIC to a kinda known state
APIC_SET32A(APIC_DFR, 0xFFFFFFFF);
APIC_SET32(APIC_LDR, (APIC_GET32(APIC_LDR)&0x00FFFFFF)|1);
APIC_SET32A(APIC_LVTTimer, 0x10000); // mask bit
APIC_SET32A(APIC_LVTPerformanceCounter, 4 << 8); // NMI bit
APIC_SET32A(APIC_LVTLINT0, 0x10000); // mask vit
APIC_SET32A(APIC_LVTLINT1, 0x10000); // mask bit
APIC_SET32A(APIC_TPR, 0); // task priority = 0 (accept all)
APIC_SET32A(APIC_SVR, 39 | 0x100); // spurious interrupt + sw enable bit
APIC_SET32A(APIC_LVTTimer, 0x1000); // mask bit
APIC_SET32A(APIC_TimerDCR, 0x03); // timer divider = bus speed / 16
kprintf("[i] LAPIC ready to calibrate timer.\n");
// calibration time! let's use the PIT
// let's unmask IRQ0 only
koutb(0x21, 0xFE);
koutb(0xA1, 0xFF);
// let's make LINT0 trigger an extINT (yay PIC-time)
APIC_SET32(APIC_LVTLINT0, 0x8700);
// let's start the APIC timer and zero the calibration counter
APIC_SET32(APIC_TimerICR, 0xFFFFFFFF);
g_APIC.CalibrationCounter = 0;
// now let's program the PIT to run a channel0 (IRQ0) timer with a 100Hz loop
interrupts_setup_isr(32, (void *)apic_calibration_interrupt, E_INTERRUPTS_RING0);
koutb(0x43, 0x36);
koutb(0x40, 0x0B);
koutb(0x40, 0xE9);
// the timer is now running!
while (g_APIC.CalibrationCounter < APIC_CALIBRATION_SAMPLES) {};
// enough samples, let's clean up
kprintf("[i] Calibration loop finished.\n");
koutb(0x21, 0xFF);
koutb(0x21, 0xFF);
APIC_SET32(APIC_LVTLINT0, 0x1000); // mask bit
APIC_SET32(APIC_LVTTimer, 0x10000); // mask bit
interrupts_setup_isr(32, (void *)apic_timer_interrupt, E_INTERRUPTS_RING0);
u64 Average = 0;
for (u32 i = 0; i < APIC_CALIBRATION_SAMPLES -1; i++)
Average += (g_APIC.CalibrationSamples[i] - g_APIC.CalibrationSamples[i + 1]);
Average /= (APIC_CALIBRATION_SAMPLES - 1);
g_APIC.BusSpeed = (Average * 16 * 50)/(1000000);
kprintf("[i] Bus speed %iMHz.\n", g_APIC.BusSpeed);
}
#undef APIC_SET