akaros/kern/arch/x86/apic.c
<<
>>
Prefs
   1/*
   2 * Copyright (c) 2009 The Regents of the University of California
   3 * Barret Rhoden <brho@cs.berkeley.edu>
   4 * See LICENSE for details.
   5 */
   6
   7#include <arch/mmu.h>
   8#include <arch/x86.h>
   9#include <arch/arch.h>
  10#include <arch/apic.h>
  11#include <trap.h>
  12#include <time.h>
  13#include <assert.h>
  14#include <stdio.h>
  15#include <bitmask.h>
  16#include <arch/topology.h>
  17#include <ros/procinfo.h>
  18
  19bool lapic_check_spurious(int trap_nr)
  20{
  21        /* FYI: lapic_spurious is 255 on qemu and 15 on the nehalem..  We
  22         * actually can set bits 4-7, and P6s have 0-3 hardwired to 0.  YMMV.
  23         * NxM seems to say the lower 3 bits are usually 1.  We'll see if the
  24         * assert trips.
  25         *
  26         * The SDM recommends not using the spurious vector for any other IRQs
  27         * (LVT or IOAPIC RTE), since the handlers don't send an EOI.  However,
  28         * our check here allows us to use the vector since we can tell the diff
  29         * btw a spurious and a real IRQ. */
  30        assert(IdtLAPIC_SPURIOUS == (apicrget(MSR_LAPIC_SPURIOUS) & 0xff));
  31        /* Note the lapic's vectors are not shifted by an offset. */
  32        if ((trap_nr == IdtLAPIC_SPURIOUS) &&
  33             !lapic_get_isr_bit(IdtLAPIC_SPURIOUS)) {
  34                /* i'm still curious about these */
  35                printk("Spurious LAPIC irq %d, core %d!\n", IdtLAPIC_SPURIOUS,
  36                       core_id());
  37                lapic_print_isr();
  38                return TRUE;
  39        }
  40        return FALSE;
  41}
  42
  43/* Debugging helper.  Note the ISR/IRR are 32 bits at a time, spaced every 16
  44 * bytes in the LAPIC address space. */
  45void lapic_print_isr(void)
  46{
  47        printk("LAPIC ISR on core %d\n--------------\n", core_id());
  48        for (int i = 7; i >= 0; i--)
  49                printk("%3d-%3d: %p\n", (i + 1) * 32 - 1, i * 32,
  50                        apicrget(MSR_LAPIC_ISR_START + i));
  51        printk("LAPIC IRR on core %d\n--------------\n", core_id());
  52        for (int i = 7; i >= 0; i--)
  53                printk("%3d-%3d: %p\n", (i + 1) * 32 - 1, i * 32,
  54                        apicrget(MSR_LAPIC_IRR_START + i));
  55}
  56
  57/* Returns TRUE if the bit 'vector' is set in the LAPIC ISR or IRR (whatever you
  58 * pass in.  These registers consist of 8, 32 byte registers spaced every 16
  59 * bytes from the base in the LAPIC. */
  60static bool __lapic_get_isrr_bit(unsigned long base, uint8_t vector)
  61{
  62        int which_reg = vector >> 5;    /* 32 bits per reg */
  63        uintptr_t lapic_reg = base + which_reg;
  64
  65        return (apicrget(lapic_reg) & (1 << (vector % 32)) ? 1 : 0);
  66}
  67
  68bool lapic_get_isr_bit(uint8_t vector)
  69{
  70        return __lapic_get_isrr_bit(MSR_LAPIC_ISR_START, vector);
  71}
  72
  73bool lapic_get_irr_bit(uint8_t vector)
  74{
  75        return __lapic_get_isrr_bit(MSR_LAPIC_IRR_START, vector);
  76}
  77
  78void lapic_mask_irq(struct irq_handler *unused, int apic_vector)
  79{
  80        uintptr_t mm_reg;
  81
  82        if (apic_vector < IdtLAPIC || IdtLAPIC + 4 < apic_vector) {
  83                warn("Bad apic vector %d\n", apic_vector);
  84                return;
  85        }
  86        mm_reg = MSR_LAPIC_LVT_TIMER + (apic_vector - IdtLAPIC);
  87        apicrput(mm_reg, apicrget(mm_reg) | LAPIC_LVT_MASK);
  88}
  89
  90void lapic_unmask_irq(struct irq_handler *unused, int apic_vector)
  91{
  92        uintptr_t mm_reg;
  93
  94        if (apic_vector < IdtLAPIC || IdtLAPIC + 4 < apic_vector) {
  95                warn("Bad apic vector %d\n", apic_vector);
  96                return;
  97        }
  98        mm_reg = MSR_LAPIC_LVT_TIMER + (apic_vector - IdtLAPIC);
  99        apicrput(mm_reg, apicrget(mm_reg) & ~LAPIC_LVT_MASK);
 100}
 101
 102/* This works for any interrupt that goes through the LAPIC, but not things like
 103 * ExtInts.  To prevent abuse, we'll use it just for IPIs for now (which only
 104 * come via the APIC).
 105 *
 106 * We only check the ISR, due to how we send EOIs.  Since we don't send til
 107 * after handlers return, the ISR will show pending for the current IRQ.  It is
 108 * the EOI that clears the bit from the ISR. */
 109bool ipi_is_pending(uint8_t vector)
 110{
 111        return lapic_get_isr_bit(vector);
 112}
 113
 114/*
 115 * Sets the LAPIC timer to go off after a certain number of ticks.  The primary
 116 * clock freq is actually the bus clock, which we figure out during timer_init
 117 * Unmasking is implied.  Ref SDM, 3A, 9.6.4
 118 */
 119void __lapic_set_timer(uint32_t ticks, uint8_t vec, bool periodic, uint8_t div)
 120{
 121#ifdef CONFIG_LOUSY_LAPIC_TIMER
 122        /* qemu without kvm seems to delay timer IRQs on occasion, and needs
 123         * extra IRQs from any source to get them delivered.  periodic does the
 124         * trick. */
 125        periodic = TRUE;
 126#endif
 127        // clears bottom bit and then set divider
 128        apicrput(MSR_LAPIC_DIVIDE_CONFIG_REG,
 129                 (apicrget(MSR_LAPIC_DIVIDE_CONFIG_REG) & ~0xf) | (div & 0xf));
 130        // set LVT with interrupt handling information.  also unmasks.
 131        apicrput(MSR_LAPIC_LVT_TIMER, vec | (periodic << 17));
 132        apicrput(MSR_LAPIC_INITIAL_COUNT, ticks);
 133}
 134
 135void lapic_set_timer(uint32_t usec, bool periodic)
 136{
 137        /* If we overflowed a uint32, send in the max timer possible.  The lapic
 138         * can only handle a 32 bit.  We could muck with changing the divisor,
 139         * but even then, we might not be able to match 4000 sec (based on the
 140         * bus speed).  The kernel alarm code can handle spurious timer
 141         * interrupts, so we just set the timer for as close as we can get to
 142         * the desired time. */
 143        uint64_t ticks64 = (usec * __proc_global_info.bus_freq)
 144                           / LAPIC_TIMER_DIVISOR_VAL / 1000000;
 145        uint32_t ticks32 = ((ticks64 >> 32) ? 0xffffffff : ticks64);
 146
 147        assert(ticks32 > 0);
 148        __lapic_set_timer(ticks32, IdtLAPIC_TIMER, periodic,
 149                          LAPIC_TIMER_DIVISOR_BITS);
 150}
 151
 152uint32_t lapic_get_default_id(void)
 153{
 154        uint32_t ebx;
 155        
 156        cpuid(0x1, 0x0, 0, &ebx, 0, 0);
 157        // p6 family only uses 4 bits here, and 0xf is reserved for the IOAPIC
 158        return (ebx & 0xFF000000) >> 24;
 159}
 160