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