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