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