Deleted Added
sdiff udiff text old ( 271346 ) new ( 271939 )
full compact
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

--- 11 unchanged lines hidden (view full) ---

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: projects/bhyve_svm/sys/amd64/vmm/amd/vmcb.c 271939 2014-09-21 23:42:54Z neel $");
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 */
57static struct vmcb_segment *
58vmcb_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 */
117int
118vmcb_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

--- 34 unchanged lines hidden (view full) ---

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 */
200int
201vmcb_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;

--- 5 unchanged lines hidden (view full) ---

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
290int
291vmcb_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
304int
305vmcb_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
355int
356vmcb_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}