13e8e985fSSepherosa Ziehau /*
23e8e985fSSepherosa Ziehau * Copyright (c) 1996, by Steve Passe
33e8e985fSSepherosa Ziehau * All rights reserved.
43e8e985fSSepherosa Ziehau *
53e8e985fSSepherosa Ziehau * Redistribution and use in source and binary forms, with or without
63e8e985fSSepherosa Ziehau * modification, are permitted provided that the following conditions
73e8e985fSSepherosa Ziehau * are met:
83e8e985fSSepherosa Ziehau * 1. Redistributions of source code must retain the above copyright
93e8e985fSSepherosa Ziehau * notice, this list of conditions and the following disclaimer.
103e8e985fSSepherosa Ziehau * 2. The name of the developer may NOT be used to endorse or promote products
113e8e985fSSepherosa Ziehau * derived from this software without specific prior written permission.
123e8e985fSSepherosa Ziehau *
133e8e985fSSepherosa Ziehau * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
143e8e985fSSepherosa Ziehau * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
153e8e985fSSepherosa Ziehau * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
163e8e985fSSepherosa Ziehau * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
173e8e985fSSepherosa Ziehau * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
183e8e985fSSepherosa Ziehau * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
193e8e985fSSepherosa Ziehau * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
203e8e985fSSepherosa Ziehau * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
213e8e985fSSepherosa Ziehau * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
223e8e985fSSepherosa Ziehau * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
233e8e985fSSepherosa Ziehau * SUCH DAMAGE.
243e8e985fSSepherosa Ziehau *
253e8e985fSSepherosa Ziehau * $FreeBSD: src/sys/i386/i386/mpapic.c,v 1.37.2.7 2003/01/25 02:31:47 peter Exp $
263e8e985fSSepherosa Ziehau */
273e8e985fSSepherosa Ziehau
283e8e985fSSepherosa Ziehau #include <sys/param.h>
293e8e985fSSepherosa Ziehau #include <sys/systm.h>
303e8e985fSSepherosa Ziehau #include <sys/kernel.h>
313c38fc60SSepherosa Ziehau #include <sys/ktr.h>
323e8e985fSSepherosa Ziehau #include <sys/bus.h>
333e8e985fSSepherosa Ziehau #include <sys/machintr.h>
34e2164e29Szrj #include <sys/malloc.h>
35f89b4a45SSepherosa Ziehau #include <sys/sysctl.h>
363e8e985fSSepherosa Ziehau #include <machine/globaldata.h>
3767534613SMatthew Dillon #include <machine/clock.h>
38185b27faSImre Vadász #include <machine/limits.h>
393e8e985fSSepherosa Ziehau #include <machine/smp.h>
403e8e985fSSepherosa Ziehau #include <machine/md_var.h>
413e8e985fSSepherosa Ziehau #include <machine/pmap.h>
42056fad5aSSepherosa Ziehau #include <machine/specialreg.h>
432b6cd37eSSepherosa Ziehau #include <machine_base/apic/lapic.h>
44ed4d621dSSepherosa Ziehau #include <machine_base/apic/ioapic.h>
453e8e985fSSepherosa Ziehau #include <machine_base/apic/ioapic_abi.h>
46f3960bc3SSepherosa Ziehau #include <machine_base/apic/apicvar.h>
473a69c113SSepherosa Ziehau #include <machine_base/icu/icu_var.h>
483e8e985fSSepherosa Ziehau #include <machine/segments.h>
4967534613SMatthew Dillon #include <sys/spinlock2.h>
503e8e985fSSepherosa Ziehau
5186b7a03aSSascha Wildner #include <machine/cputypes.h>
523e8e985fSSepherosa Ziehau #include <machine/intr_machdep.h>
533e8e985fSSepherosa Ziehau
543c38fc60SSepherosa Ziehau #if !defined(KTR_LAPIC)
553c38fc60SSepherosa Ziehau #define KTR_LAPIC KTR_ALL
563c38fc60SSepherosa Ziehau #endif
573c38fc60SSepherosa Ziehau KTR_INFO_MASTER(lapic);
588afc0c3dSSepherosa Ziehau KTR_INFO(KTR_LAPIC, lapic, mem_eoi, 0, "mem_eoi");
59f89b4a45SSepherosa Ziehau KTR_INFO(KTR_LAPIC, lapic, msr_eoi, 0, "msr_eoi");
603c38fc60SSepherosa Ziehau #define log_lapic(name) KTR_LOG(lapic_ ## name)
613c38fc60SSepherosa Ziehau
62637df2f6SSepherosa Ziehau extern int naps;
63637df2f6SSepherosa Ziehau
648afc0c3dSSepherosa Ziehau volatile lapic_t *lapic_mem;
653e8e985fSSepherosa Ziehau
663e8e985fSSepherosa Ziehau static void lapic_timer_calibrate(void);
673e8e985fSSepherosa Ziehau static void lapic_timer_set_divisor(int);
683e8e985fSSepherosa Ziehau static void lapic_timer_fixup_handler(void *);
693e8e985fSSepherosa Ziehau static void lapic_timer_restart_handler(void *);
703e8e985fSSepherosa Ziehau
71b0e79059SSepherosa Ziehau static int lapic_timer_c1e_test = -1; /* auto-detect */
72cb46bbd1SMatthew Dillon TUNABLE_INT("hw.lapic_timer_c1e_test", &lapic_timer_c1e_test);
733e8e985fSSepherosa Ziehau
743e8e985fSSepherosa Ziehau static int lapic_timer_enable = 1;
753e8e985fSSepherosa Ziehau TUNABLE_INT("hw.lapic_timer_enable", &lapic_timer_enable);
763e8e985fSSepherosa Ziehau
77185b27faSImre Vadász static int lapic_timer_tscdeadline = 1;
78185b27faSImre Vadász TUNABLE_INT("hw.lapic_timer_tscdeadline", &lapic_timer_tscdeadline);
79185b27faSImre Vadász
804098a6e5SImre Vadász static int lapic_calibrate_test = 0;
814098a6e5SImre Vadász TUNABLE_INT("hw.lapic_calibrate_test", &lapic_calibrate_test);
824098a6e5SImre Vadász
834098a6e5SImre Vadász static int lapic_calibrate_fast = 1;
844098a6e5SImre Vadász TUNABLE_INT("hw.lapic_calibrate_fast", &lapic_calibrate_fast);
854098a6e5SImre Vadász
86185b27faSImre Vadász static void lapic_timer_tscdlt_reload(struct cputimer_intr *, sysclock_t);
878afc0c3dSSepherosa Ziehau static void lapic_mem_timer_intr_reload(struct cputimer_intr *, sysclock_t);
88f89b4a45SSepherosa Ziehau static void lapic_msr_timer_intr_reload(struct cputimer_intr *, sysclock_t);
893e8e985fSSepherosa Ziehau static void lapic_timer_intr_enable(struct cputimer_intr *);
903e8e985fSSepherosa Ziehau static void lapic_timer_intr_restart(struct cputimer_intr *);
913e8e985fSSepherosa Ziehau static void lapic_timer_intr_pmfixup(struct cputimer_intr *);
923e8e985fSSepherosa Ziehau
933e8e985fSSepherosa Ziehau static struct cputimer_intr lapic_cputimer_intr = {
943e8e985fSSepherosa Ziehau .freq = 0,
958afc0c3dSSepherosa Ziehau .reload = lapic_mem_timer_intr_reload,
963e8e985fSSepherosa Ziehau .enable = lapic_timer_intr_enable,
973e8e985fSSepherosa Ziehau .config = cputimer_intr_default_config,
983e8e985fSSepherosa Ziehau .restart = lapic_timer_intr_restart,
993e8e985fSSepherosa Ziehau .pmfixup = lapic_timer_intr_pmfixup,
1003e8e985fSSepherosa Ziehau .initclock = cputimer_intr_default_initclock,
10142098fc3SSepherosa Ziehau .pcpuhand = NULL,
1023e8e985fSSepherosa Ziehau .next = SLIST_ENTRY_INITIALIZER,
1033e8e985fSSepherosa Ziehau .name = "lapic",
1043e8e985fSSepherosa Ziehau .type = CPUTIMER_INTR_LAPIC,
1053e8e985fSSepherosa Ziehau .prio = CPUTIMER_INTR_PRIO_LAPIC,
10642098fc3SSepherosa Ziehau .caps = CPUTIMER_INTR_CAP_NONE,
10742098fc3SSepherosa Ziehau .priv = NULL
1083e8e985fSSepherosa Ziehau };
1093e8e985fSSepherosa Ziehau
1103e8e985fSSepherosa Ziehau static int lapic_timer_divisor_idx = -1;
1113e8e985fSSepherosa Ziehau static const uint32_t lapic_timer_divisors[] = {
1123e8e985fSSepherosa Ziehau APIC_TDCR_2, APIC_TDCR_4, APIC_TDCR_8, APIC_TDCR_16,
1133e8e985fSSepherosa Ziehau APIC_TDCR_32, APIC_TDCR_64, APIC_TDCR_128, APIC_TDCR_1
1143e8e985fSSepherosa Ziehau };
1153e8e985fSSepherosa Ziehau #define APIC_TIMER_NDIVISORS (int)(NELEM(lapic_timer_divisors))
1163e8e985fSSepherosa Ziehau
117185b27faSImre Vadász static int lapic_use_tscdeadline = 0;
118185b27faSImre Vadász
119511ba355SSepherosa Ziehau /*
120fbac0dc4SSepherosa Ziehau * APIC ID <-> CPU ID mapping structures.
121511ba355SSepherosa Ziehau */
122fbac0dc4SSepherosa Ziehau int cpu_id_to_apic_id[NAPICID];
123fbac0dc4SSepherosa Ziehau int apic_id_to_cpu_id[NAPICID];
1242e0ed166SSepherosa Ziehau int lapic_enable = 1;
1258afc0c3dSSepherosa Ziehau int lapic_usable = 0;
126e87f025fSSepherosa Ziehau int x2apic_enable = 1;
127f89b4a45SSepherosa Ziehau
128f89b4a45SSepherosa Ziehau SYSCTL_INT(_hw, OID_AUTO, x2apic_enable, CTLFLAG_RD, &x2apic_enable, 0, "");
129511ba355SSepherosa Ziehau
130185b27faSImre Vadász /* Separate cachelines for each cpu's info. */
131185b27faSImre Vadász struct deadlines {
132185b27faSImre Vadász uint64_t timestamp;
133185b27faSImre Vadász uint64_t downcount_time;
134185b27faSImre Vadász uint64_t padding[6];
135185b27faSImre Vadász };
136*db2ec6f8SSascha Wildner static struct deadlines *tsc_deadlines = NULL;
137185b27faSImre Vadász
1388afc0c3dSSepherosa Ziehau static void lapic_mem_eoi(void);
1398afc0c3dSSepherosa Ziehau static int lapic_mem_ipi(int dest_type, int vector, int delivery_mode);
1408afc0c3dSSepherosa Ziehau static void lapic_mem_single_ipi(int cpu, int vector, int delivery_mode);
1413c38fc60SSepherosa Ziehau
142f89b4a45SSepherosa Ziehau static void lapic_msr_eoi(void);
143f89b4a45SSepherosa Ziehau static int lapic_msr_ipi(int dest_type, int vector, int delivery_mode);
144f89b4a45SSepherosa Ziehau static void lapic_msr_single_ipi(int cpu, int vector, int delivery_mode);
145f89b4a45SSepherosa Ziehau
1463c38fc60SSepherosa Ziehau void (*lapic_eoi)(void);
1478afc0c3dSSepherosa Ziehau int (*apic_ipi)(int dest_type, int vector, int delivery_mode);
1488afc0c3dSSepherosa Ziehau void (*single_apic_ipi)(int cpu, int vector, int delivery_mode);
1498afc0c3dSSepherosa Ziehau
1508afc0c3dSSepherosa Ziehau static __inline void
lapic_mem_icr_set(uint32_t apic_id,uint32_t icr_lo_val)1518afc0c3dSSepherosa Ziehau lapic_mem_icr_set(uint32_t apic_id, uint32_t icr_lo_val)
1528afc0c3dSSepherosa Ziehau {
1538afc0c3dSSepherosa Ziehau uint32_t icr_lo, icr_hi;
1548afc0c3dSSepherosa Ziehau
1558afc0c3dSSepherosa Ziehau icr_hi = (LAPIC_MEM_READ(icr_hi) & ~APIC_ID_MASK) |
1568afc0c3dSSepherosa Ziehau (apic_id << APIC_ID_SHIFT);
1578afc0c3dSSepherosa Ziehau icr_lo = (LAPIC_MEM_READ(icr_lo) & APIC_ICRLO_RESV_MASK) | icr_lo_val;
1588afc0c3dSSepherosa Ziehau
1598afc0c3dSSepherosa Ziehau LAPIC_MEM_WRITE(icr_hi, icr_hi);
1608afc0c3dSSepherosa Ziehau LAPIC_MEM_WRITE(icr_lo, icr_lo);
1618afc0c3dSSepherosa Ziehau }
1623c38fc60SSepherosa Ziehau
163f89b4a45SSepherosa Ziehau static __inline void
lapic_msr_icr_set(uint32_t apic_id,uint32_t icr_lo_val)164f89b4a45SSepherosa Ziehau lapic_msr_icr_set(uint32_t apic_id, uint32_t icr_lo_val)
165f89b4a45SSepherosa Ziehau {
166f89b4a45SSepherosa Ziehau LAPIC_MSR_WRITE(MSR_X2APIC_ICR,
167f89b4a45SSepherosa Ziehau ((uint64_t)apic_id << 32) | ((uint64_t)icr_lo_val));
168f89b4a45SSepherosa Ziehau }
169f89b4a45SSepherosa Ziehau
1703e8e985fSSepherosa Ziehau /*
1713e8e985fSSepherosa Ziehau * Enable LAPIC, configure interrupts.
1723e8e985fSSepherosa Ziehau */
1733e8e985fSSepherosa Ziehau void
lapic_init(boolean_t bsp)1743e8e985fSSepherosa Ziehau lapic_init(boolean_t bsp)
1753e8e985fSSepherosa Ziehau {
1763e8e985fSSepherosa Ziehau uint32_t timer;
1773e8e985fSSepherosa Ziehau u_int temp;
1783e8e985fSSepherosa Ziehau
179185b27faSImre Vadász if (bsp) {
180185b27faSImre Vadász /* Decide whether we want to use TSC Deadline mode. */
181185b27faSImre Vadász if (lapic_timer_tscdeadline != 0 &&
182185b27faSImre Vadász (cpu_feature2 & CPUID2_TSCDLT) &&
183185b27faSImre Vadász tsc_invariant && tsc_frequency != 0) {
184185b27faSImre Vadász lapic_use_tscdeadline = 1;
18562938642SMatthew Dillon tsc_deadlines =
18662938642SMatthew Dillon kmalloc(sizeof(struct deadlines) * (naps + 1),
18762938642SMatthew Dillon M_DEVBUF,
18862938642SMatthew Dillon M_WAITOK | M_ZERO | M_CACHEALIGN);
189185b27faSImre Vadász }
190185b27faSImre Vadász }
191185b27faSImre Vadász
1923e8e985fSSepherosa Ziehau /*
1933e8e985fSSepherosa Ziehau * Install vectors
1943e8e985fSSepherosa Ziehau *
1953e8e985fSSepherosa Ziehau * Since IDT is shared between BSP and APs, these vectors
1963e8e985fSSepherosa Ziehau * only need to be installed once; we do it on BSP.
1973e8e985fSSepherosa Ziehau */
1983e8e985fSSepherosa Ziehau if (bsp) {
199056fad5aSSepherosa Ziehau if (cpu_vendor_id == CPU_VENDOR_AMD &&
20067534613SMatthew Dillon CPUID_TO_FAMILY(cpu_id) >= 0x0f &&
20167534613SMatthew Dillon CPUID_TO_FAMILY(cpu_id) < 0x17) { /* XXX */
202056fad5aSSepherosa Ziehau uint32_t tcr;
203056fad5aSSepherosa Ziehau
204056fad5aSSepherosa Ziehau /*
205056fad5aSSepherosa Ziehau * Set the LINTEN bit in the HyperTransport
206056fad5aSSepherosa Ziehau * Transaction Control Register.
207056fad5aSSepherosa Ziehau *
208056fad5aSSepherosa Ziehau * This will cause EXTINT and NMI interrupts
209056fad5aSSepherosa Ziehau * routed over the hypertransport bus to be
210056fad5aSSepherosa Ziehau * fed into the LAPIC LINT0/LINT1. If the bit
211056fad5aSSepherosa Ziehau * isn't set, the interrupts will go to the
212056fad5aSSepherosa Ziehau * general cpu INTR/NMI pins. On a dual-core
213056fad5aSSepherosa Ziehau * cpu the interrupt winds up going to BOTH cpus.
214056fad5aSSepherosa Ziehau * The first cpu that does the interrupt ack
215056fad5aSSepherosa Ziehau * cycle will get the correct interrupt. The
216056fad5aSSepherosa Ziehau * second cpu that does it will get a spurious
217056fad5aSSepherosa Ziehau * interrupt vector (typically IRQ 7).
218056fad5aSSepherosa Ziehau */
219056fad5aSSepherosa Ziehau outl(0x0cf8,
220056fad5aSSepherosa Ziehau (1 << 31) | /* enable */
221056fad5aSSepherosa Ziehau (0 << 16) | /* bus */
222056fad5aSSepherosa Ziehau (0x18 << 11) | /* dev (cpu + 0x18) */
223056fad5aSSepherosa Ziehau (0 << 8) | /* func */
224056fad5aSSepherosa Ziehau 0x68 /* reg */
225056fad5aSSepherosa Ziehau );
226056fad5aSSepherosa Ziehau tcr = inl(0xcfc);
227056fad5aSSepherosa Ziehau if ((tcr & 0x00010000) == 0) {
228056fad5aSSepherosa Ziehau kprintf("LAPIC: AMD LINTEN on\n");
229056fad5aSSepherosa Ziehau outl(0xcfc, tcr|0x00010000);
230056fad5aSSepherosa Ziehau }
231056fad5aSSepherosa Ziehau outl(0x0cf8, 0);
232056fad5aSSepherosa Ziehau }
233056fad5aSSepherosa Ziehau
2343e8e985fSSepherosa Ziehau /* Install a 'Spurious INTerrupt' vector */
2358a06c6eeSSepherosa Ziehau setidt_global(XSPURIOUSINT_OFFSET, Xspuriousint,
2363e8e985fSSepherosa Ziehau SDT_SYSIGT, SEL_KPL, 0);
2373e8e985fSSepherosa Ziehau
2389a4bd8f3SSepherosa Ziehau /* Install a timer vector */
2398a06c6eeSSepherosa Ziehau setidt_global(XTIMER_OFFSET, Xtimer,
2409a4bd8f3SSepherosa Ziehau SDT_SYSIGT, SEL_KPL, 0);
2419a4bd8f3SSepherosa Ziehau
2423e8e985fSSepherosa Ziehau /* Install an inter-CPU IPI for TLB invalidation */
2438a06c6eeSSepherosa Ziehau setidt_global(XINVLTLB_OFFSET, Xinvltlb,
2443e8e985fSSepherosa Ziehau SDT_SYSIGT, SEL_KPL, 0);
2453e8e985fSSepherosa Ziehau
2463e8e985fSSepherosa Ziehau /* Install an inter-CPU IPI for IPIQ messaging */
2478a06c6eeSSepherosa Ziehau setidt_global(XIPIQ_OFFSET, Xipiq,
2483e8e985fSSepherosa Ziehau SDT_SYSIGT, SEL_KPL, 0);
2493e8e985fSSepherosa Ziehau
2503e8e985fSSepherosa Ziehau /* Install an inter-CPU IPI for CPU stop/restart */
2518a06c6eeSSepherosa Ziehau setidt_global(XCPUSTOP_OFFSET, Xcpustop,
2523e8e985fSSepherosa Ziehau SDT_SYSIGT, SEL_KPL, 0);
253e32d3244SMatthew Dillon
254e32d3244SMatthew Dillon /* Install an inter-CPU IPI for TLB invalidation */
255e32d3244SMatthew Dillon setidt_global(XSNIFF_OFFSET, Xsniff,
256e32d3244SMatthew Dillon SDT_SYSIGT, SEL_KPL, 0);
2573e8e985fSSepherosa Ziehau }
2583e8e985fSSepherosa Ziehau
2593e8e985fSSepherosa Ziehau /*
2603e8e985fSSepherosa Ziehau * Setup LINT0 as ExtINT on the BSP. This is theoretically an
2613e8e985fSSepherosa Ziehau * aggregate interrupt input from the 8259. The INTA cycle
2623e8e985fSSepherosa Ziehau * will be routed to the external controller (the 8259) which
2633e8e985fSSepherosa Ziehau * is expected to supply the vector.
2643e8e985fSSepherosa Ziehau *
2653e8e985fSSepherosa Ziehau * Must be setup edge triggered, active high.
2663e8e985fSSepherosa Ziehau *
2673e8e985fSSepherosa Ziehau * Disable LINT0 on BSP, if I/O APIC is enabled.
2683e8e985fSSepherosa Ziehau *
2693e8e985fSSepherosa Ziehau * Disable LINT0 on the APs. It doesn't matter what delivery
2703e8e985fSSepherosa Ziehau * mode we use because we leave it masked.
2713e8e985fSSepherosa Ziehau */
2728afc0c3dSSepherosa Ziehau temp = LAPIC_READ(lvt_lint0);
2733e8e985fSSepherosa Ziehau temp &= ~(APIC_LVT_MASKED | APIC_LVT_TRIG_MASK |
2743e8e985fSSepherosa Ziehau APIC_LVT_POLARITY_MASK | APIC_LVT_DM_MASK);
2753e8e985fSSepherosa Ziehau if (bsp) {
2763e8e985fSSepherosa Ziehau temp |= APIC_LVT_DM_EXTINT;
277f45bfca0SSepherosa Ziehau if (ioapic_enable)
2783e8e985fSSepherosa Ziehau temp |= APIC_LVT_MASKED;
2793e8e985fSSepherosa Ziehau } else {
2803e8e985fSSepherosa Ziehau temp |= APIC_LVT_DM_FIXED | APIC_LVT_MASKED;
2813e8e985fSSepherosa Ziehau }
2828afc0c3dSSepherosa Ziehau LAPIC_WRITE(lvt_lint0, temp);
2833e8e985fSSepherosa Ziehau
2843e8e985fSSepherosa Ziehau /*
2853e8e985fSSepherosa Ziehau * Setup LINT1 as NMI.
2863e8e985fSSepherosa Ziehau *
2873e8e985fSSepherosa Ziehau * Must be setup edge trigger, active high.
2883e8e985fSSepherosa Ziehau *
2893e8e985fSSepherosa Ziehau * Enable LINT1 on BSP, if I/O APIC is enabled.
2903e8e985fSSepherosa Ziehau *
2913e8e985fSSepherosa Ziehau * Disable LINT1 on the APs.
2923e8e985fSSepherosa Ziehau */
2938afc0c3dSSepherosa Ziehau temp = LAPIC_READ(lvt_lint1);
2943e8e985fSSepherosa Ziehau temp &= ~(APIC_LVT_MASKED | APIC_LVT_TRIG_MASK |
2953e8e985fSSepherosa Ziehau APIC_LVT_POLARITY_MASK | APIC_LVT_DM_MASK);
2963e8e985fSSepherosa Ziehau temp |= APIC_LVT_MASKED | APIC_LVT_DM_NMI;
297f45bfca0SSepherosa Ziehau if (bsp && ioapic_enable)
2983e8e985fSSepherosa Ziehau temp &= ~APIC_LVT_MASKED;
2998afc0c3dSSepherosa Ziehau LAPIC_WRITE(lvt_lint1, temp);
3003e8e985fSSepherosa Ziehau
3013e8e985fSSepherosa Ziehau /*
3023e8e985fSSepherosa Ziehau * Mask the LAPIC error interrupt, LAPIC performance counter
3033e8e985fSSepherosa Ziehau * interrupt.
3043e8e985fSSepherosa Ziehau */
3058afc0c3dSSepherosa Ziehau LAPIC_WRITE(lvt_error, LAPIC_READ(lvt_error) | APIC_LVT_MASKED);
3068afc0c3dSSepherosa Ziehau LAPIC_WRITE(lvt_pcint, LAPIC_READ(lvt_pcint) | APIC_LVT_MASKED);
3073e8e985fSSepherosa Ziehau
3083e8e985fSSepherosa Ziehau /*
3093e8e985fSSepherosa Ziehau * Set LAPIC timer vector and mask the LAPIC timer interrupt.
3103e8e985fSSepherosa Ziehau */
3118afc0c3dSSepherosa Ziehau timer = LAPIC_READ(lvt_timer);
3123e8e985fSSepherosa Ziehau timer &= ~APIC_LVTT_VECTOR;
3133e8e985fSSepherosa Ziehau timer |= XTIMER_OFFSET;
3143e8e985fSSepherosa Ziehau timer |= APIC_LVTT_MASKED;
3158afc0c3dSSepherosa Ziehau LAPIC_WRITE(lvt_timer, timer);
3163e8e985fSSepherosa Ziehau
3173e8e985fSSepherosa Ziehau /*
3183e8e985fSSepherosa Ziehau * Set the Task Priority Register as needed. At the moment allow
3193e8e985fSSepherosa Ziehau * interrupts on all cpus (the APs will remain CLId until they are
32031d433ffSSepherosa Ziehau * ready to deal).
3213e8e985fSSepherosa Ziehau */
3228afc0c3dSSepherosa Ziehau temp = LAPIC_READ(tpr);
3233e8e985fSSepherosa Ziehau temp &= ~APIC_TPR_PRIO; /* clear priority field */
3248afc0c3dSSepherosa Ziehau LAPIC_WRITE(tpr, temp);
3253e8e985fSSepherosa Ziehau
3263e8e985fSSepherosa Ziehau /*
32767534613SMatthew Dillon * AMD specific setup
32867534613SMatthew Dillon */
3298afc0c3dSSepherosa Ziehau if (cpu_vendor_id == CPU_VENDOR_AMD && lapic_mem != NULL &&
3308afc0c3dSSepherosa Ziehau (LAPIC_MEM_READ(version) & APIC_VER_AMD_EXT_SPACE)) {
33167534613SMatthew Dillon uint32_t ext_feat;
33267534613SMatthew Dillon uint32_t count;
33367534613SMatthew Dillon uint32_t max_count;
33467534613SMatthew Dillon uint32_t lvt;
33567534613SMatthew Dillon uint32_t i;
33667534613SMatthew Dillon
3378afc0c3dSSepherosa Ziehau ext_feat = LAPIC_MEM_READ(ext_feat);
33867534613SMatthew Dillon count = (ext_feat & APIC_EXTFEAT_MASK) >> APIC_EXTFEAT_SHIFT;
3398afc0c3dSSepherosa Ziehau max_count = sizeof(lapic_mem->ext_lvt) /
3408afc0c3dSSepherosa Ziehau sizeof(lapic_mem->ext_lvt[0]);
34167534613SMatthew Dillon if (count > max_count)
34267534613SMatthew Dillon count = max_count;
34367534613SMatthew Dillon for (i = 0; i < count; ++i) {
3448afc0c3dSSepherosa Ziehau lvt = LAPIC_MEM_READ(ext_lvt[i].lvt);
34567534613SMatthew Dillon
34667534613SMatthew Dillon lvt &= ~(APIC_LVT_POLARITY_MASK | APIC_LVT_TRIG_MASK |
34767534613SMatthew Dillon APIC_LVT_DM_MASK | APIC_LVT_MASKED);
34867534613SMatthew Dillon lvt |= APIC_LVT_MASKED | APIC_LVT_DM_FIXED;
34967534613SMatthew Dillon
35067534613SMatthew Dillon switch(i) {
35167534613SMatthew Dillon case APIC_EXTLVT_IBS:
35267534613SMatthew Dillon break;
35367534613SMatthew Dillon case APIC_EXTLVT_MCA:
35467534613SMatthew Dillon break;
35567534613SMatthew Dillon case APIC_EXTLVT_DEI:
35667534613SMatthew Dillon break;
35767534613SMatthew Dillon case APIC_EXTLVT_SBI:
35867534613SMatthew Dillon break;
35967534613SMatthew Dillon default:
36067534613SMatthew Dillon break;
36167534613SMatthew Dillon }
36267534613SMatthew Dillon if (bsp) {
36367534613SMatthew Dillon kprintf(" LAPIC AMD elvt%d: 0x%08x",
3648afc0c3dSSepherosa Ziehau i, LAPIC_MEM_READ(ext_lvt[i].lvt));
3658afc0c3dSSepherosa Ziehau if (LAPIC_MEM_READ(ext_lvt[i].lvt) != lvt)
36667534613SMatthew Dillon kprintf(" -> 0x%08x", lvt);
36767534613SMatthew Dillon kprintf("\n");
36867534613SMatthew Dillon }
3698afc0c3dSSepherosa Ziehau LAPIC_MEM_WRITE(ext_lvt[i].lvt, lvt);
37067534613SMatthew Dillon }
37167534613SMatthew Dillon }
37267534613SMatthew Dillon
37367534613SMatthew Dillon /*
3743e8e985fSSepherosa Ziehau * Enable the LAPIC
3753e8e985fSSepherosa Ziehau */
3768afc0c3dSSepherosa Ziehau temp = LAPIC_READ(svr);
3773e8e985fSSepherosa Ziehau temp |= APIC_SVR_ENABLE; /* enable the LAPIC */
3783e8e985fSSepherosa Ziehau temp &= ~APIC_SVR_FOCUS_DISABLE; /* enable lopri focus processor */
3793e8e985fSSepherosa Ziehau
3808afc0c3dSSepherosa Ziehau if (LAPIC_READ(version) & APIC_VER_EOI_SUPP) {
38167534613SMatthew Dillon if (temp & APIC_SVR_EOI_SUPP) {
38267534613SMatthew Dillon temp &= ~APIC_SVR_EOI_SUPP;
38367534613SMatthew Dillon if (bsp)
38467534613SMatthew Dillon kprintf(" LAPIC disabling EOI supp\n");
38567534613SMatthew Dillon }
3867ed7e1a5SMatthew Dillon /* (future, on KVM auto-EOI must be disabled) */
3877ed7e1a5SMatthew Dillon if (vmm_guest == VMM_GUEST_KVM)
3887ed7e1a5SMatthew Dillon temp &= ~APIC_SVR_EOI_SUPP;
38967534613SMatthew Dillon }
39067534613SMatthew Dillon
3913e8e985fSSepherosa Ziehau /*
3923e8e985fSSepherosa Ziehau * Set the spurious interrupt vector. The low 4 bits of the vector
3933e8e985fSSepherosa Ziehau * must be 1111.
3943e8e985fSSepherosa Ziehau */
3953e8e985fSSepherosa Ziehau if ((XSPURIOUSINT_OFFSET & 0x0F) != 0x0F)
3963e8e985fSSepherosa Ziehau panic("bad XSPURIOUSINT_OFFSET: 0x%08x", XSPURIOUSINT_OFFSET);
3973e8e985fSSepherosa Ziehau temp &= ~APIC_SVR_VECTOR;
3983e8e985fSSepherosa Ziehau temp |= XSPURIOUSINT_OFFSET;
3993e8e985fSSepherosa Ziehau
4008afc0c3dSSepherosa Ziehau LAPIC_WRITE(svr, temp);
4013e8e985fSSepherosa Ziehau
4023e8e985fSSepherosa Ziehau /*
4033e8e985fSSepherosa Ziehau * Pump out a few EOIs to clean out interrupts that got through
4043e8e985fSSepherosa Ziehau * before we were able to set the TPR.
4053e8e985fSSepherosa Ziehau */
4068afc0c3dSSepherosa Ziehau LAPIC_WRITE(eoi, 0);
4078afc0c3dSSepherosa Ziehau LAPIC_WRITE(eoi, 0);
4088afc0c3dSSepherosa Ziehau LAPIC_WRITE(eoi, 0);
4093e8e985fSSepherosa Ziehau
4103e8e985fSSepherosa Ziehau if (bsp) {
4113e8e985fSSepherosa Ziehau lapic_timer_calibrate();
4123e8e985fSSepherosa Ziehau if (lapic_timer_enable) {
4138d16855aSSepherosa Ziehau if (cpu_thermal_feature & CPUID_THERMAL_ARAT) {
4148d16855aSSepherosa Ziehau /*
4158d16855aSSepherosa Ziehau * Local APIC timer will not stop
4168d16855aSSepherosa Ziehau * in deep C-state.
4178d16855aSSepherosa Ziehau */
4188d16855aSSepherosa Ziehau lapic_cputimer_intr.caps |=
4198d16855aSSepherosa Ziehau CPUTIMER_INTR_CAP_PS;
4208d16855aSSepherosa Ziehau }
421185b27faSImre Vadász if (lapic_use_tscdeadline) {
422185b27faSImre Vadász lapic_cputimer_intr.reload =
423185b27faSImre Vadász lapic_timer_tscdlt_reload;
424185b27faSImre Vadász }
4253e8e985fSSepherosa Ziehau cputimer_intr_register(&lapic_cputimer_intr);
4263e8e985fSSepherosa Ziehau cputimer_intr_select(&lapic_cputimer_intr, 0);
4273e8e985fSSepherosa Ziehau }
428185b27faSImre Vadász } else if (!lapic_use_tscdeadline) {
4293e8e985fSSepherosa Ziehau lapic_timer_set_divisor(lapic_timer_divisor_idx);
4303e8e985fSSepherosa Ziehau }
4313e8e985fSSepherosa Ziehau
4323e8e985fSSepherosa Ziehau if (bootverbose)
4333e8e985fSSepherosa Ziehau apic_dump("apic_initialize()");
4343e8e985fSSepherosa Ziehau }
4353e8e985fSSepherosa Ziehau
4363e8e985fSSepherosa Ziehau static void
lapic_timer_set_divisor(int divisor_idx)4373e8e985fSSepherosa Ziehau lapic_timer_set_divisor(int divisor_idx)
4383e8e985fSSepherosa Ziehau {
4393e8e985fSSepherosa Ziehau KKASSERT(divisor_idx >= 0 && divisor_idx < APIC_TIMER_NDIVISORS);
4408afc0c3dSSepherosa Ziehau LAPIC_WRITE(dcr_timer, lapic_timer_divisors[divisor_idx]);
4413e8e985fSSepherosa Ziehau }
4423e8e985fSSepherosa Ziehau
4433e8e985fSSepherosa Ziehau static void
lapic_timer_oneshot(u_int count)4443e8e985fSSepherosa Ziehau lapic_timer_oneshot(u_int count)
4453e8e985fSSepherosa Ziehau {
4463e8e985fSSepherosa Ziehau uint32_t value;
4473e8e985fSSepherosa Ziehau
4488afc0c3dSSepherosa Ziehau value = LAPIC_READ(lvt_timer);
449185b27faSImre Vadász value &= ~(APIC_LVTT_PERIODIC | APIC_LVTT_TSCDLT);
4508afc0c3dSSepherosa Ziehau LAPIC_WRITE(lvt_timer, value);
4518afc0c3dSSepherosa Ziehau LAPIC_WRITE(icr_timer, count);
4523e8e985fSSepherosa Ziehau }
4533e8e985fSSepherosa Ziehau
4543e8e985fSSepherosa Ziehau static void
lapic_timer_oneshot_quick(u_int count)4553e8e985fSSepherosa Ziehau lapic_timer_oneshot_quick(u_int count)
4563e8e985fSSepherosa Ziehau {
4578afc0c3dSSepherosa Ziehau LAPIC_WRITE(icr_timer, count);
4583e8e985fSSepherosa Ziehau }
4593e8e985fSSepherosa Ziehau
4603e8e985fSSepherosa Ziehau static void
lapic_timer_tscdeadline_quick(uint64_t diff)461185b27faSImre Vadász lapic_timer_tscdeadline_quick(uint64_t diff)
462185b27faSImre Vadász {
463185b27faSImre Vadász uint64_t val = rdtsc() + diff;
464185b27faSImre Vadász
465185b27faSImre Vadász wrmsr(MSR_TSC_DEADLINE, val);
466185b27faSImre Vadász tsc_deadlines[mycpuid].timestamp = val;
467185b27faSImre Vadász }
468185b27faSImre Vadász
469185b27faSImre Vadász static uint64_t
lapic_scale_to_tsc(unsigned value,unsigned scale)470185b27faSImre Vadász lapic_scale_to_tsc(unsigned value, unsigned scale)
471185b27faSImre Vadász {
472185b27faSImre Vadász uint64_t val;
473185b27faSImre Vadász
474185b27faSImre Vadász val = value;
475185b27faSImre Vadász val *= tsc_frequency;
476185b27faSImre Vadász val += (scale - 1);
477185b27faSImre Vadász val /= scale;
478185b27faSImre Vadász return val;
479185b27faSImre Vadász }
480185b27faSImre Vadász
4814098a6e5SImre Vadász #define MAX_MEASURE_RETRIES 100
4824098a6e5SImre Vadász
4834098a6e5SImre Vadász static u_int64_t
do_tsc_calibration(u_int us,u_int64_t apic_delay_tsc)4844098a6e5SImre Vadász do_tsc_calibration(u_int us, u_int64_t apic_delay_tsc)
4854098a6e5SImre Vadász {
4864098a6e5SImre Vadász u_int64_t old_tsc1, old_tsc2, new_tsc1, new_tsc2;
4874098a6e5SImre Vadász u_int64_t diff, count;
4884098a6e5SImre Vadász u_int64_t a;
4894098a6e5SImre Vadász u_int32_t start, end;
4904098a6e5SImre Vadász int retries1 = 0, retries2 = 0;
4914098a6e5SImre Vadász
4924098a6e5SImre Vadász retry1:
4934098a6e5SImre Vadász lapic_timer_oneshot_quick(APIC_TIMER_MAX_COUNT);
4944098a6e5SImre Vadász old_tsc1 = rdtsc_ordered();
4958afc0c3dSSepherosa Ziehau start = LAPIC_READ(ccr_timer);
4964098a6e5SImre Vadász old_tsc2 = rdtsc_ordered();
4974098a6e5SImre Vadász if (apic_delay_tsc > 0 && retries1 < MAX_MEASURE_RETRIES &&
4984098a6e5SImre Vadász old_tsc2 - old_tsc1 > 2 * apic_delay_tsc) {
4994098a6e5SImre Vadász retries1++;
5004098a6e5SImre Vadász goto retry1;
5014098a6e5SImre Vadász }
5024098a6e5SImre Vadász DELAY(us);
5034098a6e5SImre Vadász retry2:
5044098a6e5SImre Vadász new_tsc1 = rdtsc_ordered();
5058afc0c3dSSepherosa Ziehau end = LAPIC_READ(ccr_timer);
5064098a6e5SImre Vadász new_tsc2 = rdtsc_ordered();
5074098a6e5SImre Vadász if (apic_delay_tsc > 0 && retries2 < MAX_MEASURE_RETRIES &&
5084098a6e5SImre Vadász new_tsc2 - new_tsc1 > 2 * apic_delay_tsc) {
5094098a6e5SImre Vadász retries2++;
5104098a6e5SImre Vadász goto retry2;
5114098a6e5SImre Vadász }
5124098a6e5SImre Vadász if (end == 0)
5134098a6e5SImre Vadász return 0;
5144098a6e5SImre Vadász
5154098a6e5SImre Vadász count = start - end;
5164098a6e5SImre Vadász
5174098a6e5SImre Vadász /* Make sure the lapic can count for up to 2s */
5184098a6e5SImre Vadász a = (unsigned)APIC_TIMER_MAX_COUNT;
5194098a6e5SImre Vadász if (us < 2000000 && (u_int64_t)count * 2000000 >= a * us)
5204098a6e5SImre Vadász return 0;
5214098a6e5SImre Vadász
5224098a6e5SImre Vadász if (lapic_calibrate_test > 0 && (retries1 > 0 || retries2 > 0)) {
5234098a6e5SImre Vadász kprintf("%s: retries1=%d retries2=%d\n",
5244098a6e5SImre Vadász __func__, retries1, retries2);
5254098a6e5SImre Vadász }
5264098a6e5SImre Vadász
5274098a6e5SImre Vadász diff = (new_tsc1 - old_tsc1) + (new_tsc2 - old_tsc2);
5284098a6e5SImre Vadász /* XXX First estimate if the total TSC diff value makes sense */
5294098a6e5SImre Vadász /* This will almost overflow, but only almost :) */
5304098a6e5SImre Vadász count = (2 * count * tsc_frequency) / diff;
5314098a6e5SImre Vadász
5324098a6e5SImre Vadász return count;
5334098a6e5SImre Vadász }
5344098a6e5SImre Vadász
5354098a6e5SImre Vadász static uint64_t
do_cputimer_calibration(u_int us)5364098a6e5SImre Vadász do_cputimer_calibration(u_int us)
5374098a6e5SImre Vadász {
5384098a6e5SImre Vadász sysclock_t value;
5398fbc264dSMatthew Dillon sysclock_t start, end;
5408fbc264dSMatthew Dillon uint32_t beginning, finish;
5414098a6e5SImre Vadász
5424098a6e5SImre Vadász lapic_timer_oneshot(APIC_TIMER_MAX_COUNT);
5438afc0c3dSSepherosa Ziehau beginning = LAPIC_READ(ccr_timer);
5444098a6e5SImre Vadász start = sys_cputimer->count();
5454098a6e5SImre Vadász DELAY(us);
5464098a6e5SImre Vadász end = sys_cputimer->count();
5478afc0c3dSSepherosa Ziehau finish = LAPIC_READ(ccr_timer);
5484098a6e5SImre Vadász if (finish == 0)
5494098a6e5SImre Vadász return 0;
5504098a6e5SImre Vadász /* value is the LAPIC timer difference. */
5518fbc264dSMatthew Dillon value = (uint32_t)(beginning - finish);
5524098a6e5SImre Vadász /* end is the sys_cputimer difference. */
5534098a6e5SImre Vadász end -= start;
5544098a6e5SImre Vadász if (end == 0)
5554098a6e5SImre Vadász return 0;
5568fbc264dSMatthew Dillon value = muldivu64(value, sys_cputimer->freq, end);
5578fbc264dSMatthew Dillon
5584098a6e5SImre Vadász return value;
5594098a6e5SImre Vadász }
5604098a6e5SImre Vadász
561185b27faSImre Vadász static void
lapic_timer_calibrate(void)5623e8e985fSSepherosa Ziehau lapic_timer_calibrate(void)
5633e8e985fSSepherosa Ziehau {
5643e8e985fSSepherosa Ziehau sysclock_t value;
5654098a6e5SImre Vadász u_int64_t apic_delay_tsc = 0;
5664098a6e5SImre Vadász int use_tsc_calibration = 0;
5673e8e985fSSepherosa Ziehau
568185b27faSImre Vadász /* No need to calibrate lapic_timer, if we will use TSC Deadline mode */
569185b27faSImre Vadász if (lapic_use_tscdeadline) {
5708fbc264dSMatthew Dillon lapic_cputimer_intr.freq = tsc_frequency;
571185b27faSImre Vadász kprintf(
5728fbc264dSMatthew Dillon "lapic: TSC Deadline Mode: frequency %lu Hz\n",
5738fbc264dSMatthew Dillon lapic_cputimer_intr.freq);
574185b27faSImre Vadász return;
575185b27faSImre Vadász }
576185b27faSImre Vadász
5774098a6e5SImre Vadász /*
5784098a6e5SImre Vadász * On real hardware, tsc_invariant == 0 wouldn't be an issue, but in
5794098a6e5SImre Vadász * a virtual machine the frequency may get changed by the host.
5804098a6e5SImre Vadász */
5814098a6e5SImre Vadász if (tsc_frequency != 0 && tsc_invariant && lapic_calibrate_fast)
5824098a6e5SImre Vadász use_tsc_calibration = 1;
5834098a6e5SImre Vadász
5844098a6e5SImre Vadász if (use_tsc_calibration) {
5854098a6e5SImre Vadász u_int64_t min_apic_tsc = 0, max_apic_tsc = 0;
5864098a6e5SImre Vadász u_int64_t old_tsc, new_tsc;
5878fbc264dSMatthew Dillon uint32_t val;
5884098a6e5SImre Vadász int i;
5894098a6e5SImre Vadász
5904098a6e5SImre Vadász /* warm up */
5914098a6e5SImre Vadász lapic_timer_oneshot(APIC_TIMER_MAX_COUNT);
5924098a6e5SImre Vadász for (i = 0; i < 10; i++)
5938afc0c3dSSepherosa Ziehau val = LAPIC_READ(ccr_timer);
5944098a6e5SImre Vadász
5954098a6e5SImre Vadász for (i = 0; i < 100; i++) {
5964098a6e5SImre Vadász old_tsc = rdtsc_ordered();
5978afc0c3dSSepherosa Ziehau val = LAPIC_READ(ccr_timer);
5984098a6e5SImre Vadász new_tsc = rdtsc_ordered();
5994098a6e5SImre Vadász new_tsc -= old_tsc;
6004098a6e5SImre Vadász apic_delay_tsc += new_tsc;
6014098a6e5SImre Vadász if (min_apic_tsc == 0 ||
6024098a6e5SImre Vadász min_apic_tsc > new_tsc) {
6034098a6e5SImre Vadász min_apic_tsc = new_tsc;
6044098a6e5SImre Vadász }
6054098a6e5SImre Vadász if (max_apic_tsc < new_tsc)
6064098a6e5SImre Vadász max_apic_tsc = new_tsc;
6074098a6e5SImre Vadász }
6084098a6e5SImre Vadász apic_delay_tsc /= 100;
6094098a6e5SImre Vadász kprintf(
6104098a6e5SImre Vadász "LAPIC latency (in TSC ticks): %lu min: %lu max: %lu\n",
6114098a6e5SImre Vadász apic_delay_tsc, min_apic_tsc, max_apic_tsc);
6124098a6e5SImre Vadász apic_delay_tsc = min_apic_tsc;
6134098a6e5SImre Vadász }
6144098a6e5SImre Vadász
6154098a6e5SImre Vadász if (!use_tsc_calibration) {
6164098a6e5SImre Vadász int i;
6174098a6e5SImre Vadász
6184098a6e5SImre Vadász /*
6194098a6e5SImre Vadász * Do some exercising of the lapic timer access. This improves
6204098a6e5SImre Vadász * precision of the subsequent calibration run in at least some
6214098a6e5SImre Vadász * virtualization cases.
6224098a6e5SImre Vadász */
6234098a6e5SImre Vadász lapic_timer_set_divisor(0);
6244098a6e5SImre Vadász for (i = 0; i < 10; i++)
6254098a6e5SImre Vadász (void)do_cputimer_calibration(100);
6264098a6e5SImre Vadász }
6273e8e985fSSepherosa Ziehau /* Try to calibrate the local APIC timer. */
6283e8e985fSSepherosa Ziehau for (lapic_timer_divisor_idx = 0;
6293e8e985fSSepherosa Ziehau lapic_timer_divisor_idx < APIC_TIMER_NDIVISORS;
6303e8e985fSSepherosa Ziehau lapic_timer_divisor_idx++) {
6313e8e985fSSepherosa Ziehau lapic_timer_set_divisor(lapic_timer_divisor_idx);
6324098a6e5SImre Vadász if (use_tsc_calibration) {
6334098a6e5SImre Vadász value = do_tsc_calibration(200*1000, apic_delay_tsc);
6344098a6e5SImre Vadász } else {
6354098a6e5SImre Vadász value = do_cputimer_calibration(2*1000*1000);
6364098a6e5SImre Vadász }
6374098a6e5SImre Vadász if (value != 0)
6383e8e985fSSepherosa Ziehau break;
6393e8e985fSSepherosa Ziehau }
6403e8e985fSSepherosa Ziehau if (lapic_timer_divisor_idx >= APIC_TIMER_NDIVISORS)
641ed20d0e3SSascha Wildner panic("lapic: no proper timer divisor?!");
6424098a6e5SImre Vadász lapic_cputimer_intr.freq = value;
6433e8e985fSSepherosa Ziehau
6448fbc264dSMatthew Dillon kprintf("lapic: divisor index %d, frequency %lu Hz\n",
6453e8e985fSSepherosa Ziehau lapic_timer_divisor_idx, lapic_cputimer_intr.freq);
6464098a6e5SImre Vadász
6474098a6e5SImre Vadász if (lapic_calibrate_test > 0) {
6484098a6e5SImre Vadász uint64_t freq;
6494098a6e5SImre Vadász int i;
6504098a6e5SImre Vadász
6514098a6e5SImre Vadász for (i = 1; i <= 20; i++) {
6524098a6e5SImre Vadász if (use_tsc_calibration) {
6534098a6e5SImre Vadász freq = do_tsc_calibration(i*100*1000,
6544098a6e5SImre Vadász apic_delay_tsc);
6554098a6e5SImre Vadász } else {
6564098a6e5SImre Vadász freq = do_cputimer_calibration(i*100*1000);
6574098a6e5SImre Vadász }
6584098a6e5SImre Vadász if (freq != 0)
6594098a6e5SImre Vadász kprintf("%ums: %lu\n", i * 100, freq);
6604098a6e5SImre Vadász }
6614098a6e5SImre Vadász }
6623e8e985fSSepherosa Ziehau }
6633e8e985fSSepherosa Ziehau
6643e8e985fSSepherosa Ziehau static void
lapic_timer_tscdlt_reload(struct cputimer_intr * cti,sysclock_t reload)665185b27faSImre Vadász lapic_timer_tscdlt_reload(struct cputimer_intr *cti, sysclock_t reload)
666185b27faSImre Vadász {
667185b27faSImre Vadász struct globaldata *gd = mycpu;
668185b27faSImre Vadász uint64_t diff, now, val;
669185b27faSImre Vadász
6708fbc264dSMatthew Dillon /*
6718fbc264dSMatthew Dillon * Set maximum deadline to 60 seconds
6728fbc264dSMatthew Dillon */
6738fbc264dSMatthew Dillon if (reload > sys_cputimer->freq * 60)
6748fbc264dSMatthew Dillon reload = sys_cputimer->freq * 60;
6758fbc264dSMatthew Dillon diff = muldivu64(reload, tsc_frequency, sys_cputimer->freq);
676185b27faSImre Vadász if (diff < 4)
677185b27faSImre Vadász diff = 4;
678185b27faSImre Vadász if (cpu_vendor_id == CPU_VENDOR_INTEL)
679185b27faSImre Vadász cpu_lfence();
680185b27faSImre Vadász else
681185b27faSImre Vadász cpu_mfence();
682185b27faSImre Vadász now = rdtsc();
683185b27faSImre Vadász val = now + diff;
684185b27faSImre Vadász if (gd->gd_timer_running) {
685185b27faSImre Vadász uint64_t deadline = tsc_deadlines[mycpuid].timestamp;
686185b27faSImre Vadász if (deadline == 0 || now > deadline || val < deadline) {
687185b27faSImre Vadász wrmsr(MSR_TSC_DEADLINE, val);
688185b27faSImre Vadász tsc_deadlines[mycpuid].timestamp = val;
689185b27faSImre Vadász }
690185b27faSImre Vadász } else {
691185b27faSImre Vadász gd->gd_timer_running = 1;
692185b27faSImre Vadász wrmsr(MSR_TSC_DEADLINE, val);
693185b27faSImre Vadász tsc_deadlines[mycpuid].timestamp = val;
694185b27faSImre Vadász }
695185b27faSImre Vadász }
696185b27faSImre Vadász
697185b27faSImre Vadász static void
lapic_mem_timer_intr_reload(struct cputimer_intr * cti,sysclock_t reload)6988afc0c3dSSepherosa Ziehau lapic_mem_timer_intr_reload(struct cputimer_intr *cti, sysclock_t reload)
6993e8e985fSSepherosa Ziehau {
7003e8e985fSSepherosa Ziehau struct globaldata *gd = mycpu;
7013e8e985fSSepherosa Ziehau
702feadd4aeSMatthew Dillon if ((ssysclock_t)reload < 0)
703feadd4aeSMatthew Dillon reload = 1;
7048fbc264dSMatthew Dillon reload = muldivu64(reload, cti->freq, sys_cputimer->freq);
7053e8e985fSSepherosa Ziehau if (reload < 2)
7063e8e985fSSepherosa Ziehau reload = 2;
7078fbc264dSMatthew Dillon if (reload > 0xFFFFFFFF)
7088fbc264dSMatthew Dillon reload = 0xFFFFFFFF;
7093e8e985fSSepherosa Ziehau
7103e8e985fSSepherosa Ziehau if (gd->gd_timer_running) {
7118afc0c3dSSepherosa Ziehau if (reload < LAPIC_MEM_READ(ccr_timer))
7128fbc264dSMatthew Dillon LAPIC_MEM_WRITE(icr_timer, (uint32_t)reload);
7133e8e985fSSepherosa Ziehau } else {
7143e8e985fSSepherosa Ziehau gd->gd_timer_running = 1;
7158fbc264dSMatthew Dillon LAPIC_MEM_WRITE(icr_timer, (uint32_t)reload);
7163e8e985fSSepherosa Ziehau }
7173e8e985fSSepherosa Ziehau }
7183e8e985fSSepherosa Ziehau
7193e8e985fSSepherosa Ziehau static void
lapic_msr_timer_intr_reload(struct cputimer_intr * cti,sysclock_t reload)720f89b4a45SSepherosa Ziehau lapic_msr_timer_intr_reload(struct cputimer_intr *cti, sysclock_t reload)
721f89b4a45SSepherosa Ziehau {
722f89b4a45SSepherosa Ziehau struct globaldata *gd = mycpu;
723f89b4a45SSepherosa Ziehau
724feadd4aeSMatthew Dillon if ((ssysclock_t)reload < 0)
725feadd4aeSMatthew Dillon reload = 1;
7268fbc264dSMatthew Dillon reload = muldivu64(reload, cti->freq, sys_cputimer->freq);
727f89b4a45SSepherosa Ziehau if (reload < 2)
728f89b4a45SSepherosa Ziehau reload = 2;
7298fbc264dSMatthew Dillon if (reload > 0xFFFFFFFF)
7308fbc264dSMatthew Dillon reload = 0xFFFFFFFF;
731f89b4a45SSepherosa Ziehau
732f89b4a45SSepherosa Ziehau if (gd->gd_timer_running) {
733f89b4a45SSepherosa Ziehau if (reload < LAPIC_MSR_READ(MSR_X2APIC_CCR_TIMER))
7348fbc264dSMatthew Dillon LAPIC_MSR_WRITE(MSR_X2APIC_ICR_TIMER, (uint32_t)reload);
735f89b4a45SSepherosa Ziehau } else {
736f89b4a45SSepherosa Ziehau gd->gd_timer_running = 1;
7378fbc264dSMatthew Dillon LAPIC_MSR_WRITE(MSR_X2APIC_ICR_TIMER, (uint32_t)reload);
738f89b4a45SSepherosa Ziehau }
739f89b4a45SSepherosa Ziehau }
740f89b4a45SSepherosa Ziehau
741f89b4a45SSepherosa Ziehau static void
lapic_timer_intr_enable(struct cputimer_intr * cti __unused)7423e8e985fSSepherosa Ziehau lapic_timer_intr_enable(struct cputimer_intr *cti __unused)
7433e8e985fSSepherosa Ziehau {
7443e8e985fSSepherosa Ziehau uint32_t timer;
7453e8e985fSSepherosa Ziehau
7468afc0c3dSSepherosa Ziehau timer = LAPIC_READ(lvt_timer);
747185b27faSImre Vadász timer &= ~(APIC_LVTT_MASKED | APIC_LVTT_PERIODIC | APIC_LVTT_TSCDLT);
748185b27faSImre Vadász if (lapic_use_tscdeadline)
749185b27faSImre Vadász timer |= APIC_LVTT_TSCDLT;
7508afc0c3dSSepherosa Ziehau LAPIC_WRITE(lvt_timer, timer);
751185b27faSImre Vadász if (lapic_use_tscdeadline)
752315d9939SImre Vadász cpu_mfence();
7533e8e985fSSepherosa Ziehau
7543e8e985fSSepherosa Ziehau lapic_timer_fixup_handler(NULL);
7553e8e985fSSepherosa Ziehau }
7563e8e985fSSepherosa Ziehau
7573e8e985fSSepherosa Ziehau static void
lapic_timer_fixup_handler(void * arg)7583e8e985fSSepherosa Ziehau lapic_timer_fixup_handler(void *arg)
7593e8e985fSSepherosa Ziehau {
7603e8e985fSSepherosa Ziehau int *started = arg;
7613e8e985fSSepherosa Ziehau
7623e8e985fSSepherosa Ziehau if (started != NULL)
7633e8e985fSSepherosa Ziehau *started = 0;
7643e8e985fSSepherosa Ziehau
76586b7a03aSSascha Wildner if (cpu_vendor_id == CPU_VENDOR_AMD) {
766b0e79059SSepherosa Ziehau int c1e_test = lapic_timer_c1e_test;
767b0e79059SSepherosa Ziehau
768b0e79059SSepherosa Ziehau if (c1e_test < 0) {
769b0e79059SSepherosa Ziehau if (vmm_guest == VMM_GUEST_NONE) {
770b0e79059SSepherosa Ziehau c1e_test = 1;
771b0e79059SSepherosa Ziehau } else {
772b0e79059SSepherosa Ziehau /*
773b0e79059SSepherosa Ziehau * Don't do this C1E testing and adjustment
774b0e79059SSepherosa Ziehau * on virtual machines, the best case for
775b0e79059SSepherosa Ziehau * accessing this MSR is a NOOP; the worst
776b0e79059SSepherosa Ziehau * cases could be pretty nasty, e.g. crash.
777b0e79059SSepherosa Ziehau */
778b0e79059SSepherosa Ziehau c1e_test = 0;
779b0e79059SSepherosa Ziehau }
780b0e79059SSepherosa Ziehau }
781b0e79059SSepherosa Ziehau
7823e8e985fSSepherosa Ziehau /*
7833e8e985fSSepherosa Ziehau * Detect the presence of C1E capability mostly on latest
7843e8e985fSSepherosa Ziehau * dual-cores (or future) k8 family. This feature renders
7853e8e985fSSepherosa Ziehau * the local APIC timer dead, so we disable it by reading
7863e8e985fSSepherosa Ziehau * the Interrupt Pending Message register and clearing both
7873e8e985fSSepherosa Ziehau * C1eOnCmpHalt (bit 28) and SmiOnCmpHalt (bit 27).
7883e8e985fSSepherosa Ziehau *
7893e8e985fSSepherosa Ziehau * Reference:
7903e8e985fSSepherosa Ziehau * "BIOS and Kernel Developer's Guide for AMD NPT
7913e8e985fSSepherosa Ziehau * Family 0Fh Processors"
7923e8e985fSSepherosa Ziehau * #32559 revision 3.00
7933e8e985fSSepherosa Ziehau */
7943e8e985fSSepherosa Ziehau if ((cpu_id & 0x00000f00) == 0x00000f00 &&
795cb46bbd1SMatthew Dillon (cpu_id & 0x0fff0000) >= 0x00040000 &&
796b0e79059SSepherosa Ziehau c1e_test) {
7973e8e985fSSepherosa Ziehau uint64_t msr;
7983e8e985fSSepherosa Ziehau
7993e8e985fSSepherosa Ziehau msr = rdmsr(0xc0010055);
8003e8e985fSSepherosa Ziehau if (msr & 0x18000000) {
8013e8e985fSSepherosa Ziehau struct globaldata *gd = mycpu;
8023e8e985fSSepherosa Ziehau
8033e8e985fSSepherosa Ziehau kprintf("cpu%d: AMD C1E detected\n",
8043e8e985fSSepherosa Ziehau gd->gd_cpuid);
8053e8e985fSSepherosa Ziehau wrmsr(0xc0010055, msr & ~0x18000000ULL);
8063e8e985fSSepherosa Ziehau
8073e8e985fSSepherosa Ziehau /*
8083e8e985fSSepherosa Ziehau * We are kinda stalled;
8093e8e985fSSepherosa Ziehau * kick start again.
8103e8e985fSSepherosa Ziehau */
8113e8e985fSSepherosa Ziehau gd->gd_timer_running = 1;
812185b27faSImre Vadász if (lapic_use_tscdeadline) {
813185b27faSImre Vadász /* Maybe reached in Virtual Machines? */
814185b27faSImre Vadász lapic_timer_tscdeadline_quick(5000);
815185b27faSImre Vadász } else {
8163e8e985fSSepherosa Ziehau lapic_timer_oneshot_quick(2);
817185b27faSImre Vadász }
8183e8e985fSSepherosa Ziehau
8193e8e985fSSepherosa Ziehau if (started != NULL)
8203e8e985fSSepherosa Ziehau *started = 1;
8213e8e985fSSepherosa Ziehau }
8223e8e985fSSepherosa Ziehau }
8233e8e985fSSepherosa Ziehau }
8243e8e985fSSepherosa Ziehau }
8253e8e985fSSepherosa Ziehau
8263e8e985fSSepherosa Ziehau static void
lapic_timer_restart_handler(void * dummy __unused)8273e8e985fSSepherosa Ziehau lapic_timer_restart_handler(void *dummy __unused)
8283e8e985fSSepherosa Ziehau {
8293e8e985fSSepherosa Ziehau int started;
8303e8e985fSSepherosa Ziehau
8313e8e985fSSepherosa Ziehau lapic_timer_fixup_handler(&started);
8323e8e985fSSepherosa Ziehau if (!started) {
8333e8e985fSSepherosa Ziehau struct globaldata *gd = mycpu;
8343e8e985fSSepherosa Ziehau
8353e8e985fSSepherosa Ziehau gd->gd_timer_running = 1;
836185b27faSImre Vadász if (lapic_use_tscdeadline) {
837185b27faSImre Vadász /* Maybe reached in Virtual Machines? */
838185b27faSImre Vadász lapic_timer_tscdeadline_quick(5000);
839185b27faSImre Vadász } else {
8403e8e985fSSepherosa Ziehau lapic_timer_oneshot_quick(2);
8413e8e985fSSepherosa Ziehau }
8423e8e985fSSepherosa Ziehau }
843185b27faSImre Vadász }
8443e8e985fSSepherosa Ziehau
8453e8e985fSSepherosa Ziehau /*
8469d1f0c52SSascha Wildner * This function is called only by ACPICA code currently:
8473e8e985fSSepherosa Ziehau * - AMD C1E fixup. AMD C1E only seems to happen after ACPI
8489d1f0c52SSascha Wildner * module controls PM. So once ACPICA is attached, we try
8493e8e985fSSepherosa Ziehau * to apply the fixup to prevent LAPIC timer from hanging.
8503e8e985fSSepherosa Ziehau */
8513e8e985fSSepherosa Ziehau static void
lapic_timer_intr_pmfixup(struct cputimer_intr * cti __unused)8523e8e985fSSepherosa Ziehau lapic_timer_intr_pmfixup(struct cputimer_intr *cti __unused)
8533e8e985fSSepherosa Ziehau {
8543e8e985fSSepherosa Ziehau lwkt_send_ipiq_mask(smp_active_mask,
8553e8e985fSSepherosa Ziehau lapic_timer_fixup_handler, NULL);
8563e8e985fSSepherosa Ziehau }
8573e8e985fSSepherosa Ziehau
8583e8e985fSSepherosa Ziehau static void
lapic_timer_intr_restart(struct cputimer_intr * cti __unused)8593e8e985fSSepherosa Ziehau lapic_timer_intr_restart(struct cputimer_intr *cti __unused)
8603e8e985fSSepherosa Ziehau {
8613e8e985fSSepherosa Ziehau lwkt_send_ipiq_mask(smp_active_mask, lapic_timer_restart_handler, NULL);
8623e8e985fSSepherosa Ziehau }
8633e8e985fSSepherosa Ziehau
8643e8e985fSSepherosa Ziehau
8653e8e985fSSepherosa Ziehau /*
8663e8e985fSSepherosa Ziehau * dump contents of local APIC registers
8673e8e985fSSepherosa Ziehau */
8683e8e985fSSepherosa Ziehau void
apic_dump(char * str)8693e8e985fSSepherosa Ziehau apic_dump(char* str)
8703e8e985fSSepherosa Ziehau {
8713e8e985fSSepherosa Ziehau kprintf("SMP: CPU%d %s:\n", mycpu->gd_cpuid, str);
8723e8e985fSSepherosa Ziehau kprintf(" lint0: 0x%08x lint1: 0x%08x TPR: 0x%08x SVR: 0x%08x\n",
8738afc0c3dSSepherosa Ziehau LAPIC_READ(lvt_lint0), LAPIC_READ(lvt_lint1), LAPIC_READ(tpr),
8748afc0c3dSSepherosa Ziehau LAPIC_READ(svr));
8753e8e985fSSepherosa Ziehau }
8763e8e985fSSepherosa Ziehau
8773e8e985fSSepherosa Ziehau /*
8783e8e985fSSepherosa Ziehau * Inter Processor Interrupt functions.
8793e8e985fSSepherosa Ziehau */
8803e8e985fSSepherosa Ziehau
8818afc0c3dSSepherosa Ziehau static __inline void
lapic_mem_icr_unpend(const char * func)8828afc0c3dSSepherosa Ziehau lapic_mem_icr_unpend(const char *func)
8838afc0c3dSSepherosa Ziehau {
8848afc0c3dSSepherosa Ziehau if (LAPIC_MEM_READ(icr_lo) & APIC_DELSTAT_PEND) {
8858afc0c3dSSepherosa Ziehau int64_t tsc;
8868afc0c3dSSepherosa Ziehau int loops = 1;
8878afc0c3dSSepherosa Ziehau
8888afc0c3dSSepherosa Ziehau tsc = rdtsc();
8898afc0c3dSSepherosa Ziehau while (LAPIC_MEM_READ(icr_lo) & APIC_DELSTAT_PEND) {
8908afc0c3dSSepherosa Ziehau cpu_pause();
8918afc0c3dSSepherosa Ziehau if ((tsc_sclock_t)(rdtsc() -
8928afc0c3dSSepherosa Ziehau (tsc + tsc_frequency)) > 0) {
8938afc0c3dSSepherosa Ziehau tsc = rdtsc();
8948afc0c3dSSepherosa Ziehau if (++loops > 30) {
8958afc0c3dSSepherosa Ziehau panic("%s: cpu%d apic stalled",
8968afc0c3dSSepherosa Ziehau func, mycpuid);
8978afc0c3dSSepherosa Ziehau } else {
8988afc0c3dSSepherosa Ziehau kprintf("%s: cpu%d apic stalled\n",
8998afc0c3dSSepherosa Ziehau func, mycpuid);
9008afc0c3dSSepherosa Ziehau }
9018afc0c3dSSepherosa Ziehau }
9028afc0c3dSSepherosa Ziehau }
9038afc0c3dSSepherosa Ziehau }
9048afc0c3dSSepherosa Ziehau }
9058afc0c3dSSepherosa Ziehau
9063e8e985fSSepherosa Ziehau /*
9073e8e985fSSepherosa Ziehau * Send APIC IPI 'vector' to 'destType' via 'deliveryMode'.
9083e8e985fSSepherosa Ziehau *
9093e8e985fSSepherosa Ziehau * destType is 1 of: APIC_DEST_SELF, APIC_DEST_ALLISELF, APIC_DEST_ALLESELF
9103e8e985fSSepherosa Ziehau * vector is any valid SYSTEM INT vector
9113e8e985fSSepherosa Ziehau * delivery_mode is 1 of: APIC_DELMODE_FIXED, APIC_DELMODE_LOWPRIO
9123e8e985fSSepherosa Ziehau *
913c17a6852SMatthew Dillon * WARNINGS!
914c17a6852SMatthew Dillon *
915c17a6852SMatthew Dillon * We now implement a per-cpu interlock (gd->gd_npoll) to prevent more than
916c17a6852SMatthew Dillon * one IPI from being sent to any given cpu at a time. Thus we no longer
917c17a6852SMatthew Dillon * have to process incoming IPIs while waiting for the status to clear.
918c17a6852SMatthew Dillon * No deadlock should be possible.
919c17a6852SMatthew Dillon *
920c17a6852SMatthew Dillon * We now physically disable interrupts for the lapic ICR operation. If
921c17a6852SMatthew Dillon * we do not do this then it looks like an EOI sent to the lapic (which
922c17a6852SMatthew Dillon * occurs even with a critical section) can interfere with the command
923c17a6852SMatthew Dillon * register ready status and cause an IPI to be lost.
924c17a6852SMatthew Dillon *
925c17a6852SMatthew Dillon * e.g. an interrupt can occur, issue the EOI, IRET, and cause the command
926c17a6852SMatthew Dillon * register to busy just before we write to icr_lo, resulting in a lost
927c17a6852SMatthew Dillon * issuance. This only appears to occur on Intel cpus and is not
928c17a6852SMatthew Dillon * documented. It could simply be that cpus are so fast these days that
929c17a6852SMatthew Dillon * it was always an issue, but is only now rearing its ugly head. This
930c17a6852SMatthew Dillon * is conjecture.
9313e8e985fSSepherosa Ziehau */
9328afc0c3dSSepherosa Ziehau static int
lapic_mem_ipi(int dest_type,int vector,int delivery_mode)9338afc0c3dSSepherosa Ziehau lapic_mem_ipi(int dest_type, int vector, int delivery_mode)
9343e8e985fSSepherosa Ziehau {
9358afc0c3dSSepherosa Ziehau lapic_mem_icr_unpend(__func__);
9368afc0c3dSSepherosa Ziehau lapic_mem_icr_set(0,
9378afc0c3dSSepherosa Ziehau dest_type | APIC_LEVEL_ASSERT | delivery_mode | vector);
9383e8e985fSSepherosa Ziehau return 0;
9393e8e985fSSepherosa Ziehau }
9403e8e985fSSepherosa Ziehau
941f89b4a45SSepherosa Ziehau static int
lapic_msr_ipi(int dest_type,int vector,int delivery_mode)942f89b4a45SSepherosa Ziehau lapic_msr_ipi(int dest_type, int vector, int delivery_mode)
943f89b4a45SSepherosa Ziehau {
944f89b4a45SSepherosa Ziehau lapic_msr_icr_set(0,
945f89b4a45SSepherosa Ziehau dest_type | APIC_LEVEL_ASSERT | delivery_mode | vector);
946f89b4a45SSepherosa Ziehau return 0;
947f89b4a45SSepherosa Ziehau }
948f89b4a45SSepherosa Ziehau
94967534613SMatthew Dillon /*
95067534613SMatthew Dillon * Interrupts must be hard-disabled by caller
95167534613SMatthew Dillon */
9528afc0c3dSSepherosa Ziehau static void
lapic_mem_single_ipi(int cpu,int vector,int delivery_mode)9538afc0c3dSSepherosa Ziehau lapic_mem_single_ipi(int cpu, int vector, int delivery_mode)
9543e8e985fSSepherosa Ziehau {
9558afc0c3dSSepherosa Ziehau lapic_mem_icr_unpend(__func__);
9568afc0c3dSSepherosa Ziehau lapic_mem_icr_set(CPUID_TO_APICID(cpu),
9578afc0c3dSSepherosa Ziehau APIC_DEST_DESTFLD | APIC_LEVEL_ASSERT | delivery_mode | vector);
95867534613SMatthew Dillon }
9593e8e985fSSepherosa Ziehau
960f89b4a45SSepherosa Ziehau static void
lapic_msr_single_ipi(int cpu,int vector,int delivery_mode)961f89b4a45SSepherosa Ziehau lapic_msr_single_ipi(int cpu, int vector, int delivery_mode)
962f89b4a45SSepherosa Ziehau {
963f89b4a45SSepherosa Ziehau lapic_msr_icr_set(CPUID_TO_APICID(cpu),
964f89b4a45SSepherosa Ziehau APIC_DEST_DESTFLD | APIC_LEVEL_ASSERT | delivery_mode | vector);
965f89b4a45SSepherosa Ziehau }
966f89b4a45SSepherosa Ziehau
9673e8e985fSSepherosa Ziehau /*
9683e8e985fSSepherosa Ziehau * Send APIC IPI 'vector' to 'target's via 'delivery_mode'.
9693e8e985fSSepherosa Ziehau *
9703e8e985fSSepherosa Ziehau * target is a bitmask of destination cpus. Vector is any
9713e8e985fSSepherosa Ziehau * valid system INT vector. Delivery mode may be either
9723e8e985fSSepherosa Ziehau * APIC_DELMODE_FIXED or APIC_DELMODE_LOWPRIO.
97367534613SMatthew Dillon *
97467534613SMatthew Dillon * Interrupts must be hard-disabled by caller
9753e8e985fSSepherosa Ziehau */
9763e8e985fSSepherosa Ziehau void
selected_apic_ipi(cpumask_t target,int vector,int delivery_mode)9773e8e985fSSepherosa Ziehau selected_apic_ipi(cpumask_t target, int vector, int delivery_mode)
9783e8e985fSSepherosa Ziehau {
979c07315c4SMatthew Dillon while (CPUMASK_TESTNZERO(target)) {
9803e8e985fSSepherosa Ziehau int n = BSFCPUMASK(target);
981c07315c4SMatthew Dillon CPUMASK_NANDBIT(target, n);
9823e8e985fSSepherosa Ziehau single_apic_ipi(n, vector, delivery_mode);
9833e8e985fSSepherosa Ziehau }
9843e8e985fSSepherosa Ziehau }
9853e8e985fSSepherosa Ziehau
9863e8e985fSSepherosa Ziehau /*
9873e8e985fSSepherosa Ziehau * Load a 'downcount time' in uSeconds.
9883e8e985fSSepherosa Ziehau */
9893e8e985fSSepherosa Ziehau void
set_apic_timer(int us)9903e8e985fSSepherosa Ziehau set_apic_timer(int us)
9913e8e985fSSepherosa Ziehau {
9923e8e985fSSepherosa Ziehau u_int count;
9933e8e985fSSepherosa Ziehau
994185b27faSImre Vadász if (lapic_use_tscdeadline) {
995185b27faSImre Vadász uint64_t val;
996185b27faSImre Vadász
997185b27faSImre Vadász val = lapic_scale_to_tsc(us, 1000000);
998185b27faSImre Vadász val += rdtsc();
999185b27faSImre Vadász /* No need to arm the lapic here, just track the timeout. */
1000185b27faSImre Vadász tsc_deadlines[mycpuid].downcount_time = val;
1001185b27faSImre Vadász return;
1002185b27faSImre Vadász }
1003185b27faSImre Vadász
10043e8e985fSSepherosa Ziehau /*
10053e8e985fSSepherosa Ziehau * When we reach here, lapic timer's frequency
10063e8e985fSSepherosa Ziehau * must have been calculated as well as the
10073e8e985fSSepherosa Ziehau * divisor (lapic->dcr_timer is setup during the
10083e8e985fSSepherosa Ziehau * divisor calculation).
10093e8e985fSSepherosa Ziehau */
10103e8e985fSSepherosa Ziehau KKASSERT(lapic_cputimer_intr.freq != 0 &&
10113e8e985fSSepherosa Ziehau lapic_timer_divisor_idx >= 0);
10123e8e985fSSepherosa Ziehau
10133e8e985fSSepherosa Ziehau count = ((us * (int64_t)lapic_cputimer_intr.freq) + 999999) / 1000000;
10143e8e985fSSepherosa Ziehau lapic_timer_oneshot(count);
10153e8e985fSSepherosa Ziehau }
10163e8e985fSSepherosa Ziehau
10173e8e985fSSepherosa Ziehau
10183e8e985fSSepherosa Ziehau /*
10191a8f6d13SImre Vadász * Read remaining time in timer, in microseconds (rounded up).
10203e8e985fSSepherosa Ziehau */
10213e8e985fSSepherosa Ziehau int
read_apic_timer(void)10223e8e985fSSepherosa Ziehau read_apic_timer(void)
10233e8e985fSSepherosa Ziehau {
10241a8f6d13SImre Vadász uint64_t val;
10251a8f6d13SImre Vadász
1026185b27faSImre Vadász if (lapic_use_tscdeadline) {
1027185b27faSImre Vadász uint64_t now;
1028185b27faSImre Vadász
1029185b27faSImre Vadász val = tsc_deadlines[mycpuid].downcount_time;
1030185b27faSImre Vadász now = rdtsc();
1031185b27faSImre Vadász if (val == 0 || now > val) {
1032185b27faSImre Vadász return 0;
1033185b27faSImre Vadász } else {
1034185b27faSImre Vadász val -= now;
1035185b27faSImre Vadász val *= 1000000;
1036185b27faSImre Vadász val += (tsc_frequency - 1);
1037185b27faSImre Vadász val /= tsc_frequency;
1038185b27faSImre Vadász if (val > INT_MAX)
1039185b27faSImre Vadász val = INT_MAX;
1040185b27faSImre Vadász return val;
1041185b27faSImre Vadász }
1042185b27faSImre Vadász }
1043185b27faSImre Vadász
10448afc0c3dSSepherosa Ziehau val = LAPIC_READ(ccr_timer);
10451a8f6d13SImre Vadász if (val == 0)
10461a8f6d13SImre Vadász return 0;
1047185b27faSImre Vadász
10481a8f6d13SImre Vadász KKASSERT(lapic_cputimer_intr.freq > 0);
10491a8f6d13SImre Vadász val *= 1000000;
10501a8f6d13SImre Vadász val += (lapic_cputimer_intr.freq - 1);
10511a8f6d13SImre Vadász val /= lapic_cputimer_intr.freq;
1052185b27faSImre Vadász if (val > INT_MAX)
1053185b27faSImre Vadász val = INT_MAX;
10541a8f6d13SImre Vadász return val;
10553e8e985fSSepherosa Ziehau }
10563e8e985fSSepherosa Ziehau
10573e8e985fSSepherosa Ziehau
10583e8e985fSSepherosa Ziehau /*
10593e8e985fSSepherosa Ziehau * Spin-style delay, set delay time in uS, spin till it drains.
10603e8e985fSSepherosa Ziehau */
10613e8e985fSSepherosa Ziehau void
u_sleep(int count)10623e8e985fSSepherosa Ziehau u_sleep(int count)
10633e8e985fSSepherosa Ziehau {
10643e8e985fSSepherosa Ziehau set_apic_timer(count);
10653e8e985fSSepherosa Ziehau while (read_apic_timer())
10663e8e985fSSepherosa Ziehau /* spin */ ;
10673e8e985fSSepherosa Ziehau }
10683e8e985fSSepherosa Ziehau
10693e8e985fSSepherosa Ziehau int
lapic_unused_apic_id(int start)10703e8e985fSSepherosa Ziehau lapic_unused_apic_id(int start)
10713e8e985fSSepherosa Ziehau {
10723e8e985fSSepherosa Ziehau int i;
10733e8e985fSSepherosa Ziehau
1074740024d3SSepherosa Ziehau for (i = start; i < APICID_MAX; ++i) {
1075fbac0dc4SSepherosa Ziehau if (APICID_TO_CPUID(i) == -1)
10763e8e985fSSepherosa Ziehau return i;
10773e8e985fSSepherosa Ziehau }
10783e8e985fSSepherosa Ziehau return NAPICID;
10793e8e985fSSepherosa Ziehau }
10803e8e985fSSepherosa Ziehau
10813e8e985fSSepherosa Ziehau void
lapic_map(vm_paddr_t lapic_addr)108213481c87SSepherosa Ziehau lapic_map(vm_paddr_t lapic_addr)
10833e8e985fSSepherosa Ziehau {
10848afc0c3dSSepherosa Ziehau lapic_mem = pmap_mapdev_uncacheable(lapic_addr, sizeof(struct LAPIC));
10853e8e985fSSepherosa Ziehau }
10863e8e985fSSepherosa Ziehau
1087f89b4a45SSepherosa Ziehau void
lapic_x2apic_enter(boolean_t bsp)1088f89b4a45SSepherosa Ziehau lapic_x2apic_enter(boolean_t bsp)
1089f89b4a45SSepherosa Ziehau {
1090f89b4a45SSepherosa Ziehau uint64_t apic_base;
1091f89b4a45SSepherosa Ziehau
1092f89b4a45SSepherosa Ziehau KASSERT(x2apic_enable, ("X2APIC mode is not enabled"));
1093f89b4a45SSepherosa Ziehau
1094f89b4a45SSepherosa Ziehau /*
1095f89b4a45SSepherosa Ziehau * X2APIC mode is requested, if it has not been enabled by the BIOS,
1096f89b4a45SSepherosa Ziehau * enable it now.
1097f89b4a45SSepherosa Ziehau */
1098f89b4a45SSepherosa Ziehau apic_base = rdmsr(MSR_APICBASE);
1099f89b4a45SSepherosa Ziehau if ((apic_base & APICBASE_X2APIC) == 0) {
1100f89b4a45SSepherosa Ziehau wrmsr(MSR_APICBASE,
1101f89b4a45SSepherosa Ziehau apic_base | APICBASE_X2APIC | APICBASE_ENABLED);
1102f89b4a45SSepherosa Ziehau }
1103f89b4a45SSepherosa Ziehau if (bsp) {
1104f89b4a45SSepherosa Ziehau lapic_eoi = lapic_msr_eoi;
1105f89b4a45SSepherosa Ziehau apic_ipi = lapic_msr_ipi;
1106f89b4a45SSepherosa Ziehau single_apic_ipi = lapic_msr_single_ipi;
1107f89b4a45SSepherosa Ziehau lapic_cputimer_intr.reload = lapic_msr_timer_intr_reload;
1108f89b4a45SSepherosa Ziehau }
1109f89b4a45SSepherosa Ziehau }
1110f89b4a45SSepherosa Ziehau
11113e8e985fSSepherosa Ziehau static TAILQ_HEAD(, lapic_enumerator) lapic_enumerators =
11123e8e985fSSepherosa Ziehau TAILQ_HEAD_INITIALIZER(lapic_enumerators);
11133e8e985fSSepherosa Ziehau
11143566408bSSepherosa Ziehau int
lapic_config(void)11153e8e985fSSepherosa Ziehau lapic_config(void)
11163e8e985fSSepherosa Ziehau {
11173e8e985fSSepherosa Ziehau struct lapic_enumerator *e;
1118f89b4a45SSepherosa Ziehau uint64_t apic_base;
11192e0ed166SSepherosa Ziehau int error, i, ap_max;
11202e0ed166SSepherosa Ziehau
11212e0ed166SSepherosa Ziehau KKASSERT(lapic_enable);
11223e8e985fSSepherosa Ziehau
1123f89b4a45SSepherosa Ziehau lapic_eoi = lapic_mem_eoi;
1124f89b4a45SSepherosa Ziehau apic_ipi = lapic_mem_ipi;
1125f89b4a45SSepherosa Ziehau single_apic_ipi = lapic_mem_single_ipi;
1126f89b4a45SSepherosa Ziehau
1127f89b4a45SSepherosa Ziehau TUNABLE_INT_FETCH("hw.x2apic_enable", &x2apic_enable);
1128f89b4a45SSepherosa Ziehau if (x2apic_enable < 0)
1129f89b4a45SSepherosa Ziehau x2apic_enable = 1;
1130f89b4a45SSepherosa Ziehau if ((cpu_feature2 & CPUID2_X2APIC) == 0) {
1131f89b4a45SSepherosa Ziehau /* X2APIC is not supported. */
1132f89b4a45SSepherosa Ziehau x2apic_enable = 0;
1133add2647eSMatthew Dillon } else {
1134f89b4a45SSepherosa Ziehau /*
1135f89b4a45SSepherosa Ziehau * If the BIOS enabled the X2APIC mode, then we would stick
1136f89b4a45SSepherosa Ziehau * with the X2APIC mode.
1137f89b4a45SSepherosa Ziehau */
1138f89b4a45SSepherosa Ziehau apic_base = rdmsr(MSR_APICBASE);
1139f89b4a45SSepherosa Ziehau if (apic_base & APICBASE_X2APIC) {
1140add2647eSMatthew Dillon if (x2apic_enable == 0)
1141add2647eSMatthew Dillon kprintf("LAPIC: BIOS enabled X2APIC mode, force on\n");
1142add2647eSMatthew Dillon else
1143f89b4a45SSepherosa Ziehau kprintf("LAPIC: BIOS enabled X2APIC mode\n");
1144f89b4a45SSepherosa Ziehau x2apic_enable = 1;
1145f89b4a45SSepherosa Ziehau }
1146f89b4a45SSepherosa Ziehau }
1147add2647eSMatthew Dillon if (cpu_feature2 & CPUID2_X2APIC) {
1148add2647eSMatthew Dillon apic_base = rdmsr(MSR_APICBASE);
1149add2647eSMatthew Dillon if (apic_base & APICBASE_X2APIC)
1150add2647eSMatthew Dillon kprintf("LAPIC: BIOS already enabled X2APIC mode\n");
1151add2647eSMatthew Dillon }
1152f89b4a45SSepherosa Ziehau
1153f89b4a45SSepherosa Ziehau if (x2apic_enable) {
1154f89b4a45SSepherosa Ziehau /*
1155f89b4a45SSepherosa Ziehau * Enter X2APIC mode.
1156f89b4a45SSepherosa Ziehau */
1157f89b4a45SSepherosa Ziehau kprintf("LAPIC: enter X2APIC mode\n");
1158f89b4a45SSepherosa Ziehau lapic_x2apic_enter(TRUE);
1159f89b4a45SSepherosa Ziehau }
1160f89b4a45SSepherosa Ziehau
11613e8e985fSSepherosa Ziehau for (i = 0; i < NAPICID; ++i)
1162fbac0dc4SSepherosa Ziehau APICID_TO_CPUID(i) = -1;
11633e8e985fSSepherosa Ziehau
11643e8e985fSSepherosa Ziehau TAILQ_FOREACH(e, &lapic_enumerators, lapic_link) {
11653e8e985fSSepherosa Ziehau error = e->lapic_probe(e);
11663e8e985fSSepherosa Ziehau if (!error)
11673e8e985fSSepherosa Ziehau break;
11683e8e985fSSepherosa Ziehau }
11693566408bSSepherosa Ziehau if (e == NULL) {
11703566408bSSepherosa Ziehau kprintf("LAPIC: Can't find LAPIC\n");
11713566408bSSepherosa Ziehau return ENXIO;
11723566408bSSepherosa Ziehau }
11733e8e985fSSepherosa Ziehau
1174f8ae0475SSepherosa Ziehau error = e->lapic_enumerate(e);
1175f8ae0475SSepherosa Ziehau if (error) {
1176f8ae0475SSepherosa Ziehau kprintf("LAPIC: enumeration failed\n");
1177f8ae0475SSepherosa Ziehau return ENXIO;
1178f8ae0475SSepherosa Ziehau }
117999ddb995SSepherosa Ziehau
11808afc0c3dSSepherosa Ziehau /* LAPIC is usable now. */
11818afc0c3dSSepherosa Ziehau lapic_usable = 1;
11828afc0c3dSSepherosa Ziehau
118399ddb995SSepherosa Ziehau ap_max = MAXCPU - 1;
118499ddb995SSepherosa Ziehau TUNABLE_INT_FETCH("hw.ap_max", &ap_max);
118599ddb995SSepherosa Ziehau if (ap_max > MAXCPU - 1)
118699ddb995SSepherosa Ziehau ap_max = MAXCPU - 1;
118799ddb995SSepherosa Ziehau
1188637df2f6SSepherosa Ziehau if (naps > ap_max) {
118999ddb995SSepherosa Ziehau kprintf("LAPIC: Warning use only %d out of %d "
119099ddb995SSepherosa Ziehau "available APs\n",
1191637df2f6SSepherosa Ziehau ap_max, naps);
1192637df2f6SSepherosa Ziehau naps = ap_max;
119399ddb995SSepherosa Ziehau }
119499ddb995SSepherosa Ziehau
11953566408bSSepherosa Ziehau return 0;
11963e8e985fSSepherosa Ziehau }
11973e8e985fSSepherosa Ziehau
11983e8e985fSSepherosa Ziehau void
lapic_enumerator_register(struct lapic_enumerator * ne)11993e8e985fSSepherosa Ziehau lapic_enumerator_register(struct lapic_enumerator *ne)
12003e8e985fSSepherosa Ziehau {
12013e8e985fSSepherosa Ziehau struct lapic_enumerator *e;
12023e8e985fSSepherosa Ziehau
12033e8e985fSSepherosa Ziehau TAILQ_FOREACH(e, &lapic_enumerators, lapic_link) {
12043e8e985fSSepherosa Ziehau if (e->lapic_prio < ne->lapic_prio) {
12053e8e985fSSepherosa Ziehau TAILQ_INSERT_BEFORE(e, ne, lapic_link);
12063e8e985fSSepherosa Ziehau return;
12073e8e985fSSepherosa Ziehau }
12083e8e985fSSepherosa Ziehau }
12093e8e985fSSepherosa Ziehau TAILQ_INSERT_TAIL(&lapic_enumerators, ne, lapic_link);
12103e8e985fSSepherosa Ziehau }
12110e44391aSSepherosa Ziehau
12120e44391aSSepherosa Ziehau void
lapic_set_cpuid(int cpu_id,int apic_id)12130e44391aSSepherosa Ziehau lapic_set_cpuid(int cpu_id, int apic_id)
12140e44391aSSepherosa Ziehau {
1215fbac0dc4SSepherosa Ziehau CPUID_TO_APICID(cpu_id) = apic_id;
1216fbac0dc4SSepherosa Ziehau APICID_TO_CPUID(apic_id) = cpu_id;
12170e44391aSSepherosa Ziehau }
1218944562dfSSepherosa Ziehau
1219944562dfSSepherosa Ziehau void
lapic_fixup_noioapic(void)1220944562dfSSepherosa Ziehau lapic_fixup_noioapic(void)
1221944562dfSSepherosa Ziehau {
1222944562dfSSepherosa Ziehau u_int temp;
1223944562dfSSepherosa Ziehau
1224944562dfSSepherosa Ziehau /* Only allowed on BSP */
1225944562dfSSepherosa Ziehau KKASSERT(mycpuid == 0);
1226f45bfca0SSepherosa Ziehau KKASSERT(!ioapic_enable);
1227944562dfSSepherosa Ziehau
12288afc0c3dSSepherosa Ziehau temp = LAPIC_READ(lvt_lint0);
1229944562dfSSepherosa Ziehau temp &= ~APIC_LVT_MASKED;
12308afc0c3dSSepherosa Ziehau LAPIC_WRITE(lvt_lint0, temp);
1231944562dfSSepherosa Ziehau
12328afc0c3dSSepherosa Ziehau temp = LAPIC_READ(lvt_lint1);
1233944562dfSSepherosa Ziehau temp |= APIC_LVT_MASKED;
12348afc0c3dSSepherosa Ziehau LAPIC_WRITE(lvt_lint1, temp);
1235944562dfSSepherosa Ziehau }
12363a69c113SSepherosa Ziehau
12373a69c113SSepherosa Ziehau static void
lapic_mem_eoi(void)12388afc0c3dSSepherosa Ziehau lapic_mem_eoi(void)
12393c38fc60SSepherosa Ziehau {
12408afc0c3dSSepherosa Ziehau log_lapic(mem_eoi);
12418afc0c3dSSepherosa Ziehau LAPIC_MEM_WRITE(eoi, 0);
12428afc0c3dSSepherosa Ziehau }
12438afc0c3dSSepherosa Ziehau
12448afc0c3dSSepherosa Ziehau static void
lapic_msr_eoi(void)1245f89b4a45SSepherosa Ziehau lapic_msr_eoi(void)
1246f89b4a45SSepherosa Ziehau {
1247f89b4a45SSepherosa Ziehau log_lapic(msr_eoi);
1248f89b4a45SSepherosa Ziehau LAPIC_MSR_WRITE(MSR_X2APIC_EOI, 0);
1249f89b4a45SSepherosa Ziehau }
1250f89b4a45SSepherosa Ziehau
1251f89b4a45SSepherosa Ziehau static void
lapic_mem_seticr_sync(uint32_t apic_id,uint32_t icr_lo_val)12528afc0c3dSSepherosa Ziehau lapic_mem_seticr_sync(uint32_t apic_id, uint32_t icr_lo_val)
12538afc0c3dSSepherosa Ziehau {
12548afc0c3dSSepherosa Ziehau lapic_mem_icr_set(apic_id, icr_lo_val);
12558afc0c3dSSepherosa Ziehau while (LAPIC_MEM_READ(icr_lo) & APIC_DELSTAT_PEND)
12568afc0c3dSSepherosa Ziehau /* spin */;
12578afc0c3dSSepherosa Ziehau }
12588afc0c3dSSepherosa Ziehau
12598afc0c3dSSepherosa Ziehau void
lapic_seticr_sync(uint32_t apic_id,uint32_t icr_lo_val)12608afc0c3dSSepherosa Ziehau lapic_seticr_sync(uint32_t apic_id, uint32_t icr_lo_val)
12618afc0c3dSSepherosa Ziehau {
1262f89b4a45SSepherosa Ziehau if (x2apic_enable)
1263f89b4a45SSepherosa Ziehau lapic_msr_icr_set(apic_id, icr_lo_val);
1264f89b4a45SSepherosa Ziehau else
12658afc0c3dSSepherosa Ziehau lapic_mem_seticr_sync(apic_id, icr_lo_val);
12663c38fc60SSepherosa Ziehau }
12673c38fc60SSepherosa Ziehau
12683c38fc60SSepherosa Ziehau static void
lapic_sysinit(void * dummy __unused)12693a69c113SSepherosa Ziehau lapic_sysinit(void *dummy __unused)
12703a69c113SSepherosa Ziehau {
12713a69c113SSepherosa Ziehau if (lapic_enable) {
12723a69c113SSepherosa Ziehau int error;
12733a69c113SSepherosa Ziehau
12743a69c113SSepherosa Ziehau error = lapic_config();
12753a69c113SSepherosa Ziehau if (error)
12763a69c113SSepherosa Ziehau lapic_enable = 0;
12773a69c113SSepherosa Ziehau }
1278f89b4a45SSepherosa Ziehau if (!lapic_enable)
1279f89b4a45SSepherosa Ziehau x2apic_enable = 0;
12803a69c113SSepherosa Ziehau
12813a69c113SSepherosa Ziehau if (lapic_enable) {
12823a69c113SSepherosa Ziehau /* Initialize BSP's local APIC */
12833a69c113SSepherosa Ziehau lapic_init(TRUE);
12843a69c113SSepherosa Ziehau } else if (ioapic_enable) {
12857ed7e1a5SMatthew Dillon kprintf("IOAPIC disabled - lapic was not enabled\n");
12863a69c113SSepherosa Ziehau ioapic_enable = 0;
12873a69c113SSepherosa Ziehau icu_reinit_noioapic();
12883a69c113SSepherosa Ziehau }
12893a69c113SSepherosa Ziehau }
1290f3f3eadbSSascha Wildner SYSINIT(lapic, SI_BOOT2_LAPIC, SI_ORDER_FIRST, lapic_sysinit, NULL);
1291