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