]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/amd64/vmm/io/vlapic.c
Dtrace: add optional size argument to tracemem().
[FreeBSD/FreeBSD.git] / sys / amd64 / vmm / io / vlapic.c
1 /*-
2  * Copyright (c) 2011 NetApp, Inc.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY NETAPP, INC ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL NETAPP, INC OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  *
26  * $FreeBSD$
27  */
28
29 #include <sys/cdefs.h>
30 __FBSDID("$FreeBSD$");
31
32 #include <sys/param.h>
33 #include <sys/kernel.h>
34 #include <sys/malloc.h>
35 #include <sys/systm.h>
36 #include <sys/smp.h>
37
38 #include <machine/clock.h>
39 #include <x86/specialreg.h>
40 #include <x86/apicreg.h>
41
42 #include <machine/vmm.h>
43
44 #include "vmm_lapic.h"
45 #include "vmm_ktr.h"
46 #include "vdev.h"
47 #include "vlapic.h"
48
49 #define VLAPIC_CTR0(vlapic, format)                                     \
50         VMM_CTR0((vlapic)->vm, (vlapic)->vcpuid, format)
51
52 #define VLAPIC_CTR1(vlapic, format, p1)                                 \
53         VMM_CTR1((vlapic)->vm, (vlapic)->vcpuid, format, p1)
54
55 #define VLAPIC_CTR_IRR(vlapic, msg)                                     \
56 do {                                                                    \
57         uint32_t *irrptr = &(vlapic)->apic.irr0;                        \
58         irrptr[0] = irrptr[0];  /* silence compiler */                  \
59         VLAPIC_CTR1((vlapic), msg " irr0 0x%08x", irrptr[0 << 2]);      \
60         VLAPIC_CTR1((vlapic), msg " irr1 0x%08x", irrptr[1 << 2]);      \
61         VLAPIC_CTR1((vlapic), msg " irr2 0x%08x", irrptr[2 << 2]);      \
62         VLAPIC_CTR1((vlapic), msg " irr3 0x%08x", irrptr[3 << 2]);      \
63         VLAPIC_CTR1((vlapic), msg " irr4 0x%08x", irrptr[4 << 2]);      \
64         VLAPIC_CTR1((vlapic), msg " irr5 0x%08x", irrptr[5 << 2]);      \
65         VLAPIC_CTR1((vlapic), msg " irr6 0x%08x", irrptr[6 << 2]);      \
66         VLAPIC_CTR1((vlapic), msg " irr7 0x%08x", irrptr[7 << 2]);      \
67 } while (0)
68
69 #define VLAPIC_CTR_ISR(vlapic, msg)                                     \
70 do {                                                                    \
71         uint32_t *isrptr = &(vlapic)->apic.isr0;                        \
72         isrptr[0] = isrptr[0];  /* silence compiler */                  \
73         VLAPIC_CTR1((vlapic), msg " isr0 0x%08x", isrptr[0 << 2]);      \
74         VLAPIC_CTR1((vlapic), msg " isr1 0x%08x", isrptr[1 << 2]);      \
75         VLAPIC_CTR1((vlapic), msg " isr2 0x%08x", isrptr[2 << 2]);      \
76         VLAPIC_CTR1((vlapic), msg " isr3 0x%08x", isrptr[3 << 2]);      \
77         VLAPIC_CTR1((vlapic), msg " isr4 0x%08x", isrptr[4 << 2]);      \
78         VLAPIC_CTR1((vlapic), msg " isr5 0x%08x", isrptr[5 << 2]);      \
79         VLAPIC_CTR1((vlapic), msg " isr6 0x%08x", isrptr[6 << 2]);      \
80         VLAPIC_CTR1((vlapic), msg " isr7 0x%08x", isrptr[7 << 2]);      \
81 } while (0)
82
83 static MALLOC_DEFINE(M_VLAPIC, "vlapic", "vlapic");
84
85 #define PRIO(x)                 ((x) >> 4)
86
87 #define VLAPIC_VERSION          (16)
88 #define VLAPIC_MAXLVT_ENTRIES   (5)
89
90 #define x2apic(vlapic)  (((vlapic)->msr_apicbase & APICBASE_X2APIC) ? 1 : 0)
91
92 enum boot_state {
93         BS_INIT,
94         BS_SIPI,
95         BS_RUNNING
96 };
97
98 struct vlapic {
99         struct vm               *vm;
100         int                     vcpuid;
101
102         struct io_region        *mmio;
103         struct vdev_ops         *ops;
104         struct LAPIC             apic;
105
106         int                      esr_update;
107
108         int                      divisor;
109         int                      ccr_ticks;
110
111         /*
112          * The 'isrvec_stk' is a stack of vectors injected by the local apic.
113          * A vector is popped from the stack when the processor does an EOI.
114          * The vector on the top of the stack is used to compute the
115          * Processor Priority in conjunction with the TPR.
116          */
117         uint8_t                  isrvec_stk[ISRVEC_STK_SIZE];
118         int                      isrvec_stk_top;
119
120         uint64_t                msr_apicbase;
121         enum boot_state         boot_state;
122 };
123
124 #define VLAPIC_BUS_FREQ tsc_freq
125
126 static int
127 vlapic_timer_divisor(uint32_t dcr)
128 {
129         switch (dcr & 0xB) {
130         case APIC_TDCR_2:
131                 return (2);
132         case APIC_TDCR_4:
133                 return (4);
134         case APIC_TDCR_8:
135                 return (8);
136         case APIC_TDCR_16:
137                 return (16);
138         case APIC_TDCR_32:
139                 return (32);
140         case APIC_TDCR_64:
141                 return (64);
142         case APIC_TDCR_128:
143                 return (128);
144         default:
145                 panic("vlapic_timer_divisor: invalid dcr 0x%08x", dcr);
146         }
147 }
148
149 static void
150 vlapic_mask_lvts(uint32_t *lvts, int num_lvt)
151 {
152         int i;
153         for (i = 0; i < num_lvt; i++) {
154                 *lvts |= APIC_LVT_M;
155                 lvts += 4;
156         }
157 }
158
159 #if 0
160 static inline void
161 vlapic_dump_lvt(uint32_t offset, uint32_t *lvt)
162 {
163         printf("Offset %x: lvt %08x (V:%02x DS:%x M:%x)\n", offset,
164             *lvt, *lvt & APIC_LVTT_VECTOR, *lvt & APIC_LVTT_DS,
165             *lvt & APIC_LVTT_M);
166 }
167 #endif
168
169 static uint64_t
170 vlapic_get_ccr(struct vlapic *vlapic)
171 {
172         struct LAPIC    *lapic = &vlapic->apic;
173         return lapic->ccr_timer;
174 }
175
176 static void
177 vlapic_update_errors(struct vlapic *vlapic)
178 {
179         struct LAPIC    *lapic = &vlapic->apic;
180         lapic->esr = 0; // XXX 
181 }
182
183 static void
184 vlapic_init_ipi(struct vlapic *vlapic)
185 {
186         struct LAPIC    *lapic = &vlapic->apic;
187         lapic->version = VLAPIC_VERSION;
188         lapic->version |= (VLAPIC_MAXLVT_ENTRIES < MAXLVTSHIFT);
189         lapic->dfr = 0xffffffff;
190         lapic->svr = APIC_SVR_VECTOR;
191         vlapic_mask_lvts(&lapic->lvt_timer, VLAPIC_MAXLVT_ENTRIES+1);
192 }
193
194 static int
195 vlapic_op_reset(void* dev)
196 {
197         struct vlapic   *vlapic = (struct vlapic*)dev;
198         struct LAPIC    *lapic = &vlapic->apic;
199
200         memset(lapic, 0, sizeof(*lapic));
201         lapic->apr = vlapic->vcpuid;
202         vlapic_init_ipi(vlapic);
203         vlapic->divisor = vlapic_timer_divisor(lapic->dcr_timer);
204
205         if (vlapic->vcpuid == 0)
206                 vlapic->boot_state = BS_RUNNING;        /* BSP */
207         else
208                 vlapic->boot_state = BS_INIT;           /* AP */
209         
210         return 0;
211
212 }
213
214 static int
215 vlapic_op_init(void* dev)
216 {
217         struct vlapic *vlapic = (struct vlapic*)dev;
218         vdev_register_region(vlapic->ops, vlapic, vlapic->mmio);
219         return vlapic_op_reset(dev);
220 }
221
222 static int
223 vlapic_op_halt(void* dev)
224 {
225         struct vlapic *vlapic = (struct vlapic*)dev;
226         vdev_unregister_region(vlapic, vlapic->mmio);
227         return 0;
228
229 }
230
231 void
232 vlapic_set_intr_ready(struct vlapic *vlapic, int vector)
233 {
234         struct LAPIC    *lapic = &vlapic->apic;
235         uint32_t        *irrptr;
236         int             idx;
237
238         if (vector < 0 || vector >= 256)
239                 panic("vlapic_set_intr_ready: invalid vector %d\n", vector);
240
241         idx = (vector / 32) * 4;
242         irrptr = &lapic->irr0;
243         atomic_set_int(&irrptr[idx], 1 << (vector % 32));
244         VLAPIC_CTR_IRR(vlapic, "vlapic_set_intr_ready");
245 }
246
247 static void
248 vlapic_start_timer(struct vlapic *vlapic, uint32_t elapsed)
249 {
250         uint32_t icr_timer;
251
252         icr_timer = vlapic->apic.icr_timer;
253
254         vlapic->ccr_ticks = ticks;
255         if (elapsed < icr_timer)
256                 vlapic->apic.ccr_timer = icr_timer - elapsed;
257         else {
258                 /*
259                  * This can happen when the guest is trying to run its local
260                  * apic timer higher that the setting of 'hz' in the host.
261                  *
262                  * We deal with this by running the guest local apic timer
263                  * at the rate of the host's 'hz' setting.
264                  */
265                 vlapic->apic.ccr_timer = 0;
266         }
267 }
268
269 static __inline uint32_t *
270 vlapic_get_lvt(struct vlapic *vlapic, uint32_t offset)
271 {
272         struct LAPIC    *lapic = &vlapic->apic;
273         int              i;
274
275         if (offset < APIC_OFFSET_TIMER_LVT || offset > APIC_OFFSET_ERROR_LVT) {
276                 panic("vlapic_get_lvt: invalid LVT\n");
277         }
278         i = (offset - APIC_OFFSET_TIMER_LVT) >> 2;
279         return ((&lapic->lvt_timer) + i);;
280 }
281
282 #if 1
283 static void
284 dump_isrvec_stk(struct vlapic *vlapic)
285 {
286         int i;
287         uint32_t *isrptr;
288
289         isrptr = &vlapic->apic.isr0;
290         for (i = 0; i < 8; i++)
291                 printf("ISR%d 0x%08x\n", i, isrptr[i * 4]);
292
293         for (i = 0; i <= vlapic->isrvec_stk_top; i++)
294                 printf("isrvec_stk[%d] = %d\n", i, vlapic->isrvec_stk[i]);
295 }
296 #endif
297
298 /*
299  * Algorithm adopted from section "Interrupt, Task and Processor Priority"
300  * in Intel Architecture Manual Vol 3a.
301  */
302 static void
303 vlapic_update_ppr(struct vlapic *vlapic)
304 {
305         int isrvec, tpr, ppr;
306
307         /*
308          * Note that the value on the stack at index 0 is always 0.
309          *
310          * This is a placeholder for the value of ISRV when none of the
311          * bits is set in the ISRx registers.
312          */
313         isrvec = vlapic->isrvec_stk[vlapic->isrvec_stk_top];
314         tpr = vlapic->apic.tpr;
315
316 #if 1
317         {
318                 int i, lastprio, curprio, vector, idx;
319                 uint32_t *isrptr;
320
321                 if (vlapic->isrvec_stk_top == 0 && isrvec != 0)
322                         panic("isrvec_stk is corrupted: %d", isrvec);
323
324                 /*
325                  * Make sure that the priority of the nested interrupts is
326                  * always increasing.
327                  */
328                 lastprio = -1;
329                 for (i = 1; i <= vlapic->isrvec_stk_top; i++) {
330                         curprio = PRIO(vlapic->isrvec_stk[i]);
331                         if (curprio <= lastprio) {
332                                 dump_isrvec_stk(vlapic);
333                                 panic("isrvec_stk does not satisfy invariant");
334                         }
335                         lastprio = curprio;
336                 }
337
338                 /*
339                  * Make sure that each bit set in the ISRx registers has a
340                  * corresponding entry on the isrvec stack.
341                  */
342                 i = 1;
343                 isrptr = &vlapic->apic.isr0;
344                 for (vector = 0; vector < 256; vector++) {
345                         idx = (vector / 32) * 4;
346                         if (isrptr[idx] & (1 << (vector % 32))) {
347                                 if (i > vlapic->isrvec_stk_top ||
348                                     vlapic->isrvec_stk[i] != vector) {
349                                         dump_isrvec_stk(vlapic);
350                                         panic("ISR and isrvec_stk out of sync");
351                                 }
352                                 i++;
353                         }
354                 }
355         }
356 #endif
357
358         if (PRIO(tpr) >= PRIO(isrvec))
359                 ppr = tpr;
360         else
361                 ppr = isrvec & 0xf0;
362
363         vlapic->apic.ppr = ppr;
364         VLAPIC_CTR1(vlapic, "vlapic_update_ppr 0x%02x", ppr);
365 }
366
367 static void
368 vlapic_process_eoi(struct vlapic *vlapic)
369 {
370         struct LAPIC    *lapic = &vlapic->apic;
371         uint32_t        *isrptr;
372         int             i, idx, bitpos;
373
374         isrptr = &lapic->isr0;
375
376         /*
377          * The x86 architecture reserves the the first 32 vectors for use
378          * by the processor.
379          */
380         for (i = 7; i > 0; i--) {
381                 idx = i * 4;
382                 bitpos = fls(isrptr[idx]);
383                 if (bitpos != 0) {
384                         if (vlapic->isrvec_stk_top <= 0) {
385                                 panic("invalid vlapic isrvec_stk_top %d",
386                                       vlapic->isrvec_stk_top);
387                         }
388                         isrptr[idx] &= ~(1 << (bitpos - 1));
389                         VLAPIC_CTR_ISR(vlapic, "vlapic_process_eoi");
390                         vlapic->isrvec_stk_top--;
391                         vlapic_update_ppr(vlapic);
392                         return;
393                 }
394         }
395 }
396
397 static __inline int
398 vlapic_get_lvt_field(uint32_t *lvt, uint32_t mask)
399 {
400         return (*lvt & mask);
401 }
402
403 static __inline int
404 vlapic_periodic_timer(struct vlapic *vlapic)
405 {
406         uint32_t *lvt;
407         
408         lvt = vlapic_get_lvt(vlapic, APIC_OFFSET_TIMER_LVT);
409
410         return (vlapic_get_lvt_field(lvt, APIC_LVTT_TM_PERIODIC));
411 }
412
413 static void
414 vlapic_fire_timer(struct vlapic *vlapic)
415 {
416         int vector;
417         uint32_t *lvt;
418         
419         lvt = vlapic_get_lvt(vlapic, APIC_OFFSET_TIMER_LVT);
420
421         if (!vlapic_get_lvt_field(lvt, APIC_LVTT_M)) {
422                 vector = vlapic_get_lvt_field(lvt,APIC_LVTT_VECTOR);
423                 vlapic_set_intr_ready(vlapic, vector);
424         }
425 }
426
427 static int
428 lapic_process_icr(struct vlapic *vlapic, uint64_t icrval)
429 {
430         int i;
431         cpuset_t dmask;
432         uint32_t dest, vec, mode;
433         struct vlapic *vlapic2;
434         struct vm_exit *vmexit;
435         
436         if (x2apic(vlapic))
437                 dest = icrval >> 32;
438         else
439                 dest = icrval >> (32 + 24);
440         vec = icrval & APIC_VECTOR_MASK;
441         mode = icrval & APIC_DELMODE_MASK;
442
443         if (mode == APIC_DELMODE_FIXED || mode == APIC_DELMODE_NMI) {
444                 switch (icrval & APIC_DEST_MASK) {
445                 case APIC_DEST_DESTFLD:
446                         CPU_SETOF(dest, &dmask);
447                         break;
448                 case APIC_DEST_SELF:
449                         CPU_SETOF(vlapic->vcpuid, &dmask);
450                         break;
451                 case APIC_DEST_ALLISELF:
452                         dmask = vm_active_cpus(vlapic->vm);
453                         break;
454                 case APIC_DEST_ALLESELF:
455                         dmask = vm_active_cpus(vlapic->vm);
456                         CPU_CLR(vlapic->vcpuid, &dmask);
457                         break;
458                 }
459
460                 while ((i = cpusetobj_ffs(&dmask)) != 0) {
461                         i--;
462                         CPU_CLR(i, &dmask);
463                         if (mode == APIC_DELMODE_FIXED)
464                                 lapic_set_intr(vlapic->vm, i, vec);
465                         else
466                                 vm_inject_nmi(vlapic->vm, i);
467                 }
468
469                 return (0);     /* handled completely in the kernel */
470         }
471
472         if (mode == APIC_DELMODE_INIT) {
473                 if ((icrval & APIC_LEVEL_MASK) == APIC_LEVEL_DEASSERT)
474                         return (0);
475
476                 if (vlapic->vcpuid == 0 && dest != 0 && dest < VM_MAXCPU) {
477                         vlapic2 = vm_lapic(vlapic->vm, dest);
478
479                         /* move from INIT to waiting-for-SIPI state */
480                         if (vlapic2->boot_state == BS_INIT) {
481                                 vlapic2->boot_state = BS_SIPI;
482                         }
483
484                         return (0);
485                 }
486         }
487
488         if (mode == APIC_DELMODE_STARTUP) {
489                 if (vlapic->vcpuid == 0 && dest != 0 && dest < VM_MAXCPU) {
490                         vlapic2 = vm_lapic(vlapic->vm, dest);
491
492                         /*
493                          * Ignore SIPIs in any state other than wait-for-SIPI
494                          */
495                         if (vlapic2->boot_state != BS_SIPI)
496                                 return (0);
497
498                         vmexit = vm_exitinfo(vlapic->vm, vlapic->vcpuid);
499                         vmexit->exitcode = VM_EXITCODE_SPINUP_AP;
500                         vmexit->u.spinup_ap.vcpu = dest;
501                         vmexit->u.spinup_ap.rip = vec << PAGE_SHIFT;
502
503                         /*
504                          * XXX this assumes that the startup IPI always succeeds
505                          */
506                         vlapic2->boot_state = BS_RUNNING;
507                         vm_activate_cpu(vlapic2->vm, dest);
508
509                         return (0);
510                 }
511         }
512
513         /*
514          * This will cause a return to userland.
515          */
516         return (1);
517 }
518
519 int
520 vlapic_pending_intr(struct vlapic *vlapic)
521 {
522         struct LAPIC    *lapic = &vlapic->apic;
523         int              idx, i, bitpos, vector;
524         uint32_t        *irrptr, val;
525
526         irrptr = &lapic->irr0;
527
528         /*
529          * The x86 architecture reserves the the first 32 vectors for use
530          * by the processor.
531          */
532         for (i = 7; i > 0; i--) {
533                 idx = i * 4;
534                 val = atomic_load_acq_int(&irrptr[idx]);
535                 bitpos = fls(val);
536                 if (bitpos != 0) {
537                         vector = i * 32 + (bitpos - 1);
538                         if (PRIO(vector) > PRIO(lapic->ppr)) {
539                                 VLAPIC_CTR1(vlapic, "pending intr %d", vector);
540                                 return (vector);
541                         } else 
542                                 break;
543                 }
544         }
545         VLAPIC_CTR0(vlapic, "no pending intr");
546         return (-1);
547 }
548
549 void
550 vlapic_intr_accepted(struct vlapic *vlapic, int vector)
551 {
552         struct LAPIC    *lapic = &vlapic->apic;
553         uint32_t        *irrptr, *isrptr;
554         int             idx, stk_top;
555
556         /*
557          * clear the ready bit for vector being accepted in irr 
558          * and set the vector as in service in isr.
559          */
560         idx = (vector / 32) * 4;
561
562         irrptr = &lapic->irr0;
563         atomic_clear_int(&irrptr[idx], 1 << (vector % 32));
564         VLAPIC_CTR_IRR(vlapic, "vlapic_intr_accepted");
565
566         isrptr = &lapic->isr0;
567         isrptr[idx] |= 1 << (vector % 32);
568         VLAPIC_CTR_ISR(vlapic, "vlapic_intr_accepted");
569
570         /*
571          * Update the PPR
572          */
573         vlapic->isrvec_stk_top++;
574
575         stk_top = vlapic->isrvec_stk_top;
576         if (stk_top >= ISRVEC_STK_SIZE)
577                 panic("isrvec_stk_top overflow %d", stk_top);
578
579         vlapic->isrvec_stk[stk_top] = vector;
580         vlapic_update_ppr(vlapic);
581 }
582
583 int
584 vlapic_op_mem_read(void* dev, uint64_t gpa, opsize_t size, uint64_t *data)
585 {
586         struct vlapic   *vlapic = (struct vlapic*)dev;
587         struct LAPIC    *lapic = &vlapic->apic;
588         uint64_t         offset = gpa & ~(PAGE_SIZE);
589         uint32_t        *reg;
590         int              i;
591
592         if (offset > sizeof(*lapic)) {
593                 *data = 0;
594                 return 0;
595         }
596         
597         offset &= ~3;
598         switch(offset)
599         {
600                 case APIC_OFFSET_ID:
601                         if (x2apic(vlapic))
602                                 *data = vlapic->vcpuid;
603                         else
604                                 *data = vlapic->vcpuid << 24;
605                         break;
606                 case APIC_OFFSET_VER:
607                         *data = lapic->version;
608                         break;
609                 case APIC_OFFSET_TPR:
610                         *data = lapic->tpr;
611                         break;
612                 case APIC_OFFSET_APR:
613                         *data = lapic->apr;
614                         break;
615                 case APIC_OFFSET_PPR:
616                         *data = lapic->ppr;
617                         break;
618                 case APIC_OFFSET_EOI:
619                         *data = lapic->eoi;
620                         break;
621                 case APIC_OFFSET_LDR:
622                         *data = lapic->ldr;
623                         break;
624                 case APIC_OFFSET_DFR:
625                         *data = lapic->dfr;
626                         break;
627                 case APIC_OFFSET_SVR:
628                         *data = lapic->svr;
629                         break;
630                 case APIC_OFFSET_ISR0 ... APIC_OFFSET_ISR7:
631                         i = (offset - APIC_OFFSET_ISR0) >> 2;
632                         reg = &lapic->isr0;
633                         *data = *(reg + i);
634                         break;
635                 case APIC_OFFSET_TMR0 ... APIC_OFFSET_TMR7:
636                         i = (offset - APIC_OFFSET_TMR0) >> 2;
637                         reg = &lapic->tmr0;
638                         *data = *(reg + i);
639                         break;
640                 case APIC_OFFSET_IRR0 ... APIC_OFFSET_IRR7:
641                         i = (offset - APIC_OFFSET_IRR0) >> 2;
642                         reg = &lapic->irr0;
643                         *data = atomic_load_acq_int(reg + i);
644                         break;
645                 case APIC_OFFSET_ESR:
646                         *data = lapic->esr;
647                         break;
648                 case APIC_OFFSET_ICR_LOW: 
649                         *data = lapic->icr_lo;
650                         break;
651                 case APIC_OFFSET_ICR_HI: 
652                         *data = lapic->icr_hi;
653                         break;
654                 case APIC_OFFSET_TIMER_LVT ... APIC_OFFSET_ERROR_LVT:
655                         reg = vlapic_get_lvt(vlapic, offset);   
656                         *data = *(reg);
657                         break;
658                 case APIC_OFFSET_ICR:
659                         *data = lapic->icr_timer;
660                         break;
661                 case APIC_OFFSET_CCR:
662                         *data = vlapic_get_ccr(vlapic);
663                         break;
664                 case APIC_OFFSET_DCR:
665                         *data = lapic->dcr_timer;
666                         break;
667                 case APIC_OFFSET_RRR:
668                 default:
669                         *data = 0;
670                         break;
671         }
672         return 0;
673 }
674
675 int
676 vlapic_op_mem_write(void* dev, uint64_t gpa, opsize_t size, uint64_t data)
677 {
678         struct vlapic   *vlapic = (struct vlapic*)dev;
679         struct LAPIC    *lapic = &vlapic->apic;
680         uint64_t         offset = gpa & ~(PAGE_SIZE);
681         uint32_t        *reg;
682         int             retval;
683
684         if (offset > sizeof(*lapic)) {
685                 return 0;
686         }
687
688         retval = 0;
689         offset &= ~3;
690         switch(offset)
691         {
692                 case APIC_OFFSET_ID:
693                         break;
694                 case APIC_OFFSET_TPR:
695                         lapic->tpr = data & 0xff;
696                         vlapic_update_ppr(vlapic);
697                         break;
698                 case APIC_OFFSET_EOI:
699                         vlapic_process_eoi(vlapic);
700                         break;
701                 case APIC_OFFSET_LDR:
702                         break;
703                 case APIC_OFFSET_DFR:
704                         break;
705                 case APIC_OFFSET_SVR:
706                         lapic->svr = data;
707                         break;
708                 case APIC_OFFSET_ICR_LOW: 
709                         if (!x2apic(vlapic)) {
710                                 data &= 0xffffffff;
711                                 data |= (uint64_t)lapic->icr_hi << 32;
712                         }
713                         retval = lapic_process_icr(vlapic, data);
714                         break;
715                 case APIC_OFFSET_ICR_HI:
716                         if (!x2apic(vlapic)) {
717                                 retval = 0;
718                                 lapic->icr_hi = data;
719                         }
720                         break;
721                 case APIC_OFFSET_TIMER_LVT ... APIC_OFFSET_ERROR_LVT:
722                         reg = vlapic_get_lvt(vlapic, offset);   
723                         if (!(lapic->svr & APIC_SVR_ENABLE)) {
724                                 data |= APIC_LVT_M;
725                         }
726                         *reg = data;
727                         // vlapic_dump_lvt(offset, reg);
728                         break;
729                 case APIC_OFFSET_ICR:
730                         lapic->icr_timer = data;
731                         vlapic_start_timer(vlapic, 0);
732                         break;
733
734                 case APIC_OFFSET_DCR:
735                         lapic->dcr_timer = data;
736                         vlapic->divisor = vlapic_timer_divisor(data);
737                         break;
738
739                 case APIC_OFFSET_ESR:
740                         vlapic_update_errors(vlapic);
741                         break;
742                 case APIC_OFFSET_VER:
743                 case APIC_OFFSET_APR:
744                 case APIC_OFFSET_PPR:
745                 case APIC_OFFSET_RRR:
746                 case APIC_OFFSET_ISR0 ... APIC_OFFSET_ISR7:
747                 case APIC_OFFSET_TMR0 ... APIC_OFFSET_TMR7:
748                 case APIC_OFFSET_IRR0 ... APIC_OFFSET_IRR7:
749                 case APIC_OFFSET_CCR:
750                 default:
751                         // Read only.
752                         break;
753         }
754
755         return (retval);
756 }
757
758 int
759 vlapic_timer_tick(struct vlapic *vlapic)
760 {
761         int curticks, delta, periodic, fired;
762         uint32_t ccr;
763         uint32_t decrement, leftover;
764
765 restart:
766         curticks = ticks;
767         delta = curticks - vlapic->ccr_ticks;
768
769         /* Local APIC timer is disabled */
770         if (vlapic->apic.icr_timer == 0)
771                 return (-1);
772
773         /* One-shot mode and timer has already counted down to zero */
774         periodic = vlapic_periodic_timer(vlapic);
775         if (!periodic && vlapic->apic.ccr_timer == 0)
776                 return (-1);
777         /*
778          * The 'curticks' and 'ccr_ticks' are out of sync by more than
779          * 2^31 ticks. We deal with this by restarting the timer.
780          */
781         if (delta < 0) {
782                 vlapic_start_timer(vlapic, 0);
783                 goto restart;
784         }
785
786         fired = 0;
787         decrement = (VLAPIC_BUS_FREQ / vlapic->divisor) / hz;
788
789         vlapic->ccr_ticks = curticks;
790         ccr = vlapic->apic.ccr_timer;
791
792         while (delta-- > 0) {
793                 if (ccr > decrement) {
794                         ccr -= decrement;
795                         continue;
796                 }
797
798                 /* Trigger the local apic timer interrupt */
799                 vlapic_fire_timer(vlapic);
800                 if (periodic) {
801                         leftover = decrement - ccr;
802                         vlapic_start_timer(vlapic, leftover);
803                         ccr = vlapic->apic.ccr_timer;
804                 } else {
805                         /*
806                          * One-shot timer has counted down to zero.
807                          */
808                         ccr = 0;
809                 }
810                 fired = 1;
811                 break;
812         }
813
814         vlapic->apic.ccr_timer = ccr;
815
816         if (!fired)
817                 return ((ccr / decrement) + 1);
818         else
819                 return (0);
820 }
821
822 struct vdev_ops vlapic_dev_ops = {
823         .name = "vlapic",
824         .init = vlapic_op_init,
825         .reset = vlapic_op_reset,
826         .halt = vlapic_op_halt,
827         .memread = vlapic_op_mem_read,
828         .memwrite = vlapic_op_mem_write,
829 };
830 static struct io_region vlapic_mmio[VM_MAXCPU];
831
832 struct vlapic *
833 vlapic_init(struct vm *vm, int vcpuid)
834 {
835         struct vlapic           *vlapic;
836
837         vlapic = malloc(sizeof(struct vlapic), M_VLAPIC, M_WAITOK | M_ZERO);
838         vlapic->vm = vm;
839         vlapic->vcpuid = vcpuid;
840
841         vlapic->msr_apicbase = DEFAULT_APIC_BASE | APICBASE_ENABLED;
842
843         if (vcpuid == 0)
844                 vlapic->msr_apicbase |= APICBASE_BSP;
845
846         vlapic->ops = &vlapic_dev_ops;
847
848         vlapic->mmio = vlapic_mmio + vcpuid;
849         vlapic->mmio->base = DEFAULT_APIC_BASE;
850         vlapic->mmio->len = PAGE_SIZE;
851         vlapic->mmio->attr = MMIO_READ|MMIO_WRITE;
852         vlapic->mmio->vcpu = vcpuid;
853
854         vdev_register(&vlapic_dev_ops, vlapic);
855
856         vlapic_op_init(vlapic);
857
858         return (vlapic);
859 }
860
861 void
862 vlapic_cleanup(struct vlapic *vlapic)
863 {
864         vlapic_op_halt(vlapic);
865         vdev_unregister(vlapic);
866         free(vlapic, M_VLAPIC);
867 }
868
869 uint64_t
870 vlapic_get_apicbase(struct vlapic *vlapic)
871 {
872
873         return (vlapic->msr_apicbase);
874 }
875
876 void
877 vlapic_set_apicbase(struct vlapic *vlapic, uint64_t val)
878 {
879         int err;
880         enum x2apic_state state;
881
882         err = vm_get_x2apic_state(vlapic->vm, vlapic->vcpuid, &state);
883         if (err)
884                 panic("vlapic_set_apicbase: err %d fetching x2apic state", err);
885
886         if (state == X2APIC_DISABLED)
887                 val &= ~APICBASE_X2APIC;
888
889         vlapic->msr_apicbase = val;
890 }
891
892 void
893 vlapic_set_x2apic_state(struct vm *vm, int vcpuid, enum x2apic_state state)
894 {
895         struct vlapic *vlapic;
896
897         vlapic = vm_lapic(vm, vcpuid);
898
899         if (state == X2APIC_DISABLED)
900                 vlapic->msr_apicbase &= ~APICBASE_X2APIC;
901 }