vmmapi_freebsd.c revision 331722
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: stable/11/lib/libvmmapi/vmmapi_freebsd.c 331722 2018-03-29 02:50:57Z eadler $ 27 */ 28 29#include <sys/cdefs.h> 30__FBSDID("$FreeBSD: stable/11/lib/libvmmapi/vmmapi_freebsd.c 331722 2018-03-29 02:50:57Z eadler $"); 31 32#include <sys/types.h> 33 34#include <machine/specialreg.h> 35#include <machine/segments.h> 36#include <machine/vmm.h> 37 38#include <errno.h> 39#include <string.h> 40 41#include "vmmapi.h" 42 43#define I386_TSS_SIZE 104 44 45#define DESC_PRESENT 0x00000080 46#define DESC_LONGMODE 0x00002000 47#define DESC_DEF32 0x00004000 48#define DESC_GRAN 0x00008000 49#define DESC_UNUSABLE 0x00010000 50 51#define GUEST_NULL_SEL 0 52#define GUEST_CODE_SEL 1 53#define GUEST_DATA_SEL 2 54#define GUEST_TSS_SEL 3 55#define GUEST_GDTR_LIMIT64 (3 * 8 - 1) 56 57static struct segment_descriptor i386_gdt[] = { 58 {}, /* NULL */ 59 { .sd_lolimit = 0xffff, .sd_type = SDT_MEMER, /* CODE */ 60 .sd_p = 1, .sd_hilimit = 0xf, .sd_def32 = 1, .sd_gran = 1 }, 61 { .sd_lolimit = 0xffff, .sd_type = SDT_MEMRW, /* DATA */ 62 .sd_p = 1, .sd_hilimit = 0xf, .sd_def32 = 1, .sd_gran = 1 }, 63 { .sd_lolimit = I386_TSS_SIZE - 1, /* TSS */ 64 .sd_type = SDT_SYS386TSS, .sd_p = 1 } 65}; 66 67/* 68 * Setup the 'vcpu' register set such that it will begin execution at 69 * 'eip' in flat mode. 70 */ 71int 72vm_setup_freebsd_registers_i386(struct vmctx *vmctx, int vcpu, uint32_t eip, 73 uint32_t gdtbase, uint32_t esp) 74{ 75 uint64_t cr0, rflags, desc_base; 76 uint32_t desc_access, desc_limit, tssbase; 77 uint16_t gsel; 78 struct segment_descriptor *gdt; 79 int error, tmp; 80 81 /* A 32-bit guest requires unrestricted mode. */ 82 error = vm_get_capability(vmctx, vcpu, VM_CAP_UNRESTRICTED_GUEST, &tmp); 83 if (error) 84 goto done; 85 error = vm_set_capability(vmctx, vcpu, VM_CAP_UNRESTRICTED_GUEST, 1); 86 if (error) 87 goto done; 88 89 cr0 = CR0_PE | CR0_NE; 90 if ((error = vm_set_register(vmctx, vcpu, VM_REG_GUEST_CR0, cr0)) != 0) 91 goto done; 92 93 if ((error = vm_set_register(vmctx, vcpu, VM_REG_GUEST_CR4, 0)) != 0) 94 goto done; 95 96 /* 97 * Forcing EFER to 0 causes bhyve to clear the "IA-32e guest 98 * mode" entry control. 99 */ 100 if ((error = vm_set_register(vmctx, vcpu, VM_REG_GUEST_EFER, 0))) 101 goto done; 102 103 gdt = vm_map_gpa(vmctx, gdtbase, 0x1000); 104 if (gdt == NULL) 105 return (EFAULT); 106 memcpy(gdt, i386_gdt, sizeof(i386_gdt)); 107 desc_base = gdtbase; 108 desc_limit = sizeof(i386_gdt) - 1; 109 error = vm_set_desc(vmctx, vcpu, VM_REG_GUEST_GDTR, 110 desc_base, desc_limit, 0); 111 if (error != 0) 112 goto done; 113 114 /* Place the TSS one page above the GDT. */ 115 tssbase = gdtbase + 0x1000; 116 gdt[3].sd_lobase = tssbase; 117 118 rflags = 0x2; 119 error = vm_set_register(vmctx, vcpu, VM_REG_GUEST_RFLAGS, rflags); 120 if (error) 121 goto done; 122 123 desc_base = 0; 124 desc_limit = 0xffffffff; 125 desc_access = DESC_GRAN | DESC_DEF32 | DESC_PRESENT | SDT_MEMERA; 126 error = vm_set_desc(vmctx, vcpu, VM_REG_GUEST_CS, 127 desc_base, desc_limit, desc_access); 128 129 desc_access = DESC_GRAN | DESC_DEF32 | DESC_PRESENT | SDT_MEMRWA; 130 error = vm_set_desc(vmctx, vcpu, VM_REG_GUEST_DS, 131 desc_base, desc_limit, desc_access); 132 if (error) 133 goto done; 134 135 error = vm_set_desc(vmctx, vcpu, VM_REG_GUEST_ES, 136 desc_base, desc_limit, desc_access); 137 if (error) 138 goto done; 139 140 error = vm_set_desc(vmctx, vcpu, VM_REG_GUEST_FS, 141 desc_base, desc_limit, desc_access); 142 if (error) 143 goto done; 144 145 error = vm_set_desc(vmctx, vcpu, VM_REG_GUEST_GS, 146 desc_base, desc_limit, desc_access); 147 if (error) 148 goto done; 149 150 error = vm_set_desc(vmctx, vcpu, VM_REG_GUEST_SS, 151 desc_base, desc_limit, desc_access); 152 if (error) 153 goto done; 154 155 desc_base = tssbase; 156 desc_limit = I386_TSS_SIZE - 1; 157 desc_access = DESC_PRESENT | SDT_SYS386BSY; 158 error = vm_set_desc(vmctx, vcpu, VM_REG_GUEST_TR, 159 desc_base, desc_limit, desc_access); 160 if (error) 161 goto done; 162 163 164 error = vm_set_desc(vmctx, vcpu, VM_REG_GUEST_LDTR, 0, 0, 165 DESC_UNUSABLE); 166 if (error) 167 goto done; 168 169 gsel = GSEL(GUEST_CODE_SEL, SEL_KPL); 170 if ((error = vm_set_register(vmctx, vcpu, VM_REG_GUEST_CS, gsel)) != 0) 171 goto done; 172 173 gsel = GSEL(GUEST_DATA_SEL, SEL_KPL); 174 if ((error = vm_set_register(vmctx, vcpu, VM_REG_GUEST_DS, gsel)) != 0) 175 goto done; 176 177 if ((error = vm_set_register(vmctx, vcpu, VM_REG_GUEST_ES, gsel)) != 0) 178 goto done; 179 180 if ((error = vm_set_register(vmctx, vcpu, VM_REG_GUEST_FS, gsel)) != 0) 181 goto done; 182 183 if ((error = vm_set_register(vmctx, vcpu, VM_REG_GUEST_GS, gsel)) != 0) 184 goto done; 185 186 if ((error = vm_set_register(vmctx, vcpu, VM_REG_GUEST_SS, gsel)) != 0) 187 goto done; 188 189 gsel = GSEL(GUEST_TSS_SEL, SEL_KPL); 190 if ((error = vm_set_register(vmctx, vcpu, VM_REG_GUEST_TR, gsel)) != 0) 191 goto done; 192 193 /* LDTR is pointing to the null selector */ 194 if ((error = vm_set_register(vmctx, vcpu, VM_REG_GUEST_LDTR, 0)) != 0) 195 goto done; 196 197 /* entry point */ 198 if ((error = vm_set_register(vmctx, vcpu, VM_REG_GUEST_RIP, eip)) != 0) 199 goto done; 200 201 if ((error = vm_set_register(vmctx, vcpu, VM_REG_GUEST_RSP, esp)) != 0) 202 goto done; 203 204 error = 0; 205done: 206 return (error); 207} 208 209void 210vm_setup_freebsd_gdt(uint64_t *gdtr) 211{ 212 gdtr[GUEST_NULL_SEL] = 0; 213 gdtr[GUEST_CODE_SEL] = 0x0020980000000000; 214 gdtr[GUEST_DATA_SEL] = 0x0000900000000000; 215} 216 217/* 218 * Setup the 'vcpu' register set such that it will begin execution at 219 * 'rip' in long mode. 220 */ 221int 222vm_setup_freebsd_registers(struct vmctx *vmctx, int vcpu, 223 uint64_t rip, uint64_t cr3, uint64_t gdtbase, 224 uint64_t rsp) 225{ 226 int error; 227 uint64_t cr0, cr4, efer, rflags, desc_base; 228 uint32_t desc_access, desc_limit; 229 uint16_t gsel; 230 231 cr0 = CR0_PE | CR0_PG | CR0_NE; 232 if ((error = vm_set_register(vmctx, vcpu, VM_REG_GUEST_CR0, cr0)) != 0) 233 goto done; 234 235 cr4 = CR4_PAE; 236 if ((error = vm_set_register(vmctx, vcpu, VM_REG_GUEST_CR4, cr4)) != 0) 237 goto done; 238 239 efer = EFER_LME | EFER_LMA; 240 if ((error = vm_set_register(vmctx, vcpu, VM_REG_GUEST_EFER, efer))) 241 goto done; 242 243 rflags = 0x2; 244 error = vm_set_register(vmctx, vcpu, VM_REG_GUEST_RFLAGS, rflags); 245 if (error) 246 goto done; 247 248 desc_base = 0; 249 desc_limit = 0; 250 desc_access = 0x0000209B; 251 error = vm_set_desc(vmctx, vcpu, VM_REG_GUEST_CS, 252 desc_base, desc_limit, desc_access); 253 if (error) 254 goto done; 255 256 desc_access = 0x00000093; 257 error = vm_set_desc(vmctx, vcpu, VM_REG_GUEST_DS, 258 desc_base, desc_limit, desc_access); 259 if (error) 260 goto done; 261 262 error = vm_set_desc(vmctx, vcpu, VM_REG_GUEST_ES, 263 desc_base, desc_limit, desc_access); 264 if (error) 265 goto done; 266 267 error = vm_set_desc(vmctx, vcpu, VM_REG_GUEST_FS, 268 desc_base, desc_limit, desc_access); 269 if (error) 270 goto done; 271 272 error = vm_set_desc(vmctx, vcpu, VM_REG_GUEST_GS, 273 desc_base, desc_limit, desc_access); 274 if (error) 275 goto done; 276 277 error = vm_set_desc(vmctx, vcpu, VM_REG_GUEST_SS, 278 desc_base, desc_limit, desc_access); 279 if (error) 280 goto done; 281 282 /* 283 * XXX TR is pointing to null selector even though we set the 284 * TSS segment to be usable with a base address and limit of 0. 285 */ 286 desc_access = 0x0000008b; 287 error = vm_set_desc(vmctx, vcpu, VM_REG_GUEST_TR, 0, 0, desc_access); 288 if (error) 289 goto done; 290 291 error = vm_set_desc(vmctx, vcpu, VM_REG_GUEST_LDTR, 0, 0, 292 DESC_UNUSABLE); 293 if (error) 294 goto done; 295 296 gsel = GSEL(GUEST_CODE_SEL, SEL_KPL); 297 if ((error = vm_set_register(vmctx, vcpu, VM_REG_GUEST_CS, gsel)) != 0) 298 goto done; 299 300 gsel = GSEL(GUEST_DATA_SEL, SEL_KPL); 301 if ((error = vm_set_register(vmctx, vcpu, VM_REG_GUEST_DS, gsel)) != 0) 302 goto done; 303 304 if ((error = vm_set_register(vmctx, vcpu, VM_REG_GUEST_ES, gsel)) != 0) 305 goto done; 306 307 if ((error = vm_set_register(vmctx, vcpu, VM_REG_GUEST_FS, gsel)) != 0) 308 goto done; 309 310 if ((error = vm_set_register(vmctx, vcpu, VM_REG_GUEST_GS, gsel)) != 0) 311 goto done; 312 313 if ((error = vm_set_register(vmctx, vcpu, VM_REG_GUEST_SS, gsel)) != 0) 314 goto done; 315 316 /* XXX TR is pointing to the null selector */ 317 if ((error = vm_set_register(vmctx, vcpu, VM_REG_GUEST_TR, 0)) != 0) 318 goto done; 319 320 /* LDTR is pointing to the null selector */ 321 if ((error = vm_set_register(vmctx, vcpu, VM_REG_GUEST_LDTR, 0)) != 0) 322 goto done; 323 324 /* entry point */ 325 if ((error = vm_set_register(vmctx, vcpu, VM_REG_GUEST_RIP, rip)) != 0) 326 goto done; 327 328 /* page table base */ 329 if ((error = vm_set_register(vmctx, vcpu, VM_REG_GUEST_CR3, cr3)) != 0) 330 goto done; 331 332 desc_base = gdtbase; 333 desc_limit = GUEST_GDTR_LIMIT64; 334 error = vm_set_desc(vmctx, vcpu, VM_REG_GUEST_GDTR, 335 desc_base, desc_limit, 0); 336 if (error != 0) 337 goto done; 338 339 if ((error = vm_set_register(vmctx, vcpu, VM_REG_GUEST_RSP, rsp)) != 0) 340 goto done; 341 342 error = 0; 343done: 344 return (error); 345} 346