reset APIC before timer initialization

This commit is contained in:
Stefan Lankes 2011-04-07 20:35:13 +02:00
parent e96a549349
commit ea5b7e4930

View file

@ -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<MAX_CORES; i++) {
if (apic_processors[i] && (old != (((size_t)apic_processors[i]) & 0xFFFFF000)))
old = map_region(((size_t) apic_processors[i]) & 0xFFFFF000, ((size_t) apic_processors[i]) & 0xFFFFF000, 1, MAP_KERNEL_SPACE|MAP_NO_CACHE);
}
}
#ifndef CONFIG_ROCKCREEK
old = get_clock_tick();
/* wait for the next time slice */
while((ticks = get_clock_tick()) - old == 0)
;
HALT;
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);
irq_nested_enable(flags);
/* wait 3 time slices to determine a ICR */
while(get_clock_tick() - ticks < 3)
;
HALT;
diff = 0xFFFFFFFFUL - lapic_read(APIC_CCR);
diff = diff / 3;
flags = irq_nested_disable();
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);
// Now, MetalSVM is able to use the APIC => 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