X-Git-Url: https://scm.lunaixsky.com/lunaix-os.git/blobdiff_plain/05b7549a0f980efa33265a091a5174a78851ce05..bb793d5c6918efee6a86de442463a7c9aaa4ecdd:/lunaix-os/hal/apic.c diff --git a/lunaix-os/hal/apic.c b/lunaix-os/hal/apic.c index c74d3f9..44385a4 100644 --- a/lunaix-os/hal/apic.c +++ b/lunaix-os/hal/apic.c @@ -15,19 +15,19 @@ #include +#include #include #include LOG_MODULE("APIC") -void -apic_setup_timer(); +static volatile uintptr_t _apic_base; void apic_setup_lvts(); void -init_apic() +apic_init() { // ensure that external interrupt is disabled cpu_disable_interrupt(); @@ -39,57 +39,55 @@ init_apic() // As we are going to use APIC, disable the old 8259 PIC pic_disable(); + _apic_base = (ptr_t)ioremap(__APIC_BASE_PADDR, 4096); + // Hardware enable the APIC // By setting bit 11 of IA32_APIC_BASE register - // Note: After this point, you can't disable then re-enable it until a reset (i.e., reboot) - asm volatile ( - "movl %0, %%ecx\n" - "rdmsr\n" - "orl %1, %%eax\n" - "wrmsr\n" - ::"i"(IA32_APIC_BASE_MSR), "i"(IA32_APIC_ENABLE) - : "eax", "ecx", "edx" - ); + // Note: After this point, you can't disable then re-enable it until a + // reset (i.e., reboot) + asm volatile("movl %0, %%ecx\n" + "rdmsr\n" + "orl %1, %%eax\n" + "wrmsr\n" ::"i"(IA32_MSR_APIC_BASE), + "i"(IA32_APIC_ENABLE) + : "eax", "ecx", "edx"); // Print the basic information of our current local APIC - uint32_t apic_id = apic_read_reg(APIC_IDR) >> 24; - uint32_t apic_ver = apic_read_reg(APIC_VER); + u32_t apic_id = apic_read_reg(APIC_IDR) >> 24; + u32_t apic_ver = apic_read_reg(APIC_VER); kprintf(KINFO "ID: %x, Version: %x, Max LVT: %u\n", - apic_id, - apic_ver & 0xff, - (apic_ver >> 16) & 0xff); + apic_id, + apic_ver & 0xff, + (apic_ver >> 16) & 0xff); // initialize the local vector table (LVT) apic_setup_lvts(); // initialize priority registers - - // set the task priority to the lowest possible, so all external interrupts are acceptable - // Note, the lowest possible priority class is 2, not 0, 1, as they are reserved for - // internal interrupts (vector 0-31, and each p-class resposible for 16 vectors). - // See Intel Manual Vol. 3A, 10-29 + + // set the task priority to the lowest possible, so all external interrupts + // are acceptable + // Note, the lowest possible priority class is 2, not 0, 1, as they are + // reserved for internal interrupts (vector 0-31, and each p-class + // resposible for 16 vectors). See Intel Manual Vol. 3A, 10-29 apic_write_reg(APIC_TPR, APIC_PRIORITY(2, 0)); // enable APIC - uint32_t spiv = apic_read_reg(APIC_SPIVR); + u32_t spiv = apic_read_reg(APIC_SPIVR); // install our handler for spurious interrupt. - spiv = (spiv & ~0xff) | APIC_SPIV_APIC_ENABLE | APIC_SPIV_IV; + spiv = (spiv & ~0xff) | APIC_SPIV_APIC_ENABLE | APIC_SPIV_IV; apic_write_reg(APIC_SPIVR, spiv); - - // setup timer and performing calibrations - apic_setup_timer(); } -#define LVT_ENTRY_LINT0(vector) (LVT_DELIVERY_FIXED | vector) +#define LVT_ENTRY_LINT0(vector) (LVT_DELIVERY_FIXED | vector) // Pin LINT#1 is configured for relaying NMI, but we masked it here as I think // it is too early for that // LINT#1 *must* be edge trigged (Intel manual vol3. 10-14) -#define LVT_ENTRY_LINT1 (LVT_DELIVERY_NMI | LVT_MASKED | LVT_TRIGGER_EDGE) -#define LVT_ENTRY_ERROR(vector) (LVT_DELIVERY_FIXED | vector) -#define LVT_ENTRY_TIMER(vector, mode) (LVT_DELIVERY_FIXED | mode | vector) +#define LVT_ENTRY_LINT1 (LVT_DELIVERY_NMI | LVT_MASKED | LVT_TRIGGER_EDGE) +#define LVT_ENTRY_ERROR(vector) (LVT_DELIVERY_FIXED | vector) void apic_setup_lvts() @@ -100,122 +98,19 @@ apic_setup_lvts() } void -temp_intr_routine_rtc_tick(const isr_param* param); - -void -temp_intr_routine_apic_timer(const isr_param* param); - -void -test_timer(const isr_param* param); - -uint32_t apic_timer_base_freq = 0; - -// Don't optimize them! Took me an half hour to figure that out... - -volatile uint32_t rtc_counter = 0; -volatile uint8_t apic_timer_done = 0; - -#define APIC_CALIBRATION_CONST 0x100000 - -void -apic_setup_timer() +apic_done_servicing() { - cpu_disable_interrupt(); - - // Setup APIC timer - - // Setup a one-shot timer, we will use this to measure the bus speed. So we can - // then calibrate apic timer to work at *nearly* accurate hz - apic_write_reg(APIC_TIMER_LVT, LVT_ENTRY_TIMER(APIC_TIMER_IV, LVT_TIMER_ONESHOT)); - - // Set divider to 64 - apic_write_reg(APIC_TIMER_DCR, APIC_TIMER_DIV64); - - /* - Timer calibration process - measure the APIC timer base frequency - - step 1: setup a temporary isr for RTC timer which trigger at each tick (1024Hz) - step 2: setup a temporary isr for #APIC_TIMER_IV - step 3: setup the divider, APIC_TIMER_DCR - step 4: Startup RTC timer - step 5: Write a large value, v, to APIC_TIMER_ICR to start APIC timer - (this must be followed immediately after step 4) - step 6: issue a write to EOI and clean up. - - When the APIC ICR counting down to 0 #APIC_TIMER_IV triggered, save the rtc timer's - counter, k, and disable RTC timer immediately (although the RTC interrupts should be - blocked by local APIC as we are currently busy on handling #APIC_TIMER_IV) - - So the apic timer frequency F_apic in Hz can be calculate as - v / F_apic = k / 1024 - => F_apic = v / k * 1024 - - */ - - apic_timer_base_freq = 0; - rtc_counter = 0; - apic_timer_done = 0; - - intr_subscribe(APIC_TIMER_IV, temp_intr_routine_apic_timer); - intr_subscribe(RTC_TIMER_IV, temp_intr_routine_rtc_tick); - - - rtc_enable_timer(); // start RTC timer - apic_write_reg(APIC_TIMER_ICR, APIC_CALIBRATION_CONST); // start APIC timer - - // enable interrupt, just for our RTC start ticking! - cpu_enable_interrupt(); - - wait_until(apic_timer_done); - - // cpu_disable_interrupt(); - - assert_msg(apic_timer_base_freq, "Fail to initialize timer"); - - kprintf(KINFO "Timer base frequency: %u Hz\n", apic_timer_base_freq); - - // cleanup - intr_unsubscribe(APIC_TIMER_IV, temp_intr_routine_apic_timer); - intr_unsubscribe(RTC_TIMER_IV, temp_intr_routine_rtc_tick); - - // TODO: now setup timer with our custom frequency which we can derived from the base frequency - // we measured - - apic_write_reg(APIC_TIMER_LVT, LVT_ENTRY_TIMER(APIC_TIMER_IV, LVT_TIMER_PERIODIC)); - intr_subscribe(APIC_TIMER_IV, test_timer); - - apic_write_reg(APIC_TIMER_ICR, apic_timer_base_freq); + *(unsigned int*)(_apic_base + APIC_EOI) = 0; } -volatile rtc_datetime datetime; - -void -test_timer(const isr_param* param) { - - rtc_get_datetime(&datetime); - - kprintf(KWARN "%u/%02u/%02u %02u:%02u:%02u\r", - datetime.year, - datetime.month, - datetime.day, - datetime.hour, - datetime.minute, - datetime.second); -} - -void -temp_intr_routine_rtc_tick(const isr_param* param) { - rtc_counter++; - - // dummy read on register C so RTC can send anther interrupt - // This strange behaviour observed in virtual box & bochs - (void) rtc_read_reg(RTC_REG_C); +unsigned int +apic_read_reg(unsigned int reg) +{ + return *(unsigned int*)(_apic_base + (reg)); } void -temp_intr_routine_apic_timer(const isr_param* param) { - apic_timer_base_freq = APIC_CALIBRATION_CONST / rtc_counter * RTC_TIMER_BASE_FREQUENCY; - apic_timer_done = 1; - - rtc_disable_timer(); +apic_write_reg(unsigned int reg, unsigned int val) +{ + *(unsigned int*)(_apic_base + reg) = val; } \ No newline at end of file