Cucumber/Kernel/src/Tier0/apic.c

99 lines
3.1 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)
struct {
void *LAPIC;
} 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");
}
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);
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 bit
APIC_SET32A(APIC_LVTLINT1, 0x10000); // mask bit
APIC_SET32A(APIC_TPR, 0); // task priority = 0 (accept all)
// prepare interrupts ..
interrupts_setup_isr(39, (void *)apic_spurious_interrupt, E_INTERRUPTS_RING0);
interrupts_setup_isr(32, (void *)apic_timer_interrupt, E_INTERRUPTS_RING0);
APIC_SET32A(APIC_SVR, 39 | 0x100); // spurious interrupt + sw enable bit
APIC_SET32A(APIC_LVTTimer, 32); // apic timer interrupt
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
koutb(0x61, kinb(0x61) | 0xFD); // disable speaker
koutb(0x43, 0xB2); // one shot, channel 2
// to setup a 100Hz loop, wee need a divider of...
// 1193180 / 100 Hz = 11931 = 0x2e9b
koutb(0x42, 0x9b); // write low byte
kinb(0x60); // wait a bit
koutb(0x42, 0x2e); // write high bte
// now we are ready to fire the PIT timer
kprintf("[i] Firing PIT for calibration...\n");
u8 Temporary = kinb(0x61) & 0xFE;
koutb(0x61, Temporary); // gate low
koutb(0x61, Temporary | 1); // gate high
// don't forget to actually fire the APIC timer at the same time...
APIC_SET32(APIC_TimerICR, 0xFFFFFFFF);
// for(;;)
// {
// kprintf("APIC: %x\n", APIC_GET32(APIC_TimerICR));
// }
// wait for the PIT counter to reach zero...
while (!(kinb(0x61) & 0x020));
// disable APIC timer
APIC_SET32(APIC_LVTTimer, 0x10000); // mask bit
kprintf("[i] Timer reached zero, APIC Timer @%x.\n", APIC_GET32(APIC_TimerICR));
}
#undef APIC_SET