2011-08-28 23:44:16 +00:00
|
|
|
#include "Tier0/apic.h"
|
|
|
|
#include "Tier0/system.h"
|
|
|
|
#include "Tier0/kstdio.h"
|
|
|
|
#include "Tier0/paging.h"
|
2012-10-29 13:57:48 +00:00
|
|
|
#include "Tier0/interrupts.h"
|
|
|
|
#include "Tier0/panic.h"
|
|
|
|
|
2013-02-03 13:38:48 +00:00
|
|
|
#define APIC_SET32(field, value) do { *((volatile u32 *)(g_APIC.LAPIC + field)) = (value);} while(0)
|
|
|
|
#define APIC_GET32(field) (*((volatile u32*)(g_APIC.LAPIC + field)))
|
2012-10-29 13:57:48 +00:00
|
|
|
#define APIC_SET32A(field, value) do { APIC_SET32(field, value); ASSERT(APIC_GET32(field) == (value));} while(0)
|
2011-08-28 23:44:16 +00:00
|
|
|
|
2012-10-29 17:59:09 +00:00
|
|
|
#define APIC_CALIBRATION_SAMPLES 8
|
2011-08-28 23:44:16 +00:00
|
|
|
struct {
|
2013-02-03 13:38:48 +00:00
|
|
|
volatile void *LAPIC;
|
2012-10-29 17:59:09 +00:00
|
|
|
volatile u32 CalibrationCounter;
|
|
|
|
volatile u32 CalibrationSamples[APIC_CALIBRATION_SAMPLES];
|
|
|
|
u32 BusSpeed;
|
2011-08-28 23:44:16 +00:00
|
|
|
} g_APIC;
|
|
|
|
|
2012-10-29 13:57:48 +00:00
|
|
|
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");
|
2012-10-29 17:59:09 +00:00
|
|
|
apic_eoi();
|
2012-10-29 13:57:48 +00:00
|
|
|
}
|
|
|
|
|
2012-10-29 17:59:09 +00:00
|
|
|
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);
|
|
|
|
}
|
2012-10-29 13:57:48 +00:00
|
|
|
|
2011-08-28 23:44:16 +00:00
|
|
|
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");
|
2012-10-29 13:57:48 +00:00
|
|
|
APICMSR |= (1 << 11);
|
2011-08-28 23:44:16 +00:00
|
|
|
system_msr_set(0x1B, APICMSR);
|
|
|
|
}
|
|
|
|
|
2013-07-10 13:17:29 +00:00
|
|
|
// g_APIC.LAPIC = paging_scratch_map(0xFEE00000);
|
|
|
|
PANIC("nanananoapic");
|
2013-02-03 13:38:48 +00:00
|
|
|
kprintf("[i] LAPIC will be @0x%x.\n", g_APIC.LAPIC);
|
2012-10-29 13:57:48 +00:00
|
|
|
|
2012-10-29 17:59:09 +00:00
|
|
|
// prepare interrupts ..
|
|
|
|
interrupts_setup_isr(39, (void *)apic_spurious_interrupt, E_INTERRUPTS_RING0);
|
|
|
|
interrupts_setup_isr(200, (void *)apic_timer_interrupt, E_INTERRUPTS_RING0);
|
|
|
|
|
2012-10-29 13:57:48 +00:00
|
|
|
// reset APIC to a kinda known state
|
2013-02-03 13:38:48 +00:00
|
|
|
APIC_SET32(APIC_DFR, 0xFFFFFFFF);
|
2012-10-29 13:57:48 +00:00
|
|
|
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
|
2012-10-29 17:59:09 +00:00
|
|
|
APIC_SET32A(APIC_LVTLINT0, 0x10000); // mask vit
|
2012-10-29 13:57:48 +00:00
|
|
|
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
|
2012-10-29 17:59:09 +00:00
|
|
|
APIC_SET32A(APIC_LVTTimer, 0x1000); // mask bit
|
2012-10-29 13:57:48 +00:00
|
|
|
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
|
2012-10-29 17:59:09 +00:00
|
|
|
// 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
|
2012-10-29 13:57:48 +00:00
|
|
|
APIC_SET32(APIC_TimerICR, 0xFFFFFFFF);
|
2012-10-29 17:59:09 +00:00
|
|
|
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
|
2012-10-29 13:57:48 +00:00
|
|
|
APIC_SET32(APIC_LVTTimer, 0x10000); // mask bit
|
2012-10-29 17:59:09 +00:00
|
|
|
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);
|
2011-08-28 23:44:16 +00:00
|
|
|
}
|
2012-10-29 13:57:48 +00:00
|
|
|
|
|
|
|
#undef APIC_SET
|