]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/amd64/vmm/intel/vmcs.c
Merge ^/vendor/lvm-project/master up to its last change (upstream commit
[FreeBSD/FreeBSD.git] / sys / amd64 / vmm / intel / vmcs.c
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 "opt_ddb.h"
32
33 #include <sys/cdefs.h>
34 __FBSDID("$FreeBSD$");
35
36 #include <sys/param.h>
37 #include <sys/sysctl.h>
38 #include <sys/systm.h>
39 #include <sys/pcpu.h>
40
41 #include <vm/vm.h>
42 #include <vm/pmap.h>
43
44 #include <machine/segments.h>
45 #include <machine/vmm.h>
46 #include "vmm_host.h"
47 #include "vmx_cpufunc.h"
48 #include "vmcs.h"
49 #include "ept.h"
50 #include "vmx.h"
51
52 #ifdef DDB
53 #include <ddb/ddb.h>
54 #endif
55
56 SYSCTL_DECL(_hw_vmm_vmx);
57
58 static int no_flush_rsb;
59 SYSCTL_INT(_hw_vmm_vmx, OID_AUTO, no_flush_rsb, CTLFLAG_RW,
60     &no_flush_rsb, 0, "Do not flush RSB upon vmexit");
61
62 static uint64_t
63 vmcs_fix_regval(uint32_t encoding, uint64_t val)
64 {
65
66         switch (encoding) {
67         case VMCS_GUEST_CR0:
68                 val = vmx_fix_cr0(val);
69                 break;
70         case VMCS_GUEST_CR4:
71                 val = vmx_fix_cr4(val);
72                 break;
73         default:
74                 break;
75         }
76         return (val);
77 }
78
79 static uint32_t
80 vmcs_field_encoding(int ident)
81 {
82         switch (ident) {
83         case VM_REG_GUEST_CR0:
84                 return (VMCS_GUEST_CR0);
85         case VM_REG_GUEST_CR3:
86                 return (VMCS_GUEST_CR3);
87         case VM_REG_GUEST_CR4:
88                 return (VMCS_GUEST_CR4);
89         case VM_REG_GUEST_DR7:
90                 return (VMCS_GUEST_DR7);
91         case VM_REG_GUEST_RSP:
92                 return (VMCS_GUEST_RSP);
93         case VM_REG_GUEST_RIP:
94                 return (VMCS_GUEST_RIP);
95         case VM_REG_GUEST_RFLAGS:
96                 return (VMCS_GUEST_RFLAGS);
97         case VM_REG_GUEST_ES:
98                 return (VMCS_GUEST_ES_SELECTOR);
99         case VM_REG_GUEST_CS:
100                 return (VMCS_GUEST_CS_SELECTOR);
101         case VM_REG_GUEST_SS:
102                 return (VMCS_GUEST_SS_SELECTOR);
103         case VM_REG_GUEST_DS:
104                 return (VMCS_GUEST_DS_SELECTOR);
105         case VM_REG_GUEST_FS:
106                 return (VMCS_GUEST_FS_SELECTOR);
107         case VM_REG_GUEST_GS:
108                 return (VMCS_GUEST_GS_SELECTOR);
109         case VM_REG_GUEST_TR:
110                 return (VMCS_GUEST_TR_SELECTOR);
111         case VM_REG_GUEST_LDTR:
112                 return (VMCS_GUEST_LDTR_SELECTOR);
113         case VM_REG_GUEST_EFER:
114                 return (VMCS_GUEST_IA32_EFER);
115         case VM_REG_GUEST_PDPTE0:
116                 return (VMCS_GUEST_PDPTE0);
117         case VM_REG_GUEST_PDPTE1:
118                 return (VMCS_GUEST_PDPTE1);
119         case VM_REG_GUEST_PDPTE2:
120                 return (VMCS_GUEST_PDPTE2);
121         case VM_REG_GUEST_PDPTE3:
122                 return (VMCS_GUEST_PDPTE3);
123         case VM_REG_GUEST_ENTRY_INST_LENGTH:
124                 return (VMCS_ENTRY_INST_LENGTH);
125         default:
126                 return (-1);
127         }
128
129 }
130
131 static int
132 vmcs_seg_desc_encoding(int seg, uint32_t *base, uint32_t *lim, uint32_t *acc)
133 {
134
135         switch (seg) {
136         case VM_REG_GUEST_ES:
137                 *base = VMCS_GUEST_ES_BASE;
138                 *lim = VMCS_GUEST_ES_LIMIT;
139                 *acc = VMCS_GUEST_ES_ACCESS_RIGHTS;
140                 break;
141         case VM_REG_GUEST_CS:
142                 *base = VMCS_GUEST_CS_BASE;
143                 *lim = VMCS_GUEST_CS_LIMIT;
144                 *acc = VMCS_GUEST_CS_ACCESS_RIGHTS;
145                 break;
146         case VM_REG_GUEST_SS:
147                 *base = VMCS_GUEST_SS_BASE;
148                 *lim = VMCS_GUEST_SS_LIMIT;
149                 *acc = VMCS_GUEST_SS_ACCESS_RIGHTS;
150                 break;
151         case VM_REG_GUEST_DS:
152                 *base = VMCS_GUEST_DS_BASE;
153                 *lim = VMCS_GUEST_DS_LIMIT;
154                 *acc = VMCS_GUEST_DS_ACCESS_RIGHTS;
155                 break;
156         case VM_REG_GUEST_FS:
157                 *base = VMCS_GUEST_FS_BASE;
158                 *lim = VMCS_GUEST_FS_LIMIT;
159                 *acc = VMCS_GUEST_FS_ACCESS_RIGHTS;
160                 break;
161         case VM_REG_GUEST_GS:
162                 *base = VMCS_GUEST_GS_BASE;
163                 *lim = VMCS_GUEST_GS_LIMIT;
164                 *acc = VMCS_GUEST_GS_ACCESS_RIGHTS;
165                 break;
166         case VM_REG_GUEST_TR:
167                 *base = VMCS_GUEST_TR_BASE;
168                 *lim = VMCS_GUEST_TR_LIMIT;
169                 *acc = VMCS_GUEST_TR_ACCESS_RIGHTS;
170                 break;
171         case VM_REG_GUEST_LDTR:
172                 *base = VMCS_GUEST_LDTR_BASE;
173                 *lim = VMCS_GUEST_LDTR_LIMIT;
174                 *acc = VMCS_GUEST_LDTR_ACCESS_RIGHTS;
175                 break;
176         case VM_REG_GUEST_IDTR:
177                 *base = VMCS_GUEST_IDTR_BASE;
178                 *lim = VMCS_GUEST_IDTR_LIMIT;
179                 *acc = VMCS_INVALID_ENCODING;
180                 break;
181         case VM_REG_GUEST_GDTR:
182                 *base = VMCS_GUEST_GDTR_BASE;
183                 *lim = VMCS_GUEST_GDTR_LIMIT;
184                 *acc = VMCS_INVALID_ENCODING;
185                 break;
186         default:
187                 return (EINVAL);
188         }
189
190         return (0);
191 }
192
193 int
194 vmcs_getreg(struct vmcs *vmcs, int running, int ident, uint64_t *retval)
195 {
196         int error;
197         uint32_t encoding;
198
199         /*
200          * If we need to get at vmx-specific state in the VMCS we can bypass
201          * the translation of 'ident' to 'encoding' by simply setting the
202          * sign bit. As it so happens the upper 16 bits are reserved (i.e
203          * set to 0) in the encodings for the VMCS so we are free to use the
204          * sign bit.
205          */
206         if (ident < 0)
207                 encoding = ident & 0x7fffffff;
208         else
209                 encoding = vmcs_field_encoding(ident);
210
211         if (encoding == (uint32_t)-1)
212                 return (EINVAL);
213
214         if (!running)
215                 VMPTRLD(vmcs);
216
217         error = vmread(encoding, retval);
218
219         if (!running)
220                 VMCLEAR(vmcs);
221
222         return (error);
223 }
224
225 int
226 vmcs_setreg(struct vmcs *vmcs, int running, int ident, uint64_t val)
227 {
228         int error;
229         uint32_t encoding;
230
231         if (ident < 0)
232                 encoding = ident & 0x7fffffff;
233         else
234                 encoding = vmcs_field_encoding(ident);
235
236         if (encoding == (uint32_t)-1)
237                 return (EINVAL);
238
239         val = vmcs_fix_regval(encoding, val);
240
241         if (!running)
242                 VMPTRLD(vmcs);
243
244         error = vmwrite(encoding, val);
245
246         if (!running)
247                 VMCLEAR(vmcs);
248
249         return (error);
250 }
251
252 int
253 vmcs_setdesc(struct vmcs *vmcs, int running, int seg, struct seg_desc *desc)
254 {
255         int error;
256         uint32_t base, limit, access;
257
258         error = vmcs_seg_desc_encoding(seg, &base, &limit, &access);
259         if (error != 0)
260                 panic("vmcs_setdesc: invalid segment register %d", seg);
261
262         if (!running)
263                 VMPTRLD(vmcs);
264         if ((error = vmwrite(base, desc->base)) != 0)
265                 goto done;
266
267         if ((error = vmwrite(limit, desc->limit)) != 0)
268                 goto done;
269
270         if (access != VMCS_INVALID_ENCODING) {
271                 if ((error = vmwrite(access, desc->access)) != 0)
272                         goto done;
273         }
274 done:
275         if (!running)
276                 VMCLEAR(vmcs);
277         return (error);
278 }
279
280 int
281 vmcs_getdesc(struct vmcs *vmcs, int running, int seg, struct seg_desc *desc)
282 {
283         int error;
284         uint32_t base, limit, access;
285         uint64_t u64;
286
287         error = vmcs_seg_desc_encoding(seg, &base, &limit, &access);
288         if (error != 0)
289                 panic("vmcs_getdesc: invalid segment register %d", seg);
290
291         if (!running)
292                 VMPTRLD(vmcs);
293         if ((error = vmread(base, &u64)) != 0)
294                 goto done;
295         desc->base = u64;
296
297         if ((error = vmread(limit, &u64)) != 0)
298                 goto done;
299         desc->limit = u64;
300
301         if (access != VMCS_INVALID_ENCODING) {
302                 if ((error = vmread(access, &u64)) != 0)
303                         goto done;
304                 desc->access = u64;
305         }
306 done:
307         if (!running)
308                 VMCLEAR(vmcs);
309         return (error);
310 }
311
312 int
313 vmcs_set_msr_save(struct vmcs *vmcs, u_long g_area, u_int g_count)
314 {
315         int error;
316
317         VMPTRLD(vmcs);
318
319         /*
320          * Guest MSRs are saved in the VM-exit MSR-store area.
321          * Guest MSRs are loaded from the VM-entry MSR-load area.
322          * Both areas point to the same location in memory.
323          */
324         if ((error = vmwrite(VMCS_EXIT_MSR_STORE, g_area)) != 0)
325                 goto done;
326         if ((error = vmwrite(VMCS_EXIT_MSR_STORE_COUNT, g_count)) != 0)
327                 goto done;
328
329         if ((error = vmwrite(VMCS_ENTRY_MSR_LOAD, g_area)) != 0)
330                 goto done;
331         if ((error = vmwrite(VMCS_ENTRY_MSR_LOAD_COUNT, g_count)) != 0)
332                 goto done;
333
334         error = 0;
335 done:
336         VMCLEAR(vmcs);
337         return (error);
338 }
339
340 int
341 vmcs_init(struct vmcs *vmcs)
342 {
343         int error, codesel, datasel, tsssel;
344         u_long cr0, cr4, efer;
345         uint64_t pat, fsbase, idtrbase;
346
347         codesel = vmm_get_host_codesel();
348         datasel = vmm_get_host_datasel();
349         tsssel = vmm_get_host_tsssel();
350
351         /*
352          * Make sure we have a "current" VMCS to work with.
353          */
354         VMPTRLD(vmcs);
355
356         /* Host state */
357
358         /* Initialize host IA32_PAT MSR */
359         pat = vmm_get_host_pat();
360         if ((error = vmwrite(VMCS_HOST_IA32_PAT, pat)) != 0)
361                 goto done;
362
363         /* Load the IA32_EFER MSR */
364         efer = vmm_get_host_efer();
365         if ((error = vmwrite(VMCS_HOST_IA32_EFER, efer)) != 0)
366                 goto done;
367
368         /* Load the control registers */
369
370         cr0 = vmm_get_host_cr0();
371         if ((error = vmwrite(VMCS_HOST_CR0, cr0)) != 0)
372                 goto done;
373         
374         cr4 = vmm_get_host_cr4() | CR4_VMXE;
375         if ((error = vmwrite(VMCS_HOST_CR4, cr4)) != 0)
376                 goto done;
377
378         /* Load the segment selectors */
379         if ((error = vmwrite(VMCS_HOST_ES_SELECTOR, datasel)) != 0)
380                 goto done;
381
382         if ((error = vmwrite(VMCS_HOST_CS_SELECTOR, codesel)) != 0)
383                 goto done;
384
385         if ((error = vmwrite(VMCS_HOST_SS_SELECTOR, datasel)) != 0)
386                 goto done;
387
388         if ((error = vmwrite(VMCS_HOST_DS_SELECTOR, datasel)) != 0)
389                 goto done;
390
391         if ((error = vmwrite(VMCS_HOST_FS_SELECTOR, datasel)) != 0)
392                 goto done;
393
394         if ((error = vmwrite(VMCS_HOST_GS_SELECTOR, datasel)) != 0)
395                 goto done;
396
397         if ((error = vmwrite(VMCS_HOST_TR_SELECTOR, tsssel)) != 0)
398                 goto done;
399
400         /*
401          * Load the Base-Address for %fs and idtr.
402          *
403          * Note that we exclude %gs, tss and gdtr here because their base
404          * address is pcpu specific.
405          */
406         fsbase = vmm_get_host_fsbase();
407         if ((error = vmwrite(VMCS_HOST_FS_BASE, fsbase)) != 0)
408                 goto done;
409
410         idtrbase = vmm_get_host_idtrbase();
411         if ((error = vmwrite(VMCS_HOST_IDTR_BASE, idtrbase)) != 0)
412                 goto done;
413
414         /* instruction pointer */
415         if (no_flush_rsb) {
416                 if ((error = vmwrite(VMCS_HOST_RIP,
417                     (u_long)vmx_exit_guest)) != 0)
418                         goto done;
419         } else {
420                 if ((error = vmwrite(VMCS_HOST_RIP,
421                     (u_long)vmx_exit_guest_flush_rsb)) != 0)
422                         goto done;
423         }
424
425         /* link pointer */
426         if ((error = vmwrite(VMCS_LINK_POINTER, ~0)) != 0)
427                 goto done;
428 done:
429         VMCLEAR(vmcs);
430         return (error);
431 }
432
433 #ifdef DDB
434 extern int vmxon_enabled[];
435
436 DB_SHOW_COMMAND(vmcs, db_show_vmcs)
437 {
438         uint64_t cur_vmcs, val;
439         uint32_t exit;
440
441         if (!vmxon_enabled[curcpu]) {
442                 db_printf("VMX not enabled\n");
443                 return;
444         }
445
446         if (have_addr) {
447                 db_printf("Only current VMCS supported\n");
448                 return;
449         }
450
451         vmptrst(&cur_vmcs);
452         if (cur_vmcs == VMCS_INITIAL) {
453                 db_printf("No current VM context\n");
454                 return;
455         }
456         db_printf("VMCS: %jx\n", cur_vmcs);
457         db_printf("VPID: %lu\n", vmcs_read(VMCS_VPID));
458         db_printf("Activity: ");
459         val = vmcs_read(VMCS_GUEST_ACTIVITY);
460         switch (val) {
461         case 0:
462                 db_printf("Active");
463                 break;
464         case 1:
465                 db_printf("HLT");
466                 break;
467         case 2:
468                 db_printf("Shutdown");
469                 break;
470         case 3:
471                 db_printf("Wait for SIPI");
472                 break;
473         default:
474                 db_printf("Unknown: %#lx", val);
475         }
476         db_printf("\n");
477         exit = vmcs_read(VMCS_EXIT_REASON);
478         if (exit & 0x80000000)
479                 db_printf("Entry Failure Reason: %u\n", exit & 0xffff);
480         else
481                 db_printf("Exit Reason: %u\n", exit & 0xffff);
482         db_printf("Qualification: %#lx\n", vmcs_exit_qualification());
483         db_printf("Guest Linear Address: %#lx\n",
484             vmcs_read(VMCS_GUEST_LINEAR_ADDRESS));
485         switch (exit & 0x8000ffff) {
486         case EXIT_REASON_EXCEPTION:
487         case EXIT_REASON_EXT_INTR:
488                 val = vmcs_read(VMCS_EXIT_INTR_INFO);
489                 db_printf("Interrupt Type: ");
490                 switch (val >> 8 & 0x7) {
491                 case 0:
492                         db_printf("external");
493                         break;
494                 case 2:
495                         db_printf("NMI");
496                         break;
497                 case 3:
498                         db_printf("HW exception");
499                         break;
500                 case 4:
501                         db_printf("SW exception");
502                         break;
503                 default:
504                         db_printf("?? %lu", val >> 8 & 0x7);
505                         break;
506                 }
507                 db_printf("  Vector: %lu", val & 0xff);
508                 if (val & 0x800)
509                         db_printf("  Error Code: %lx",
510                             vmcs_read(VMCS_EXIT_INTR_ERRCODE));
511                 db_printf("\n");
512                 break;
513         case EXIT_REASON_EPT_FAULT:
514         case EXIT_REASON_EPT_MISCONFIG:
515                 db_printf("Guest Physical Address: %#lx\n",
516                     vmcs_read(VMCS_GUEST_PHYSICAL_ADDRESS));
517                 break;
518         }
519         db_printf("VM-instruction error: %#lx\n", vmcs_instruction_error());
520 }
521 #endif