From ea5b7e4930e6d184fb1e60bfa8516e08e6105804 Mon Sep 17 00:00:00 2001 From: Stefan Lankes Date: Thu, 7 Apr 2011 20:35:13 +0200 Subject: [PATCH] reset APIC before timer initialization --- arch/x86/kernel/apic.c | 95 +++++++++++++++++++++++++++--------------- 1 file changed, 61 insertions(+), 34 deletions(-) diff --git a/arch/x86/kernel/apic.c b/arch/x86/kernel/apic.c index f7eee024..9b80a700 100644 --- a/arch/x86/kernel/apic.c +++ b/arch/x86/kernel/apic.c @@ -76,8 +76,7 @@ static inline void lapic_write(uint32_t addr, uint32_t value) * to avoid a pentium bug, we have to read a apic register * before we write a value to this register */ - asm volatile ("cmpl (%0), %%eax; movl %1, (%0)" :: "r"(lapic+addr), "r"(value)); - //asm volatile ("movl (%%eax), %%edx; movl %%ebx, (%%eax)" :: "a"(lapic+addr), "b"(value) : "%edx"); + asm volatile ("movl (%%eax), %%edx; movl %%ebx, (%%eax)" :: "a"(lapic+addr), "b"(value) : "%edx"); //*((volatile uint32_t*) (lapic+addr)) = value; } @@ -255,6 +254,24 @@ int smp_init(void) } #endif +static int lapic_reset(void) +{ + uint32_t max_lvt = apic_lvt_entries(); + + lapic_write(APIC_SVR, 0x17F); // enable the apic and connect to the idt entry 127 + lapic_write(APIC_TPR, 0x00); // allow all interrupts + lapic_write(APIC_LVT_T, 0x10000); // disable timer interrupt + if (max_lvt >= 4) + lapic_write(APIC_LVT_TSR, 0x10000); // disable thermal sensor interrupt + if (max_lvt >= 5) + lapic_write(APIC_LVT_PMC, 0x10000); // disable performance counter interrupt + lapic_write(APIC_LINT0, 0x7C); // connect LINT0 to idt entry 124 + lapic_write(APIC_LINT1, 0x7D); // connect LINT1 to idt entry 125 + lapic_write(APIC_LVT_ER, 0x7E); // connect error to idt entry 126 + + return 0; +} + /* * detects the timer frequency of the APIC and restart * the APIC timer with the correct period @@ -263,10 +280,13 @@ int apic_calibration(void) { uint8_t i; uint32_t flags; - #ifndef CONFIG_ROCKCREEK uint64_t ticks, old; uint32_t diff; +#else + uint64_t start, end, ticks; + uint32_t diff; +#endif if (!has_apic()) return -ENXIO; @@ -274,30 +294,47 @@ int apic_calibration(void) lapic = map_region(0 /*lapic*/, lapic, 1, MAP_KERNEL_SPACE|MAP_NO_CACHE); if (BUILTIN_EXPECT(!lapic, 0)) return -ENXIO; + kprintf("Mapped LAPIC at 0x%x\n", lapic); + + if (ioapic) { + size_t old = 0; - if (ioapic) ioapic = (ioapic_t*) map_region(0 /*(size_t)ioapic*/, (size_t) ioapic, 1, MAP_KERNEL_SPACE|MAP_NO_CACHE); + kprintf("Mapped IOAPIC at 0x%x\n", ioapic); + // map all processor entries + for(i=0; i Therefore, we disable the PIC outportb(0xA1, 0xFF); @@ -308,27 +345,17 @@ int apic_calibration(void) * and possess no PIC timer. Therfore, we use the rdtsc to * to calibrate the APIC timer. */ - uint64_t start, end, ticks; - uint32_t diff; - - if (!has_apic()) - return -ENXIO; - - lapic = map_region(0 /*lapic*/, lapic, 1, MAP_KERNEL_SPACE|MAP_NO_CACHE); - if (BUILTIN_EXPECT(!lapic, 0)) - return -ENXIO; - - if (ioapic) - ioapic = (ioapic_t*) map_region(0 /*(size_t)ioapic*/, (size_t)ioapic, 1, MAP_KERNEL_SPACE|MAP_NO_CACHE); + flags = irq_nested_disable(); lapic_write(APIC_DCR, 0xB); // set it to 1 clock increments lapic_write(APIC_LVT_T, 0x2007B); // connects the timer to 123 and enables it lapic_write(APIC_ICR, 0xFFFFFFFFUL); /* wait 3 time slices to determine a ICR */ + rmb(); start = rdtsc(); do { - flush_pipeline(); + rmb(); end = rdtsc(); ticks = end > start ? end - start : start - end; } while(ticks*TIMER_FREQ < 3*RC_REFCLOCKMHZ*1000000UL); @@ -336,9 +363,12 @@ int apic_calibration(void) diff = 0xFFFFFFFFUL - lapic_read(APIC_CCR); diff = diff / 3; + lapic_reset(); lapic_write(APIC_DCR, 0xB); // set it to 1 clock increments lapic_write(APIC_LVT_T, 0x2007B); // connects the timer to 123 and enables it lapic_write(APIC_ICR, diff); + + irq_nested_enable(flags); #endif kprintf("APIC calibration determines an ICR of 0x%x\n", diff); @@ -520,31 +550,28 @@ no_mp: goto check_lapic; } +static void apic_err_handler(struct state *s) +{ + kprintf("Got APIC error 0x%x\n", lapic_read(APIC_ESR)); +} + int apic_init(void) { int ret; uint8_t i; - uint32_t v; - uint32_t max_lvt; ret = apic_probe(); if (!ret) return ret; - max_lvt = apic_lvt_entries(); + // set APIC error handler + irq_install_handler(126, apic_err_handler); - lapic_write(APIC_SVR, 0x17F); // enable the apic and connect to the idt entry 127 - lapic_write(APIC_TPR, 0x00); // allow all interrupts - lapic_write(APIC_LVT_T, 0x10000); // disable timer interrupt - if (max_lvt >= 4) - lapic_write(APIC_LVT_TSR, 0x10000); // disable thermal sensor interrupt - if (max_lvt >= 5) - lapic_write(APIC_LVT_PMC, 0x10000); // disable performance counter interrupt - lapic_write(APIC_LINT0, 0x7C); // connect LINT0 to idt entry 124 - lapic_write(APIC_LINT1, 0x7D); // connect LINT1 to idt entry 125 - lapic_write(APIC_LVT_ER, 0x7E); // connect error to idt entry 126 + ret = lapic_reset(); + if (!ret) + return ret; - if (0) { //ioapic) { + if (ioapic) { // enable timer interrupt ioapic_inton(0, apic_processors[boot_processor]->id); // now lets turn everything else off