1*967264cfSMark Johnston /*- 2*967264cfSMark Johnston * SPDX-License-Identifier: BSD-2-Clause 3*967264cfSMark Johnston * 4*967264cfSMark Johnston * Copyright (c) 2011 NetApp, Inc. 5*967264cfSMark Johnston * All rights reserved. 6*967264cfSMark Johnston * 7*967264cfSMark Johnston * Redistribution and use in source and binary forms, with or without 8*967264cfSMark Johnston * modification, are permitted provided that the following conditions 9*967264cfSMark Johnston * are met: 10*967264cfSMark Johnston * 1. Redistributions of source code must retain the above copyright 11*967264cfSMark Johnston * notice, this list of conditions and the following disclaimer. 12*967264cfSMark Johnston * 2. Redistributions in binary form must reproduce the above copyright 13*967264cfSMark Johnston * notice, this list of conditions and the following disclaimer in the 14*967264cfSMark Johnston * documentation and/or other materials provided with the distribution. 15*967264cfSMark Johnston * 16*967264cfSMark Johnston * THIS SOFTWARE IS PROVIDED BY NETAPP, INC ``AS IS'' AND 17*967264cfSMark Johnston * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18*967264cfSMark Johnston * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19*967264cfSMark Johnston * ARE DISCLAIMED. IN NO EVENT SHALL NETAPP, INC OR CONTRIBUTORS BE LIABLE 20*967264cfSMark Johnston * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21*967264cfSMark Johnston * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22*967264cfSMark Johnston * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23*967264cfSMark Johnston * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24*967264cfSMark Johnston * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25*967264cfSMark Johnston * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26*967264cfSMark Johnston * SUCH DAMAGE. 27*967264cfSMark Johnston */ 28*967264cfSMark Johnston 29*967264cfSMark Johnston #include <sys/types.h> 30*967264cfSMark Johnston 31*967264cfSMark Johnston #include <machine/specialreg.h> 32*967264cfSMark Johnston #include <machine/segments.h> 33*967264cfSMark Johnston #include <machine/vmm.h> 34*967264cfSMark Johnston 35*967264cfSMark Johnston #include <errno.h> 36*967264cfSMark Johnston #include <string.h> 37*967264cfSMark Johnston 38*967264cfSMark Johnston #include "vmmapi.h" 39*967264cfSMark Johnston #include "internal.h" 40*967264cfSMark Johnston 41*967264cfSMark Johnston #define I386_TSS_SIZE 104 42*967264cfSMark Johnston 43*967264cfSMark Johnston #define DESC_PRESENT 0x00000080 44*967264cfSMark Johnston #define DESC_LONGMODE 0x00002000 45*967264cfSMark Johnston #define DESC_DEF32 0x00004000 46*967264cfSMark Johnston #define DESC_GRAN 0x00008000 47*967264cfSMark Johnston #define DESC_UNUSABLE 0x00010000 48*967264cfSMark Johnston 49*967264cfSMark Johnston #define GUEST_NULL_SEL 0 50*967264cfSMark Johnston #define GUEST_CODE_SEL 1 51*967264cfSMark Johnston #define GUEST_DATA_SEL 2 52*967264cfSMark Johnston #define GUEST_TSS_SEL 3 53*967264cfSMark Johnston #define GUEST_GDTR_LIMIT64 (3 * 8 - 1) 54*967264cfSMark Johnston 55*967264cfSMark Johnston static struct segment_descriptor i386_gdt[] = { 56*967264cfSMark Johnston {}, /* NULL */ 57*967264cfSMark Johnston { .sd_lolimit = 0xffff, .sd_type = SDT_MEMER, /* CODE */ 58*967264cfSMark Johnston .sd_p = 1, .sd_hilimit = 0xf, .sd_def32 = 1, .sd_gran = 1 }, 59*967264cfSMark Johnston { .sd_lolimit = 0xffff, .sd_type = SDT_MEMRW, /* DATA */ 60*967264cfSMark Johnston .sd_p = 1, .sd_hilimit = 0xf, .sd_def32 = 1, .sd_gran = 1 }, 61*967264cfSMark Johnston { .sd_lolimit = I386_TSS_SIZE - 1, /* TSS */ 62*967264cfSMark Johnston .sd_type = SDT_SYS386TSS, .sd_p = 1 } 63*967264cfSMark Johnston }; 64*967264cfSMark Johnston 65*967264cfSMark Johnston /* 66*967264cfSMark Johnston * Setup the 'vcpu' register set such that it will begin execution at 67*967264cfSMark Johnston * 'eip' in flat mode. 68*967264cfSMark Johnston */ 69*967264cfSMark Johnston int 70*967264cfSMark Johnston vm_setup_freebsd_registers_i386(struct vcpu *vcpu, uint32_t eip, 71*967264cfSMark Johnston uint32_t gdtbase, uint32_t esp) 72*967264cfSMark Johnston { 73*967264cfSMark Johnston uint64_t cr0, rflags, desc_base; 74*967264cfSMark Johnston uint32_t desc_access, desc_limit, tssbase; 75*967264cfSMark Johnston uint16_t gsel; 76*967264cfSMark Johnston struct segment_descriptor *gdt; 77*967264cfSMark Johnston int error, tmp; 78*967264cfSMark Johnston 79*967264cfSMark Johnston /* A 32-bit guest requires unrestricted mode. */ 80*967264cfSMark Johnston error = vm_get_capability(vcpu, VM_CAP_UNRESTRICTED_GUEST, &tmp); 81*967264cfSMark Johnston if (error) 82*967264cfSMark Johnston goto done; 83*967264cfSMark Johnston error = vm_set_capability(vcpu, VM_CAP_UNRESTRICTED_GUEST, 1); 84*967264cfSMark Johnston if (error) 85*967264cfSMark Johnston goto done; 86*967264cfSMark Johnston 87*967264cfSMark Johnston cr0 = CR0_PE | CR0_NE; 88*967264cfSMark Johnston if ((error = vm_set_register(vcpu, VM_REG_GUEST_CR0, cr0)) != 0) 89*967264cfSMark Johnston goto done; 90*967264cfSMark Johnston 91*967264cfSMark Johnston if ((error = vm_set_register(vcpu, VM_REG_GUEST_CR4, 0)) != 0) 92*967264cfSMark Johnston goto done; 93*967264cfSMark Johnston 94*967264cfSMark Johnston /* 95*967264cfSMark Johnston * Forcing EFER to 0 causes bhyve to clear the "IA-32e guest 96*967264cfSMark Johnston * mode" entry control. 97*967264cfSMark Johnston */ 98*967264cfSMark Johnston if ((error = vm_set_register(vcpu, VM_REG_GUEST_EFER, 0))) 99*967264cfSMark Johnston goto done; 100*967264cfSMark Johnston 101*967264cfSMark Johnston gdt = vm_map_gpa(vcpu->ctx, gdtbase, 0x1000); 102*967264cfSMark Johnston if (gdt == NULL) 103*967264cfSMark Johnston return (EFAULT); 104*967264cfSMark Johnston memcpy(gdt, i386_gdt, sizeof(i386_gdt)); 105*967264cfSMark Johnston desc_base = gdtbase; 106*967264cfSMark Johnston desc_limit = sizeof(i386_gdt) - 1; 107*967264cfSMark Johnston error = vm_set_desc(vcpu, VM_REG_GUEST_GDTR, 108*967264cfSMark Johnston desc_base, desc_limit, 0); 109*967264cfSMark Johnston if (error != 0) 110*967264cfSMark Johnston goto done; 111*967264cfSMark Johnston 112*967264cfSMark Johnston /* Place the TSS one page above the GDT. */ 113*967264cfSMark Johnston tssbase = gdtbase + 0x1000; 114*967264cfSMark Johnston gdt[3].sd_lobase = tssbase; 115*967264cfSMark Johnston 116*967264cfSMark Johnston rflags = 0x2; 117*967264cfSMark Johnston error = vm_set_register(vcpu, VM_REG_GUEST_RFLAGS, rflags); 118*967264cfSMark Johnston if (error) 119*967264cfSMark Johnston goto done; 120*967264cfSMark Johnston 121*967264cfSMark Johnston desc_base = 0; 122*967264cfSMark Johnston desc_limit = 0xffffffff; 123*967264cfSMark Johnston desc_access = DESC_GRAN | DESC_DEF32 | DESC_PRESENT | SDT_MEMERA; 124*967264cfSMark Johnston error = vm_set_desc(vcpu, VM_REG_GUEST_CS, 125*967264cfSMark Johnston desc_base, desc_limit, desc_access); 126*967264cfSMark Johnston 127*967264cfSMark Johnston desc_access = DESC_GRAN | DESC_DEF32 | DESC_PRESENT | SDT_MEMRWA; 128*967264cfSMark Johnston error = vm_set_desc(vcpu, VM_REG_GUEST_DS, 129*967264cfSMark Johnston desc_base, desc_limit, desc_access); 130*967264cfSMark Johnston if (error) 131*967264cfSMark Johnston goto done; 132*967264cfSMark Johnston 133*967264cfSMark Johnston error = vm_set_desc(vcpu, VM_REG_GUEST_ES, 134*967264cfSMark Johnston desc_base, desc_limit, desc_access); 135*967264cfSMark Johnston if (error) 136*967264cfSMark Johnston goto done; 137*967264cfSMark Johnston 138*967264cfSMark Johnston error = vm_set_desc(vcpu, VM_REG_GUEST_FS, 139*967264cfSMark Johnston desc_base, desc_limit, desc_access); 140*967264cfSMark Johnston if (error) 141*967264cfSMark Johnston goto done; 142*967264cfSMark Johnston 143*967264cfSMark Johnston error = vm_set_desc(vcpu, VM_REG_GUEST_GS, 144*967264cfSMark Johnston desc_base, desc_limit, desc_access); 145*967264cfSMark Johnston if (error) 146*967264cfSMark Johnston goto done; 147*967264cfSMark Johnston 148*967264cfSMark Johnston error = vm_set_desc(vcpu, VM_REG_GUEST_SS, 149*967264cfSMark Johnston desc_base, desc_limit, desc_access); 150*967264cfSMark Johnston if (error) 151*967264cfSMark Johnston goto done; 152*967264cfSMark Johnston 153*967264cfSMark Johnston desc_base = tssbase; 154*967264cfSMark Johnston desc_limit = I386_TSS_SIZE - 1; 155*967264cfSMark Johnston desc_access = DESC_PRESENT | SDT_SYS386BSY; 156*967264cfSMark Johnston error = vm_set_desc(vcpu, VM_REG_GUEST_TR, 157*967264cfSMark Johnston desc_base, desc_limit, desc_access); 158*967264cfSMark Johnston if (error) 159*967264cfSMark Johnston goto done; 160*967264cfSMark Johnston 161*967264cfSMark Johnston 162*967264cfSMark Johnston error = vm_set_desc(vcpu, VM_REG_GUEST_LDTR, 0, 0, 163*967264cfSMark Johnston DESC_UNUSABLE); 164*967264cfSMark Johnston if (error) 165*967264cfSMark Johnston goto done; 166*967264cfSMark Johnston 167*967264cfSMark Johnston gsel = GSEL(GUEST_CODE_SEL, SEL_KPL); 168*967264cfSMark Johnston if ((error = vm_set_register(vcpu, VM_REG_GUEST_CS, gsel)) != 0) 169*967264cfSMark Johnston goto done; 170*967264cfSMark Johnston 171*967264cfSMark Johnston gsel = GSEL(GUEST_DATA_SEL, SEL_KPL); 172*967264cfSMark Johnston if ((error = vm_set_register(vcpu, VM_REG_GUEST_DS, gsel)) != 0) 173*967264cfSMark Johnston goto done; 174*967264cfSMark Johnston 175*967264cfSMark Johnston if ((error = vm_set_register(vcpu, VM_REG_GUEST_ES, gsel)) != 0) 176*967264cfSMark Johnston goto done; 177*967264cfSMark Johnston 178*967264cfSMark Johnston if ((error = vm_set_register(vcpu, VM_REG_GUEST_FS, gsel)) != 0) 179*967264cfSMark Johnston goto done; 180*967264cfSMark Johnston 181*967264cfSMark Johnston if ((error = vm_set_register(vcpu, VM_REG_GUEST_GS, gsel)) != 0) 182*967264cfSMark Johnston goto done; 183*967264cfSMark Johnston 184*967264cfSMark Johnston if ((error = vm_set_register(vcpu, VM_REG_GUEST_SS, gsel)) != 0) 185*967264cfSMark Johnston goto done; 186*967264cfSMark Johnston 187*967264cfSMark Johnston gsel = GSEL(GUEST_TSS_SEL, SEL_KPL); 188*967264cfSMark Johnston if ((error = vm_set_register(vcpu, VM_REG_GUEST_TR, gsel)) != 0) 189*967264cfSMark Johnston goto done; 190*967264cfSMark Johnston 191*967264cfSMark Johnston /* LDTR is pointing to the null selector */ 192*967264cfSMark Johnston if ((error = vm_set_register(vcpu, VM_REG_GUEST_LDTR, 0)) != 0) 193*967264cfSMark Johnston goto done; 194*967264cfSMark Johnston 195*967264cfSMark Johnston /* entry point */ 196*967264cfSMark Johnston if ((error = vm_set_register(vcpu, VM_REG_GUEST_RIP, eip)) != 0) 197*967264cfSMark Johnston goto done; 198*967264cfSMark Johnston 199*967264cfSMark Johnston if ((error = vm_set_register(vcpu, VM_REG_GUEST_RSP, esp)) != 0) 200*967264cfSMark Johnston goto done; 201*967264cfSMark Johnston 202*967264cfSMark Johnston error = 0; 203*967264cfSMark Johnston done: 204*967264cfSMark Johnston return (error); 205*967264cfSMark Johnston } 206*967264cfSMark Johnston 207*967264cfSMark Johnston void 208*967264cfSMark Johnston vm_setup_freebsd_gdt(uint64_t *gdtr) 209*967264cfSMark Johnston { 210*967264cfSMark Johnston gdtr[GUEST_NULL_SEL] = 0; 211*967264cfSMark Johnston gdtr[GUEST_CODE_SEL] = 0x0020980000000000; 212*967264cfSMark Johnston gdtr[GUEST_DATA_SEL] = 0x0000900000000000; 213*967264cfSMark Johnston } 214*967264cfSMark Johnston 215*967264cfSMark Johnston /* 216*967264cfSMark Johnston * Setup the 'vcpu' register set such that it will begin execution at 217*967264cfSMark Johnston * 'rip' in long mode. 218*967264cfSMark Johnston */ 219*967264cfSMark Johnston int 220*967264cfSMark Johnston vm_setup_freebsd_registers(struct vcpu *vcpu, 221*967264cfSMark Johnston uint64_t rip, uint64_t cr3, uint64_t gdtbase, 222*967264cfSMark Johnston uint64_t rsp) 223*967264cfSMark Johnston { 224*967264cfSMark Johnston int error; 225*967264cfSMark Johnston uint64_t cr0, cr4, efer, rflags, desc_base; 226*967264cfSMark Johnston uint32_t desc_access, desc_limit; 227*967264cfSMark Johnston uint16_t gsel; 228*967264cfSMark Johnston 229*967264cfSMark Johnston cr0 = CR0_PE | CR0_PG | CR0_NE; 230*967264cfSMark Johnston if ((error = vm_set_register(vcpu, VM_REG_GUEST_CR0, cr0)) != 0) 231*967264cfSMark Johnston goto done; 232*967264cfSMark Johnston 233*967264cfSMark Johnston cr4 = CR4_PAE; 234*967264cfSMark Johnston if ((error = vm_set_register(vcpu, VM_REG_GUEST_CR4, cr4)) != 0) 235*967264cfSMark Johnston goto done; 236*967264cfSMark Johnston 237*967264cfSMark Johnston efer = EFER_LME | EFER_LMA; 238*967264cfSMark Johnston if ((error = vm_set_register(vcpu, VM_REG_GUEST_EFER, efer))) 239*967264cfSMark Johnston goto done; 240*967264cfSMark Johnston 241*967264cfSMark Johnston rflags = 0x2; 242*967264cfSMark Johnston error = vm_set_register(vcpu, VM_REG_GUEST_RFLAGS, rflags); 243*967264cfSMark Johnston if (error) 244*967264cfSMark Johnston goto done; 245*967264cfSMark Johnston 246*967264cfSMark Johnston desc_base = 0; 247*967264cfSMark Johnston desc_limit = 0; 248*967264cfSMark Johnston desc_access = 0x0000209B; 249*967264cfSMark Johnston error = vm_set_desc(vcpu, VM_REG_GUEST_CS, 250*967264cfSMark Johnston desc_base, desc_limit, desc_access); 251*967264cfSMark Johnston if (error) 252*967264cfSMark Johnston goto done; 253*967264cfSMark Johnston 254*967264cfSMark Johnston desc_access = 0x00000093; 255*967264cfSMark Johnston error = vm_set_desc(vcpu, VM_REG_GUEST_DS, 256*967264cfSMark Johnston desc_base, desc_limit, desc_access); 257*967264cfSMark Johnston if (error) 258*967264cfSMark Johnston goto done; 259*967264cfSMark Johnston 260*967264cfSMark Johnston error = vm_set_desc(vcpu, VM_REG_GUEST_ES, 261*967264cfSMark Johnston desc_base, desc_limit, desc_access); 262*967264cfSMark Johnston if (error) 263*967264cfSMark Johnston goto done; 264*967264cfSMark Johnston 265*967264cfSMark Johnston error = vm_set_desc(vcpu, VM_REG_GUEST_FS, 266*967264cfSMark Johnston desc_base, desc_limit, desc_access); 267*967264cfSMark Johnston if (error) 268*967264cfSMark Johnston goto done; 269*967264cfSMark Johnston 270*967264cfSMark Johnston error = vm_set_desc(vcpu, VM_REG_GUEST_GS, 271*967264cfSMark Johnston desc_base, desc_limit, desc_access); 272*967264cfSMark Johnston if (error) 273*967264cfSMark Johnston goto done; 274*967264cfSMark Johnston 275*967264cfSMark Johnston error = vm_set_desc(vcpu, VM_REG_GUEST_SS, 276*967264cfSMark Johnston desc_base, desc_limit, desc_access); 277*967264cfSMark Johnston if (error) 278*967264cfSMark Johnston goto done; 279*967264cfSMark Johnston 280*967264cfSMark Johnston /* 281*967264cfSMark Johnston * XXX TR is pointing to null selector even though we set the 282*967264cfSMark Johnston * TSS segment to be usable with a base address and limit of 0. 283*967264cfSMark Johnston */ 284*967264cfSMark Johnston desc_access = 0x0000008b; 285*967264cfSMark Johnston error = vm_set_desc(vcpu, VM_REG_GUEST_TR, 0, 0, desc_access); 286*967264cfSMark Johnston if (error) 287*967264cfSMark Johnston goto done; 288*967264cfSMark Johnston 289*967264cfSMark Johnston error = vm_set_desc(vcpu, VM_REG_GUEST_LDTR, 0, 0, 290*967264cfSMark Johnston DESC_UNUSABLE); 291*967264cfSMark Johnston if (error) 292*967264cfSMark Johnston goto done; 293*967264cfSMark Johnston 294*967264cfSMark Johnston gsel = GSEL(GUEST_CODE_SEL, SEL_KPL); 295*967264cfSMark Johnston if ((error = vm_set_register(vcpu, VM_REG_GUEST_CS, gsel)) != 0) 296*967264cfSMark Johnston goto done; 297*967264cfSMark Johnston 298*967264cfSMark Johnston gsel = GSEL(GUEST_DATA_SEL, SEL_KPL); 299*967264cfSMark Johnston if ((error = vm_set_register(vcpu, VM_REG_GUEST_DS, gsel)) != 0) 300*967264cfSMark Johnston goto done; 301*967264cfSMark Johnston 302*967264cfSMark Johnston if ((error = vm_set_register(vcpu, VM_REG_GUEST_ES, gsel)) != 0) 303*967264cfSMark Johnston goto done; 304*967264cfSMark Johnston 305*967264cfSMark Johnston if ((error = vm_set_register(vcpu, VM_REG_GUEST_FS, gsel)) != 0) 306*967264cfSMark Johnston goto done; 307*967264cfSMark Johnston 308*967264cfSMark Johnston if ((error = vm_set_register(vcpu, VM_REG_GUEST_GS, gsel)) != 0) 309*967264cfSMark Johnston goto done; 310*967264cfSMark Johnston 311*967264cfSMark Johnston if ((error = vm_set_register(vcpu, VM_REG_GUEST_SS, gsel)) != 0) 312*967264cfSMark Johnston goto done; 313*967264cfSMark Johnston 314*967264cfSMark Johnston /* XXX TR is pointing to the null selector */ 315*967264cfSMark Johnston if ((error = vm_set_register(vcpu, VM_REG_GUEST_TR, 0)) != 0) 316*967264cfSMark Johnston goto done; 317*967264cfSMark Johnston 318*967264cfSMark Johnston /* LDTR is pointing to the null selector */ 319*967264cfSMark Johnston if ((error = vm_set_register(vcpu, VM_REG_GUEST_LDTR, 0)) != 0) 320*967264cfSMark Johnston goto done; 321*967264cfSMark Johnston 322*967264cfSMark Johnston /* entry point */ 323*967264cfSMark Johnston if ((error = vm_set_register(vcpu, VM_REG_GUEST_RIP, rip)) != 0) 324*967264cfSMark Johnston goto done; 325*967264cfSMark Johnston 326*967264cfSMark Johnston /* page table base */ 327*967264cfSMark Johnston if ((error = vm_set_register(vcpu, VM_REG_GUEST_CR3, cr3)) != 0) 328*967264cfSMark Johnston goto done; 329*967264cfSMark Johnston 330*967264cfSMark Johnston desc_base = gdtbase; 331*967264cfSMark Johnston desc_limit = GUEST_GDTR_LIMIT64; 332*967264cfSMark Johnston error = vm_set_desc(vcpu, VM_REG_GUEST_GDTR, 333*967264cfSMark Johnston desc_base, desc_limit, 0); 334*967264cfSMark Johnston if (error != 0) 335*967264cfSMark Johnston goto done; 336*967264cfSMark Johnston 337*967264cfSMark Johnston if ((error = vm_set_register(vcpu, VM_REG_GUEST_RSP, rsp)) != 0) 338*967264cfSMark Johnston goto done; 339*967264cfSMark Johnston 340*967264cfSMark Johnston error = 0; 341*967264cfSMark Johnston done: 342*967264cfSMark Johnston return (error); 343*967264cfSMark Johnston } 344