blob: 9eadd5b2408cbb44fc62a3ed4730cca65d1bc26d [file] [log] [blame]
/*
* Copyright (c) 2009 The Regents of the University of California
* Barret Rhoden <brho@cs.berkeley.edu>
* See LICENSE for details.
*/
#include <arch/mmu.h>
#include <arch/x86.h>
#include <arch/arch.h>
#include <arch/apic.h>
#include <trap.h>
#include <time.h>
#include <assert.h>
#include <stdio.h>
#include <bitmask.h>
#include <arch/topology.h>
#include <ros/procinfo.h>
bool lapic_check_spurious(int trap_nr)
{
/* FYI: lapic_spurious is 255 on qemu and 15 on the nehalem.. We
* actually can set bits 4-7, and P6s have 0-3 hardwired to 0. YMMV.
* NxM seems to say the lower 3 bits are usually 1. We'll see if the
* assert trips.
*
* The SDM recommends not using the spurious vector for any other IRQs
* (LVT or IOAPIC RTE), since the handlers don't send an EOI. However,
* our check here allows us to use the vector since we can tell the diff
* btw a spurious and a real IRQ. */
assert(IdtLAPIC_SPURIOUS == (apicrget(MSR_LAPIC_SPURIOUS) & 0xff));
/* Note the lapic's vectors are not shifted by an offset. */
if ((trap_nr == IdtLAPIC_SPURIOUS) &&
!lapic_get_isr_bit(IdtLAPIC_SPURIOUS)) {
/* i'm still curious about these */
printk("Spurious LAPIC irq %d, core %d!\n", IdtLAPIC_SPURIOUS,
core_id());
lapic_print_isr();
return TRUE;
}
return FALSE;
}
/* Debugging helper. Note the ISR/IRR are 32 bits at a time, spaced every 16
* bytes in the LAPIC address space. */
void lapic_print_isr(void)
{
printk("LAPIC ISR on core %d\n--------------\n", core_id());
for (int i = 7; i >= 0; i--)
printk("%3d-%3d: %p\n", (i + 1) * 32 - 1, i * 32,
apicrget(MSR_LAPIC_ISR_START + i));
printk("LAPIC IRR on core %d\n--------------\n", core_id());
for (int i = 7; i >= 0; i--)
printk("%3d-%3d: %p\n", (i + 1) * 32 - 1, i * 32,
apicrget(MSR_LAPIC_IRR_START + i));
}
/* Returns TRUE if the bit 'vector' is set in the LAPIC ISR or IRR (whatever you
* pass in. These registers consist of 8, 32 byte registers spaced every 16
* bytes from the base in the LAPIC. */
static bool __lapic_get_isrr_bit(unsigned long base, uint8_t vector)
{
int which_reg = vector >> 5; /* 32 bits per reg */
uintptr_t lapic_reg = base + which_reg;
return (apicrget(lapic_reg) & (1 << (vector % 32)) ? 1 : 0);
}
bool lapic_get_isr_bit(uint8_t vector)
{
return __lapic_get_isrr_bit(MSR_LAPIC_ISR_START, vector);
}
bool lapic_get_irr_bit(uint8_t vector)
{
return __lapic_get_isrr_bit(MSR_LAPIC_IRR_START, vector);
}
void lapic_mask_irq(struct irq_handler *unused, int apic_vector)
{
uintptr_t mm_reg;
if (apic_vector < IdtLAPIC || IdtLAPIC + 4 < apic_vector) {
warn("Bad apic vector %d\n", apic_vector);
return;
}
mm_reg = MSR_LAPIC_LVT_TIMER + (apic_vector - IdtLAPIC);
apicrput(mm_reg, apicrget(mm_reg) | LAPIC_LVT_MASK);
}
void lapic_unmask_irq(struct irq_handler *unused, int apic_vector)
{
uintptr_t mm_reg;
if (apic_vector < IdtLAPIC || IdtLAPIC + 4 < apic_vector) {
warn("Bad apic vector %d\n", apic_vector);
return;
}
mm_reg = MSR_LAPIC_LVT_TIMER + (apic_vector - IdtLAPIC);
apicrput(mm_reg, apicrget(mm_reg) & ~LAPIC_LVT_MASK);
}
/* This works for any interrupt that goes through the LAPIC, but not things like
* ExtInts. To prevent abuse, we'll use it just for IPIs for now (which only
* come via the APIC).
*
* We only check the ISR, due to how we send EOIs. Since we don't send til
* after handlers return, the ISR will show pending for the current IRQ. It is
* the EOI that clears the bit from the ISR. */
bool ipi_is_pending(uint8_t vector)
{
return lapic_get_isr_bit(vector);
}
/*
* Sets the LAPIC timer to go off after a certain number of ticks. The primary
* clock freq is actually the bus clock, which we figure out during timer_init
* Unmasking is implied. Ref SDM, 3A, 9.6.4
*/
void __lapic_set_timer(uint32_t ticks, uint8_t vec, bool periodic, uint8_t div)
{
#ifdef CONFIG_LOUSY_LAPIC_TIMER
/* qemu without kvm seems to delay timer IRQs on occasion, and needs
* extra IRQs from any source to get them delivered. periodic does the
* trick. */
periodic = TRUE;
#endif
// clears bottom bit and then set divider
apicrput(MSR_LAPIC_DIVIDE_CONFIG_REG,
(apicrget(MSR_LAPIC_DIVIDE_CONFIG_REG) & ~0xf) | (div & 0xf));
// set LVT with interrupt handling information. also unmasks.
apicrput(MSR_LAPIC_LVT_TIMER, vec | (periodic << 17));
apicrput(MSR_LAPIC_INITIAL_COUNT, ticks);
}
void lapic_set_timer(uint32_t usec, bool periodic)
{
/* If we overflowed a uint32, send in the max timer possible. The lapic
* can only handle a 32 bit. We could muck with changing the divisor,
* but even then, we might not be able to match 4000 sec (based on the
* bus speed). The kernel alarm code can handle spurious timer
* interrupts, so we just set the timer for as close as we can get to
* the desired time. */
uint64_t ticks64 = (usec * __proc_global_info.bus_freq)
/ LAPIC_TIMER_DIVISOR_VAL / 1000000;
uint32_t ticks32 = ((ticks64 >> 32) ? 0xffffffff : ticks64);
assert(ticks32 > 0);
__lapic_set_timer(ticks32, IdtLAPIC_TIMER, periodic,
LAPIC_TIMER_DIVISOR_BITS);
}
uint32_t lapic_get_default_id(void)
{
uint32_t ebx;
cpuid(0x1, 0x0, 0, &ebx, 0, 0);
// p6 family only uses 4 bits here, and 0xf is reserved for the IOAPIC
return (ebx & 0xFF000000) >> 24;
}