| /* |
| * 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; |
| } |