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