xref: /freebsd/sys/amd64/vmm/io/vlapic.c (revision 77d8fd9bb3f38693e1cda1bec687b4eb3f849190)
1366f6083SPeter Grehan /*-
2366f6083SPeter Grehan  * Copyright (c) 2011 NetApp, Inc.
3366f6083SPeter Grehan  * All rights reserved.
4366f6083SPeter Grehan  *
5366f6083SPeter Grehan  * Redistribution and use in source and binary forms, with or without
6366f6083SPeter Grehan  * modification, are permitted provided that the following conditions
7366f6083SPeter Grehan  * are met:
8366f6083SPeter Grehan  * 1. Redistributions of source code must retain the above copyright
9366f6083SPeter Grehan  *    notice, this list of conditions and the following disclaimer.
10366f6083SPeter Grehan  * 2. Redistributions in binary form must reproduce the above copyright
11366f6083SPeter Grehan  *    notice, this list of conditions and the following disclaimer in the
12366f6083SPeter Grehan  *    documentation and/or other materials provided with the distribution.
13366f6083SPeter Grehan  *
14366f6083SPeter Grehan  * THIS SOFTWARE IS PROVIDED BY NETAPP, INC ``AS IS'' AND
15366f6083SPeter Grehan  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16366f6083SPeter Grehan  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17366f6083SPeter Grehan  * ARE DISCLAIMED.  IN NO EVENT SHALL NETAPP, INC OR CONTRIBUTORS BE LIABLE
18366f6083SPeter Grehan  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19366f6083SPeter Grehan  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20366f6083SPeter Grehan  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21366f6083SPeter Grehan  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22366f6083SPeter Grehan  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23366f6083SPeter Grehan  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24366f6083SPeter Grehan  * SUCH DAMAGE.
25366f6083SPeter Grehan  *
26366f6083SPeter Grehan  * $FreeBSD$
27366f6083SPeter Grehan  */
28366f6083SPeter Grehan 
29366f6083SPeter Grehan #include <sys/cdefs.h>
30366f6083SPeter Grehan __FBSDID("$FreeBSD$");
31366f6083SPeter Grehan 
32366f6083SPeter Grehan #include <sys/param.h>
33366f6083SPeter Grehan #include <sys/kernel.h>
34366f6083SPeter Grehan #include <sys/malloc.h>
35366f6083SPeter Grehan #include <sys/systm.h>
36a5615c90SPeter Grehan #include <sys/smp.h>
37366f6083SPeter Grehan 
38366f6083SPeter Grehan #include <machine/clock.h>
392d3a73edSNeel Natu #include <x86/specialreg.h>
4034a6b2d6SJohn Baldwin #include <x86/apicreg.h>
41366f6083SPeter Grehan 
42366f6083SPeter Grehan #include <machine/vmm.h>
43366f6083SPeter Grehan 
44*77d8fd9bSNeel Natu #include "vmm_stat.h"
45366f6083SPeter Grehan #include "vmm_lapic.h"
46366f6083SPeter Grehan #include "vmm_ktr.h"
47366f6083SPeter Grehan #include "vdev.h"
48366f6083SPeter Grehan #include "vlapic.h"
49366f6083SPeter Grehan 
50366f6083SPeter Grehan #define	VLAPIC_CTR0(vlapic, format)					\
51366f6083SPeter Grehan 	VMM_CTR0((vlapic)->vm, (vlapic)->vcpuid, format)
52366f6083SPeter Grehan 
53366f6083SPeter Grehan #define	VLAPIC_CTR1(vlapic, format, p1)					\
54366f6083SPeter Grehan 	VMM_CTR1((vlapic)->vm, (vlapic)->vcpuid, format, p1)
55366f6083SPeter Grehan 
56366f6083SPeter Grehan #define	VLAPIC_CTR_IRR(vlapic, msg)					\
57366f6083SPeter Grehan do {									\
58366f6083SPeter Grehan 	uint32_t *irrptr = &(vlapic)->apic.irr0;			\
59366f6083SPeter Grehan 	irrptr[0] = irrptr[0];	/* silence compiler */			\
60366f6083SPeter Grehan 	VLAPIC_CTR1((vlapic), msg " irr0 0x%08x", irrptr[0 << 2]);	\
61366f6083SPeter Grehan 	VLAPIC_CTR1((vlapic), msg " irr1 0x%08x", irrptr[1 << 2]);	\
62366f6083SPeter Grehan 	VLAPIC_CTR1((vlapic), msg " irr2 0x%08x", irrptr[2 << 2]);	\
63366f6083SPeter Grehan 	VLAPIC_CTR1((vlapic), msg " irr3 0x%08x", irrptr[3 << 2]);	\
64366f6083SPeter Grehan 	VLAPIC_CTR1((vlapic), msg " irr4 0x%08x", irrptr[4 << 2]);	\
65366f6083SPeter Grehan 	VLAPIC_CTR1((vlapic), msg " irr5 0x%08x", irrptr[5 << 2]);	\
66366f6083SPeter Grehan 	VLAPIC_CTR1((vlapic), msg " irr6 0x%08x", irrptr[6 << 2]);	\
67366f6083SPeter Grehan 	VLAPIC_CTR1((vlapic), msg " irr7 0x%08x", irrptr[7 << 2]);	\
68366f6083SPeter Grehan } while (0)
69366f6083SPeter Grehan 
70366f6083SPeter Grehan #define	VLAPIC_CTR_ISR(vlapic, msg)					\
71366f6083SPeter Grehan do {									\
72366f6083SPeter Grehan 	uint32_t *isrptr = &(vlapic)->apic.isr0;			\
73366f6083SPeter Grehan 	isrptr[0] = isrptr[0];	/* silence compiler */			\
74366f6083SPeter Grehan 	VLAPIC_CTR1((vlapic), msg " isr0 0x%08x", isrptr[0 << 2]);	\
75366f6083SPeter Grehan 	VLAPIC_CTR1((vlapic), msg " isr1 0x%08x", isrptr[1 << 2]);	\
76366f6083SPeter Grehan 	VLAPIC_CTR1((vlapic), msg " isr2 0x%08x", isrptr[2 << 2]);	\
77366f6083SPeter Grehan 	VLAPIC_CTR1((vlapic), msg " isr3 0x%08x", isrptr[3 << 2]);	\
78366f6083SPeter Grehan 	VLAPIC_CTR1((vlapic), msg " isr4 0x%08x", isrptr[4 << 2]);	\
79366f6083SPeter Grehan 	VLAPIC_CTR1((vlapic), msg " isr5 0x%08x", isrptr[5 << 2]);	\
80366f6083SPeter Grehan 	VLAPIC_CTR1((vlapic), msg " isr6 0x%08x", isrptr[6 << 2]);	\
81366f6083SPeter Grehan 	VLAPIC_CTR1((vlapic), msg " isr7 0x%08x", isrptr[7 << 2]);	\
82366f6083SPeter Grehan } while (0)
83366f6083SPeter Grehan 
84366f6083SPeter Grehan static MALLOC_DEFINE(M_VLAPIC, "vlapic", "vlapic");
85366f6083SPeter Grehan 
86366f6083SPeter Grehan #define	PRIO(x)			((x) >> 4)
87366f6083SPeter Grehan 
88366f6083SPeter Grehan #define VLAPIC_VERSION		(16)
89366f6083SPeter Grehan #define VLAPIC_MAXLVT_ENTRIES	(5)
90366f6083SPeter Grehan 
91a2da7af6SNeel Natu #define	x2apic(vlapic)	(((vlapic)->msr_apicbase & APICBASE_X2APIC) ? 1 : 0)
922d3a73edSNeel Natu 
93edf89256SNeel Natu enum boot_state {
94edf89256SNeel Natu 	BS_INIT,
95edf89256SNeel Natu 	BS_SIPI,
96edf89256SNeel Natu 	BS_RUNNING
97edf89256SNeel Natu };
98edf89256SNeel Natu 
99366f6083SPeter Grehan struct vlapic {
100366f6083SPeter Grehan 	struct vm		*vm;
101366f6083SPeter Grehan 	int			vcpuid;
102366f6083SPeter Grehan 
103366f6083SPeter Grehan 	struct io_region	*mmio;
104366f6083SPeter Grehan 	struct vdev_ops		*ops;
105366f6083SPeter Grehan 	struct LAPIC		 apic;
106366f6083SPeter Grehan 
107366f6083SPeter Grehan 	int			 esr_update;
108366f6083SPeter Grehan 
109366f6083SPeter Grehan 	int			 divisor;
110366f6083SPeter Grehan 	int			 ccr_ticks;
111366f6083SPeter Grehan 
112366f6083SPeter Grehan 	/*
113366f6083SPeter Grehan 	 * The 'isrvec_stk' is a stack of vectors injected by the local apic.
114366f6083SPeter Grehan 	 * A vector is popped from the stack when the processor does an EOI.
115366f6083SPeter Grehan 	 * The vector on the top of the stack is used to compute the
116366f6083SPeter Grehan 	 * Processor Priority in conjunction with the TPR.
117366f6083SPeter Grehan 	 */
118366f6083SPeter Grehan 	uint8_t			 isrvec_stk[ISRVEC_STK_SIZE];
119366f6083SPeter Grehan 	int			 isrvec_stk_top;
1202d3a73edSNeel Natu 
1212d3a73edSNeel Natu 	uint64_t		msr_apicbase;
122edf89256SNeel Natu 	enum boot_state		boot_state;
123366f6083SPeter Grehan };
124366f6083SPeter Grehan 
1252e25737aSNeel Natu #define VLAPIC_BUS_FREQ	tsc_freq
1262e25737aSNeel Natu 
1272e25737aSNeel Natu static int
1282e25737aSNeel Natu vlapic_timer_divisor(uint32_t dcr)
1292e25737aSNeel Natu {
1302e25737aSNeel Natu 	switch (dcr & 0xB) {
1312e25737aSNeel Natu 	case APIC_TDCR_2:
1322e25737aSNeel Natu 		return (2);
1332e25737aSNeel Natu 	case APIC_TDCR_4:
1342e25737aSNeel Natu 		return (4);
1352e25737aSNeel Natu 	case APIC_TDCR_8:
1362e25737aSNeel Natu 		return (8);
1372e25737aSNeel Natu 	case APIC_TDCR_16:
1382e25737aSNeel Natu 		return (16);
1392e25737aSNeel Natu 	case APIC_TDCR_32:
1402e25737aSNeel Natu 		return (32);
1412e25737aSNeel Natu 	case APIC_TDCR_64:
1422e25737aSNeel Natu 		return (64);
1432e25737aSNeel Natu 	case APIC_TDCR_128:
1442e25737aSNeel Natu 		return (128);
1452e25737aSNeel Natu 	default:
1462e25737aSNeel Natu 		panic("vlapic_timer_divisor: invalid dcr 0x%08x", dcr);
1472e25737aSNeel Natu 	}
1482e25737aSNeel Natu }
1492e25737aSNeel Natu 
150366f6083SPeter Grehan static void
151366f6083SPeter Grehan vlapic_mask_lvts(uint32_t *lvts, int num_lvt)
152366f6083SPeter Grehan {
153366f6083SPeter Grehan 	int i;
154366f6083SPeter Grehan 	for (i = 0; i < num_lvt; i++) {
155366f6083SPeter Grehan 		*lvts |= APIC_LVT_M;
156366f6083SPeter Grehan 		lvts += 4;
157366f6083SPeter Grehan 	}
158366f6083SPeter Grehan }
159366f6083SPeter Grehan 
160366f6083SPeter Grehan #if 0
161366f6083SPeter Grehan static inline void
162366f6083SPeter Grehan vlapic_dump_lvt(uint32_t offset, uint32_t *lvt)
163366f6083SPeter Grehan {
164366f6083SPeter Grehan 	printf("Offset %x: lvt %08x (V:%02x DS:%x M:%x)\n", offset,
165366f6083SPeter Grehan 	    *lvt, *lvt & APIC_LVTT_VECTOR, *lvt & APIC_LVTT_DS,
166366f6083SPeter Grehan 	    *lvt & APIC_LVTT_M);
167366f6083SPeter Grehan }
168366f6083SPeter Grehan #endif
169366f6083SPeter Grehan 
170366f6083SPeter Grehan static uint64_t
171366f6083SPeter Grehan vlapic_get_ccr(struct vlapic *vlapic)
172366f6083SPeter Grehan {
173366f6083SPeter Grehan 	struct LAPIC    *lapic = &vlapic->apic;
174366f6083SPeter Grehan 	return lapic->ccr_timer;
175366f6083SPeter Grehan }
176366f6083SPeter Grehan 
177366f6083SPeter Grehan static void
178366f6083SPeter Grehan vlapic_update_errors(struct vlapic *vlapic)
179366f6083SPeter Grehan {
180366f6083SPeter Grehan 	struct LAPIC    *lapic = &vlapic->apic;
181366f6083SPeter Grehan 	lapic->esr = 0; // XXX
182366f6083SPeter Grehan }
183366f6083SPeter Grehan 
184366f6083SPeter Grehan static void
185366f6083SPeter Grehan vlapic_init_ipi(struct vlapic *vlapic)
186366f6083SPeter Grehan {
187366f6083SPeter Grehan 	struct LAPIC    *lapic = &vlapic->apic;
188366f6083SPeter Grehan 	lapic->version = VLAPIC_VERSION;
189366f6083SPeter Grehan 	lapic->version |= (VLAPIC_MAXLVT_ENTRIES < MAXLVTSHIFT);
190366f6083SPeter Grehan 	lapic->dfr = 0xffffffff;
191366f6083SPeter Grehan 	lapic->svr = APIC_SVR_VECTOR;
192366f6083SPeter Grehan 	vlapic_mask_lvts(&lapic->lvt_timer, VLAPIC_MAXLVT_ENTRIES+1);
193366f6083SPeter Grehan }
194366f6083SPeter Grehan 
195366f6083SPeter Grehan static int
196366f6083SPeter Grehan vlapic_op_reset(void* dev)
197366f6083SPeter Grehan {
198366f6083SPeter Grehan 	struct vlapic 	*vlapic = (struct vlapic*)dev;
199366f6083SPeter Grehan 	struct LAPIC	*lapic = &vlapic->apic;
200366f6083SPeter Grehan 
201366f6083SPeter Grehan 	memset(lapic, 0, sizeof(*lapic));
202366f6083SPeter Grehan 	lapic->apr = vlapic->vcpuid;
203366f6083SPeter Grehan 	vlapic_init_ipi(vlapic);
2042e25737aSNeel Natu 	vlapic->divisor = vlapic_timer_divisor(lapic->dcr_timer);
205366f6083SPeter Grehan 
206edf89256SNeel Natu 	if (vlapic->vcpuid == 0)
207edf89256SNeel Natu 		vlapic->boot_state = BS_RUNNING;	/* BSP */
208edf89256SNeel Natu 	else
209edf89256SNeel Natu 		vlapic->boot_state = BS_INIT;		/* AP */
210edf89256SNeel Natu 
211366f6083SPeter Grehan 	return 0;
212366f6083SPeter Grehan 
213366f6083SPeter Grehan }
214366f6083SPeter Grehan 
215366f6083SPeter Grehan static int
216366f6083SPeter Grehan vlapic_op_init(void* dev)
217366f6083SPeter Grehan {
218366f6083SPeter Grehan 	struct vlapic *vlapic = (struct vlapic*)dev;
219366f6083SPeter Grehan 	vdev_register_region(vlapic->ops, vlapic, vlapic->mmio);
220366f6083SPeter Grehan 	return vlapic_op_reset(dev);
221366f6083SPeter Grehan }
222366f6083SPeter Grehan 
223366f6083SPeter Grehan static int
224366f6083SPeter Grehan vlapic_op_halt(void* dev)
225366f6083SPeter Grehan {
226366f6083SPeter Grehan 	struct vlapic *vlapic = (struct vlapic*)dev;
227366f6083SPeter Grehan 	vdev_unregister_region(vlapic, vlapic->mmio);
228366f6083SPeter Grehan 	return 0;
229366f6083SPeter Grehan 
230366f6083SPeter Grehan }
231366f6083SPeter Grehan 
232366f6083SPeter Grehan void
233366f6083SPeter Grehan vlapic_set_intr_ready(struct vlapic *vlapic, int vector)
234366f6083SPeter Grehan {
235366f6083SPeter Grehan 	struct LAPIC	*lapic = &vlapic->apic;
236366f6083SPeter Grehan 	uint32_t	*irrptr;
237366f6083SPeter Grehan 	int		idx;
238366f6083SPeter Grehan 
239366f6083SPeter Grehan 	if (vector < 0 || vector >= 256)
240366f6083SPeter Grehan 		panic("vlapic_set_intr_ready: invalid vector %d\n", vector);
241366f6083SPeter Grehan 
242366f6083SPeter Grehan 	idx = (vector / 32) * 4;
243366f6083SPeter Grehan 	irrptr = &lapic->irr0;
244366f6083SPeter Grehan 	atomic_set_int(&irrptr[idx], 1 << (vector % 32));
245366f6083SPeter Grehan 	VLAPIC_CTR_IRR(vlapic, "vlapic_set_intr_ready");
246366f6083SPeter Grehan }
247366f6083SPeter Grehan 
248366f6083SPeter Grehan static void
249366f6083SPeter Grehan vlapic_start_timer(struct vlapic *vlapic, uint32_t elapsed)
250366f6083SPeter Grehan {
251366f6083SPeter Grehan 	uint32_t icr_timer;
252366f6083SPeter Grehan 
253366f6083SPeter Grehan 	icr_timer = vlapic->apic.icr_timer;
254366f6083SPeter Grehan 
255366f6083SPeter Grehan 	vlapic->ccr_ticks = ticks;
256366f6083SPeter Grehan 	if (elapsed < icr_timer)
257366f6083SPeter Grehan 		vlapic->apic.ccr_timer = icr_timer - elapsed;
258366f6083SPeter Grehan 	else {
259366f6083SPeter Grehan 		/*
260366f6083SPeter Grehan 		 * This can happen when the guest is trying to run its local
261366f6083SPeter Grehan 		 * apic timer higher that the setting of 'hz' in the host.
262366f6083SPeter Grehan 		 *
263366f6083SPeter Grehan 		 * We deal with this by running the guest local apic timer
264366f6083SPeter Grehan 		 * at the rate of the host's 'hz' setting.
265366f6083SPeter Grehan 		 */
266366f6083SPeter Grehan 		vlapic->apic.ccr_timer = 0;
267366f6083SPeter Grehan 	}
268366f6083SPeter Grehan }
269366f6083SPeter Grehan 
270366f6083SPeter Grehan static __inline uint32_t *
271366f6083SPeter Grehan vlapic_get_lvt(struct vlapic *vlapic, uint32_t offset)
272366f6083SPeter Grehan {
273366f6083SPeter Grehan 	struct LAPIC	*lapic = &vlapic->apic;
274366f6083SPeter Grehan 	int 		 i;
275366f6083SPeter Grehan 
276366f6083SPeter Grehan 	if (offset < APIC_OFFSET_TIMER_LVT || offset > APIC_OFFSET_ERROR_LVT) {
277366f6083SPeter Grehan 		panic("vlapic_get_lvt: invalid LVT\n");
278366f6083SPeter Grehan 	}
279366f6083SPeter Grehan 	i = (offset - APIC_OFFSET_TIMER_LVT) >> 2;
280366f6083SPeter Grehan 	return ((&lapic->lvt_timer) + i);;
281366f6083SPeter Grehan }
282366f6083SPeter Grehan 
283366f6083SPeter Grehan #if 1
284366f6083SPeter Grehan static void
285366f6083SPeter Grehan dump_isrvec_stk(struct vlapic *vlapic)
286366f6083SPeter Grehan {
287366f6083SPeter Grehan 	int i;
288366f6083SPeter Grehan 	uint32_t *isrptr;
289366f6083SPeter Grehan 
290366f6083SPeter Grehan 	isrptr = &vlapic->apic.isr0;
291366f6083SPeter Grehan 	for (i = 0; i < 8; i++)
292366f6083SPeter Grehan 		printf("ISR%d 0x%08x\n", i, isrptr[i * 4]);
293366f6083SPeter Grehan 
294366f6083SPeter Grehan 	for (i = 0; i <= vlapic->isrvec_stk_top; i++)
295366f6083SPeter Grehan 		printf("isrvec_stk[%d] = %d\n", i, vlapic->isrvec_stk[i]);
296366f6083SPeter Grehan }
297366f6083SPeter Grehan #endif
298366f6083SPeter Grehan 
299366f6083SPeter Grehan /*
300366f6083SPeter Grehan  * Algorithm adopted from section "Interrupt, Task and Processor Priority"
301366f6083SPeter Grehan  * in Intel Architecture Manual Vol 3a.
302366f6083SPeter Grehan  */
303366f6083SPeter Grehan static void
304366f6083SPeter Grehan vlapic_update_ppr(struct vlapic *vlapic)
305366f6083SPeter Grehan {
306366f6083SPeter Grehan 	int isrvec, tpr, ppr;
307366f6083SPeter Grehan 
308366f6083SPeter Grehan 	/*
309366f6083SPeter Grehan 	 * Note that the value on the stack at index 0 is always 0.
310366f6083SPeter Grehan 	 *
311366f6083SPeter Grehan 	 * This is a placeholder for the value of ISRV when none of the
312366f6083SPeter Grehan 	 * bits is set in the ISRx registers.
313366f6083SPeter Grehan 	 */
314366f6083SPeter Grehan 	isrvec = vlapic->isrvec_stk[vlapic->isrvec_stk_top];
315366f6083SPeter Grehan 	tpr = vlapic->apic.tpr;
316366f6083SPeter Grehan 
317366f6083SPeter Grehan #if 1
318366f6083SPeter Grehan 	{
319366f6083SPeter Grehan 		int i, lastprio, curprio, vector, idx;
320366f6083SPeter Grehan 		uint32_t *isrptr;
321366f6083SPeter Grehan 
322366f6083SPeter Grehan 		if (vlapic->isrvec_stk_top == 0 && isrvec != 0)
323366f6083SPeter Grehan 			panic("isrvec_stk is corrupted: %d", isrvec);
324366f6083SPeter Grehan 
325366f6083SPeter Grehan 		/*
326366f6083SPeter Grehan 		 * Make sure that the priority of the nested interrupts is
327366f6083SPeter Grehan 		 * always increasing.
328366f6083SPeter Grehan 		 */
329366f6083SPeter Grehan 		lastprio = -1;
330366f6083SPeter Grehan 		for (i = 1; i <= vlapic->isrvec_stk_top; i++) {
331366f6083SPeter Grehan 			curprio = PRIO(vlapic->isrvec_stk[i]);
332366f6083SPeter Grehan 			if (curprio <= lastprio) {
333366f6083SPeter Grehan 				dump_isrvec_stk(vlapic);
334366f6083SPeter Grehan 				panic("isrvec_stk does not satisfy invariant");
335366f6083SPeter Grehan 			}
336366f6083SPeter Grehan 			lastprio = curprio;
337366f6083SPeter Grehan 		}
338366f6083SPeter Grehan 
339366f6083SPeter Grehan 		/*
340366f6083SPeter Grehan 		 * Make sure that each bit set in the ISRx registers has a
341366f6083SPeter Grehan 		 * corresponding entry on the isrvec stack.
342366f6083SPeter Grehan 		 */
343366f6083SPeter Grehan 		i = 1;
344366f6083SPeter Grehan 		isrptr = &vlapic->apic.isr0;
345366f6083SPeter Grehan 		for (vector = 0; vector < 256; vector++) {
346366f6083SPeter Grehan 			idx = (vector / 32) * 4;
347366f6083SPeter Grehan 			if (isrptr[idx] & (1 << (vector % 32))) {
348366f6083SPeter Grehan 				if (i > vlapic->isrvec_stk_top ||
349366f6083SPeter Grehan 				    vlapic->isrvec_stk[i] != vector) {
350366f6083SPeter Grehan 					dump_isrvec_stk(vlapic);
351366f6083SPeter Grehan 					panic("ISR and isrvec_stk out of sync");
352366f6083SPeter Grehan 				}
353366f6083SPeter Grehan 				i++;
354366f6083SPeter Grehan 			}
355366f6083SPeter Grehan 		}
356366f6083SPeter Grehan 	}
357366f6083SPeter Grehan #endif
358366f6083SPeter Grehan 
359366f6083SPeter Grehan 	if (PRIO(tpr) >= PRIO(isrvec))
360366f6083SPeter Grehan 		ppr = tpr;
361366f6083SPeter Grehan 	else
362366f6083SPeter Grehan 		ppr = isrvec & 0xf0;
363366f6083SPeter Grehan 
364366f6083SPeter Grehan 	vlapic->apic.ppr = ppr;
365366f6083SPeter Grehan 	VLAPIC_CTR1(vlapic, "vlapic_update_ppr 0x%02x", ppr);
366366f6083SPeter Grehan }
367366f6083SPeter Grehan 
368366f6083SPeter Grehan static void
369366f6083SPeter Grehan vlapic_process_eoi(struct vlapic *vlapic)
370366f6083SPeter Grehan {
371366f6083SPeter Grehan 	struct LAPIC	*lapic = &vlapic->apic;
372366f6083SPeter Grehan 	uint32_t	*isrptr;
373366f6083SPeter Grehan 	int		i, idx, bitpos;
374366f6083SPeter Grehan 
375366f6083SPeter Grehan 	isrptr = &lapic->isr0;
376366f6083SPeter Grehan 
377366f6083SPeter Grehan 	/*
378366f6083SPeter Grehan 	 * The x86 architecture reserves the the first 32 vectors for use
379366f6083SPeter Grehan 	 * by the processor.
380366f6083SPeter Grehan 	 */
381366f6083SPeter Grehan 	for (i = 7; i > 0; i--) {
382366f6083SPeter Grehan 		idx = i * 4;
383366f6083SPeter Grehan 		bitpos = fls(isrptr[idx]);
384366f6083SPeter Grehan 		if (bitpos != 0) {
385366f6083SPeter Grehan 			if (vlapic->isrvec_stk_top <= 0) {
386366f6083SPeter Grehan 				panic("invalid vlapic isrvec_stk_top %d",
387366f6083SPeter Grehan 				      vlapic->isrvec_stk_top);
388366f6083SPeter Grehan 			}
389366f6083SPeter Grehan 			isrptr[idx] &= ~(1 << (bitpos - 1));
390366f6083SPeter Grehan 			VLAPIC_CTR_ISR(vlapic, "vlapic_process_eoi");
391366f6083SPeter Grehan 			vlapic->isrvec_stk_top--;
392366f6083SPeter Grehan 			vlapic_update_ppr(vlapic);
393366f6083SPeter Grehan 			return;
394366f6083SPeter Grehan 		}
395366f6083SPeter Grehan 	}
396366f6083SPeter Grehan }
397366f6083SPeter Grehan 
398366f6083SPeter Grehan static __inline int
399366f6083SPeter Grehan vlapic_get_lvt_field(uint32_t *lvt, uint32_t mask)
400366f6083SPeter Grehan {
401366f6083SPeter Grehan 	return (*lvt & mask);
402366f6083SPeter Grehan }
403366f6083SPeter Grehan 
404366f6083SPeter Grehan static __inline int
405366f6083SPeter Grehan vlapic_periodic_timer(struct vlapic *vlapic)
406366f6083SPeter Grehan {
407366f6083SPeter Grehan 	uint32_t *lvt;
408366f6083SPeter Grehan 
409366f6083SPeter Grehan 	lvt = vlapic_get_lvt(vlapic, APIC_OFFSET_TIMER_LVT);
410366f6083SPeter Grehan 
411366f6083SPeter Grehan 	return (vlapic_get_lvt_field(lvt, APIC_LVTT_TM_PERIODIC));
412366f6083SPeter Grehan }
413366f6083SPeter Grehan 
414*77d8fd9bSNeel Natu static VMM_STAT(VLAPIC_INTR_TIMER, "timer interrupts generated by vlapic");
415*77d8fd9bSNeel Natu 
416366f6083SPeter Grehan static void
417366f6083SPeter Grehan vlapic_fire_timer(struct vlapic *vlapic)
418366f6083SPeter Grehan {
419366f6083SPeter Grehan 	int vector;
420366f6083SPeter Grehan 	uint32_t *lvt;
421366f6083SPeter Grehan 
422366f6083SPeter Grehan 	lvt = vlapic_get_lvt(vlapic, APIC_OFFSET_TIMER_LVT);
423366f6083SPeter Grehan 
424366f6083SPeter Grehan 	if (!vlapic_get_lvt_field(lvt, APIC_LVTT_M)) {
425*77d8fd9bSNeel Natu 		vmm_stat_incr(vlapic->vm, vlapic->vcpuid, VLAPIC_INTR_TIMER, 1);
426366f6083SPeter Grehan 		vector = vlapic_get_lvt_field(lvt,APIC_LVTT_VECTOR);
427366f6083SPeter Grehan 		vlapic_set_intr_ready(vlapic, vector);
428366f6083SPeter Grehan 	}
429366f6083SPeter Grehan }
430366f6083SPeter Grehan 
431366f6083SPeter Grehan static int
432366f6083SPeter Grehan lapic_process_icr(struct vlapic *vlapic, uint64_t icrval)
433366f6083SPeter Grehan {
434366f6083SPeter Grehan 	int i;
435a5615c90SPeter Grehan 	cpuset_t dmask;
436366f6083SPeter Grehan 	uint32_t dest, vec, mode;
437edf89256SNeel Natu 	struct vlapic *vlapic2;
438edf89256SNeel Natu 	struct vm_exit *vmexit;
439366f6083SPeter Grehan 
440a2da7af6SNeel Natu 	if (x2apic(vlapic))
441366f6083SPeter Grehan 		dest = icrval >> 32;
442a2da7af6SNeel Natu 	else
443a2da7af6SNeel Natu 		dest = icrval >> (32 + 24);
444366f6083SPeter Grehan 	vec = icrval & APIC_VECTOR_MASK;
445366f6083SPeter Grehan 	mode = icrval & APIC_DELMODE_MASK;
446366f6083SPeter Grehan 
447366f6083SPeter Grehan 	if (mode == APIC_DELMODE_FIXED || mode == APIC_DELMODE_NMI) {
448366f6083SPeter Grehan 		switch (icrval & APIC_DEST_MASK) {
449366f6083SPeter Grehan 		case APIC_DEST_DESTFLD:
450a5615c90SPeter Grehan 			CPU_SETOF(dest, &dmask);
451366f6083SPeter Grehan 			break;
452366f6083SPeter Grehan 		case APIC_DEST_SELF:
453a5615c90SPeter Grehan 			CPU_SETOF(vlapic->vcpuid, &dmask);
454366f6083SPeter Grehan 			break;
455366f6083SPeter Grehan 		case APIC_DEST_ALLISELF:
456366f6083SPeter Grehan 			dmask = vm_active_cpus(vlapic->vm);
457366f6083SPeter Grehan 			break;
458366f6083SPeter Grehan 		case APIC_DEST_ALLESELF:
459a5615c90SPeter Grehan 			dmask = vm_active_cpus(vlapic->vm);
460a5615c90SPeter Grehan 			CPU_CLR(vlapic->vcpuid, &dmask);
461366f6083SPeter Grehan 			break;
462366f6083SPeter Grehan 		}
463366f6083SPeter Grehan 
464a5615c90SPeter Grehan 		while ((i = cpusetobj_ffs(&dmask)) != 0) {
465a5615c90SPeter Grehan 			i--;
466a5615c90SPeter Grehan 			CPU_CLR(i, &dmask);
467366f6083SPeter Grehan 			if (mode == APIC_DELMODE_FIXED)
468366f6083SPeter Grehan 				lapic_set_intr(vlapic->vm, i, vec);
469366f6083SPeter Grehan 			else
470366f6083SPeter Grehan 				vm_inject_nmi(vlapic->vm, i);
471366f6083SPeter Grehan 		}
472366f6083SPeter Grehan 
473366f6083SPeter Grehan 		return (0);	/* handled completely in the kernel */
474366f6083SPeter Grehan 	}
475366f6083SPeter Grehan 
476edf89256SNeel Natu 	if (mode == APIC_DELMODE_INIT) {
477edf89256SNeel Natu 		if ((icrval & APIC_LEVEL_MASK) == APIC_LEVEL_DEASSERT)
478edf89256SNeel Natu 			return (0);
479edf89256SNeel Natu 
480edf89256SNeel Natu 		if (vlapic->vcpuid == 0 && dest != 0 && dest < VM_MAXCPU) {
481edf89256SNeel Natu 			vlapic2 = vm_lapic(vlapic->vm, dest);
482edf89256SNeel Natu 
483edf89256SNeel Natu 			/* move from INIT to waiting-for-SIPI state */
484edf89256SNeel Natu 			if (vlapic2->boot_state == BS_INIT) {
485edf89256SNeel Natu 				vlapic2->boot_state = BS_SIPI;
486edf89256SNeel Natu 			}
487edf89256SNeel Natu 
488edf89256SNeel Natu 			return (0);
489edf89256SNeel Natu 		}
490edf89256SNeel Natu 	}
491edf89256SNeel Natu 
492edf89256SNeel Natu 	if (mode == APIC_DELMODE_STARTUP) {
493edf89256SNeel Natu 		if (vlapic->vcpuid == 0 && dest != 0 && dest < VM_MAXCPU) {
494edf89256SNeel Natu 			vlapic2 = vm_lapic(vlapic->vm, dest);
495edf89256SNeel Natu 
496edf89256SNeel Natu 			/*
497edf89256SNeel Natu 			 * Ignore SIPIs in any state other than wait-for-SIPI
498edf89256SNeel Natu 			 */
499edf89256SNeel Natu 			if (vlapic2->boot_state != BS_SIPI)
500edf89256SNeel Natu 				return (0);
501edf89256SNeel Natu 
502edf89256SNeel Natu 			vmexit = vm_exitinfo(vlapic->vm, vlapic->vcpuid);
503edf89256SNeel Natu 			vmexit->exitcode = VM_EXITCODE_SPINUP_AP;
504edf89256SNeel Natu 			vmexit->u.spinup_ap.vcpu = dest;
505edf89256SNeel Natu 			vmexit->u.spinup_ap.rip = vec << PAGE_SHIFT;
506edf89256SNeel Natu 
507366f6083SPeter Grehan 			/*
508366f6083SPeter Grehan 			 * XXX this assumes that the startup IPI always succeeds
509366f6083SPeter Grehan 			 */
510edf89256SNeel Natu 			vlapic2->boot_state = BS_RUNNING;
511edf89256SNeel Natu 			vm_activate_cpu(vlapic2->vm, dest);
512edf89256SNeel Natu 
513edf89256SNeel Natu 			return (0);
514edf89256SNeel Natu 		}
515edf89256SNeel Natu 	}
516366f6083SPeter Grehan 
517366f6083SPeter Grehan 	/*
518366f6083SPeter Grehan 	 * This will cause a return to userland.
519366f6083SPeter Grehan 	 */
520366f6083SPeter Grehan 	return (1);
521366f6083SPeter Grehan }
522366f6083SPeter Grehan 
523366f6083SPeter Grehan int
524366f6083SPeter Grehan vlapic_pending_intr(struct vlapic *vlapic)
525366f6083SPeter Grehan {
526366f6083SPeter Grehan 	struct LAPIC	*lapic = &vlapic->apic;
527366f6083SPeter Grehan 	int	  	 idx, i, bitpos, vector;
528366f6083SPeter Grehan 	uint32_t	*irrptr, val;
529366f6083SPeter Grehan 
530366f6083SPeter Grehan 	irrptr = &lapic->irr0;
531366f6083SPeter Grehan 
532366f6083SPeter Grehan 	/*
533366f6083SPeter Grehan 	 * The x86 architecture reserves the the first 32 vectors for use
534366f6083SPeter Grehan 	 * by the processor.
535366f6083SPeter Grehan 	 */
536366f6083SPeter Grehan 	for (i = 7; i > 0; i--) {
537366f6083SPeter Grehan 		idx = i * 4;
538366f6083SPeter Grehan 		val = atomic_load_acq_int(&irrptr[idx]);
539366f6083SPeter Grehan 		bitpos = fls(val);
540366f6083SPeter Grehan 		if (bitpos != 0) {
541366f6083SPeter Grehan 			vector = i * 32 + (bitpos - 1);
542366f6083SPeter Grehan 			if (PRIO(vector) > PRIO(lapic->ppr)) {
543366f6083SPeter Grehan 				VLAPIC_CTR1(vlapic, "pending intr %d", vector);
544366f6083SPeter Grehan 				return (vector);
545366f6083SPeter Grehan 			} else
546366f6083SPeter Grehan 				break;
547366f6083SPeter Grehan 		}
548366f6083SPeter Grehan 	}
549366f6083SPeter Grehan 	VLAPIC_CTR0(vlapic, "no pending intr");
550366f6083SPeter Grehan 	return (-1);
551366f6083SPeter Grehan }
552366f6083SPeter Grehan 
553366f6083SPeter Grehan void
554366f6083SPeter Grehan vlapic_intr_accepted(struct vlapic *vlapic, int vector)
555366f6083SPeter Grehan {
556366f6083SPeter Grehan 	struct LAPIC	*lapic = &vlapic->apic;
557366f6083SPeter Grehan 	uint32_t	*irrptr, *isrptr;
558366f6083SPeter Grehan 	int		idx, stk_top;
559366f6083SPeter Grehan 
560366f6083SPeter Grehan 	/*
561366f6083SPeter Grehan 	 * clear the ready bit for vector being accepted in irr
562366f6083SPeter Grehan 	 * and set the vector as in service in isr.
563366f6083SPeter Grehan 	 */
564366f6083SPeter Grehan 	idx = (vector / 32) * 4;
565366f6083SPeter Grehan 
566366f6083SPeter Grehan 	irrptr = &lapic->irr0;
567366f6083SPeter Grehan 	atomic_clear_int(&irrptr[idx], 1 << (vector % 32));
568366f6083SPeter Grehan 	VLAPIC_CTR_IRR(vlapic, "vlapic_intr_accepted");
569366f6083SPeter Grehan 
570366f6083SPeter Grehan 	isrptr = &lapic->isr0;
571366f6083SPeter Grehan 	isrptr[idx] |= 1 << (vector % 32);
572366f6083SPeter Grehan 	VLAPIC_CTR_ISR(vlapic, "vlapic_intr_accepted");
573366f6083SPeter Grehan 
574366f6083SPeter Grehan 	/*
575366f6083SPeter Grehan 	 * Update the PPR
576366f6083SPeter Grehan 	 */
577366f6083SPeter Grehan 	vlapic->isrvec_stk_top++;
578366f6083SPeter Grehan 
579366f6083SPeter Grehan 	stk_top = vlapic->isrvec_stk_top;
580366f6083SPeter Grehan 	if (stk_top >= ISRVEC_STK_SIZE)
581366f6083SPeter Grehan 		panic("isrvec_stk_top overflow %d", stk_top);
582366f6083SPeter Grehan 
583366f6083SPeter Grehan 	vlapic->isrvec_stk[stk_top] = vector;
584366f6083SPeter Grehan 	vlapic_update_ppr(vlapic);
585366f6083SPeter Grehan }
586366f6083SPeter Grehan 
587366f6083SPeter Grehan int
588366f6083SPeter Grehan vlapic_op_mem_read(void* dev, uint64_t gpa, opsize_t size, uint64_t *data)
589366f6083SPeter Grehan {
590366f6083SPeter Grehan 	struct vlapic 	*vlapic = (struct vlapic*)dev;
591366f6083SPeter Grehan 	struct LAPIC	*lapic = &vlapic->apic;
592366f6083SPeter Grehan 	uint64_t	 offset = gpa & ~(PAGE_SIZE);
593366f6083SPeter Grehan 	uint32_t	*reg;
594366f6083SPeter Grehan 	int		 i;
595366f6083SPeter Grehan 
596366f6083SPeter Grehan 	if (offset > sizeof(*lapic)) {
597366f6083SPeter Grehan 		*data = 0;
598366f6083SPeter Grehan 		return 0;
599366f6083SPeter Grehan 	}
600366f6083SPeter Grehan 
601366f6083SPeter Grehan 	offset &= ~3;
602366f6083SPeter Grehan 	switch(offset)
603366f6083SPeter Grehan 	{
604366f6083SPeter Grehan 		case APIC_OFFSET_ID:
6052d3a73edSNeel Natu 			if (x2apic(vlapic))
6062d3a73edSNeel Natu 				*data = vlapic->vcpuid;
6072d3a73edSNeel Natu 			else
6082d3a73edSNeel Natu 				*data = vlapic->vcpuid << 24;
609366f6083SPeter Grehan 			break;
610366f6083SPeter Grehan 		case APIC_OFFSET_VER:
611366f6083SPeter Grehan 			*data = lapic->version;
612366f6083SPeter Grehan 			break;
613366f6083SPeter Grehan 		case APIC_OFFSET_TPR:
614366f6083SPeter Grehan 			*data = lapic->tpr;
615366f6083SPeter Grehan 			break;
616366f6083SPeter Grehan 		case APIC_OFFSET_APR:
617366f6083SPeter Grehan 			*data = lapic->apr;
618366f6083SPeter Grehan 			break;
619366f6083SPeter Grehan 		case APIC_OFFSET_PPR:
620366f6083SPeter Grehan 			*data = lapic->ppr;
621366f6083SPeter Grehan 			break;
622366f6083SPeter Grehan 		case APIC_OFFSET_EOI:
623366f6083SPeter Grehan 			*data = lapic->eoi;
624366f6083SPeter Grehan 			break;
625366f6083SPeter Grehan 		case APIC_OFFSET_LDR:
626366f6083SPeter Grehan 			*data = lapic->ldr;
627366f6083SPeter Grehan 			break;
628366f6083SPeter Grehan 		case APIC_OFFSET_DFR:
629366f6083SPeter Grehan 			*data = lapic->dfr;
630366f6083SPeter Grehan 			break;
631366f6083SPeter Grehan 		case APIC_OFFSET_SVR:
632366f6083SPeter Grehan 			*data = lapic->svr;
633366f6083SPeter Grehan 			break;
634366f6083SPeter Grehan 		case APIC_OFFSET_ISR0 ... APIC_OFFSET_ISR7:
635366f6083SPeter Grehan 			i = (offset - APIC_OFFSET_ISR0) >> 2;
636366f6083SPeter Grehan 			reg = &lapic->isr0;
637366f6083SPeter Grehan 			*data = *(reg + i);
638366f6083SPeter Grehan 			break;
639366f6083SPeter Grehan 		case APIC_OFFSET_TMR0 ... APIC_OFFSET_TMR7:
640366f6083SPeter Grehan 			i = (offset - APIC_OFFSET_TMR0) >> 2;
641366f6083SPeter Grehan 			reg = &lapic->tmr0;
642366f6083SPeter Grehan 			*data = *(reg + i);
643366f6083SPeter Grehan 			break;
644366f6083SPeter Grehan 		case APIC_OFFSET_IRR0 ... APIC_OFFSET_IRR7:
645366f6083SPeter Grehan 			i = (offset - APIC_OFFSET_IRR0) >> 2;
646366f6083SPeter Grehan 			reg = &lapic->irr0;
647366f6083SPeter Grehan 			*data = atomic_load_acq_int(reg + i);
648366f6083SPeter Grehan 			break;
649366f6083SPeter Grehan 		case APIC_OFFSET_ESR:
650366f6083SPeter Grehan 			*data = lapic->esr;
651366f6083SPeter Grehan 			break;
652366f6083SPeter Grehan 		case APIC_OFFSET_ICR_LOW:
653366f6083SPeter Grehan 			*data = lapic->icr_lo;
654366f6083SPeter Grehan 			break;
655366f6083SPeter Grehan 		case APIC_OFFSET_ICR_HI:
656366f6083SPeter Grehan 			*data = lapic->icr_hi;
657366f6083SPeter Grehan 			break;
658366f6083SPeter Grehan 		case APIC_OFFSET_TIMER_LVT ... APIC_OFFSET_ERROR_LVT:
659366f6083SPeter Grehan 			reg = vlapic_get_lvt(vlapic, offset);
660366f6083SPeter Grehan 			*data = *(reg);
661366f6083SPeter Grehan 			break;
662366f6083SPeter Grehan 		case APIC_OFFSET_ICR:
663366f6083SPeter Grehan 			*data = lapic->icr_timer;
664366f6083SPeter Grehan 			break;
665366f6083SPeter Grehan 		case APIC_OFFSET_CCR:
666366f6083SPeter Grehan 			*data = vlapic_get_ccr(vlapic);
667366f6083SPeter Grehan 			break;
668366f6083SPeter Grehan 		case APIC_OFFSET_DCR:
669366f6083SPeter Grehan 			*data = lapic->dcr_timer;
670366f6083SPeter Grehan 			break;
671366f6083SPeter Grehan 		case APIC_OFFSET_RRR:
672366f6083SPeter Grehan 		default:
673366f6083SPeter Grehan 			*data = 0;
674366f6083SPeter Grehan 			break;
675366f6083SPeter Grehan 	}
676366f6083SPeter Grehan 	return 0;
677366f6083SPeter Grehan }
678366f6083SPeter Grehan 
679366f6083SPeter Grehan int
680366f6083SPeter Grehan vlapic_op_mem_write(void* dev, uint64_t gpa, opsize_t size, uint64_t data)
681366f6083SPeter Grehan {
682366f6083SPeter Grehan 	struct vlapic 	*vlapic = (struct vlapic*)dev;
683366f6083SPeter Grehan 	struct LAPIC	*lapic = &vlapic->apic;
684366f6083SPeter Grehan 	uint64_t	 offset = gpa & ~(PAGE_SIZE);
685366f6083SPeter Grehan 	uint32_t	*reg;
686366f6083SPeter Grehan 	int		retval;
687366f6083SPeter Grehan 
688366f6083SPeter Grehan 	if (offset > sizeof(*lapic)) {
689366f6083SPeter Grehan 		return 0;
690366f6083SPeter Grehan 	}
691366f6083SPeter Grehan 
692366f6083SPeter Grehan 	retval = 0;
693366f6083SPeter Grehan 	offset &= ~3;
694366f6083SPeter Grehan 	switch(offset)
695366f6083SPeter Grehan 	{
696366f6083SPeter Grehan 		case APIC_OFFSET_ID:
697366f6083SPeter Grehan 			break;
698366f6083SPeter Grehan 		case APIC_OFFSET_TPR:
699366f6083SPeter Grehan 			lapic->tpr = data & 0xff;
700366f6083SPeter Grehan 			vlapic_update_ppr(vlapic);
701366f6083SPeter Grehan 			break;
702366f6083SPeter Grehan 		case APIC_OFFSET_EOI:
703366f6083SPeter Grehan 			vlapic_process_eoi(vlapic);
704366f6083SPeter Grehan 			break;
705366f6083SPeter Grehan 		case APIC_OFFSET_LDR:
706366f6083SPeter Grehan 			break;
707366f6083SPeter Grehan 		case APIC_OFFSET_DFR:
708366f6083SPeter Grehan 			break;
709366f6083SPeter Grehan 		case APIC_OFFSET_SVR:
710366f6083SPeter Grehan 			lapic->svr = data;
711366f6083SPeter Grehan 			break;
712366f6083SPeter Grehan 		case APIC_OFFSET_ICR_LOW:
713a2da7af6SNeel Natu 			if (!x2apic(vlapic)) {
714a2da7af6SNeel Natu 				data &= 0xffffffff;
715a2da7af6SNeel Natu 				data |= (uint64_t)lapic->icr_hi << 32;
716a2da7af6SNeel Natu 			}
717366f6083SPeter Grehan 			retval = lapic_process_icr(vlapic, data);
718366f6083SPeter Grehan 			break;
719a2da7af6SNeel Natu 		case APIC_OFFSET_ICR_HI:
720a2da7af6SNeel Natu 			if (!x2apic(vlapic)) {
721a2da7af6SNeel Natu 				retval = 0;
722a2da7af6SNeel Natu 				lapic->icr_hi = data;
723a2da7af6SNeel Natu 			}
724a2da7af6SNeel Natu 			break;
725366f6083SPeter Grehan 		case APIC_OFFSET_TIMER_LVT ... APIC_OFFSET_ERROR_LVT:
726366f6083SPeter Grehan 			reg = vlapic_get_lvt(vlapic, offset);
727366f6083SPeter Grehan 			if (!(lapic->svr & APIC_SVR_ENABLE)) {
728366f6083SPeter Grehan 				data |= APIC_LVT_M;
729366f6083SPeter Grehan 			}
730366f6083SPeter Grehan 			*reg = data;
731366f6083SPeter Grehan 			// vlapic_dump_lvt(offset, reg);
732366f6083SPeter Grehan 			break;
733366f6083SPeter Grehan 		case APIC_OFFSET_ICR:
734366f6083SPeter Grehan 			lapic->icr_timer = data;
735366f6083SPeter Grehan 			vlapic_start_timer(vlapic, 0);
736366f6083SPeter Grehan 			break;
737366f6083SPeter Grehan 
738366f6083SPeter Grehan 		case APIC_OFFSET_DCR:
739366f6083SPeter Grehan 			lapic->dcr_timer = data;
740366f6083SPeter Grehan 			vlapic->divisor = vlapic_timer_divisor(data);
741366f6083SPeter Grehan 			break;
742366f6083SPeter Grehan 
743366f6083SPeter Grehan 		case APIC_OFFSET_ESR:
744366f6083SPeter Grehan 			vlapic_update_errors(vlapic);
745366f6083SPeter Grehan 			break;
746366f6083SPeter Grehan 		case APIC_OFFSET_VER:
747366f6083SPeter Grehan 		case APIC_OFFSET_APR:
748366f6083SPeter Grehan 		case APIC_OFFSET_PPR:
749366f6083SPeter Grehan 		case APIC_OFFSET_RRR:
750366f6083SPeter Grehan 		case APIC_OFFSET_ISR0 ... APIC_OFFSET_ISR7:
751366f6083SPeter Grehan 		case APIC_OFFSET_TMR0 ... APIC_OFFSET_TMR7:
752366f6083SPeter Grehan 		case APIC_OFFSET_IRR0 ... APIC_OFFSET_IRR7:
753366f6083SPeter Grehan 		case APIC_OFFSET_CCR:
754366f6083SPeter Grehan 		default:
755366f6083SPeter Grehan 			// Read only.
756366f6083SPeter Grehan 			break;
757366f6083SPeter Grehan 	}
758366f6083SPeter Grehan 
759366f6083SPeter Grehan 	return (retval);
760366f6083SPeter Grehan }
761366f6083SPeter Grehan 
7622e25737aSNeel Natu int
763366f6083SPeter Grehan vlapic_timer_tick(struct vlapic *vlapic)
764366f6083SPeter Grehan {
7652e25737aSNeel Natu 	int curticks, delta, periodic, fired;
766366f6083SPeter Grehan 	uint32_t ccr;
7672e25737aSNeel Natu 	uint32_t decrement, leftover;
768366f6083SPeter Grehan 
7692e25737aSNeel Natu restart:
770366f6083SPeter Grehan 	curticks = ticks;
771366f6083SPeter Grehan 	delta = curticks - vlapic->ccr_ticks;
772366f6083SPeter Grehan 
773366f6083SPeter Grehan 	/* Local APIC timer is disabled */
774366f6083SPeter Grehan 	if (vlapic->apic.icr_timer == 0)
7752e25737aSNeel Natu 		return (-1);
776366f6083SPeter Grehan 
777366f6083SPeter Grehan 	/* One-shot mode and timer has already counted down to zero */
778366f6083SPeter Grehan 	periodic = vlapic_periodic_timer(vlapic);
779366f6083SPeter Grehan 	if (!periodic && vlapic->apic.ccr_timer == 0)
7802e25737aSNeel Natu 		return (-1);
781366f6083SPeter Grehan 	/*
782366f6083SPeter Grehan 	 * The 'curticks' and 'ccr_ticks' are out of sync by more than
783366f6083SPeter Grehan 	 * 2^31 ticks. We deal with this by restarting the timer.
784366f6083SPeter Grehan 	 */
785366f6083SPeter Grehan 	if (delta < 0) {
786366f6083SPeter Grehan 		vlapic_start_timer(vlapic, 0);
7872e25737aSNeel Natu 		goto restart;
788366f6083SPeter Grehan 	}
789366f6083SPeter Grehan 
7902e25737aSNeel Natu 	fired = 0;
791366f6083SPeter Grehan 	decrement = (VLAPIC_BUS_FREQ / vlapic->divisor) / hz;
7922e25737aSNeel Natu 
7932e25737aSNeel Natu 	vlapic->ccr_ticks = curticks;
7942e25737aSNeel Natu 	ccr = vlapic->apic.ccr_timer;
7952e25737aSNeel Natu 
796366f6083SPeter Grehan 	while (delta-- > 0) {
7972e25737aSNeel Natu 		if (ccr > decrement) {
7982e25737aSNeel Natu 			ccr -= decrement;
7992e25737aSNeel Natu 			continue;
8002e25737aSNeel Natu 		}
8012e25737aSNeel Natu 
8022e25737aSNeel Natu 		/* Trigger the local apic timer interrupt */
803366f6083SPeter Grehan 		vlapic_fire_timer(vlapic);
804366f6083SPeter Grehan 		if (periodic) {
8052e25737aSNeel Natu 			leftover = decrement - ccr;
8062e25737aSNeel Natu 			vlapic_start_timer(vlapic, leftover);
807366f6083SPeter Grehan 			ccr = vlapic->apic.ccr_timer;
808366f6083SPeter Grehan 		} else {
809366f6083SPeter Grehan 			/*
810366f6083SPeter Grehan 			 * One-shot timer has counted down to zero.
811366f6083SPeter Grehan 			 */
812366f6083SPeter Grehan 			ccr = 0;
8132e25737aSNeel Natu 		}
8142e25737aSNeel Natu 		fired = 1;
815366f6083SPeter Grehan 		break;
816366f6083SPeter Grehan 	}
817366f6083SPeter Grehan 
818366f6083SPeter Grehan 	vlapic->apic.ccr_timer = ccr;
8192e25737aSNeel Natu 
8202e25737aSNeel Natu 	if (!fired)
8212e25737aSNeel Natu 		return ((ccr / decrement) + 1);
8222e25737aSNeel Natu 	else
8232e25737aSNeel Natu 		return (0);
824366f6083SPeter Grehan }
825366f6083SPeter Grehan 
826366f6083SPeter Grehan struct vdev_ops vlapic_dev_ops = {
827366f6083SPeter Grehan 	.name = "vlapic",
828366f6083SPeter Grehan 	.init = vlapic_op_init,
829366f6083SPeter Grehan 	.reset = vlapic_op_reset,
830366f6083SPeter Grehan 	.halt = vlapic_op_halt,
831366f6083SPeter Grehan 	.memread = vlapic_op_mem_read,
832366f6083SPeter Grehan 	.memwrite = vlapic_op_mem_write,
833366f6083SPeter Grehan };
834366f6083SPeter Grehan static struct io_region vlapic_mmio[VM_MAXCPU];
835366f6083SPeter Grehan 
836366f6083SPeter Grehan struct vlapic *
837366f6083SPeter Grehan vlapic_init(struct vm *vm, int vcpuid)
838366f6083SPeter Grehan {
839366f6083SPeter Grehan 	struct vlapic 		*vlapic;
840366f6083SPeter Grehan 
841366f6083SPeter Grehan 	vlapic = malloc(sizeof(struct vlapic), M_VLAPIC, M_WAITOK | M_ZERO);
842366f6083SPeter Grehan 	vlapic->vm = vm;
843366f6083SPeter Grehan 	vlapic->vcpuid = vcpuid;
8442d3a73edSNeel Natu 
845a2da7af6SNeel Natu 	vlapic->msr_apicbase = DEFAULT_APIC_BASE | APICBASE_ENABLED;
8462d3a73edSNeel Natu 
8472d3a73edSNeel Natu 	if (vcpuid == 0)
8482d3a73edSNeel Natu 		vlapic->msr_apicbase |= APICBASE_BSP;
8492d3a73edSNeel Natu 
850366f6083SPeter Grehan 	vlapic->ops = &vlapic_dev_ops;
851366f6083SPeter Grehan 
852366f6083SPeter Grehan 	vlapic->mmio = vlapic_mmio + vcpuid;
853366f6083SPeter Grehan 	vlapic->mmio->base = DEFAULT_APIC_BASE;
854366f6083SPeter Grehan 	vlapic->mmio->len = PAGE_SIZE;
855366f6083SPeter Grehan 	vlapic->mmio->attr = MMIO_READ|MMIO_WRITE;
856366f6083SPeter Grehan 	vlapic->mmio->vcpu = vcpuid;
857366f6083SPeter Grehan 
858366f6083SPeter Grehan 	vdev_register(&vlapic_dev_ops, vlapic);
859366f6083SPeter Grehan 
860366f6083SPeter Grehan 	vlapic_op_init(vlapic);
861366f6083SPeter Grehan 
862366f6083SPeter Grehan 	return (vlapic);
863366f6083SPeter Grehan }
864366f6083SPeter Grehan 
865366f6083SPeter Grehan void
866366f6083SPeter Grehan vlapic_cleanup(struct vlapic *vlapic)
867366f6083SPeter Grehan {
868cd942e0fSPeter Grehan 	vlapic_op_halt(vlapic);
869366f6083SPeter Grehan 	vdev_unregister(vlapic);
870366f6083SPeter Grehan 	free(vlapic, M_VLAPIC);
871366f6083SPeter Grehan }
8722d3a73edSNeel Natu 
8732d3a73edSNeel Natu uint64_t
8742d3a73edSNeel Natu vlapic_get_apicbase(struct vlapic *vlapic)
8752d3a73edSNeel Natu {
8762d3a73edSNeel Natu 
8772d3a73edSNeel Natu 	return (vlapic->msr_apicbase);
8782d3a73edSNeel Natu }
8792d3a73edSNeel Natu 
8802d3a73edSNeel Natu void
8812d3a73edSNeel Natu vlapic_set_apicbase(struct vlapic *vlapic, uint64_t val)
8822d3a73edSNeel Natu {
883a2da7af6SNeel Natu 	int err;
884a2da7af6SNeel Natu 	enum x2apic_state state;
885a2da7af6SNeel Natu 
886a2da7af6SNeel Natu 	err = vm_get_x2apic_state(vlapic->vm, vlapic->vcpuid, &state);
887a2da7af6SNeel Natu 	if (err)
888a2da7af6SNeel Natu 		panic("vlapic_set_apicbase: err %d fetching x2apic state", err);
889a2da7af6SNeel Natu 
890a2da7af6SNeel Natu 	if (state == X2APIC_DISABLED)
891a2da7af6SNeel Natu 		val &= ~APICBASE_X2APIC;
8922d3a73edSNeel Natu 
8932d3a73edSNeel Natu 	vlapic->msr_apicbase = val;
8942d3a73edSNeel Natu }
89573820fb0SNeel Natu 
89673820fb0SNeel Natu void
89773820fb0SNeel Natu vlapic_set_x2apic_state(struct vm *vm, int vcpuid, enum x2apic_state state)
89873820fb0SNeel Natu {
89973820fb0SNeel Natu 	struct vlapic *vlapic;
90073820fb0SNeel Natu 
90173820fb0SNeel Natu 	vlapic = vm_lapic(vm, vcpuid);
90273820fb0SNeel Natu 
903485f986aSNeel Natu 	if (state == X2APIC_DISABLED)
90473820fb0SNeel Natu 		vlapic->msr_apicbase &= ~APICBASE_X2APIC;
90573820fb0SNeel Natu }
906