+++ /dev/null
-#include <lwk/kernel.h>
-#include <lwk/init.h>
-#include <lwk/resource.h>
-#include <lwk/cpuinfo.h>
-#include <lwk/smp.h>
-#include <lwk/delay.h>
-#include <arch/page.h>
-#include <arch/pgtable.h>
-#include <arch/fixmap.h>
-#include <arch/apicdef.h>
-#include <arch/apic.h>
-#include <arch/idt_vectors.h>
-#include <arch/tsc.h>
-
-/**
- * Physical address of the local APIC memory mapping.
- * If the system BIOS provided an MP configuration table, this is set in
- * arch/x86_64/kernel/mpparse.c to the value parsed from the table.
- * Otherwise, the default address is used.
- */
-unsigned long lapic_phys_addr = APIC_DEFAULT_PHYS_BASE;
-
-/**
- * Resource entry for the local APIC memory mapping.
- */
-static struct resource lapic_resource = {
- .name = "Local APIC",
- .flags = IORESOURCE_MEM | IORESOURCE_BUSY,
- /* .start and .end filled in later based on detected address */
-};
-
-/**
- * Creates a kernel mapping for the local APIC.
- *
- * The hardware/platform/BIOS maps each CPU's local APIC at the same location
- * in physical memory. This function uses the 'fixmap' to map the local APIC
- * into the kernel's virtual memory space at a fixed virtual address that is
- * known at compile time. Since the local APIC's virtual address is known
- * at compile time, local APIC registers can be accessed directly, without
- * any pointer dereferencing.
- */
-void __init
-lapic_map(void)
-{
- if (!cpu_has_apic)
- panic("No local APIC.");
-
- /* Reserve physical memory used by the local APIC */
- lapic_resource.start = lapic_phys_addr;
- lapic_resource.end = lapic_phys_addr + 4096 - 1;
- request_resource(&iomem_resource, &lapic_resource);
-
- /* Map local APIC into the kernel */
- set_fixmap_nocache(FIX_APIC_BASE, lapic_phys_addr);
-
- printk(KERN_DEBUG "Local APIC mapped to virtual address 0x%016lx\n",
- fix_to_virt(FIX_APIC_BASE));
-}
-
-/**
- * Initializes the calling CPU's local APIC.
- */
-void __init
-lapic_init(void)
-{
- uint32_t val;
-
- /*
- * Initialize Destination Format Register.
- * When using logical destination mode, we want to use the flat model.
- */
- apic_write(APIC_DFR, APIC_DFR_FLAT);
-
- /*
- * Initialize the Logical Destination Register.
- * The LWK never uses logical destination mode, so just set it to the
- * APIC's physical ID to avoid possible confusion.
- */
- val = apic_read(APIC_LDR) & ~APIC_LDR_MASK;
- val |= SET_APIC_LOGICAL_ID( GET_APIC_ID(apic_read(APIC_ID)) );
- apic_write(APIC_LDR, val);
-
- /*
- * Initialize the Task Priority Register.
- * We set this to accept all (0) and never touch it again.
- */
- val = apic_read(APIC_TASKPRI) & ~APIC_TPRI_MASK;
- apic_write(APIC_TASKPRI, val);
-
- /*
- * Intialize the Spurious-Interrupt Vector Register.
- * This also enables the local APIC.
- */
- val = apic_read(APIC_SPIV) & ~APIC_VECTOR_MASK;
- val |= (APIC_SPIV_APIC_ENABLED | APIC_SPURIOUS_VECTOR);
- apic_write(APIC_SPIV, val);
-
- /* Setup LVT[0] = APIC Timer Interrupt */
- apic_write(APIC_LVTT, 0
- | APIC_DM_FIXED /* route to fixed IDT vector */
- | APIC_TIMER_VECTOR /* IDT vector to route to */
- | APIC_LVT_MASKED /* initially disable */
- );
-
- /* Setup LVT[1] = Thermal Sensor Interrupt */
- apic_write(APIC_LVTTHMR, 0
- | APIC_DM_FIXED /* route to fixed IDT vector */
- | APIC_THERMAL_VECTOR /* IDT vector to route to */
- );
-
- /* Setup LVT[2] = Performance Counter Interrupt */
- apic_write(APIC_LVTPC, 0
- | APIC_DM_NMI /* treat as non-maskable interrupt */
- /* NMIs are routed to IDT vector 2 */
- | APIC_LVT_MASKED /* initially disable */
- );
-
- /* Setup LVT[3] = Local Interrupt Pin 0 */
- apic_write(APIC_LVT0, 0
- | APIC_DM_EXTINT /* hooked up to old 8259A PIC */
- /* IDT vector provided by 8259A */
- | APIC_LVT_MASKED /* disable */
- );
-
- /* Setup LVT[4] = Local Interrupt Pin 1 */
- apic_write(APIC_LVT1, 0
- | APIC_DM_NMI /* treat as non-maskable interrupt */
- /* NMIs are routed to IDT vector 2 */
- | ((this_cpu != 0)
- ? APIC_LVT_MASKED /* mask on all but bootstrap CPU */
- : 0) /* bootstrap CPU (0) receives NMIs */
- );
-
- /* Setup LVT[5] = Internal APIC Error Detector Interrupt */
- apic_write(APIC_LVTERR, 0
- | APIC_DM_FIXED /* route to fixed IDT vector */
- | APIC_ERROR_VECTOR /* IDT vector to route to */
- );
- apic_write(APIC_ESR, 0); /* spec says to clear after enabling LVTERR */
-}
-
-void
-lapic_set_timer(uint32_t count)
-{
- uint32_t lvt;
-
- /* Setup Divide Count Register to use the bus frequency directly. */
- apic_write(APIC_TDCR, APIC_TDR_DIV_1);
-
- /* Program the initial count register */
- apic_write(APIC_TMICT, count);
-
- /* Enable the local APIC timer */
- lvt = apic_read(APIC_LVTT);
- lvt &= ~APIC_LVT_MASKED;
- lvt |= APIC_LVT_TIMER_PERIODIC;
- apic_write(APIC_LVTT, lvt);
-}
-
-void
-lapic_stop_timer(void)
-{
- uint32_t lvt;
-
- /* Set the initial count to 0 */
- apic_write(APIC_TMICT, 0);
-
- /* Enable the local APIC timer */
- lvt = apic_read(APIC_LVTT);
- lvt |= APIC_LVT_MASKED;
- apic_write(APIC_LVTT, lvt);
-}
-
-/**
- * Detects the local APIC reference bus clock. The only sure-fire way to do
- * this is to depend on some other absolute timing source. This function uses
- * the CPU's cycle counter and the previously detected CPU clock frequency.
- *
- * NOTE: This assumes that the CPU's clock frequency has already been detected.
- * (i.e., cpu_info[cpu_id()].arch.tsc_khz has been initialized.
- */
-unsigned int __init
-lapic_calibrate_timer(void)
-{
- const unsigned int tick_count = 100000000;
- cycles_t tsc_start, tsc_now;
- uint32_t apic_start, apic_now;
- unsigned int apic_Hz;
-
- /* Start the APIC counter running for calibration */
- lapic_set_timer(4000000000);
-
- apic_start = apic_read(APIC_TMCCT);
- tsc_start = get_cycles_sync();
-
- /* Spin until enough ticks for a meaningful result have elapsed */
- do {
- apic_now = apic_read(APIC_TMCCT);
- tsc_now = get_cycles_sync();
- } while ( ((tsc_now - tsc_start) < tick_count) &&
- ((apic_start - apic_now) < tick_count) );
-
- apic_Hz = (apic_start - apic_now) * 1000L *
- cpu_info[this_cpu].arch.tsc_khz / (tsc_now - tsc_start);
-
- lapic_stop_timer();
-
- return (apic_Hz / 1000);
-}
-
-static uint32_t
-lapic_wait4_icr_idle(void)
-{
- uint32_t send_status;
- int timeout;
-
- /* Wait up to 100 milliseconds */
- timeout = 0;
- do {
- send_status = apic_read(APIC_ICR) & APIC_ICR_BUSY;
- if (!send_status)
- break;
- udelay(100);
- } while (timeout++ < 1000);
-
- return send_status;
-}
-
-/**
- * Returns the number of entries in the Local Vector Table minus one.
- *
- * This should return 5 or higher on all x86_64 CPUs.
- * 6 is returned if the APIC Thermal Interrupt is supported, 5 otherwise.
- */
-static uint32_t
-lapic_get_maxlvt(void)
-{
- return GET_APIC_MAXLVT(apic_read(APIC_LVR));
-}
-
-/**
- * Sends an INIT inter-processor interrupt.
- * This is used during bootstrap to wakeup the AP CPUs.
- */
-void __init
-lapic_send_init_ipi(unsigned int cpu)
-{
- uint32_t status;
- unsigned int apic_id = cpu_info[cpu].arch.apic_id;
-
- /* Turn on INIT at target CPU */
- apic_write(APIC_ICR2, SET_APIC_DEST_FIELD(apic_id));
- apic_write(APIC_ICR, APIC_INT_LEVELTRIG | APIC_INT_ASSERT
- | APIC_DM_INIT);
- status = lapic_wait4_icr_idle();
- if (status)
- panic("INIT IPI ERROR: failed to assert INIT. (%x)", status);
- mdelay(10);
-
- /* Turn off INIT at target CPU */
- apic_write(APIC_ICR2, SET_APIC_DEST_FIELD(apic_id));
- apic_write(APIC_ICR, APIC_INT_LEVELTRIG | APIC_DM_INIT);
- status = lapic_wait4_icr_idle();
- if (status)
- panic("INIT IPI ERROR: failed to deassert INIT. (%x)", status);
-}
-
-/**
- * Send a STARTUP inter-processor interrupt.
- * This is used during bootstrap to wakeup the AP CPUs.
- */
-void __init
-lapic_send_startup_ipi(
- unsigned int cpu, /* Logical CPU ID */
- unsigned long start_rip /* Physical addr */
-)
-{
- uint32_t status;
- unsigned int maxlvt = lapic_get_maxlvt();
- unsigned int apic_id = cpu_info[cpu].arch.apic_id;
-
- /* Clear errors */
- apic_write(APIC_ESR, 0);
- apic_read(APIC_ESR);
-
- /* Set target CPU */
- apic_write(APIC_ICR2, SET_APIC_DEST_FIELD(apic_id));
-
- /* Send Startup IPI to target CPU */
- apic_write(APIC_ICR2, SET_APIC_DEST_FIELD(apic_id));
- apic_write(APIC_ICR, APIC_DM_STARTUP | (start_rip >> 12));
- udelay(300); /* Give AP CPU some time to accept the IPI */
- status = lapic_wait4_icr_idle();
- if (status)
- panic("STARTUP IPI ERROR: failed to send. (%x)", status);
- udelay(300); /* Give AP CPU some time to accept the IPI */
-
- /* Fixup for Pentium erratum 3AP, clear errors */
- if (maxlvt > 3)
- apic_write(APIC_ESR, 0);
-
- /* Verify that IPI was accepted */
- status = (apic_read(APIC_ESR) & 0xEF);
- if (status)
- panic("STARTUP IPI ERROR: failed to accept. (%x)", status);
-}
-
-/**
- * Sends an inter-processor interrupt (IPI) to the specified CPU.
- * Note that the IPI has not necessarily been delivered when this function
- * returns.
- */
-void
-lapic_send_ipi(
- unsigned int cpu, /* Logical CPU ID */
- unsigned int vector /* Interrupt vector to send */
-)
-{
- uint32_t status;
- unsigned int apic_id;
-
- /* Wait for idle */
- status = lapic_wait4_icr_idle();
- if (status)
- panic("lapic_wait4_icr_idle() timed out. (%x)", status);
-
- /* Set target CPU */
- apic_id = cpu_info[cpu].arch.apic_id;
- apic_write(APIC_ICR2, SET_APIC_DEST_FIELD(apic_id));
-
- /* Send the IPI */
- if (unlikely(vector == NMI_VECTOR))
- apic_write(APIC_ICR, APIC_DEST_PHYSICAL|APIC_DM_NMI);
- else
- apic_write(APIC_ICR, APIC_DEST_PHYSICAL|APIC_DM_FIXED|vector);
-}
-
-/**
- * Converts an entry in a local APIC's Local Vector Table to a
- * human-readable string.
- */
-static char *
-lvt_stringify(uint32_t entry, char *buf)
-{
- uint32_t delivery_mode = GET_APIC_DELIVERY_MODE(entry);
-
- if (delivery_mode == APIC_MODE_FIXED) {
- sprintf(buf, "FIXED -> IDT_VECTOR %d",
- entry & APIC_VECTOR_MASK
- );
- } else if (delivery_mode == APIC_MODE_NMI) {
- sprintf(buf, "NMI -> IDT VECTOR 2");
- } else if (delivery_mode == APIC_MODE_EXTINT) {
- sprintf(buf, "ExtINT, hooked to old 8259A PIC");
- } else {
- sprintf(buf, "UNKNOWN");
- }
-
- if (entry & APIC_LVT_MASKED)
- strcat(buf, ", MASKED");
-
- return buf;
-}
-
-/**
- * Prints various local APIC registers of interest to the console.
- */
-void
-lapic_dump(void)
-{
- char buf[128];
-
- printk(KERN_DEBUG "LOCAL APIC DUMP (LOGICAL CPU #%d):\n", this_cpu);
-
- /*
- * Lead off with the important stuff...
- */
- printk(KERN_DEBUG
- " ID: 0x%08x (id=%d)\n",
- apic_read(APIC_ID),
- GET_APIC_ID(apic_read(APIC_ID))
- );
- printk(KERN_DEBUG
- " VER: 0x%08x (version=0x%x, max_lvt=%d)\n",
- apic_read(APIC_LVR),
- GET_APIC_VERSION(apic_read(APIC_LVR)),
- GET_APIC_MAXLVT(apic_read(APIC_LVR))
- );
- printk(KERN_DEBUG
- " ESR: 0x%08x (Error Status Reg, non-zero is bad)\n",
- apic_read(APIC_ESR)
- );
- printk(KERN_DEBUG
- " SVR: 0x%08x (Spurious vector=%d, %s)\n",
- apic_read(APIC_SPIV),
- apic_read(APIC_SPIV) & APIC_VECTOR_MASK,
- (apic_read(APIC_SPIV) & APIC_SPIV_APIC_ENABLED)
- ? "APIC IS ENABLED"
- : "APIC IS DISABLED"
- );
-
- /*
- * Local Vector Table
- */
- printk(KERN_DEBUG " Local Vector Table Entries:\n");
- printk(KERN_DEBUG " LVT[0] Timer: 0x%08x (%s)\n",
- apic_read(APIC_LVTT),
- lvt_stringify(apic_read(APIC_LVTT), buf)
- );
- printk(KERN_DEBUG " LVT[1] Thermal: 0x%08x (%s)\n",
- apic_read(APIC_LVTTHMR),
- lvt_stringify(apic_read(APIC_LVTTHMR), buf)
- );
- printk(KERN_DEBUG " LVT[2] Perf Cnt: 0x%08x (%s)\n",
- apic_read(APIC_LVTPC),
- lvt_stringify(apic_read(APIC_LVTPC), buf)
- );
- printk(KERN_DEBUG " LVT[3] LINT0 Pin: 0x%08x (%s)\n",
- apic_read(APIC_LVT0),
- lvt_stringify(apic_read(APIC_LVT0), buf)
- );
- printk(KERN_DEBUG " LVT[4] LINT1 Pin: 0x%08x (%s)\n",
- apic_read(APIC_LVT1),
- lvt_stringify(apic_read(APIC_LVT1), buf)
- );
- printk(KERN_DEBUG " LVT[5] Error: 0x%08x (%s)\n",
- apic_read(APIC_LVTERR),
- lvt_stringify(apic_read(APIC_LVTERR), buf)
- );
-
- /*
- * APIC timer configuration registers
- */
- printk(KERN_DEBUG " Local APIC Timer:\n");
- printk(KERN_DEBUG " DCR (Divide Config Reg): 0x%08x\n",
- apic_read(APIC_TDCR)
- );
- printk(KERN_DEBUG " ICT (Initial Count Reg): 0x%08x\n",
- apic_read(APIC_TMICT)
- );
- printk(KERN_DEBUG " CCT (Current Count Reg): 0x%08x\n",
- apic_read(APIC_TMCCT)
- );
-
- /*
- * Logical APIC addressing mode registers
- */
- printk(KERN_DEBUG " Logical Addressing Mode Information:\n");
- printk(KERN_DEBUG " LDR (Logical Dest Reg): 0x%08x (id=%d)\n",
- apic_read(APIC_LDR),
- GET_APIC_LOGICAL_ID(apic_read(APIC_LDR))
- );
- printk(KERN_DEBUG " DFR (Dest Format Reg): 0x%08x (%s)\n",
- apic_read(APIC_DFR),
- (apic_read(APIC_DFR) == APIC_DFR_FLAT) ? "FLAT" : "CLUSTER"
- );
-
- /*
- * Task/processor/arbitration priority registers
- */
- printk(KERN_DEBUG " Task/Processor/Arbitration Priorities:\n");
- printk(KERN_DEBUG " TPR (Task Priority Reg): 0x%08x\n",
- apic_read(APIC_TASKPRI)
- );
- printk(KERN_DEBUG " PPR (Processor Priority Reg): 0x%08x\n",
- apic_read(APIC_PROCPRI)
- );
- printk(KERN_DEBUG " APR (Arbitration Priority Reg): 0x%08x\n",
- apic_read(APIC_ARBPRI)
- );
-}
-