]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/amd64/vmm/amd/vmcb.c
Allow more VMCB fields to be cached:
[FreeBSD/FreeBSD.git] / sys / amd64 / vmm / amd / vmcb.c
1 /*-
2  * Copyright (c) 2013 Anish Gupta (akgupt3@gmail.com)
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 unmodified, this list of conditions, and the following
10  *    disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25  */
26
27 #include <sys/cdefs.h>
28 __FBSDID("$FreeBSD$");
29
30 #include <sys/param.h>
31 #include <sys/systm.h>
32 #include <sys/cpuset.h>
33
34 #include <machine/segments.h>
35 #include <machine/specialreg.h>
36 #include <machine/vmm.h>
37
38 #include "vmm_ktr.h"
39
40 #include "vmcb.h"
41 #include "svm.h"
42 #include "svm_softc.h"
43
44 /*
45  * The VMCB aka Virtual Machine Control Block is a 4KB aligned page
46  * in memory that describes the virtual machine.
47  *
48  * The VMCB contains:
49  * - instructions or events in the guest to intercept
50  * - control bits that modify execution environment of the guest
51  * - guest processor state (e.g. general purpose registers)
52  */
53
54 /*
55  * Return VMCB segment area.
56  */
57 static struct vmcb_segment *
58 vmcb_segptr(struct vmcb *vmcb, int type)
59 {
60         struct vmcb_state *state;
61         struct vmcb_segment *seg;
62
63         state = &vmcb->state;
64
65         switch (type) {
66         case VM_REG_GUEST_CS:
67                 seg = &state->cs;
68                 break;
69
70         case VM_REG_GUEST_DS:
71                 seg = &state->ds;
72                 break;
73
74         case VM_REG_GUEST_ES:
75                 seg = &state->es;
76                 break;
77
78         case VM_REG_GUEST_FS:
79                 seg = &state->fs;
80                 break;
81
82         case VM_REG_GUEST_GS:
83                 seg = &state->gs;
84                 break;
85
86         case VM_REG_GUEST_SS:
87                 seg = &state->ss;
88                 break;
89
90         case VM_REG_GUEST_GDTR:
91                 seg = &state->gdt;
92                 break;
93
94         case VM_REG_GUEST_IDTR:
95                 seg = &state->idt;
96                 break;
97
98         case VM_REG_GUEST_LDTR:
99                 seg = &state->ldt;
100                 break;
101
102         case VM_REG_GUEST_TR:
103                 seg = &state->tr;
104                 break;
105
106         default:
107                 seg = NULL;
108                 break;
109         }
110
111         return (seg);
112 }
113
114 /*
115  * Read from segment selector, control and general purpose register of VMCB.
116  */
117 int
118 vmcb_read(struct svm_softc *sc, int vcpu, int ident, uint64_t *retval)
119 {
120         struct vmcb *vmcb;
121         struct vmcb_state *state;
122         struct vmcb_segment *seg;
123         int err;
124
125         vmcb = svm_get_vmcb(sc, vcpu);
126         state = &vmcb->state;
127         err = 0;
128
129         switch (ident) {
130         case VM_REG_GUEST_CR0:
131                 *retval = state->cr0;
132                 break;
133
134         case VM_REG_GUEST_CR2:
135                 *retval = state->cr2;
136                 break;
137
138         case VM_REG_GUEST_CR3:
139                 *retval = state->cr3;
140                 break;
141
142         case VM_REG_GUEST_CR4:
143                 *retval = state->cr4;
144                 break;
145
146         case VM_REG_GUEST_DR7:
147                 *retval = state->dr7;
148                 break;
149
150         case VM_REG_GUEST_EFER:
151                 *retval = state->efer;
152                 break;
153
154         case VM_REG_GUEST_RAX:
155                 *retval = state->rax;
156                 break;
157
158         case VM_REG_GUEST_RFLAGS:
159                 *retval = state->rflags;
160                 break;
161
162         case VM_REG_GUEST_RIP:
163                 *retval = state->rip;
164                 break;
165
166         case VM_REG_GUEST_RSP:
167                 *retval = state->rsp;
168                 break;
169
170         case VM_REG_GUEST_CS:
171         case VM_REG_GUEST_DS:
172         case VM_REG_GUEST_ES:
173         case VM_REG_GUEST_FS:
174         case VM_REG_GUEST_GS:
175         case VM_REG_GUEST_SS:
176         case VM_REG_GUEST_LDTR:
177         case VM_REG_GUEST_TR:
178                 seg = vmcb_segptr(vmcb, ident);
179                 KASSERT(seg != NULL, ("%s: unable to get segment %d from VMCB",
180                     __func__, ident));
181                 *retval = seg->selector;
182                 break;
183
184         case VM_REG_GUEST_GDTR:
185         case VM_REG_GUEST_IDTR:
186                 /* GDTR and IDTR don't have segment selectors */
187                 err = EINVAL;
188                 break;
189         default:
190                 err =  EINVAL;
191                 break;
192         }
193
194         return (err);
195 }
196
197 /*
198  * Write to segment selector, control and general purpose register of VMCB.
199  */
200 int
201 vmcb_write(struct svm_softc *sc, int vcpu, int ident, uint64_t val)
202 {
203         struct vmcb *vmcb;
204         struct vmcb_state *state;
205         struct vmcb_segment *seg;
206         int err, dirtyseg;
207
208         vmcb = svm_get_vmcb(sc, vcpu);
209         state = &vmcb->state;
210         dirtyseg = 0;
211         err = 0;
212
213         switch (ident) {
214         case VM_REG_GUEST_CR0:
215                 state->cr0 = val;
216                 svm_set_dirty(sc, vcpu, VMCB_CACHE_CR);
217                 break;
218
219         case VM_REG_GUEST_CR2:
220                 state->cr2 = val;
221                 svm_set_dirty(sc, vcpu, VMCB_CACHE_CR2);
222                 break;
223
224         case VM_REG_GUEST_CR3:
225                 state->cr3 = val;
226                 svm_set_dirty(sc, vcpu, VMCB_CACHE_CR);
227                 break;
228
229         case VM_REG_GUEST_CR4:
230                 state->cr4 = val;
231                 svm_set_dirty(sc, vcpu, VMCB_CACHE_CR);
232                 break;
233
234         case VM_REG_GUEST_DR7:
235                 state->dr7 = val;
236                 break;
237
238         case VM_REG_GUEST_EFER:
239                 /* EFER_SVM must always be set when the guest is executing */
240                 state->efer = val | EFER_SVM;
241                 svm_set_dirty(sc, vcpu, VMCB_CACHE_CR);
242                 break;
243
244         case VM_REG_GUEST_RAX:
245                 state->rax = val;
246                 break;
247
248         case VM_REG_GUEST_RFLAGS:
249                 state->rflags = val;
250                 break;
251
252         case VM_REG_GUEST_RIP:
253                 state->rip = val;
254                 break;
255
256         case VM_REG_GUEST_RSP:
257                 state->rsp = val;
258                 break;
259
260         case VM_REG_GUEST_CS:
261         case VM_REG_GUEST_DS:
262         case VM_REG_GUEST_ES:
263         case VM_REG_GUEST_SS:
264                 dirtyseg = 1;           /* FALLTHROUGH */
265         case VM_REG_GUEST_FS:
266         case VM_REG_GUEST_GS:
267         case VM_REG_GUEST_LDTR:
268         case VM_REG_GUEST_TR:
269                 seg = vmcb_segptr(vmcb, ident);
270                 KASSERT(seg != NULL, ("%s: unable to get segment %d from VMCB",
271                     __func__, ident));
272                 seg->selector = val;
273                 if (dirtyseg)
274                         svm_set_dirty(sc, vcpu, VMCB_CACHE_SEG);
275                 break;
276
277         case VM_REG_GUEST_GDTR:
278         case VM_REG_GUEST_IDTR:
279                 /* GDTR and IDTR don't have segment selectors */
280                 err = EINVAL;
281                 break;
282         default:
283                 err = EINVAL;
284                 break;
285         }
286
287         return (err);
288 }
289
290 int
291 vmcb_seg(struct vmcb *vmcb, int ident, struct vmcb_segment *seg2)
292 {
293         struct vmcb_segment *seg;
294
295         seg = vmcb_segptr(vmcb, ident);
296         if (seg != NULL) {
297                 bcopy(seg, seg2, sizeof(struct vmcb_segment));
298                 return (0);
299         } else {
300                 return (EINVAL);
301         }
302 }
303
304 int
305 vmcb_setdesc(void *arg, int vcpu, int reg, struct seg_desc *desc)
306 {
307         struct vmcb *vmcb;
308         struct svm_softc *sc;
309         struct vmcb_segment *seg;
310         uint16_t attrib;
311
312         sc = arg;
313         vmcb = svm_get_vmcb(sc, vcpu);
314
315         seg = vmcb_segptr(vmcb, reg);
316         KASSERT(seg != NULL, ("%s: invalid segment descriptor %d",
317             __func__, reg));
318
319         seg->base = desc->base;
320         seg->limit = desc->limit;
321         if (reg != VM_REG_GUEST_GDTR && reg != VM_REG_GUEST_IDTR) {
322                 /*
323                  * Map seg_desc access to VMCB attribute format.
324                  *
325                  * SVM uses the 'P' bit in the segment attributes to indicate a
326                  * NULL segment so clear it if the segment is marked unusable.
327                  */
328                 attrib = ((desc->access & 0xF000) >> 4) | (desc->access & 0xFF);
329                 if (SEG_DESC_UNUSABLE(desc->access)) {
330                         attrib &= ~0x80;
331                 }
332                 seg->attrib = attrib;
333         }
334
335         VCPU_CTR4(sc->vm, vcpu, "Setting desc %d: base (%#lx), limit (%#x), "
336             "attrib (%#x)", reg, seg->base, seg->limit, seg->attrib);
337
338         switch (reg) {
339         case VM_REG_GUEST_CS:
340         case VM_REG_GUEST_DS:
341         case VM_REG_GUEST_ES:
342         case VM_REG_GUEST_SS:
343                 svm_set_dirty(sc, vcpu, VMCB_CACHE_SEG);
344         case VM_REG_GUEST_GDTR:
345         case VM_REG_GUEST_IDTR:
346                 svm_set_dirty(sc, vcpu, VMCB_CACHE_DT);
347                 break;
348         default:
349                 break;
350         }
351
352         return (0);
353 }
354
355 int
356 vmcb_getdesc(void *arg, int vcpu, int reg, struct seg_desc *desc)
357 {
358         struct vmcb *vmcb;
359         struct svm_softc *sc;
360         struct vmcb_segment *seg;
361
362         sc = arg;
363         vmcb = svm_get_vmcb(sc, vcpu);
364         seg = vmcb_segptr(vmcb, reg);
365         KASSERT(seg != NULL, ("%s: invalid segment descriptor %d",
366             __func__, reg));
367
368         desc->base = seg->base;
369         desc->limit = seg->limit;
370         desc->access = 0;
371
372         if (reg != VM_REG_GUEST_GDTR && reg != VM_REG_GUEST_IDTR) {
373                 /* Map seg_desc access to VMCB attribute format */
374                 desc->access = ((seg->attrib & 0xF00) << 4) |
375                     (seg->attrib & 0xFF);
376
377                 /*
378                  * VT-x uses bit 16 to indicate a segment that has been loaded
379                  * with a NULL selector (aka unusable). The 'desc->access'
380                  * field is interpreted in the VT-x format by the
381                  * processor-independent code.
382                  *
383                  * SVM uses the 'P' bit to convey the same information so
384                  * convert it into the VT-x format. For more details refer to
385                  * section "Segment State in the VMCB" in APMv2.
386                  */
387                 if (reg != VM_REG_GUEST_CS && reg != VM_REG_GUEST_TR) {
388                         if ((desc->access & 0x80) == 0)
389                                 desc->access |= 0x10000;  /* Unusable segment */
390                 }
391         }
392
393         return (0);
394 }