xref: /freebsd/sys/amd64/vmm/io/vlapic.c (revision a2da7af6bc22d3df7f45e71d4233a3a155f66b8a)
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 
44366f6083SPeter Grehan #include "vmm_lapic.h"
45366f6083SPeter Grehan #include "vmm_ktr.h"
46366f6083SPeter Grehan #include "vdev.h"
47366f6083SPeter Grehan #include "vlapic.h"
48366f6083SPeter Grehan 
49366f6083SPeter Grehan #define	VLAPIC_CTR0(vlapic, format)					\
50366f6083SPeter Grehan 	VMM_CTR0((vlapic)->vm, (vlapic)->vcpuid, format)
51366f6083SPeter Grehan 
52366f6083SPeter Grehan #define	VLAPIC_CTR1(vlapic, format, p1)					\
53366f6083SPeter Grehan 	VMM_CTR1((vlapic)->vm, (vlapic)->vcpuid, format, p1)
54366f6083SPeter Grehan 
55366f6083SPeter Grehan #define	VLAPIC_CTR_IRR(vlapic, msg)					\
56366f6083SPeter Grehan do {									\
57366f6083SPeter Grehan 	uint32_t *irrptr = &(vlapic)->apic.irr0;			\
58366f6083SPeter Grehan 	irrptr[0] = irrptr[0];	/* silence compiler */			\
59366f6083SPeter Grehan 	VLAPIC_CTR1((vlapic), msg " irr0 0x%08x", irrptr[0 << 2]);	\
60366f6083SPeter Grehan 	VLAPIC_CTR1((vlapic), msg " irr1 0x%08x", irrptr[1 << 2]);	\
61366f6083SPeter Grehan 	VLAPIC_CTR1((vlapic), msg " irr2 0x%08x", irrptr[2 << 2]);	\
62366f6083SPeter Grehan 	VLAPIC_CTR1((vlapic), msg " irr3 0x%08x", irrptr[3 << 2]);	\
63366f6083SPeter Grehan 	VLAPIC_CTR1((vlapic), msg " irr4 0x%08x", irrptr[4 << 2]);	\
64366f6083SPeter Grehan 	VLAPIC_CTR1((vlapic), msg " irr5 0x%08x", irrptr[5 << 2]);	\
65366f6083SPeter Grehan 	VLAPIC_CTR1((vlapic), msg " irr6 0x%08x", irrptr[6 << 2]);	\
66366f6083SPeter Grehan 	VLAPIC_CTR1((vlapic), msg " irr7 0x%08x", irrptr[7 << 2]);	\
67366f6083SPeter Grehan } while (0)
68366f6083SPeter Grehan 
69366f6083SPeter Grehan #define	VLAPIC_CTR_ISR(vlapic, msg)					\
70366f6083SPeter Grehan do {									\
71366f6083SPeter Grehan 	uint32_t *isrptr = &(vlapic)->apic.isr0;			\
72366f6083SPeter Grehan 	isrptr[0] = isrptr[0];	/* silence compiler */			\
73366f6083SPeter Grehan 	VLAPIC_CTR1((vlapic), msg " isr0 0x%08x", isrptr[0 << 2]);	\
74366f6083SPeter Grehan 	VLAPIC_CTR1((vlapic), msg " isr1 0x%08x", isrptr[1 << 2]);	\
75366f6083SPeter Grehan 	VLAPIC_CTR1((vlapic), msg " isr2 0x%08x", isrptr[2 << 2]);	\
76366f6083SPeter Grehan 	VLAPIC_CTR1((vlapic), msg " isr3 0x%08x", isrptr[3 << 2]);	\
77366f6083SPeter Grehan 	VLAPIC_CTR1((vlapic), msg " isr4 0x%08x", isrptr[4 << 2]);	\
78366f6083SPeter Grehan 	VLAPIC_CTR1((vlapic), msg " isr5 0x%08x", isrptr[5 << 2]);	\
79366f6083SPeter Grehan 	VLAPIC_CTR1((vlapic), msg " isr6 0x%08x", isrptr[6 << 2]);	\
80366f6083SPeter Grehan 	VLAPIC_CTR1((vlapic), msg " isr7 0x%08x", isrptr[7 << 2]);	\
81366f6083SPeter Grehan } while (0)
82366f6083SPeter Grehan 
83366f6083SPeter Grehan static MALLOC_DEFINE(M_VLAPIC, "vlapic", "vlapic");
84366f6083SPeter Grehan 
85366f6083SPeter Grehan #define	PRIO(x)			((x) >> 4)
86366f6083SPeter Grehan 
87366f6083SPeter Grehan #define VLAPIC_VERSION		(16)
88366f6083SPeter Grehan #define VLAPIC_MAXLVT_ENTRIES	(5)
89366f6083SPeter Grehan 
90*a2da7af6SNeel Natu #define	x2apic(vlapic)	(((vlapic)->msr_apicbase & APICBASE_X2APIC) ? 1 : 0)
912d3a73edSNeel Natu 
92edf89256SNeel Natu enum boot_state {
93edf89256SNeel Natu 	BS_INIT,
94edf89256SNeel Natu 	BS_SIPI,
95edf89256SNeel Natu 	BS_RUNNING
96edf89256SNeel Natu };
97edf89256SNeel Natu 
98366f6083SPeter Grehan struct vlapic {
99366f6083SPeter Grehan 	struct vm		*vm;
100366f6083SPeter Grehan 	int			vcpuid;
101366f6083SPeter Grehan 
102366f6083SPeter Grehan 	struct io_region	*mmio;
103366f6083SPeter Grehan 	struct vdev_ops		*ops;
104366f6083SPeter Grehan 	struct LAPIC		 apic;
105366f6083SPeter Grehan 
106366f6083SPeter Grehan 	int			 esr_update;
107366f6083SPeter Grehan 
108366f6083SPeter Grehan 	int			 divisor;
109366f6083SPeter Grehan 	int			 ccr_ticks;
110366f6083SPeter Grehan 
111366f6083SPeter Grehan 	/*
112366f6083SPeter Grehan 	 * The 'isrvec_stk' is a stack of vectors injected by the local apic.
113366f6083SPeter Grehan 	 * A vector is popped from the stack when the processor does an EOI.
114366f6083SPeter Grehan 	 * The vector on the top of the stack is used to compute the
115366f6083SPeter Grehan 	 * Processor Priority in conjunction with the TPR.
116366f6083SPeter Grehan 	 */
117366f6083SPeter Grehan 	uint8_t			 isrvec_stk[ISRVEC_STK_SIZE];
118366f6083SPeter Grehan 	int			 isrvec_stk_top;
1192d3a73edSNeel Natu 
1202d3a73edSNeel Natu 	uint64_t		msr_apicbase;
121edf89256SNeel Natu 	enum boot_state		boot_state;
122366f6083SPeter Grehan };
123366f6083SPeter Grehan 
124366f6083SPeter Grehan static void
125366f6083SPeter Grehan vlapic_mask_lvts(uint32_t *lvts, int num_lvt)
126366f6083SPeter Grehan {
127366f6083SPeter Grehan 	int i;
128366f6083SPeter Grehan 	for (i = 0; i < num_lvt; i++) {
129366f6083SPeter Grehan 		*lvts |= APIC_LVT_M;
130366f6083SPeter Grehan 		lvts += 4;
131366f6083SPeter Grehan 	}
132366f6083SPeter Grehan }
133366f6083SPeter Grehan 
134366f6083SPeter Grehan #if 0
135366f6083SPeter Grehan static inline void
136366f6083SPeter Grehan vlapic_dump_lvt(uint32_t offset, uint32_t *lvt)
137366f6083SPeter Grehan {
138366f6083SPeter Grehan 	printf("Offset %x: lvt %08x (V:%02x DS:%x M:%x)\n", offset,
139366f6083SPeter Grehan 	    *lvt, *lvt & APIC_LVTT_VECTOR, *lvt & APIC_LVTT_DS,
140366f6083SPeter Grehan 	    *lvt & APIC_LVTT_M);
141366f6083SPeter Grehan }
142366f6083SPeter Grehan #endif
143366f6083SPeter Grehan 
144366f6083SPeter Grehan static uint64_t
145366f6083SPeter Grehan vlapic_get_ccr(struct vlapic *vlapic)
146366f6083SPeter Grehan {
147366f6083SPeter Grehan 	struct LAPIC    *lapic = &vlapic->apic;
148366f6083SPeter Grehan 	return lapic->ccr_timer;
149366f6083SPeter Grehan }
150366f6083SPeter Grehan 
151366f6083SPeter Grehan static void
152366f6083SPeter Grehan vlapic_update_errors(struct vlapic *vlapic)
153366f6083SPeter Grehan {
154366f6083SPeter Grehan 	struct LAPIC    *lapic = &vlapic->apic;
155366f6083SPeter Grehan 	lapic->esr = 0; // XXX
156366f6083SPeter Grehan }
157366f6083SPeter Grehan 
158366f6083SPeter Grehan static void
159366f6083SPeter Grehan vlapic_init_ipi(struct vlapic *vlapic)
160366f6083SPeter Grehan {
161366f6083SPeter Grehan 	struct LAPIC    *lapic = &vlapic->apic;
162366f6083SPeter Grehan 	lapic->version = VLAPIC_VERSION;
163366f6083SPeter Grehan 	lapic->version |= (VLAPIC_MAXLVT_ENTRIES < MAXLVTSHIFT);
164366f6083SPeter Grehan 	lapic->dfr = 0xffffffff;
165366f6083SPeter Grehan 	lapic->svr = APIC_SVR_VECTOR;
166366f6083SPeter Grehan 	vlapic_mask_lvts(&lapic->lvt_timer, VLAPIC_MAXLVT_ENTRIES+1);
167366f6083SPeter Grehan }
168366f6083SPeter Grehan 
169366f6083SPeter Grehan static int
170366f6083SPeter Grehan vlapic_op_reset(void* dev)
171366f6083SPeter Grehan {
172366f6083SPeter Grehan 	struct vlapic 	*vlapic = (struct vlapic*)dev;
173366f6083SPeter Grehan 	struct LAPIC	*lapic = &vlapic->apic;
174366f6083SPeter Grehan 
175366f6083SPeter Grehan 	memset(lapic, 0, sizeof(*lapic));
176366f6083SPeter Grehan 	lapic->apr = vlapic->vcpuid;
177366f6083SPeter Grehan 	vlapic_init_ipi(vlapic);
178366f6083SPeter Grehan 
179edf89256SNeel Natu 	if (vlapic->vcpuid == 0)
180edf89256SNeel Natu 		vlapic->boot_state = BS_RUNNING;	/* BSP */
181edf89256SNeel Natu 	else
182edf89256SNeel Natu 		vlapic->boot_state = BS_INIT;		/* AP */
183edf89256SNeel Natu 
184366f6083SPeter Grehan 	return 0;
185366f6083SPeter Grehan 
186366f6083SPeter Grehan }
187366f6083SPeter Grehan 
188366f6083SPeter Grehan static int
189366f6083SPeter Grehan vlapic_op_init(void* dev)
190366f6083SPeter Grehan {
191366f6083SPeter Grehan 	struct vlapic *vlapic = (struct vlapic*)dev;
192366f6083SPeter Grehan 	vdev_register_region(vlapic->ops, vlapic, vlapic->mmio);
193366f6083SPeter Grehan 	return vlapic_op_reset(dev);
194366f6083SPeter Grehan }
195366f6083SPeter Grehan 
196366f6083SPeter Grehan static int
197366f6083SPeter Grehan vlapic_op_halt(void* dev)
198366f6083SPeter Grehan {
199366f6083SPeter Grehan 	struct vlapic *vlapic = (struct vlapic*)dev;
200366f6083SPeter Grehan 	vdev_unregister_region(vlapic, vlapic->mmio);
201366f6083SPeter Grehan 	return 0;
202366f6083SPeter Grehan 
203366f6083SPeter Grehan }
204366f6083SPeter Grehan 
205366f6083SPeter Grehan void
206366f6083SPeter Grehan vlapic_set_intr_ready(struct vlapic *vlapic, int vector)
207366f6083SPeter Grehan {
208366f6083SPeter Grehan 	struct LAPIC	*lapic = &vlapic->apic;
209366f6083SPeter Grehan 	uint32_t	*irrptr;
210366f6083SPeter Grehan 	int		idx;
211366f6083SPeter Grehan 
212366f6083SPeter Grehan 	if (vector < 0 || vector >= 256)
213366f6083SPeter Grehan 		panic("vlapic_set_intr_ready: invalid vector %d\n", vector);
214366f6083SPeter Grehan 
215366f6083SPeter Grehan 	idx = (vector / 32) * 4;
216366f6083SPeter Grehan 	irrptr = &lapic->irr0;
217366f6083SPeter Grehan 	atomic_set_int(&irrptr[idx], 1 << (vector % 32));
218366f6083SPeter Grehan 	VLAPIC_CTR_IRR(vlapic, "vlapic_set_intr_ready");
219366f6083SPeter Grehan }
220366f6083SPeter Grehan 
221366f6083SPeter Grehan #define VLAPIC_BUS_FREQ	tsc_freq
222366f6083SPeter Grehan #define VLAPIC_DCR(x)	((x->dcr_timer & 0x8) >> 1)|(x->dcr_timer & 0x3)
223366f6083SPeter Grehan 
224366f6083SPeter Grehan static int
225366f6083SPeter Grehan vlapic_timer_divisor(uint32_t dcr)
226366f6083SPeter Grehan {
227366f6083SPeter Grehan 	switch (dcr & 0xB) {
228366f6083SPeter Grehan 	case APIC_TDCR_2:
229366f6083SPeter Grehan 		return (2);
230366f6083SPeter Grehan 	case APIC_TDCR_4:
231366f6083SPeter Grehan 		return (4);
232366f6083SPeter Grehan 	case APIC_TDCR_8:
233366f6083SPeter Grehan 		return (8);
234366f6083SPeter Grehan 	case APIC_TDCR_16:
235366f6083SPeter Grehan 		return (16);
236366f6083SPeter Grehan 	case APIC_TDCR_32:
237366f6083SPeter Grehan 		return (32);
238366f6083SPeter Grehan 	case APIC_TDCR_64:
239366f6083SPeter Grehan 		return (64);
240366f6083SPeter Grehan 	case APIC_TDCR_128:
241366f6083SPeter Grehan 		return (128);
242366f6083SPeter Grehan 	default:
243366f6083SPeter Grehan 		panic("vlapic_timer_divisor: invalid dcr 0x%08x", dcr);
244366f6083SPeter Grehan 	}
245366f6083SPeter Grehan }
246366f6083SPeter Grehan 
247366f6083SPeter Grehan static void
248366f6083SPeter Grehan vlapic_start_timer(struct vlapic *vlapic, uint32_t elapsed)
249366f6083SPeter Grehan {
250366f6083SPeter Grehan 	uint32_t icr_timer;
251366f6083SPeter Grehan 
252366f6083SPeter Grehan 	icr_timer = vlapic->apic.icr_timer;
253366f6083SPeter Grehan 
254366f6083SPeter Grehan 	vlapic->ccr_ticks = ticks;
255366f6083SPeter Grehan 	if (elapsed < icr_timer)
256366f6083SPeter Grehan 		vlapic->apic.ccr_timer = icr_timer - elapsed;
257366f6083SPeter Grehan 	else {
258366f6083SPeter Grehan 		/*
259366f6083SPeter Grehan 		 * This can happen when the guest is trying to run its local
260366f6083SPeter Grehan 		 * apic timer higher that the setting of 'hz' in the host.
261366f6083SPeter Grehan 		 *
262366f6083SPeter Grehan 		 * We deal with this by running the guest local apic timer
263366f6083SPeter Grehan 		 * at the rate of the host's 'hz' setting.
264366f6083SPeter Grehan 		 */
265366f6083SPeter Grehan 		vlapic->apic.ccr_timer = 0;
266366f6083SPeter Grehan 	}
267366f6083SPeter Grehan }
268366f6083SPeter Grehan 
269366f6083SPeter Grehan static __inline uint32_t *
270366f6083SPeter Grehan vlapic_get_lvt(struct vlapic *vlapic, uint32_t offset)
271366f6083SPeter Grehan {
272366f6083SPeter Grehan 	struct LAPIC	*lapic = &vlapic->apic;
273366f6083SPeter Grehan 	int 		 i;
274366f6083SPeter Grehan 
275366f6083SPeter Grehan 	if (offset < APIC_OFFSET_TIMER_LVT || offset > APIC_OFFSET_ERROR_LVT) {
276366f6083SPeter Grehan 		panic("vlapic_get_lvt: invalid LVT\n");
277366f6083SPeter Grehan 	}
278366f6083SPeter Grehan 	i = (offset - APIC_OFFSET_TIMER_LVT) >> 2;
279366f6083SPeter Grehan 	return ((&lapic->lvt_timer) + i);;
280366f6083SPeter Grehan }
281366f6083SPeter Grehan 
282366f6083SPeter Grehan #if 1
283366f6083SPeter Grehan static void
284366f6083SPeter Grehan dump_isrvec_stk(struct vlapic *vlapic)
285366f6083SPeter Grehan {
286366f6083SPeter Grehan 	int i;
287366f6083SPeter Grehan 	uint32_t *isrptr;
288366f6083SPeter Grehan 
289366f6083SPeter Grehan 	isrptr = &vlapic->apic.isr0;
290366f6083SPeter Grehan 	for (i = 0; i < 8; i++)
291366f6083SPeter Grehan 		printf("ISR%d 0x%08x\n", i, isrptr[i * 4]);
292366f6083SPeter Grehan 
293366f6083SPeter Grehan 	for (i = 0; i <= vlapic->isrvec_stk_top; i++)
294366f6083SPeter Grehan 		printf("isrvec_stk[%d] = %d\n", i, vlapic->isrvec_stk[i]);
295366f6083SPeter Grehan }
296366f6083SPeter Grehan #endif
297366f6083SPeter Grehan 
298366f6083SPeter Grehan /*
299366f6083SPeter Grehan  * Algorithm adopted from section "Interrupt, Task and Processor Priority"
300366f6083SPeter Grehan  * in Intel Architecture Manual Vol 3a.
301366f6083SPeter Grehan  */
302366f6083SPeter Grehan static void
303366f6083SPeter Grehan vlapic_update_ppr(struct vlapic *vlapic)
304366f6083SPeter Grehan {
305366f6083SPeter Grehan 	int isrvec, tpr, ppr;
306366f6083SPeter Grehan 
307366f6083SPeter Grehan 	/*
308366f6083SPeter Grehan 	 * Note that the value on the stack at index 0 is always 0.
309366f6083SPeter Grehan 	 *
310366f6083SPeter Grehan 	 * This is a placeholder for the value of ISRV when none of the
311366f6083SPeter Grehan 	 * bits is set in the ISRx registers.
312366f6083SPeter Grehan 	 */
313366f6083SPeter Grehan 	isrvec = vlapic->isrvec_stk[vlapic->isrvec_stk_top];
314366f6083SPeter Grehan 	tpr = vlapic->apic.tpr;
315366f6083SPeter Grehan 
316366f6083SPeter Grehan #if 1
317366f6083SPeter Grehan 	{
318366f6083SPeter Grehan 		int i, lastprio, curprio, vector, idx;
319366f6083SPeter Grehan 		uint32_t *isrptr;
320366f6083SPeter Grehan 
321366f6083SPeter Grehan 		if (vlapic->isrvec_stk_top == 0 && isrvec != 0)
322366f6083SPeter Grehan 			panic("isrvec_stk is corrupted: %d", isrvec);
323366f6083SPeter Grehan 
324366f6083SPeter Grehan 		/*
325366f6083SPeter Grehan 		 * Make sure that the priority of the nested interrupts is
326366f6083SPeter Grehan 		 * always increasing.
327366f6083SPeter Grehan 		 */
328366f6083SPeter Grehan 		lastprio = -1;
329366f6083SPeter Grehan 		for (i = 1; i <= vlapic->isrvec_stk_top; i++) {
330366f6083SPeter Grehan 			curprio = PRIO(vlapic->isrvec_stk[i]);
331366f6083SPeter Grehan 			if (curprio <= lastprio) {
332366f6083SPeter Grehan 				dump_isrvec_stk(vlapic);
333366f6083SPeter Grehan 				panic("isrvec_stk does not satisfy invariant");
334366f6083SPeter Grehan 			}
335366f6083SPeter Grehan 			lastprio = curprio;
336366f6083SPeter Grehan 		}
337366f6083SPeter Grehan 
338366f6083SPeter Grehan 		/*
339366f6083SPeter Grehan 		 * Make sure that each bit set in the ISRx registers has a
340366f6083SPeter Grehan 		 * corresponding entry on the isrvec stack.
341366f6083SPeter Grehan 		 */
342366f6083SPeter Grehan 		i = 1;
343366f6083SPeter Grehan 		isrptr = &vlapic->apic.isr0;
344366f6083SPeter Grehan 		for (vector = 0; vector < 256; vector++) {
345366f6083SPeter Grehan 			idx = (vector / 32) * 4;
346366f6083SPeter Grehan 			if (isrptr[idx] & (1 << (vector % 32))) {
347366f6083SPeter Grehan 				if (i > vlapic->isrvec_stk_top ||
348366f6083SPeter Grehan 				    vlapic->isrvec_stk[i] != vector) {
349366f6083SPeter Grehan 					dump_isrvec_stk(vlapic);
350366f6083SPeter Grehan 					panic("ISR and isrvec_stk out of sync");
351366f6083SPeter Grehan 				}
352366f6083SPeter Grehan 				i++;
353366f6083SPeter Grehan 			}
354366f6083SPeter Grehan 		}
355366f6083SPeter Grehan 	}
356366f6083SPeter Grehan #endif
357366f6083SPeter Grehan 
358366f6083SPeter Grehan 	if (PRIO(tpr) >= PRIO(isrvec))
359366f6083SPeter Grehan 		ppr = tpr;
360366f6083SPeter Grehan 	else
361366f6083SPeter Grehan 		ppr = isrvec & 0xf0;
362366f6083SPeter Grehan 
363366f6083SPeter Grehan 	vlapic->apic.ppr = ppr;
364366f6083SPeter Grehan 	VLAPIC_CTR1(vlapic, "vlapic_update_ppr 0x%02x", ppr);
365366f6083SPeter Grehan }
366366f6083SPeter Grehan 
367366f6083SPeter Grehan static void
368366f6083SPeter Grehan vlapic_process_eoi(struct vlapic *vlapic)
369366f6083SPeter Grehan {
370366f6083SPeter Grehan 	struct LAPIC	*lapic = &vlapic->apic;
371366f6083SPeter Grehan 	uint32_t	*isrptr;
372366f6083SPeter Grehan 	int		i, idx, bitpos;
373366f6083SPeter Grehan 
374366f6083SPeter Grehan 	isrptr = &lapic->isr0;
375366f6083SPeter Grehan 
376366f6083SPeter Grehan 	/*
377366f6083SPeter Grehan 	 * The x86 architecture reserves the the first 32 vectors for use
378366f6083SPeter Grehan 	 * by the processor.
379366f6083SPeter Grehan 	 */
380366f6083SPeter Grehan 	for (i = 7; i > 0; i--) {
381366f6083SPeter Grehan 		idx = i * 4;
382366f6083SPeter Grehan 		bitpos = fls(isrptr[idx]);
383366f6083SPeter Grehan 		if (bitpos != 0) {
384366f6083SPeter Grehan 			if (vlapic->isrvec_stk_top <= 0) {
385366f6083SPeter Grehan 				panic("invalid vlapic isrvec_stk_top %d",
386366f6083SPeter Grehan 				      vlapic->isrvec_stk_top);
387366f6083SPeter Grehan 			}
388366f6083SPeter Grehan 			isrptr[idx] &= ~(1 << (bitpos - 1));
389366f6083SPeter Grehan 			VLAPIC_CTR_ISR(vlapic, "vlapic_process_eoi");
390366f6083SPeter Grehan 			vlapic->isrvec_stk_top--;
391366f6083SPeter Grehan 			vlapic_update_ppr(vlapic);
392366f6083SPeter Grehan 			return;
393366f6083SPeter Grehan 		}
394366f6083SPeter Grehan 	}
395366f6083SPeter Grehan }
396366f6083SPeter Grehan 
397366f6083SPeter Grehan static __inline int
398366f6083SPeter Grehan vlapic_get_lvt_field(uint32_t *lvt, uint32_t mask)
399366f6083SPeter Grehan {
400366f6083SPeter Grehan 	return (*lvt & mask);
401366f6083SPeter Grehan }
402366f6083SPeter Grehan 
403366f6083SPeter Grehan static __inline int
404366f6083SPeter Grehan vlapic_periodic_timer(struct vlapic *vlapic)
405366f6083SPeter Grehan {
406366f6083SPeter Grehan 	uint32_t *lvt;
407366f6083SPeter Grehan 
408366f6083SPeter Grehan 	lvt = vlapic_get_lvt(vlapic, APIC_OFFSET_TIMER_LVT);
409366f6083SPeter Grehan 
410366f6083SPeter Grehan 	return (vlapic_get_lvt_field(lvt, APIC_LVTT_TM_PERIODIC));
411366f6083SPeter Grehan }
412366f6083SPeter Grehan 
413366f6083SPeter Grehan static void
414366f6083SPeter Grehan vlapic_fire_timer(struct vlapic *vlapic)
415366f6083SPeter Grehan {
416366f6083SPeter Grehan 	int vector;
417366f6083SPeter Grehan 	uint32_t *lvt;
418366f6083SPeter Grehan 
419366f6083SPeter Grehan 	lvt = vlapic_get_lvt(vlapic, APIC_OFFSET_TIMER_LVT);
420366f6083SPeter Grehan 
421366f6083SPeter Grehan 	if (!vlapic_get_lvt_field(lvt, APIC_LVTT_M)) {
422366f6083SPeter Grehan 		vector = vlapic_get_lvt_field(lvt,APIC_LVTT_VECTOR);
423366f6083SPeter Grehan 		vlapic_set_intr_ready(vlapic, vector);
424366f6083SPeter Grehan 	}
425366f6083SPeter Grehan }
426366f6083SPeter Grehan 
427366f6083SPeter Grehan static int
428366f6083SPeter Grehan lapic_process_icr(struct vlapic *vlapic, uint64_t icrval)
429366f6083SPeter Grehan {
430366f6083SPeter Grehan 	int i;
431a5615c90SPeter Grehan 	cpuset_t dmask;
432366f6083SPeter Grehan 	uint32_t dest, vec, mode;
433edf89256SNeel Natu 	struct vlapic *vlapic2;
434edf89256SNeel Natu 	struct vm_exit *vmexit;
435366f6083SPeter Grehan 
436*a2da7af6SNeel Natu 	if (x2apic(vlapic))
437366f6083SPeter Grehan 		dest = icrval >> 32;
438*a2da7af6SNeel Natu 	else
439*a2da7af6SNeel Natu 		dest = icrval >> (32 + 24);
440366f6083SPeter Grehan 	vec = icrval & APIC_VECTOR_MASK;
441366f6083SPeter Grehan 	mode = icrval & APIC_DELMODE_MASK;
442366f6083SPeter Grehan 
443366f6083SPeter Grehan 	if (mode == APIC_DELMODE_FIXED || mode == APIC_DELMODE_NMI) {
444366f6083SPeter Grehan 		switch (icrval & APIC_DEST_MASK) {
445366f6083SPeter Grehan 		case APIC_DEST_DESTFLD:
446a5615c90SPeter Grehan 			CPU_SETOF(dest, &dmask);
447366f6083SPeter Grehan 			break;
448366f6083SPeter Grehan 		case APIC_DEST_SELF:
449a5615c90SPeter Grehan 			CPU_SETOF(vlapic->vcpuid, &dmask);
450366f6083SPeter Grehan 			break;
451366f6083SPeter Grehan 		case APIC_DEST_ALLISELF:
452366f6083SPeter Grehan 			dmask = vm_active_cpus(vlapic->vm);
453366f6083SPeter Grehan 			break;
454366f6083SPeter Grehan 		case APIC_DEST_ALLESELF:
455a5615c90SPeter Grehan 			dmask = vm_active_cpus(vlapic->vm);
456a5615c90SPeter Grehan 			CPU_CLR(vlapic->vcpuid, &dmask);
457366f6083SPeter Grehan 			break;
458366f6083SPeter Grehan 		}
459366f6083SPeter Grehan 
460a5615c90SPeter Grehan 		while ((i = cpusetobj_ffs(&dmask)) != 0) {
461a5615c90SPeter Grehan 			i--;
462a5615c90SPeter Grehan 			CPU_CLR(i, &dmask);
463366f6083SPeter Grehan 			if (mode == APIC_DELMODE_FIXED)
464366f6083SPeter Grehan 				lapic_set_intr(vlapic->vm, i, vec);
465366f6083SPeter Grehan 			else
466366f6083SPeter Grehan 				vm_inject_nmi(vlapic->vm, i);
467366f6083SPeter Grehan 		}
468366f6083SPeter Grehan 
469366f6083SPeter Grehan 		return (0);	/* handled completely in the kernel */
470366f6083SPeter Grehan 	}
471366f6083SPeter Grehan 
472edf89256SNeel Natu 	if (mode == APIC_DELMODE_INIT) {
473edf89256SNeel Natu 		if ((icrval & APIC_LEVEL_MASK) == APIC_LEVEL_DEASSERT)
474edf89256SNeel Natu 			return (0);
475edf89256SNeel Natu 
476edf89256SNeel Natu 		if (vlapic->vcpuid == 0 && dest != 0 && dest < VM_MAXCPU) {
477edf89256SNeel Natu 			vlapic2 = vm_lapic(vlapic->vm, dest);
478edf89256SNeel Natu 
479edf89256SNeel Natu 			/* move from INIT to waiting-for-SIPI state */
480edf89256SNeel Natu 			if (vlapic2->boot_state == BS_INIT) {
481edf89256SNeel Natu 				vlapic2->boot_state = BS_SIPI;
482edf89256SNeel Natu 			}
483edf89256SNeel Natu 
484edf89256SNeel Natu 			return (0);
485edf89256SNeel Natu 		}
486edf89256SNeel Natu 	}
487edf89256SNeel Natu 
488edf89256SNeel Natu 	if (mode == APIC_DELMODE_STARTUP) {
489edf89256SNeel Natu 		if (vlapic->vcpuid == 0 && dest != 0 && dest < VM_MAXCPU) {
490edf89256SNeel Natu 			vlapic2 = vm_lapic(vlapic->vm, dest);
491edf89256SNeel Natu 
492edf89256SNeel Natu 			/*
493edf89256SNeel Natu 			 * Ignore SIPIs in any state other than wait-for-SIPI
494edf89256SNeel Natu 			 */
495edf89256SNeel Natu 			if (vlapic2->boot_state != BS_SIPI)
496edf89256SNeel Natu 				return (0);
497edf89256SNeel Natu 
498edf89256SNeel Natu 			vmexit = vm_exitinfo(vlapic->vm, vlapic->vcpuid);
499edf89256SNeel Natu 			vmexit->exitcode = VM_EXITCODE_SPINUP_AP;
500edf89256SNeel Natu 			vmexit->u.spinup_ap.vcpu = dest;
501edf89256SNeel Natu 			vmexit->u.spinup_ap.rip = vec << PAGE_SHIFT;
502edf89256SNeel Natu 
503366f6083SPeter Grehan 			/*
504366f6083SPeter Grehan 			 * XXX this assumes that the startup IPI always succeeds
505366f6083SPeter Grehan 			 */
506edf89256SNeel Natu 			vlapic2->boot_state = BS_RUNNING;
507edf89256SNeel Natu 			vm_activate_cpu(vlapic2->vm, dest);
508edf89256SNeel Natu 
509edf89256SNeel Natu 			return (0);
510edf89256SNeel Natu 		}
511edf89256SNeel Natu 	}
512366f6083SPeter Grehan 
513366f6083SPeter Grehan 	/*
514366f6083SPeter Grehan 	 * This will cause a return to userland.
515366f6083SPeter Grehan 	 */
516366f6083SPeter Grehan 	return (1);
517366f6083SPeter Grehan }
518366f6083SPeter Grehan 
519366f6083SPeter Grehan int
520366f6083SPeter Grehan vlapic_pending_intr(struct vlapic *vlapic)
521366f6083SPeter Grehan {
522366f6083SPeter Grehan 	struct LAPIC	*lapic = &vlapic->apic;
523366f6083SPeter Grehan 	int	  	 idx, i, bitpos, vector;
524366f6083SPeter Grehan 	uint32_t	*irrptr, val;
525366f6083SPeter Grehan 
526366f6083SPeter Grehan 	irrptr = &lapic->irr0;
527366f6083SPeter Grehan 
528366f6083SPeter Grehan 	/*
529366f6083SPeter Grehan 	 * The x86 architecture reserves the the first 32 vectors for use
530366f6083SPeter Grehan 	 * by the processor.
531366f6083SPeter Grehan 	 */
532366f6083SPeter Grehan 	for (i = 7; i > 0; i--) {
533366f6083SPeter Grehan 		idx = i * 4;
534366f6083SPeter Grehan 		val = atomic_load_acq_int(&irrptr[idx]);
535366f6083SPeter Grehan 		bitpos = fls(val);
536366f6083SPeter Grehan 		if (bitpos != 0) {
537366f6083SPeter Grehan 			vector = i * 32 + (bitpos - 1);
538366f6083SPeter Grehan 			if (PRIO(vector) > PRIO(lapic->ppr)) {
539366f6083SPeter Grehan 				VLAPIC_CTR1(vlapic, "pending intr %d", vector);
540366f6083SPeter Grehan 				return (vector);
541366f6083SPeter Grehan 			} else
542366f6083SPeter Grehan 				break;
543366f6083SPeter Grehan 		}
544366f6083SPeter Grehan 	}
545366f6083SPeter Grehan 	VLAPIC_CTR0(vlapic, "no pending intr");
546366f6083SPeter Grehan 	return (-1);
547366f6083SPeter Grehan }
548366f6083SPeter Grehan 
549366f6083SPeter Grehan void
550366f6083SPeter Grehan vlapic_intr_accepted(struct vlapic *vlapic, int vector)
551366f6083SPeter Grehan {
552366f6083SPeter Grehan 	struct LAPIC	*lapic = &vlapic->apic;
553366f6083SPeter Grehan 	uint32_t	*irrptr, *isrptr;
554366f6083SPeter Grehan 	int		idx, stk_top;
555366f6083SPeter Grehan 
556366f6083SPeter Grehan 	/*
557366f6083SPeter Grehan 	 * clear the ready bit for vector being accepted in irr
558366f6083SPeter Grehan 	 * and set the vector as in service in isr.
559366f6083SPeter Grehan 	 */
560366f6083SPeter Grehan 	idx = (vector / 32) * 4;
561366f6083SPeter Grehan 
562366f6083SPeter Grehan 	irrptr = &lapic->irr0;
563366f6083SPeter Grehan 	atomic_clear_int(&irrptr[idx], 1 << (vector % 32));
564366f6083SPeter Grehan 	VLAPIC_CTR_IRR(vlapic, "vlapic_intr_accepted");
565366f6083SPeter Grehan 
566366f6083SPeter Grehan 	isrptr = &lapic->isr0;
567366f6083SPeter Grehan 	isrptr[idx] |= 1 << (vector % 32);
568366f6083SPeter Grehan 	VLAPIC_CTR_ISR(vlapic, "vlapic_intr_accepted");
569366f6083SPeter Grehan 
570366f6083SPeter Grehan 	/*
571366f6083SPeter Grehan 	 * Update the PPR
572366f6083SPeter Grehan 	 */
573366f6083SPeter Grehan 	vlapic->isrvec_stk_top++;
574366f6083SPeter Grehan 
575366f6083SPeter Grehan 	stk_top = vlapic->isrvec_stk_top;
576366f6083SPeter Grehan 	if (stk_top >= ISRVEC_STK_SIZE)
577366f6083SPeter Grehan 		panic("isrvec_stk_top overflow %d", stk_top);
578366f6083SPeter Grehan 
579366f6083SPeter Grehan 	vlapic->isrvec_stk[stk_top] = vector;
580366f6083SPeter Grehan 	vlapic_update_ppr(vlapic);
581366f6083SPeter Grehan }
582366f6083SPeter Grehan 
583366f6083SPeter Grehan int
584366f6083SPeter Grehan vlapic_op_mem_read(void* dev, uint64_t gpa, opsize_t size, uint64_t *data)
585366f6083SPeter Grehan {
586366f6083SPeter Grehan 	struct vlapic 	*vlapic = (struct vlapic*)dev;
587366f6083SPeter Grehan 	struct LAPIC	*lapic = &vlapic->apic;
588366f6083SPeter Grehan 	uint64_t	 offset = gpa & ~(PAGE_SIZE);
589366f6083SPeter Grehan 	uint32_t	*reg;
590366f6083SPeter Grehan 	int		 i;
591366f6083SPeter Grehan 
592366f6083SPeter Grehan 	if (offset > sizeof(*lapic)) {
593366f6083SPeter Grehan 		*data = 0;
594366f6083SPeter Grehan 		return 0;
595366f6083SPeter Grehan 	}
596366f6083SPeter Grehan 
597366f6083SPeter Grehan 	offset &= ~3;
598366f6083SPeter Grehan 	switch(offset)
599366f6083SPeter Grehan 	{
600366f6083SPeter Grehan 		case APIC_OFFSET_ID:
6012d3a73edSNeel Natu 			if (x2apic(vlapic))
6022d3a73edSNeel Natu 				*data = vlapic->vcpuid;
6032d3a73edSNeel Natu 			else
6042d3a73edSNeel Natu 				*data = vlapic->vcpuid << 24;
605366f6083SPeter Grehan 			break;
606366f6083SPeter Grehan 		case APIC_OFFSET_VER:
607366f6083SPeter Grehan 			*data = lapic->version;
608366f6083SPeter Grehan 			break;
609366f6083SPeter Grehan 		case APIC_OFFSET_TPR:
610366f6083SPeter Grehan 			*data = lapic->tpr;
611366f6083SPeter Grehan 			break;
612366f6083SPeter Grehan 		case APIC_OFFSET_APR:
613366f6083SPeter Grehan 			*data = lapic->apr;
614366f6083SPeter Grehan 			break;
615366f6083SPeter Grehan 		case APIC_OFFSET_PPR:
616366f6083SPeter Grehan 			*data = lapic->ppr;
617366f6083SPeter Grehan 			break;
618366f6083SPeter Grehan 		case APIC_OFFSET_EOI:
619366f6083SPeter Grehan 			*data = lapic->eoi;
620366f6083SPeter Grehan 			break;
621366f6083SPeter Grehan 		case APIC_OFFSET_LDR:
622366f6083SPeter Grehan 			*data = lapic->ldr;
623366f6083SPeter Grehan 			break;
624366f6083SPeter Grehan 		case APIC_OFFSET_DFR:
625366f6083SPeter Grehan 			*data = lapic->dfr;
626366f6083SPeter Grehan 			break;
627366f6083SPeter Grehan 		case APIC_OFFSET_SVR:
628366f6083SPeter Grehan 			*data = lapic->svr;
629366f6083SPeter Grehan 			break;
630366f6083SPeter Grehan 		case APIC_OFFSET_ISR0 ... APIC_OFFSET_ISR7:
631366f6083SPeter Grehan 			i = (offset - APIC_OFFSET_ISR0) >> 2;
632366f6083SPeter Grehan 			reg = &lapic->isr0;
633366f6083SPeter Grehan 			*data = *(reg + i);
634366f6083SPeter Grehan 			break;
635366f6083SPeter Grehan 		case APIC_OFFSET_TMR0 ... APIC_OFFSET_TMR7:
636366f6083SPeter Grehan 			i = (offset - APIC_OFFSET_TMR0) >> 2;
637366f6083SPeter Grehan 			reg = &lapic->tmr0;
638366f6083SPeter Grehan 			*data = *(reg + i);
639366f6083SPeter Grehan 			break;
640366f6083SPeter Grehan 		case APIC_OFFSET_IRR0 ... APIC_OFFSET_IRR7:
641366f6083SPeter Grehan 			i = (offset - APIC_OFFSET_IRR0) >> 2;
642366f6083SPeter Grehan 			reg = &lapic->irr0;
643366f6083SPeter Grehan 			*data = atomic_load_acq_int(reg + i);
644366f6083SPeter Grehan 			break;
645366f6083SPeter Grehan 		case APIC_OFFSET_ESR:
646366f6083SPeter Grehan 			*data = lapic->esr;
647366f6083SPeter Grehan 			break;
648366f6083SPeter Grehan 		case APIC_OFFSET_ICR_LOW:
649366f6083SPeter Grehan 			*data = lapic->icr_lo;
650366f6083SPeter Grehan 			break;
651366f6083SPeter Grehan 		case APIC_OFFSET_ICR_HI:
652366f6083SPeter Grehan 			*data = lapic->icr_hi;
653366f6083SPeter Grehan 			break;
654366f6083SPeter Grehan 		case APIC_OFFSET_TIMER_LVT ... APIC_OFFSET_ERROR_LVT:
655366f6083SPeter Grehan 			reg = vlapic_get_lvt(vlapic, offset);
656366f6083SPeter Grehan 			*data = *(reg);
657366f6083SPeter Grehan 			break;
658366f6083SPeter Grehan 		case APIC_OFFSET_ICR:
659366f6083SPeter Grehan 			*data = lapic->icr_timer;
660366f6083SPeter Grehan 			break;
661366f6083SPeter Grehan 		case APIC_OFFSET_CCR:
662366f6083SPeter Grehan 			*data = vlapic_get_ccr(vlapic);
663366f6083SPeter Grehan 			break;
664366f6083SPeter Grehan 		case APIC_OFFSET_DCR:
665366f6083SPeter Grehan 			*data = lapic->dcr_timer;
666366f6083SPeter Grehan 			break;
667366f6083SPeter Grehan 		case APIC_OFFSET_RRR:
668366f6083SPeter Grehan 		default:
669366f6083SPeter Grehan 			*data = 0;
670366f6083SPeter Grehan 			break;
671366f6083SPeter Grehan 	}
672366f6083SPeter Grehan 	return 0;
673366f6083SPeter Grehan }
674366f6083SPeter Grehan 
675366f6083SPeter Grehan int
676366f6083SPeter Grehan vlapic_op_mem_write(void* dev, uint64_t gpa, opsize_t size, uint64_t data)
677366f6083SPeter Grehan {
678366f6083SPeter Grehan 	struct vlapic 	*vlapic = (struct vlapic*)dev;
679366f6083SPeter Grehan 	struct LAPIC	*lapic = &vlapic->apic;
680366f6083SPeter Grehan 	uint64_t	 offset = gpa & ~(PAGE_SIZE);
681366f6083SPeter Grehan 	uint32_t	*reg;
682366f6083SPeter Grehan 	int		retval;
683366f6083SPeter Grehan 
684366f6083SPeter Grehan 	if (offset > sizeof(*lapic)) {
685366f6083SPeter Grehan 		return 0;
686366f6083SPeter Grehan 	}
687366f6083SPeter Grehan 
688366f6083SPeter Grehan 	retval = 0;
689366f6083SPeter Grehan 	offset &= ~3;
690366f6083SPeter Grehan 	switch(offset)
691366f6083SPeter Grehan 	{
692366f6083SPeter Grehan 		case APIC_OFFSET_ID:
693366f6083SPeter Grehan 			break;
694366f6083SPeter Grehan 		case APIC_OFFSET_TPR:
695366f6083SPeter Grehan 			lapic->tpr = data & 0xff;
696366f6083SPeter Grehan 			vlapic_update_ppr(vlapic);
697366f6083SPeter Grehan 			break;
698366f6083SPeter Grehan 		case APIC_OFFSET_EOI:
699366f6083SPeter Grehan 			vlapic_process_eoi(vlapic);
700366f6083SPeter Grehan 			break;
701366f6083SPeter Grehan 		case APIC_OFFSET_LDR:
702366f6083SPeter Grehan 			break;
703366f6083SPeter Grehan 		case APIC_OFFSET_DFR:
704366f6083SPeter Grehan 			break;
705366f6083SPeter Grehan 		case APIC_OFFSET_SVR:
706366f6083SPeter Grehan 			lapic->svr = data;
707366f6083SPeter Grehan 			break;
708366f6083SPeter Grehan 		case APIC_OFFSET_ICR_LOW:
709*a2da7af6SNeel Natu 			if (!x2apic(vlapic)) {
710*a2da7af6SNeel Natu 				data &= 0xffffffff;
711*a2da7af6SNeel Natu 				data |= (uint64_t)lapic->icr_hi << 32;
712*a2da7af6SNeel Natu 			}
713366f6083SPeter Grehan 			retval = lapic_process_icr(vlapic, data);
714366f6083SPeter Grehan 			break;
715*a2da7af6SNeel Natu 		case APIC_OFFSET_ICR_HI:
716*a2da7af6SNeel Natu 			if (!x2apic(vlapic)) {
717*a2da7af6SNeel Natu 				retval = 0;
718*a2da7af6SNeel Natu 				lapic->icr_hi = data;
719*a2da7af6SNeel Natu 			}
720*a2da7af6SNeel Natu 			break;
721366f6083SPeter Grehan 		case APIC_OFFSET_TIMER_LVT ... APIC_OFFSET_ERROR_LVT:
722366f6083SPeter Grehan 			reg = vlapic_get_lvt(vlapic, offset);
723366f6083SPeter Grehan 			if (!(lapic->svr & APIC_SVR_ENABLE)) {
724366f6083SPeter Grehan 				data |= APIC_LVT_M;
725366f6083SPeter Grehan 			}
726366f6083SPeter Grehan 			*reg = data;
727366f6083SPeter Grehan 			// vlapic_dump_lvt(offset, reg);
728366f6083SPeter Grehan 			break;
729366f6083SPeter Grehan 		case APIC_OFFSET_ICR:
730366f6083SPeter Grehan 			lapic->icr_timer = data;
731366f6083SPeter Grehan 			vlapic_start_timer(vlapic, 0);
732366f6083SPeter Grehan 			break;
733366f6083SPeter Grehan 
734366f6083SPeter Grehan 		case APIC_OFFSET_DCR:
735366f6083SPeter Grehan 			lapic->dcr_timer = data;
736366f6083SPeter Grehan 			vlapic->divisor = vlapic_timer_divisor(data);
737366f6083SPeter Grehan 			break;
738366f6083SPeter Grehan 
739366f6083SPeter Grehan 		case APIC_OFFSET_ESR:
740366f6083SPeter Grehan 			vlapic_update_errors(vlapic);
741366f6083SPeter Grehan 			break;
742366f6083SPeter Grehan 		case APIC_OFFSET_VER:
743366f6083SPeter Grehan 		case APIC_OFFSET_APR:
744366f6083SPeter Grehan 		case APIC_OFFSET_PPR:
745366f6083SPeter Grehan 		case APIC_OFFSET_RRR:
746366f6083SPeter Grehan 		case APIC_OFFSET_ISR0 ... APIC_OFFSET_ISR7:
747366f6083SPeter Grehan 		case APIC_OFFSET_TMR0 ... APIC_OFFSET_TMR7:
748366f6083SPeter Grehan 		case APIC_OFFSET_IRR0 ... APIC_OFFSET_IRR7:
749366f6083SPeter Grehan 		case APIC_OFFSET_CCR:
750366f6083SPeter Grehan 		default:
751366f6083SPeter Grehan 			// Read only.
752366f6083SPeter Grehan 			break;
753366f6083SPeter Grehan 	}
754366f6083SPeter Grehan 
755366f6083SPeter Grehan 	return (retval);
756366f6083SPeter Grehan }
757366f6083SPeter Grehan 
758366f6083SPeter Grehan void
759366f6083SPeter Grehan vlapic_timer_tick(struct vlapic *vlapic)
760366f6083SPeter Grehan {
761366f6083SPeter Grehan 	int curticks, delta, periodic;
762366f6083SPeter Grehan 	uint32_t ccr;
763366f6083SPeter Grehan 	uint32_t decrement, remainder;
764366f6083SPeter Grehan 
765366f6083SPeter Grehan 	curticks = ticks;
766366f6083SPeter Grehan 
767366f6083SPeter Grehan 	/* Common case */
768366f6083SPeter Grehan 	delta = curticks - vlapic->ccr_ticks;
769366f6083SPeter Grehan 	if (delta == 0)
770366f6083SPeter Grehan 		return;
771366f6083SPeter Grehan 
772366f6083SPeter Grehan 	/* Local APIC timer is disabled */
773366f6083SPeter Grehan 	if (vlapic->apic.icr_timer == 0)
774366f6083SPeter Grehan 		return;
775366f6083SPeter Grehan 
776366f6083SPeter Grehan 	/* One-shot mode and timer has already counted down to zero */
777366f6083SPeter Grehan 	periodic = vlapic_periodic_timer(vlapic);
778366f6083SPeter Grehan 	if (!periodic && vlapic->apic.ccr_timer == 0)
779366f6083SPeter Grehan 		return;
780366f6083SPeter Grehan 	/*
781366f6083SPeter Grehan 	 * The 'curticks' and 'ccr_ticks' are out of sync by more than
782366f6083SPeter Grehan 	 * 2^31 ticks. We deal with this by restarting the timer.
783366f6083SPeter Grehan 	 */
784366f6083SPeter Grehan 	if (delta < 0) {
785366f6083SPeter Grehan 		vlapic_start_timer(vlapic, 0);
786366f6083SPeter Grehan 		return;
787366f6083SPeter Grehan 	}
788366f6083SPeter Grehan 
789366f6083SPeter Grehan 	ccr = vlapic->apic.ccr_timer;
790366f6083SPeter Grehan 	decrement = (VLAPIC_BUS_FREQ / vlapic->divisor) / hz;
791366f6083SPeter Grehan 	while (delta-- > 0) {
792366f6083SPeter Grehan 		if (ccr <= decrement) {
793366f6083SPeter Grehan 			remainder = decrement - ccr;
794366f6083SPeter Grehan 			vlapic_fire_timer(vlapic);
795366f6083SPeter Grehan 			if (periodic) {
796366f6083SPeter Grehan 				vlapic_start_timer(vlapic, remainder);
797366f6083SPeter Grehan 				ccr = vlapic->apic.ccr_timer;
798366f6083SPeter Grehan 			} else {
799366f6083SPeter Grehan 				/*
800366f6083SPeter Grehan 				 * One-shot timer has counted down to zero.
801366f6083SPeter Grehan 				 */
802366f6083SPeter Grehan 				ccr = 0;
803366f6083SPeter Grehan 				break;
804366f6083SPeter Grehan 			}
805366f6083SPeter Grehan 		} else
806366f6083SPeter Grehan 			ccr -= decrement;
807366f6083SPeter Grehan 	}
808366f6083SPeter Grehan 
809366f6083SPeter Grehan 	vlapic->ccr_ticks = curticks;
810366f6083SPeter Grehan 	vlapic->apic.ccr_timer = ccr;
811366f6083SPeter Grehan }
812366f6083SPeter Grehan 
813366f6083SPeter Grehan struct vdev_ops vlapic_dev_ops = {
814366f6083SPeter Grehan 	.name = "vlapic",
815366f6083SPeter Grehan 	.init = vlapic_op_init,
816366f6083SPeter Grehan 	.reset = vlapic_op_reset,
817366f6083SPeter Grehan 	.halt = vlapic_op_halt,
818366f6083SPeter Grehan 	.memread = vlapic_op_mem_read,
819366f6083SPeter Grehan 	.memwrite = vlapic_op_mem_write,
820366f6083SPeter Grehan };
821366f6083SPeter Grehan static struct io_region vlapic_mmio[VM_MAXCPU];
822366f6083SPeter Grehan 
823366f6083SPeter Grehan struct vlapic *
824366f6083SPeter Grehan vlapic_init(struct vm *vm, int vcpuid)
825366f6083SPeter Grehan {
826*a2da7af6SNeel Natu 	int err;
827*a2da7af6SNeel Natu 	enum x2apic_state state;
828366f6083SPeter Grehan 	struct vlapic 		*vlapic;
829366f6083SPeter Grehan 
830*a2da7af6SNeel Natu 	err = vm_get_x2apic_state(vm, vcpuid, &state);
831*a2da7af6SNeel Natu 	if (err)
832*a2da7af6SNeel Natu 		panic("vlapic_set_apicbase: err %d fetching x2apic state", err);
833*a2da7af6SNeel Natu 
834366f6083SPeter Grehan 	vlapic = malloc(sizeof(struct vlapic), M_VLAPIC, M_WAITOK | M_ZERO);
835366f6083SPeter Grehan 	vlapic->vm = vm;
836366f6083SPeter Grehan 	vlapic->vcpuid = vcpuid;
8372d3a73edSNeel Natu 
838*a2da7af6SNeel Natu 	vlapic->msr_apicbase = DEFAULT_APIC_BASE | APICBASE_ENABLED;
8392d3a73edSNeel Natu 
8402d3a73edSNeel Natu 	if (vcpuid == 0)
8412d3a73edSNeel Natu 		vlapic->msr_apicbase |= APICBASE_BSP;
8422d3a73edSNeel Natu 
843*a2da7af6SNeel Natu 	if (state == X2APIC_ENABLED)
844*a2da7af6SNeel Natu 		vlapic->msr_apicbase |= APICBASE_X2APIC;
845*a2da7af6SNeel Natu 
846366f6083SPeter Grehan 	vlapic->ops = &vlapic_dev_ops;
847366f6083SPeter Grehan 
848366f6083SPeter Grehan 	vlapic->mmio = vlapic_mmio + vcpuid;
849366f6083SPeter Grehan 	vlapic->mmio->base = DEFAULT_APIC_BASE;
850366f6083SPeter Grehan 	vlapic->mmio->len = PAGE_SIZE;
851366f6083SPeter Grehan 	vlapic->mmio->attr = MMIO_READ|MMIO_WRITE;
852366f6083SPeter Grehan 	vlapic->mmio->vcpu = vcpuid;
853366f6083SPeter Grehan 
854366f6083SPeter Grehan 	vdev_register(&vlapic_dev_ops, vlapic);
855366f6083SPeter Grehan 
856366f6083SPeter Grehan 	vlapic_op_init(vlapic);
857366f6083SPeter Grehan 
858366f6083SPeter Grehan 	return (vlapic);
859366f6083SPeter Grehan }
860366f6083SPeter Grehan 
861366f6083SPeter Grehan void
862366f6083SPeter Grehan vlapic_cleanup(struct vlapic *vlapic)
863366f6083SPeter Grehan {
864cd942e0fSPeter Grehan 	vlapic_op_halt(vlapic);
865366f6083SPeter Grehan 	vdev_unregister(vlapic);
866366f6083SPeter Grehan 	free(vlapic, M_VLAPIC);
867366f6083SPeter Grehan }
8682d3a73edSNeel Natu 
8692d3a73edSNeel Natu uint64_t
8702d3a73edSNeel Natu vlapic_get_apicbase(struct vlapic *vlapic)
8712d3a73edSNeel Natu {
8722d3a73edSNeel Natu 
8732d3a73edSNeel Natu 	return (vlapic->msr_apicbase);
8742d3a73edSNeel Natu }
8752d3a73edSNeel Natu 
8762d3a73edSNeel Natu void
8772d3a73edSNeel Natu vlapic_set_apicbase(struct vlapic *vlapic, uint64_t val)
8782d3a73edSNeel Natu {
879*a2da7af6SNeel Natu 	int err;
880*a2da7af6SNeel Natu 	enum x2apic_state state;
881*a2da7af6SNeel Natu 
882*a2da7af6SNeel Natu 	err = vm_get_x2apic_state(vlapic->vm, vlapic->vcpuid, &state);
883*a2da7af6SNeel Natu 	if (err)
884*a2da7af6SNeel Natu 		panic("vlapic_set_apicbase: err %d fetching x2apic state", err);
885*a2da7af6SNeel Natu 
886*a2da7af6SNeel Natu 	if (state == X2APIC_DISABLED)
887*a2da7af6SNeel Natu 		val &= ~APICBASE_X2APIC;
8882d3a73edSNeel Natu 
8892d3a73edSNeel Natu 	vlapic->msr_apicbase = val;
8902d3a73edSNeel Natu }
891