vmmapi_freebsd.c revision 267399
1221828Sgrehan/*- 2221828Sgrehan * Copyright (c) 2011 NetApp, Inc. 3221828Sgrehan * All rights reserved. 4221828Sgrehan * 5221828Sgrehan * Redistribution and use in source and binary forms, with or without 6221828Sgrehan * modification, are permitted provided that the following conditions 7221828Sgrehan * are met: 8221828Sgrehan * 1. Redistributions of source code must retain the above copyright 9221828Sgrehan * notice, this list of conditions and the following disclaimer. 10221828Sgrehan * 2. Redistributions in binary form must reproduce the above copyright 11221828Sgrehan * notice, this list of conditions and the following disclaimer in the 12221828Sgrehan * documentation and/or other materials provided with the distribution. 13221828Sgrehan * 14221828Sgrehan * THIS SOFTWARE IS PROVIDED BY NETAPP, INC ``AS IS'' AND 15221828Sgrehan * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16221828Sgrehan * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17221828Sgrehan * ARE DISCLAIMED. IN NO EVENT SHALL NETAPP, INC OR CONTRIBUTORS BE LIABLE 18221828Sgrehan * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19221828Sgrehan * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20221828Sgrehan * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21221828Sgrehan * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22221828Sgrehan * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23221828Sgrehan * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24221828Sgrehan * SUCH DAMAGE. 25221828Sgrehan * 26221828Sgrehan * $FreeBSD: stable/10/lib/libvmmapi/vmmapi_freebsd.c 267399 2014-06-12 15:20:59Z jhb $ 27221828Sgrehan */ 28221828Sgrehan 29221828Sgrehan#include <sys/cdefs.h> 30221828Sgrehan__FBSDID("$FreeBSD: stable/10/lib/libvmmapi/vmmapi_freebsd.c 267399 2014-06-12 15:20:59Z jhb $"); 31221828Sgrehan 32221828Sgrehan#include <sys/types.h> 33221828Sgrehan 34221828Sgrehan#include <machine/specialreg.h> 35221828Sgrehan#include <machine/segments.h> 36221828Sgrehan#include <machine/vmm.h> 37221828Sgrehan 38267399Sjhb#include <errno.h> 39267399Sjhb#include <string.h> 40267399Sjhb 41221828Sgrehan#include "vmmapi.h" 42221828Sgrehan 43267399Sjhb#define I386_TSS_SIZE 104 44267399Sjhb 45267399Sjhb#define DESC_PRESENT 0x00000080 46267399Sjhb#define DESC_LONGMODE 0x00002000 47267399Sjhb#define DESC_DEF32 0x00004000 48267399Sjhb#define DESC_GRAN 0x00008000 49221828Sgrehan#define DESC_UNUSABLE 0x00010000 50221828Sgrehan 51221828Sgrehan#define GUEST_NULL_SEL 0 52221828Sgrehan#define GUEST_CODE_SEL 1 53221828Sgrehan#define GUEST_DATA_SEL 2 54267399Sjhb#define GUEST_TSS_SEL 3 55267399Sjhb#define GUEST_GDTR_LIMIT64 (3 * 8 - 1) 56221828Sgrehan 57267399Sjhbstatic struct segment_descriptor i386_gdt[] = { 58267399Sjhb {}, /* NULL */ 59267399Sjhb { .sd_lolimit = 0xffff, .sd_type = SDT_MEMER, /* CODE */ 60267399Sjhb .sd_p = 1, .sd_hilimit = 0xf, .sd_def32 = 1, .sd_gran = 1 }, 61267399Sjhb { .sd_lolimit = 0xffff, .sd_type = SDT_MEMRW, /* DATA */ 62267399Sjhb .sd_p = 1, .sd_hilimit = 0xf, .sd_def32 = 1, .sd_gran = 1 }, 63267399Sjhb { .sd_lolimit = I386_TSS_SIZE - 1, /* TSS */ 64267399Sjhb .sd_type = SDT_SYS386TSS, .sd_p = 1 } 65267399Sjhb}; 66267399Sjhb 67267399Sjhb/* 68267399Sjhb * Setup the 'vcpu' register set such that it will begin execution at 69267399Sjhb * 'eip' in flat mode. 70267399Sjhb */ 71267399Sjhbint 72267399Sjhbvm_setup_freebsd_registers_i386(struct vmctx *vmctx, int vcpu, uint32_t eip, 73267399Sjhb uint32_t gdtbase, uint32_t esp) 74267399Sjhb{ 75267399Sjhb uint64_t cr0, rflags, desc_base; 76267399Sjhb uint32_t desc_access, desc_limit, tssbase; 77267399Sjhb uint16_t gsel; 78267399Sjhb struct segment_descriptor *gdt; 79267399Sjhb int error, tmp; 80267399Sjhb 81267399Sjhb /* A 32-bit guest requires unrestricted mode. */ 82267399Sjhb error = vm_get_capability(vmctx, vcpu, VM_CAP_UNRESTRICTED_GUEST, &tmp); 83267399Sjhb if (error) 84267399Sjhb goto done; 85267399Sjhb error = vm_set_capability(vmctx, vcpu, VM_CAP_UNRESTRICTED_GUEST, 1); 86267399Sjhb if (error) 87267399Sjhb goto done; 88267399Sjhb 89267399Sjhb cr0 = CR0_PE | CR0_NE; 90267399Sjhb if ((error = vm_set_register(vmctx, vcpu, VM_REG_GUEST_CR0, cr0)) != 0) 91267399Sjhb goto done; 92267399Sjhb 93267399Sjhb if ((error = vm_set_register(vmctx, vcpu, VM_REG_GUEST_CR4, 0)) != 0) 94267399Sjhb goto done; 95267399Sjhb 96267399Sjhb /* 97267399Sjhb * Forcing EFER to 0 causes bhyve to clear the "IA-32e guest 98267399Sjhb * mode" entry control. 99267399Sjhb */ 100267399Sjhb if ((error = vm_set_register(vmctx, vcpu, VM_REG_GUEST_EFER, 0))) 101267399Sjhb goto done; 102267399Sjhb 103267399Sjhb gdt = vm_map_gpa(vmctx, gdtbase, 0x1000); 104267399Sjhb if (gdt == NULL) 105267399Sjhb return (EFAULT); 106267399Sjhb memcpy(gdt, i386_gdt, sizeof(i386_gdt)); 107267399Sjhb desc_base = gdtbase; 108267399Sjhb desc_limit = sizeof(i386_gdt) - 1; 109267399Sjhb error = vm_set_desc(vmctx, vcpu, VM_REG_GUEST_GDTR, 110267399Sjhb desc_base, desc_limit, 0); 111267399Sjhb if (error != 0) 112267399Sjhb goto done; 113267399Sjhb 114267399Sjhb /* Place the TSS one page above the GDT. */ 115267399Sjhb tssbase = gdtbase + 0x1000; 116267399Sjhb gdt[3].sd_lobase = tssbase; 117267399Sjhb 118267399Sjhb rflags = 0x2; 119267399Sjhb error = vm_set_register(vmctx, vcpu, VM_REG_GUEST_RFLAGS, rflags); 120267399Sjhb if (error) 121267399Sjhb goto done; 122267399Sjhb 123267399Sjhb desc_base = 0; 124267399Sjhb desc_limit = 0xffffffff; 125267399Sjhb desc_access = DESC_GRAN | DESC_DEF32 | DESC_PRESENT | SDT_MEMERA; 126267399Sjhb error = vm_set_desc(vmctx, vcpu, VM_REG_GUEST_CS, 127267399Sjhb desc_base, desc_limit, desc_access); 128267399Sjhb 129267399Sjhb desc_access = DESC_GRAN | DESC_DEF32 | DESC_PRESENT | SDT_MEMRWA; 130267399Sjhb error = vm_set_desc(vmctx, vcpu, VM_REG_GUEST_DS, 131267399Sjhb desc_base, desc_limit, desc_access); 132267399Sjhb if (error) 133267399Sjhb goto done; 134267399Sjhb 135267399Sjhb error = vm_set_desc(vmctx, vcpu, VM_REG_GUEST_ES, 136267399Sjhb desc_base, desc_limit, desc_access); 137267399Sjhb if (error) 138267399Sjhb goto done; 139267399Sjhb 140267399Sjhb error = vm_set_desc(vmctx, vcpu, VM_REG_GUEST_FS, 141267399Sjhb desc_base, desc_limit, desc_access); 142267399Sjhb if (error) 143267399Sjhb goto done; 144267399Sjhb 145267399Sjhb error = vm_set_desc(vmctx, vcpu, VM_REG_GUEST_GS, 146267399Sjhb desc_base, desc_limit, desc_access); 147267399Sjhb if (error) 148267399Sjhb goto done; 149267399Sjhb 150267399Sjhb error = vm_set_desc(vmctx, vcpu, VM_REG_GUEST_SS, 151267399Sjhb desc_base, desc_limit, desc_access); 152267399Sjhb if (error) 153267399Sjhb goto done; 154267399Sjhb 155267399Sjhb desc_base = tssbase; 156267399Sjhb desc_limit = I386_TSS_SIZE - 1; 157267399Sjhb desc_access = DESC_PRESENT | SDT_SYS386BSY; 158267399Sjhb error = vm_set_desc(vmctx, vcpu, VM_REG_GUEST_TR, 159267399Sjhb desc_base, desc_limit, desc_access); 160267399Sjhb if (error) 161267399Sjhb goto done; 162267399Sjhb 163267399Sjhb 164267399Sjhb error = vm_set_desc(vmctx, vcpu, VM_REG_GUEST_LDTR, 0, 0, 165267399Sjhb DESC_UNUSABLE); 166267399Sjhb if (error) 167267399Sjhb goto done; 168267399Sjhb 169267399Sjhb gsel = GSEL(GUEST_CODE_SEL, SEL_KPL); 170267399Sjhb if ((error = vm_set_register(vmctx, vcpu, VM_REG_GUEST_CS, gsel)) != 0) 171267399Sjhb goto done; 172267399Sjhb 173267399Sjhb gsel = GSEL(GUEST_DATA_SEL, SEL_KPL); 174267399Sjhb if ((error = vm_set_register(vmctx, vcpu, VM_REG_GUEST_DS, gsel)) != 0) 175267399Sjhb goto done; 176267399Sjhb 177267399Sjhb if ((error = vm_set_register(vmctx, vcpu, VM_REG_GUEST_ES, gsel)) != 0) 178267399Sjhb goto done; 179267399Sjhb 180267399Sjhb if ((error = vm_set_register(vmctx, vcpu, VM_REG_GUEST_FS, gsel)) != 0) 181267399Sjhb goto done; 182267399Sjhb 183267399Sjhb if ((error = vm_set_register(vmctx, vcpu, VM_REG_GUEST_GS, gsel)) != 0) 184267399Sjhb goto done; 185267399Sjhb 186267399Sjhb if ((error = vm_set_register(vmctx, vcpu, VM_REG_GUEST_SS, gsel)) != 0) 187267399Sjhb goto done; 188267399Sjhb 189267399Sjhb gsel = GSEL(GUEST_TSS_SEL, SEL_KPL); 190267399Sjhb if ((error = vm_set_register(vmctx, vcpu, VM_REG_GUEST_TR, gsel)) != 0) 191267399Sjhb goto done; 192267399Sjhb 193267399Sjhb /* LDTR is pointing to the null selector */ 194267399Sjhb if ((error = vm_set_register(vmctx, vcpu, VM_REG_GUEST_LDTR, 0)) != 0) 195267399Sjhb goto done; 196267399Sjhb 197267399Sjhb /* entry point */ 198267399Sjhb if ((error = vm_set_register(vmctx, vcpu, VM_REG_GUEST_RIP, eip)) != 0) 199267399Sjhb goto done; 200267399Sjhb 201267399Sjhb if ((error = vm_set_register(vmctx, vcpu, VM_REG_GUEST_RSP, esp)) != 0) 202267399Sjhb goto done; 203267399Sjhb 204267399Sjhb error = 0; 205267399Sjhbdone: 206267399Sjhb return (error); 207267399Sjhb} 208267399Sjhb 209221828Sgrehanvoid 210221828Sgrehanvm_setup_freebsd_gdt(uint64_t *gdtr) 211221828Sgrehan{ 212221828Sgrehan gdtr[GUEST_NULL_SEL] = 0; 213221828Sgrehan gdtr[GUEST_CODE_SEL] = 0x0020980000000000; 214221828Sgrehan gdtr[GUEST_DATA_SEL] = 0x0000900000000000; 215221828Sgrehan} 216221828Sgrehan 217221828Sgrehan/* 218221828Sgrehan * Setup the 'vcpu' register set such that it will begin execution at 219221828Sgrehan * 'rip' in long mode. 220221828Sgrehan */ 221221828Sgrehanint 222221828Sgrehanvm_setup_freebsd_registers(struct vmctx *vmctx, int vcpu, 223221828Sgrehan uint64_t rip, uint64_t cr3, uint64_t gdtbase, 224221828Sgrehan uint64_t rsp) 225221828Sgrehan{ 226221828Sgrehan int error; 227221828Sgrehan uint64_t cr0, cr4, efer, rflags, desc_base; 228221828Sgrehan uint32_t desc_access, desc_limit; 229221828Sgrehan uint16_t gsel; 230221828Sgrehan 231221828Sgrehan cr0 = CR0_PE | CR0_PG | CR0_NE; 232221828Sgrehan if ((error = vm_set_register(vmctx, vcpu, VM_REG_GUEST_CR0, cr0)) != 0) 233221828Sgrehan goto done; 234221828Sgrehan 235239025Sneel cr4 = CR4_PAE; 236221828Sgrehan if ((error = vm_set_register(vmctx, vcpu, VM_REG_GUEST_CR4, cr4)) != 0) 237221828Sgrehan goto done; 238221828Sgrehan 239221828Sgrehan efer = EFER_LME | EFER_LMA; 240221828Sgrehan if ((error = vm_set_register(vmctx, vcpu, VM_REG_GUEST_EFER, efer))) 241221828Sgrehan goto done; 242221828Sgrehan 243221828Sgrehan rflags = 0x2; 244221828Sgrehan error = vm_set_register(vmctx, vcpu, VM_REG_GUEST_RFLAGS, rflags); 245221828Sgrehan if (error) 246221828Sgrehan goto done; 247221828Sgrehan 248221828Sgrehan desc_base = 0; 249221828Sgrehan desc_limit = 0; 250221828Sgrehan desc_access = 0x0000209B; 251221828Sgrehan error = vm_set_desc(vmctx, vcpu, VM_REG_GUEST_CS, 252221828Sgrehan desc_base, desc_limit, desc_access); 253221828Sgrehan if (error) 254221828Sgrehan goto done; 255221828Sgrehan 256221828Sgrehan desc_access = 0x00000093; 257221828Sgrehan error = vm_set_desc(vmctx, vcpu, VM_REG_GUEST_DS, 258221828Sgrehan desc_base, desc_limit, desc_access); 259221828Sgrehan if (error) 260221828Sgrehan goto done; 261221828Sgrehan 262221828Sgrehan error = vm_set_desc(vmctx, vcpu, VM_REG_GUEST_ES, 263221828Sgrehan desc_base, desc_limit, desc_access); 264221828Sgrehan if (error) 265221828Sgrehan goto done; 266221828Sgrehan 267221828Sgrehan error = vm_set_desc(vmctx, vcpu, VM_REG_GUEST_FS, 268221828Sgrehan desc_base, desc_limit, desc_access); 269221828Sgrehan if (error) 270221828Sgrehan goto done; 271221828Sgrehan 272221828Sgrehan error = vm_set_desc(vmctx, vcpu, VM_REG_GUEST_GS, 273221828Sgrehan desc_base, desc_limit, desc_access); 274221828Sgrehan if (error) 275221828Sgrehan goto done; 276221828Sgrehan 277221828Sgrehan error = vm_set_desc(vmctx, vcpu, VM_REG_GUEST_SS, 278221828Sgrehan desc_base, desc_limit, desc_access); 279221828Sgrehan if (error) 280221828Sgrehan goto done; 281221828Sgrehan 282221828Sgrehan /* 283221828Sgrehan * XXX TR is pointing to null selector even though we set the 284221828Sgrehan * TSS segment to be usable with a base address and limit of 0. 285221828Sgrehan */ 286221828Sgrehan desc_access = 0x0000008b; 287221828Sgrehan error = vm_set_desc(vmctx, vcpu, VM_REG_GUEST_TR, 0, 0, desc_access); 288221828Sgrehan if (error) 289221828Sgrehan goto done; 290221828Sgrehan 291221828Sgrehan error = vm_set_desc(vmctx, vcpu, VM_REG_GUEST_LDTR, 0, 0, 292221828Sgrehan DESC_UNUSABLE); 293221828Sgrehan if (error) 294221828Sgrehan goto done; 295221828Sgrehan 296221828Sgrehan gsel = GSEL(GUEST_CODE_SEL, SEL_KPL); 297221828Sgrehan if ((error = vm_set_register(vmctx, vcpu, VM_REG_GUEST_CS, gsel)) != 0) 298221828Sgrehan goto done; 299221828Sgrehan 300221828Sgrehan gsel = GSEL(GUEST_DATA_SEL, SEL_KPL); 301221828Sgrehan if ((error = vm_set_register(vmctx, vcpu, VM_REG_GUEST_DS, gsel)) != 0) 302221828Sgrehan goto done; 303221828Sgrehan 304221828Sgrehan if ((error = vm_set_register(vmctx, vcpu, VM_REG_GUEST_ES, gsel)) != 0) 305221828Sgrehan goto done; 306221828Sgrehan 307221828Sgrehan if ((error = vm_set_register(vmctx, vcpu, VM_REG_GUEST_FS, gsel)) != 0) 308221828Sgrehan goto done; 309221828Sgrehan 310221828Sgrehan if ((error = vm_set_register(vmctx, vcpu, VM_REG_GUEST_GS, gsel)) != 0) 311221828Sgrehan goto done; 312221828Sgrehan 313221828Sgrehan if ((error = vm_set_register(vmctx, vcpu, VM_REG_GUEST_SS, gsel)) != 0) 314221828Sgrehan goto done; 315221828Sgrehan 316221828Sgrehan /* XXX TR is pointing to the null selector */ 317221828Sgrehan if ((error = vm_set_register(vmctx, vcpu, VM_REG_GUEST_TR, 0)) != 0) 318221828Sgrehan goto done; 319221828Sgrehan 320221828Sgrehan /* LDTR is pointing to the null selector */ 321221828Sgrehan if ((error = vm_set_register(vmctx, vcpu, VM_REG_GUEST_LDTR, 0)) != 0) 322221828Sgrehan goto done; 323221828Sgrehan 324221828Sgrehan /* entry point */ 325221828Sgrehan if ((error = vm_set_register(vmctx, vcpu, VM_REG_GUEST_RIP, rip)) != 0) 326221828Sgrehan goto done; 327221828Sgrehan 328221828Sgrehan /* page table base */ 329221828Sgrehan if ((error = vm_set_register(vmctx, vcpu, VM_REG_GUEST_CR3, cr3)) != 0) 330221828Sgrehan goto done; 331221828Sgrehan 332221828Sgrehan desc_base = gdtbase; 333267399Sjhb desc_limit = GUEST_GDTR_LIMIT64; 334221828Sgrehan error = vm_set_desc(vmctx, vcpu, VM_REG_GUEST_GDTR, 335221828Sgrehan desc_base, desc_limit, 0); 336221828Sgrehan if (error != 0) 337221828Sgrehan goto done; 338221828Sgrehan 339221828Sgrehan if ((error = vm_set_register(vmctx, vcpu, VM_REG_GUEST_RSP, rsp)) != 0) 340221828Sgrehan goto done; 341221828Sgrehan 342221828Sgrehan error = 0; 343221828Sgrehandone: 344221828Sgrehan return (error); 345221828Sgrehan} 346