diff --git a/kernel/boot/kmain.c b/kernel/boot/kmain.c index c2f7447..0392381 100644 --- a/kernel/boot/kmain.c +++ b/kernel/boot/kmain.c @@ -11,6 +11,7 @@ #include #include #include +#include void thread_function() { @@ -18,6 +19,7 @@ void thread_function() { debug((char *)0x10000); schedule(); + pit_delay(100); } } diff --git a/kernel/include/apic.h b/kernel/include/apic.h index e79a64e..955638e 100644 --- a/kernel/include/apic.h +++ b/kernel/include/apic.h @@ -4,6 +4,8 @@ void apic_init(); void apic_interrupt(uint8_t destination, uint8_t level, uint8_t type, uint8_t vector); void apic_ack(); +void apic_timer(uint64_t us); +void apic_timer_stop(); #define APIC_INT_LEVEL_ASSERT 0x1 #define APIC_INT_LEVEL_DEASSERT 0x0 #define APIC_INT_TYPE_FIXED 0x0 diff --git a/kernel/interrupts/apic.c b/kernel/interrupts/apic.c index e47528a..d682c5d 100644 --- a/kernel/interrupts/apic.c +++ b/kernel/interrupts/apic.c @@ -1,5 +1,5 @@ #include -#include +#include #include #include #include @@ -25,9 +25,28 @@ #define SPURIOUS_LVT_ENABLED 0x100 +#define TIMER_LVT_ONESHOT 0x00000 +#define TIMER_LVT_PERIODIC 0x20000 +#define TIMER_LVT_DEADLINE 0x40000 + +#define R_TIMER_INIT 0x380 +#define R_TIMER_CURR 0x390 +#define R_TIMER_DIV 0x3E0 +#define TIMER_DIV1 0xB +#define TIMER_DIV2 0x0 +#define TIMER_DIV4 0x1 +#define TIMER_DIV8 0x2 +#define TIMER_DIV16 0x3 +#define TIMER_DIV32 0x8 +#define TIMER_DIV64 0x9 +#define TIMER_DIV128 0xA + + #define APIC(reg) apic[reg/4] uint32_t volatile *apic = P2V(APIC_BASE); +uint32_t apic_ticks_per_us; + void apic_interrupt(uint8_t destination, uint8_t level, uint8_t type, uint8_t vector) { uint64_t data = ((level & 0x1)<<14) | ((type & 0x7)<<8) | vector; @@ -39,6 +58,36 @@ void apic_ack() { APIC(R_EOI) = 0; } +registers_t *apic_timer_handler(registers_t *r) +{ + // APIC timer timeout occurred + APIC(R_EOI) = 0; + debug_putch('-'); + apic_timer(1000000); + return r; +} +void apic_timer(uint64_t us) +{ + APIC(R_TIMER_DIV) = TIMER_DIV1; + APIC(R_TIMER_INIT) = us*apic_ticks_per_us; + APIC(R_TIMER_LVT) = TIMER_LVT_ONESHOT | INT_APIC_TIMER; +} +void apic_timer_stop() +{ + APIC(R_TIMER_LVT) = LVT_MASKED | INT_APIC_TIMER; +} + +uint32_t calibrate_apic_timer(uint32_t resolution) +{ + // Use the PIT to calibrate the APIC timer + // Returns number of apic timer ticks per ms + APIC(R_TIMER_DIV) = TIMER_DIV1; + APIC(R_TIMER_LVT) = LVT_MASKED | INT_APIC_TIMER; + APIC(R_TIMER_INIT) = 0xFFFFFFFF; + pit_delay(resolution); + uint32_t ticks = APIC(R_TIMER_CURR); + return (0xFFFFFFFF-ticks)/resolution/1000; +} void apic_init() { @@ -55,4 +104,14 @@ void apic_init() APIC(R_SPURIOUS) = SPURIOUS_LVT_ENABLED | INT_APIC_SPUR; APIC(R_EOI) = 0; + + // Calibrate timer + apic_ticks_per_us = calibrate_apic_timer(100); + debug_info("APIC - ticks per us:%d\n", apic_ticks_per_us); + debug(" corresponds to processor frequency: %d MHz\n", apic_ticks_per_us); + + // Register temporary timer handler to go off every 10 ms + register_int_handler(INT_APIC_TIMER, apic_timer_handler); + + apic_timer(1000000); }