xref: /openbsd/sys/arch/amd64/amd64/lapic.c (revision daa3eda4)
1 /*	$OpenBSD: lapic.c,v 1.74 2024/11/07 17:24:42 bluhm Exp $	*/
2 /* $NetBSD: lapic.c,v 1.2 2003/05/08 01:04:35 fvdl Exp $ */
3 
4 /*-
5  * Copyright (c) 2000 The NetBSD Foundation, Inc.
6  * All rights reserved.
7  *
8  * This code is derived from software contributed to The NetBSD Foundation
9  * by RedBack Networks Inc.
10  *
11  * Author: Bill Sommerfeld
12  *
13  * Redistribution and use in source and binary forms, with or without
14  * modification, are permitted provided that the following conditions
15  * are met:
16  * 1. Redistributions of source code must retain the above copyright
17  *    notice, this list of conditions and the following disclaimer.
18  * 2. Redistributions in binary form must reproduce the above copyright
19  *    notice, this list of conditions and the following disclaimer in the
20  *    documentation and/or other materials provided with the distribution.
21  *
22  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
23  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
24  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
25  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
26  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
27  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
28  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
29  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
30  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
31  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32  * POSSIBILITY OF SUCH DAMAGE.
33  */
34 
35 #include <sys/param.h>
36 #include <sys/systm.h>
37 #include <sys/atomic.h>
38 #include <sys/clockintr.h>
39 #include <sys/device.h>
40 
41 #include <uvm/uvm_extern.h>
42 
43 #include <machine/codepatch.h>
44 #include <machine/cpu.h>
45 #include <machine/cpufunc.h>
46 #include <machine/pmap.h>
47 #include <machine/mpbiosvar.h>
48 #include <machine/specialreg.h>
49 #include <machine/segments.h>
50 
51 #include <machine/i82489reg.h>
52 #include <machine/i82489var.h>
53 
54 #include <dev/ic/i8253reg.h>
55 
56 #include "ioapic.h"
57 #include "xen.h"
58 #include "hyperv.h"
59 #include "vmm.h"
60 
61 #if NIOAPIC > 0
62 #include <machine/i82093var.h>
63 #endif
64 
65 /* #define LAPIC_DEBUG */
66 
67 #ifdef LAPIC_DEBUG
68 #define DPRINTF(x...)	do { printf(x); } while(0)
69 #else
70 #define DPRINTF(x...)
71 #endif /* LAPIC_DEBUG */
72 
73 struct evcount clk_count;
74 #ifdef MULTIPROCESSOR
75 struct evcount ipi_count;
76 #endif
77 
78 static u_int32_t lapic_gettick(void);
79 void	lapic_clockintr(void *, struct intrframe);
80 void	lapic_initclocks(void);
81 void	lapic_map(paddr_t);
82 
83 void lapic_hwmask(struct pic *, int);
84 void lapic_hwunmask(struct pic *, int);
85 void lapic_setup(struct pic *, struct cpu_info *, int, int, int);
86 
87 extern char idt_allocmap[];
88 
89 struct pic local_pic = {
90 	{0, {NULL}, NULL, 0, "lapic", NULL, 0, 0},
91 	PIC_LAPIC,
92 #ifdef MULTIPROCESSOR
93 	{},
94 #endif
95 	lapic_hwmask,
96 	lapic_hwunmask,
97 	lapic_setup,
98 	lapic_setup,
99 };
100 
101 extern int x2apic_eoi;
102 int x2apic_enabled = 0;
103 
104 u_int32_t x2apic_readreg(int reg);
105 u_int32_t x2apic_cpu_number(void);
106 void x2apic_writereg(int reg, u_int32_t val);
107 void x2apic_ipi(int vec, int target, int dl);
108 
109 u_int32_t i82489_readreg(int reg);
110 u_int32_t i82489_cpu_number(void);
111 void i82489_writereg(int reg, u_int32_t val);
112 void i82489_ipi(int vec, int target, int dl);
113 
114 u_int32_t (*lapic_readreg)(int)			= i82489_readreg;
115 void (*lapic_writereg)(int, u_int32_t)		= i82489_writereg;
116 #ifdef MULTIPROCESSOR
117 void (*x86_ipi)(int vec, int target, int dl)	= i82489_ipi;
118 #endif
119 
120 u_int32_t
i82489_readreg(int reg)121 i82489_readreg(int reg)
122 {
123 	return *((volatile u_int32_t *)(((volatile u_int8_t *)local_apic)
124 	    + reg));
125 }
126 
127 u_int32_t
i82489_cpu_number(void)128 i82489_cpu_number(void)
129 {
130 	return i82489_readreg(LAPIC_ID) >> LAPIC_ID_SHIFT;
131 }
132 
133 void
i82489_writereg(int reg,u_int32_t val)134 i82489_writereg(int reg, u_int32_t val)
135 {
136 	*((volatile u_int32_t *)(((volatile u_int8_t *)local_apic) + reg)) =
137 	    val;
138 }
139 
140 u_int32_t
x2apic_readreg(int reg)141 x2apic_readreg(int reg)
142 {
143 	return rdmsr(MSR_X2APIC_BASE + (reg >> 4));
144 }
145 
146 u_int32_t
x2apic_cpu_number(void)147 x2apic_cpu_number(void)
148 {
149 	return x2apic_readreg(LAPIC_ID) & X2APIC_ID_MASK;
150 }
151 
152 void
x2apic_writereg(int reg,u_int32_t val)153 x2apic_writereg(int reg, u_int32_t val)
154 {
155 	wrmsr(MSR_X2APIC_BASE + (reg >> 4), val);
156 }
157 
158 #ifdef MULTIPROCESSOR
159 static inline void
x2apic_writeicr(u_int32_t hi,u_int32_t lo)160 x2apic_writeicr(u_int32_t hi, u_int32_t lo)
161 {
162 	u_int32_t msr = MSR_X2APIC_BASE + (LAPIC_ICRLO >> 4);
163 	__asm volatile("wrmsr" : : "a" (lo), "d" (hi), "c" (msr));
164 }
165 #endif
166 
167 u_int32_t
lapic_cpu_number(void)168 lapic_cpu_number(void)
169 {
170 	if (x2apic_enabled)
171 		return x2apic_cpu_number();
172 	return i82489_cpu_number();
173 }
174 
175 void
lapic_map(paddr_t lapic_base)176 lapic_map(paddr_t lapic_base)
177 {
178 	pt_entry_t *pte;
179 	vaddr_t va;
180 	u_int64_t msr;
181 	u_long s;
182 	int tpr;
183 
184 	s = intr_disable();
185 	tpr = lapic_tpr;
186 
187 	msr = rdmsr(MSR_APICBASE);
188 
189 	if (ISSET(msr, APICBASE_ENABLE_X2APIC) ||
190 	    (ISSET(cpu_ecxfeature, CPUIDECX_HV) &&
191 	    ISSET(cpu_ecxfeature, CPUIDECX_X2APIC))) {
192 		 /*
193 		  * On real hardware, x2apic must only be enabled if interrupt
194 		  * remapping is also enabled. See 10.12.7 of the SDM vol 3.
195 		  * On hypervisors, this is not necessary. Hypervisors can
196 		  * implement x2apic support even if the host CPU does not
197 		  * support it.  Until we support interrupt remapping, use
198 		  * x2apic only if the hypervisor flag is also set or it is
199 		  * enabled by BIOS.
200 		  */
201 		if (!ISSET(msr, APICBASE_ENABLE_X2APIC)) {
202 			msr |= APICBASE_ENABLE_X2APIC;
203 			wrmsr(MSR_APICBASE, msr);
204 		}
205 		lapic_readreg = x2apic_readreg;
206 		lapic_writereg = x2apic_writereg;
207 #ifdef MULTIPROCESSOR
208 		x86_ipi = x2apic_ipi;
209 #endif
210 		x2apic_enabled = 1;
211 		codepatch_call(CPTAG_EOI, &x2apic_eoi);
212 
213 		lapic_writereg(LAPIC_TPRI, tpr);
214 		va = (vaddr_t)&local_apic;
215 	} else {
216 		/*
217 		 * Map local apic.
218 		 *
219 		 * Whap the PTE "by hand" rather than calling pmap_kenter_pa
220 		 * because the latter will attempt to invoke TLB shootdown
221 		 * code just as we might have changed the value of
222 		 * cpu_number()..
223 		 */
224 		va = (vaddr_t)&local_apic;
225 		pte = kvtopte(va);
226 		*pte = lapic_base | PG_RW | PG_V | PG_N | PG_G | pg_nx;
227 		invlpg(va);
228 
229 		lapic_tpr = tpr;
230 	}
231 
232 	/*
233 	 * Enter the LAPIC MMIO page in the U-K page table for handling
234 	 * Meltdown (needed in the interrupt stub to acknowledge the
235 	 * incoming interrupt). On CPUs unaffected by Meltdown,
236 	 * pmap_enter_special is a no-op.
237 	 */
238 	pmap_enter_special(va, lapic_base, PROT_READ | PROT_WRITE);
239 	DPRINTF("%s: entered lapic page va 0x%llx pa 0x%llx\n", __func__,
240 	    (uint64_t)va, (uint64_t)lapic_base);
241 
242 	intr_restore(s);
243 }
244 
245 /*
246  * enable local apic
247  */
248 void
lapic_enable(void)249 lapic_enable(void)
250 {
251 	lapic_writereg(LAPIC_SVR, LAPIC_SVR_ENABLE | LAPIC_SPURIOUS_VECTOR);
252 }
253 
254 void
lapic_disable(void)255 lapic_disable(void)
256 {
257 	lapic_writereg(LAPIC_SVR, 0);
258 }
259 
260 void
lapic_set_lvt(void)261 lapic_set_lvt(void)
262 {
263 	struct cpu_info *ci = curcpu();
264 	int i;
265 	struct mp_intr_map *mpi;
266 	uint32_t lint0;
267 
268 #ifdef MULTIPROCESSOR
269 	if (mp_verbose) {
270 		apic_format_redir(ci->ci_dev->dv_xname, "prelint", 0, 0,
271 		    lapic_readreg(LAPIC_LVINT0));
272 		apic_format_redir(ci->ci_dev->dv_xname, "prelint", 1, 0,
273 		    lapic_readreg(LAPIC_LVINT1));
274 	}
275 #endif
276 
277 #if NIOAPIC > 0
278 	/*
279 	 * Disable ExtINT by default when using I/O APICs.
280 	 */
281 	if (nioapics > 0) {
282 		lint0 = lapic_readreg(LAPIC_LVINT0);
283 		lint0 |= LAPIC_LVT_MASKED;
284 		lapic_writereg(LAPIC_LVINT0, lint0);
285 	}
286 #endif
287 
288 	if (ci->ci_vendor == CPUV_AMD) {
289 		/*
290 		 * Detect the presence of C1E capability mostly on latest
291 		 * dual-cores (or future) k8 family. This mis-feature renders
292 		 * the local APIC timer dead, so we disable it by reading
293 		 * the Interrupt Pending Message register and clearing both
294 		 * C1eOnCmpHalt (bit 28) and SmiOnCmpHalt (bit 27).
295 		 *
296 		 * Reference:
297 		 *   "BIOS and Kernel Developer's Guide for AMD NPT
298 		 *    Family 0Fh Processors"
299 		 *   #32559 revision 3.00
300 		 */
301 		if (ci->ci_family == 0xf || ci->ci_family == 0x10) {
302 			uint64_t msr;
303 
304 			msr = rdmsr(MSR_INT_PEN_MSG);
305 			if (msr & (IPM_C1E_CMP_HLT|IPM_SMI_CMP_HLT)) {
306 				msr &= ~(IPM_C1E_CMP_HLT|IPM_SMI_CMP_HLT);
307 				wrmsr(MSR_INT_PEN_MSG, msr);
308 			}
309 		}
310 	}
311 
312 	for (i = 0; i < mp_nintrs; i++) {
313 		mpi = &mp_intrs[i];
314 		if (mpi->ioapic == NULL && (mpi->cpu_id == MPS_ALL_APICS
315 					    || mpi->cpu_id == ci->ci_apicid)) {
316 #ifdef DIAGNOSTIC
317 			if (mpi->ioapic_pin > 1)
318 				panic("lapic_set_lvt: bad pin value %d",
319 				    mpi->ioapic_pin);
320 #endif
321 			if (mpi->ioapic_pin == 0)
322 				lapic_writereg(LAPIC_LVINT0, mpi->redir);
323 			else
324 				lapic_writereg(LAPIC_LVINT1, mpi->redir);
325 		}
326 	}
327 
328 #ifdef MULTIPROCESSOR
329 	if (mp_verbose) {
330 		apic_format_redir(ci->ci_dev->dv_xname, "timer", 0, 0,
331 		    lapic_readreg(LAPIC_LVTT));
332 		apic_format_redir(ci->ci_dev->dv_xname, "pcint", 0, 0,
333 		    lapic_readreg(LAPIC_PCINT));
334 		apic_format_redir(ci->ci_dev->dv_xname, "lint", 0, 0,
335 		    lapic_readreg(LAPIC_LVINT0));
336 		apic_format_redir(ci->ci_dev->dv_xname, "lint", 1, 0,
337 		    lapic_readreg(LAPIC_LVINT1));
338 		apic_format_redir(ci->ci_dev->dv_xname, "err", 0, 0,
339 		    lapic_readreg(LAPIC_LVERR));
340 	}
341 #endif
342 }
343 
344 /*
345  * Initialize fixed idt vectors for use by local apic.
346  */
347 void
lapic_boot_init(paddr_t lapic_base)348 lapic_boot_init(paddr_t lapic_base)
349 {
350 	static u_int64_t clk_irq = 0;
351 #ifdef MULTIPROCESSOR
352 	static u_int64_t ipi_irq = 0;
353 #endif
354 
355 	lapic_map(lapic_base);
356 
357 #ifdef MULTIPROCESSOR
358 	idt_allocmap[LAPIC_IPI_VECTOR] = 1;
359 	idt_vec_set(LAPIC_IPI_VECTOR, Xintr_lapic_ipi);
360 	idt_allocmap[LAPIC_IPI_INVLTLB] = 1;
361 	idt_allocmap[LAPIC_IPI_INVLPG] = 1;
362 	idt_allocmap[LAPIC_IPI_INVLRANGE] = 1;
363 	if (!pmap_use_pcid) {
364 		idt_vec_set(LAPIC_IPI_INVLTLB, Xipi_invltlb);
365 		idt_vec_set(LAPIC_IPI_INVLPG, Xipi_invlpg);
366 		idt_vec_set(LAPIC_IPI_INVLRANGE, Xipi_invlrange);
367 	} else {
368 		idt_vec_set(LAPIC_IPI_INVLTLB, Xipi_invltlb_pcid);
369 		idt_vec_set(LAPIC_IPI_INVLPG, Xipi_invlpg_pcid);
370 		idt_vec_set(LAPIC_IPI_INVLRANGE, Xipi_invlrange_pcid);
371 	}
372 	idt_allocmap[LAPIC_IPI_WBINVD] = 1;
373 	idt_vec_set(LAPIC_IPI_WBINVD, Xipi_wbinvd);
374 #if NVMM > 0
375 	idt_allocmap[LAPIC_IPI_INVEPT] = 1;
376 	idt_vec_set(LAPIC_IPI_INVEPT, Xipi_invept);
377 #endif /* NVMM > 0 */
378 #endif /* MULTIPROCESSOR */
379 	idt_allocmap[LAPIC_SPURIOUS_VECTOR] = 1;
380 	idt_vec_set(LAPIC_SPURIOUS_VECTOR, Xintrspurious);
381 
382 	idt_allocmap[LAPIC_TIMER_VECTOR] = 1;
383 	idt_vec_set(LAPIC_TIMER_VECTOR, Xintr_lapic_ltimer);
384 
385 #if NXEN > 0
386 	/* Xen HVM Event Channel Interrupt Vector */
387 	idt_allocmap[LAPIC_XEN_VECTOR] = 1;
388 	idt_vec_set(LAPIC_XEN_VECTOR, Xintr_xen_upcall);
389 #endif
390 #if NHYPERV > 0
391 	/* Hyper-V Interrupt Vector */
392 	idt_allocmap[LAPIC_HYPERV_VECTOR] = 1;
393 	idt_vec_set(LAPIC_HYPERV_VECTOR, Xintr_hyperv_upcall);
394 #endif
395 
396 	evcount_attach(&clk_count, "clock", &clk_irq);
397 	evcount_percpu(&clk_count);
398 #ifdef MULTIPROCESSOR
399 	evcount_attach(&ipi_count, "ipi", &ipi_irq);
400 	evcount_percpu(&ipi_count);
401 #endif
402 }
403 
404 static __inline u_int32_t
lapic_gettick(void)405 lapic_gettick(void)
406 {
407 	return lapic_readreg(LAPIC_CCR_TIMER);
408 }
409 
410 #include <sys/kernel.h>		/* for hz */
411 
412 /*
413  * this gets us up to a 4GHz busclock....
414  */
415 u_int32_t lapic_per_second = 0;
416 uint64_t lapic_timer_nsec_cycle_ratio;
417 uint64_t lapic_timer_nsec_max;
418 
419 void lapic_timer_rearm(void *, uint64_t);
420 void lapic_timer_trigger(void *);
421 
422 const struct intrclock lapic_timer_intrclock = {
423 	.ic_rearm = lapic_timer_rearm,
424 	.ic_trigger = lapic_timer_trigger
425 };
426 
427 void lapic_timer_oneshot(uint32_t, uint32_t);
428 void lapic_timer_periodic(uint32_t, uint32_t);
429 
430 void
lapic_timer_rearm(void * unused,uint64_t nsecs)431 lapic_timer_rearm(void *unused, uint64_t nsecs)
432 {
433 	uint32_t cycles;
434 
435 	if (nsecs > lapic_timer_nsec_max)
436 		nsecs = lapic_timer_nsec_max;
437 	cycles = (nsecs * lapic_timer_nsec_cycle_ratio) >> 32;
438 	if (cycles == 0)
439 		cycles = 1;
440 	lapic_writereg(LAPIC_ICR_TIMER, cycles);
441 }
442 
443 void
lapic_timer_trigger(void * unused)444 lapic_timer_trigger(void *unused)
445 {
446 	u_long s;
447 
448 	s = intr_disable();
449 	lapic_timer_oneshot(0, 1);
450 	intr_restore(s);
451 }
452 
453 /*
454  * Start the local apic countdown timer.
455  *
456  * First set the mode, mask, and vector.  Then set the
457  * divisor.  Last, set the cycle count: this restarts
458  * the countdown.
459  */
460 static inline void
lapic_timer_start(uint32_t mode,uint32_t mask,uint32_t cycles)461 lapic_timer_start(uint32_t mode, uint32_t mask, uint32_t cycles)
462 {
463 	lapic_writereg(LAPIC_LVTT, mode | mask | LAPIC_TIMER_VECTOR);
464 	lapic_writereg(LAPIC_DCR_TIMER, LAPIC_DCRT_DIV1);
465 	lapic_writereg(LAPIC_ICR_TIMER, cycles);
466 }
467 
468 void
lapic_timer_oneshot(uint32_t mask,uint32_t cycles)469 lapic_timer_oneshot(uint32_t mask, uint32_t cycles)
470 {
471 	lapic_timer_start(LAPIC_LVTT_TM_ONESHOT, mask, cycles);
472 }
473 
474 void
lapic_timer_periodic(uint32_t mask,uint32_t cycles)475 lapic_timer_periodic(uint32_t mask, uint32_t cycles)
476 {
477 	lapic_timer_start(LAPIC_LVTT_TM_PERIODIC, mask, cycles);
478 }
479 
480 void
lapic_clockintr(void * arg,struct intrframe frame)481 lapic_clockintr(void *arg, struct intrframe frame)
482 {
483 	struct cpu_info *ci = curcpu();
484 	int floor;
485 
486 	floor = ci->ci_handled_intr_level;
487 	ci->ci_handled_intr_level = ci->ci_ilevel;
488 	clockintr_dispatch(&frame);
489 	ci->ci_handled_intr_level = floor;
490 
491 	evcount_inc(&clk_count);
492 }
493 
494 void
lapic_startclock(void)495 lapic_startclock(void)
496 {
497 	clockintr_cpu_init(&lapic_timer_intrclock);
498 	clockintr_trigger();
499 }
500 
501 void
lapic_initclocks(void)502 lapic_initclocks(void)
503 {
504 	i8254_inittimecounter_simple();
505 
506 	stathz = hz;
507 	profhz = stathz * 10;
508 	statclock_is_randomized = 1;
509 }
510 
511 
512 extern int gettick(void);	/* XXX put in header file */
513 extern u_long rtclock_tval; /* XXX put in header file */
514 
515 static __inline void
wait_next_cycle(void)516 wait_next_cycle(void)
517 {
518 	unsigned int tick, tlast;
519 
520 	tlast = (1 << 16);	/* i8254 counter has 16 bits at most */
521 	for (;;) {
522 		tick = gettick();
523 		if (tick > tlast)
524 			return;
525 		tlast = tick;
526 	}
527 }
528 
529 /*
530  * Calibrate the local apic count-down timer (which is running at
531  * bus-clock speed) vs. the i8254 counter/timer (which is running at
532  * a fixed rate).
533  *
534  * The Intel MP spec says: "An MP operating system may use the IRQ8
535  * real-time clock as a reference to determine the actual APIC timer clock
536  * speed."
537  *
538  * We're actually using the IRQ0 timer.  Hmm.
539  */
540 void
lapic_calibrate_timer(struct cpu_info * ci)541 lapic_calibrate_timer(struct cpu_info *ci)
542 {
543 	unsigned int startapic, endapic;
544 	u_int64_t dtick, dapic, tmp;
545 	u_long s;
546 	int i;
547 
548 	if (lapic_per_second)
549 		goto skip_calibration;
550 
551 	if (mp_verbose)
552 		printf("%s: calibrating local timer\n", ci->ci_dev->dv_xname);
553 
554 	/*
555 	 * Configure timer to one-shot, interrupt masked,
556 	 * large positive number.
557 	 */
558 	lapic_timer_oneshot(LAPIC_LVTT_M, 0x80000000);
559 
560 	if (delay_func == i8254_delay) {
561 		s = intr_disable();
562 
563 		/* wait for current cycle to finish */
564 		wait_next_cycle();
565 
566 		startapic = lapic_gettick();
567 
568 		/* wait the next hz cycles */
569 		for (i = 0; i < hz; i++)
570 			wait_next_cycle();
571 
572 		endapic = lapic_gettick();
573 
574 		intr_restore(s);
575 
576 		dtick = hz * rtclock_tval;
577 		dapic = startapic-endapic;
578 
579 		/*
580 		 * there are TIMER_FREQ ticks per second.
581 		 * in dtick ticks, there are dapic bus clocks.
582 		 */
583 		tmp = (TIMER_FREQ * dapic) / dtick;
584 
585 		lapic_per_second = tmp;
586 	} else {
587 		s = intr_disable();
588 		startapic = lapic_gettick();
589 		delay(1 * 1000 * 1000);
590 		endapic = lapic_gettick();
591 		intr_restore(s);
592 		lapic_per_second = startapic - endapic;
593 	}
594 
595 skip_calibration:
596 	printf("%s: apic clock running at %dMHz\n",
597 	    ci->ci_dev->dv_xname, lapic_per_second / (1000 * 1000));
598 
599 	/* XXX What do we do if this is zero? */
600 	if (lapic_per_second == 0)
601 		return;
602 
603 	lapic_timer_nsec_cycle_ratio =
604 	    lapic_per_second * (1ULL << 32) / 1000000000;
605 	lapic_timer_nsec_max = UINT64_MAX / lapic_timer_nsec_cycle_ratio;
606 	initclock_func = lapic_initclocks;
607 	startclock_func = lapic_startclock;
608 }
609 
610 /*
611  * XXX the following belong mostly or partly elsewhere..
612  */
613 
614 #ifdef MULTIPROCESSOR
615 static __inline void i82489_icr_wait(void);
616 
617 static __inline void
i82489_icr_wait(void)618 i82489_icr_wait(void)
619 {
620 #ifdef DIAGNOSTIC
621 	unsigned j = 100000;
622 #endif /* DIAGNOSTIC */
623 
624 	while ((i82489_readreg(LAPIC_ICRLO) & LAPIC_DLSTAT_BUSY) != 0) {
625 		__asm volatile("pause": : :"memory");
626 #ifdef DIAGNOSTIC
627 		j--;
628 		if (j == 0)
629 			panic("i82489_icr_wait: busy");
630 #endif /* DIAGNOSTIC */
631 	}
632 }
633 
634 void
i82489_ipi_init(int target)635 i82489_ipi_init(int target)
636 {
637 
638 	if ((target & LAPIC_DEST_MASK) == 0)
639 		i82489_writereg(LAPIC_ICRHI, target << LAPIC_ID_SHIFT);
640 
641 	i82489_writereg(LAPIC_ICRLO, (target & LAPIC_DEST_MASK) |
642 	    LAPIC_DLMODE_INIT | LAPIC_LVL_ASSERT );
643 
644 	i82489_icr_wait();
645 
646 	delay(10000);
647 
648 	i82489_writereg(LAPIC_ICRLO, (target & LAPIC_DEST_MASK) |
649 	     LAPIC_DLMODE_INIT | LAPIC_LVL_TRIG | LAPIC_LVL_DEASSERT);
650 
651 	i82489_icr_wait();
652 }
653 
654 void
i82489_ipi(int vec,int target,int dl)655 i82489_ipi(int vec, int target, int dl)
656 {
657 	int s;
658 
659 	s = splhigh();
660 
661 	i82489_icr_wait();
662 
663 	if ((target & LAPIC_DEST_MASK) == 0)
664 		i82489_writereg(LAPIC_ICRHI, target << LAPIC_ID_SHIFT);
665 
666 	i82489_writereg(LAPIC_ICRLO,
667 	    (target & LAPIC_DEST_MASK) | vec | dl | LAPIC_LVL_ASSERT);
668 
669 	i82489_icr_wait();
670 
671 	splx(s);
672 }
673 
674 void
x2apic_ipi_init(int target)675 x2apic_ipi_init(int target)
676 {
677 	u_int64_t hi = 0;
678 
679 	if ((target & LAPIC_DEST_MASK) == 0)
680 		hi = target & 0xff;
681 
682 	x2apic_writeicr(hi, (target & LAPIC_DEST_MASK) | LAPIC_DLMODE_INIT |
683 	    LAPIC_LVL_ASSERT );
684 
685 	delay(10000);
686 
687 	x2apic_writeicr(0, (target & LAPIC_DEST_MASK) | LAPIC_DLMODE_INIT |
688 	    LAPIC_LVL_TRIG | LAPIC_LVL_DEASSERT);
689 }
690 
691 void
x2apic_ipi(int vec,int target,int dl)692 x2apic_ipi(int vec, int target, int dl)
693 {
694 	u_int64_t hi = 0, lo;
695 
696 	if ((target & LAPIC_DEST_MASK) == 0)
697 		hi = target & 0xff;
698 
699 	lo = (target & LAPIC_DEST_MASK) | vec | dl | LAPIC_LVL_ASSERT;
700 
701 	x2apic_writeicr(hi, lo);
702 }
703 
704 void
x86_ipi_init(int target)705 x86_ipi_init(int target)
706 {
707 	if (x2apic_enabled)
708 		x2apic_ipi_init(target);
709 	else
710 		i82489_ipi_init(target);
711 }
712 #endif /* MULTIPROCESSOR */
713 
714 
715 /*
716  * Using 'pin numbers' as:
717  * 0 - timer
718  * 1 - unused
719  * 2 - PCINT
720  * 3 - LVINT0
721  * 4 - LVINT1
722  * 5 - LVERR
723  */
724 
725 void
lapic_hwmask(struct pic * pic,int pin)726 lapic_hwmask(struct pic *pic, int pin)
727 {
728 	int reg;
729 	u_int32_t val;
730 
731 	reg = LAPIC_LVTT + (pin << 4);
732 	val = lapic_readreg(reg);
733 	val |= LAPIC_LVT_MASKED;
734 	lapic_writereg(reg, val);
735 }
736 
737 void
lapic_hwunmask(struct pic * pic,int pin)738 lapic_hwunmask(struct pic *pic, int pin)
739 {
740 	int reg;
741 	u_int32_t val;
742 
743 	reg = LAPIC_LVTT + (pin << 4);
744 	val = lapic_readreg(reg);
745 	val &= ~LAPIC_LVT_MASKED;
746 	lapic_writereg(reg, val);
747 }
748 
749 void
lapic_setup(struct pic * pic,struct cpu_info * ci,int pin,int idtvec,int type)750 lapic_setup(struct pic *pic, struct cpu_info *ci, int pin, int idtvec, int type)
751 {
752 }
753