VirtualBox

source: vbox/trunk/src/VBox/VMM/VMMGC/SELMGC.cpp@ 6738

Last change on this file since 6738 was 5999, checked in by vboxsync, 17 years ago

The Giant CDDL Dual-License Header Change.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 16.8 KB
Line 
1/* $Id: SELMGC.cpp 5999 2007-12-07 15:05:06Z vboxsync $ */
2/** @file
3 * SELM - The Selector Manager, Guest Context.
4 */
5
6/*
7 * Copyright (C) 2006-2007 innotek GmbH
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18/*******************************************************************************
19* Header Files *
20*******************************************************************************/
21#define LOG_GROUP LOG_GROUP_SELM
22#include <VBox/selm.h>
23#include <VBox/mm.h>
24#include <VBox/em.h>
25#include <VBox/trpm.h>
26#include "SELMInternal.h"
27#include <VBox/vm.h>
28#include <VBox/pgm.h>
29
30#include <VBox/param.h>
31#include <VBox/err.h>
32#include <VBox/log.h>
33#include <iprt/assert.h>
34#include <iprt/asm.h>
35
36
37/**
38 * Synchronizes one GDT entry (guest -> shadow).
39 *
40 * @returns VBox status code (appropriate for trap handling and GC return).
41 * @param pVM VM Handle.
42 * @param pRegFrame Trap register frame.
43 * @param iGDTEntry The GDT entry to sync.
44 */
45static int selmGCSyncGDTEntry(PVM pVM, PCPUMCTXCORE pRegFrame, unsigned iGDTEntry)
46{
47 Log2(("GDT %04X LDTR=%04X\n", iGDTEntry, CPUMGetGuestLDTR(pVM)));
48
49 /*
50 * Validate the offset.
51 */
52 VBOXGDTR GdtrGuest;
53 CPUMGetGuestGDTR(pVM, &GdtrGuest);
54 unsigned offEntry = iGDTEntry * sizeof(VBOXDESC);
55 if ( iGDTEntry >= SELM_GDT_ELEMENTS
56 || offEntry > GdtrGuest.cbGdt)
57 return VINF_EM_RAW_EMULATE_INSTR_GDT_FAULT;
58
59 /*
60 * Read the guest descriptor.
61 */
62 VBOXDESC Desc;
63 int rc = MMGCRamRead(pVM, &Desc, (uint8_t *)GdtrGuest.pGdt + offEntry, sizeof(VBOXDESC));
64 if (VBOX_FAILURE(rc))
65 return VINF_EM_RAW_EMULATE_INSTR_GDT_FAULT;
66
67 /*
68 * Check for conflicts.
69 */
70 RTSEL Sel = iGDTEntry << X86_SEL_SHIFT;
71 Assert( !(pVM->selm.s.aHyperSel[SELM_HYPER_SEL_CS] & ~X86_SEL_MASK)
72 && !(pVM->selm.s.aHyperSel[SELM_HYPER_SEL_DS] & ~X86_SEL_MASK)
73 && !(pVM->selm.s.aHyperSel[SELM_HYPER_SEL_CS64] & ~X86_SEL_MASK)
74 && !(pVM->selm.s.aHyperSel[SELM_HYPER_SEL_TSS] & ~X86_SEL_MASK)
75 && !(pVM->selm.s.aHyperSel[SELM_HYPER_SEL_TSS_TRAP08] & ~X86_SEL_MASK));
76 if ( pVM->selm.s.aHyperSel[SELM_HYPER_SEL_CS] == Sel
77 || pVM->selm.s.aHyperSel[SELM_HYPER_SEL_DS] == Sel
78 || pVM->selm.s.aHyperSel[SELM_HYPER_SEL_CS64] == Sel
79 || pVM->selm.s.aHyperSel[SELM_HYPER_SEL_TSS] == Sel
80 || pVM->selm.s.aHyperSel[SELM_HYPER_SEL_TSS_TRAP08] == Sel)
81 {
82 if (Desc.Gen.u1Present)
83 {
84 Log(("selmGCSyncGDTEntry: Sel=%d Desc=%.8Vhxs: detected conflict!!\n", Sel, &Desc));
85 return VINF_SELM_SYNC_GDT;
86 }
87 Log(("selmGCSyncGDTEntry: Sel=%d Desc=%.8Vhxs: potential conflict (still not present)!\n", Sel, &Desc));
88
89 /* Note: we can't continue below or else we'll change the shadow descriptor!! */
90 /* When the guest makes the selector present, then we'll do a GDT sync. */
91 return VINF_SUCCESS;
92 }
93
94 /*
95 * Code and data selectors are generally 1:1, with the
96 * 'little' adjustment we do for DPL 0 selectors.
97 */
98 PVBOXDESC pShadowDescr = &pVM->selm.s.paGdtGC[iGDTEntry];
99 if (Desc.Gen.u1DescType)
100 {
101 /*
102 * Hack for A-bit against Trap E on read-only GDT.
103 */
104 /** @todo Fix this by loading ds and cs before turning off WP. */
105 Desc.Gen.u4Type |= X86_SEL_TYPE_ACCESSED;
106
107 /*
108 * All DPL 0 code and data segments are squeezed into DPL 1.
109 *
110 * We're skipping conforming segments here because those
111 * cannot give us any trouble.
112 */
113 if ( Desc.Gen.u2Dpl == 0
114 && (Desc.Gen.u4Type & (X86_SEL_TYPE_CODE | X86_SEL_TYPE_CONF))
115 != (X86_SEL_TYPE_CODE | X86_SEL_TYPE_CONF) )
116 Desc.Gen.u2Dpl = 1;
117 }
118 else
119 {
120 /*
121 * System type selectors are marked not present.
122 * Recompiler or special handling is required for these.
123 */
124 /** @todo what about interrupt gates and rawr0? */
125 Desc.Gen.u1Present = 0;
126 }
127 //Log(("O: base=%08X limit=%08X attr=%04X\n", pShadowDescr->Gen.u16BaseLow | (pShadowDescr->Gen.u8BaseHigh1 << 16) | (pShadowDescr->Gen.u8BaseHigh2 << 24), pShadowDescr->Gen.u16LimitLow | (pShadowDescr->Gen.u4LimitHigh << 16), (pShadowDescr->au32[1] >> 8) & 0xFFFF ));
128 //Log(("N: base=%08X limit=%08X attr=%04X\n", Desc.Gen.u16BaseLow | (Desc.Gen.u8BaseHigh1 << 16) | (Desc.Gen.u8BaseHigh2 << 24), Desc.Gen.u16LimitLow | (Desc.Gen.u4LimitHigh << 16), (Desc.au32[1] >> 8) & 0xFFFF ));
129 *pShadowDescr = Desc;
130
131 /* Check if we change the LDT selector */
132 if (Sel == CPUMGetGuestLDTR(pVM))
133 {
134 VM_FF_SET(pVM, VM_FF_SELM_SYNC_LDT);
135 return VINF_EM_RAW_EMULATE_INSTR_LDT_FAULT;
136 }
137
138 /* Or the TR selector */
139 if (Sel == CPUMGetGuestTR(pVM))
140 {
141 VM_FF_SET(pVM, VM_FF_SELM_SYNC_TSS);
142 return VINF_EM_RAW_EMULATE_INSTR_TSS_FAULT;
143 }
144
145#ifdef VBOX_STRICT
146 if (Sel == (pRegFrame->cs & X86_SEL_MASK))
147 Log(("GDT write to selector in CS register %04X\n", pRegFrame->cs));
148 else
149 if (Sel == (pRegFrame->ds & X86_SEL_MASK))
150 Log(("GDT write to selector in DS register %04X\n", pRegFrame->ds));
151 else
152 if (Sel == (pRegFrame->es & X86_SEL_MASK))
153 Log(("GDT write to selector in ES register %04X\n", pRegFrame->es));
154 else
155 if (Sel == (pRegFrame->fs & X86_SEL_MASK))
156 Log(("GDT write to selector in FS register %04X\n", pRegFrame->fs));
157 else
158 if (Sel == (pRegFrame->gs & X86_SEL_MASK))
159 Log(("GDT write to selector in GS register %04X\n", pRegFrame->gs));
160 else
161 if (Sel == (pRegFrame->ss & X86_SEL_MASK))
162 Log(("GDT write to selector in SS register %04X\n", pRegFrame->ss));
163#endif
164 return VINF_SUCCESS;
165}
166
167
168/**
169 * \#PF Virtual Handler callback for Guest write access to the Guest's own GDT.
170 *
171 * @returns VBox status code (appropriate for trap handling and GC return).
172 * @param pVM VM Handle.
173 * @param uErrorCode CPU Error code.
174 * @param pRegFrame Trap register frame.
175 * @param pvFault The fault address (cr2).
176 * @param pvRange The base address of the handled virtual range.
177 * @param offRange The offset of the access into this range.
178 * (If it's a EIP range this's the EIP, if not it's pvFault.)
179 */
180SELMGCDECL(int) selmgcGuestGDTWriteHandler(PVM pVM, RTGCUINT uErrorCode, PCPUMCTXCORE pRegFrame, void *pvFault, void *pvRange, uintptr_t offRange)
181{
182 LogFlow(("selmgcGuestGDTWriteHandler errcode=%x fault=%08x offRange=%08x\n", uErrorCode, pvFault, offRange));
183
184 /*
185 * First check if this is the LDT entry.
186 * LDT updates are problemous since an invalid LDT entry will cause trouble during worldswitch.
187 */
188 int rc;
189 if (CPUMGetGuestLDTR(pVM) / sizeof(VBOXDESC) == offRange / sizeof(VBOXDESC))
190 {
191 Log(("LDTR selector change -> fall back to HC!!\n"));
192 rc = VINF_EM_RAW_EMULATE_INSTR_GDT_FAULT;
193 /** @todo We're not handling changed to the selectors in LDTR and TR correctly at all.
194 * We should ignore any changes to those and sync them only when they are loaded by the guest! */
195 }
196 else
197 {
198 /*
199 * Attempt to emulate the instruction and sync the affected entries.
200 */
201 /** @todo should check if any affected selectors are loaded. */
202 uint32_t cb;
203 rc = EMInterpretInstruction(pVM, pRegFrame, pvFault, &cb);
204 if (VBOX_SUCCESS(rc) && cb)
205 {
206 unsigned iGDTE1 = offRange / sizeof(VBOXDESC);
207 int rc2 = selmGCSyncGDTEntry(pVM, pRegFrame, iGDTE1);
208 if (rc2 == VINF_SUCCESS)
209 {
210 Assert(cb);
211 unsigned iGDTE2 = (offRange + cb - 1) / sizeof(VBOXDESC);
212 if (iGDTE1 != iGDTE2)
213 rc2 = selmGCSyncGDTEntry(pVM, pRegFrame, iGDTE2);
214 if (rc2 == VINF_SUCCESS)
215 {
216 STAM_COUNTER_INC(&pVM->selm.s.StatGCWriteGuestGDTHandled);
217 return rc;
218 }
219 }
220 if (rc == VINF_SUCCESS || VBOX_FAILURE(rc2))
221 rc = rc2;
222 }
223 else
224 {
225 Assert(VBOX_FAILURE(rc));
226 if (rc == VERR_EM_INTERPRETER)
227 rc = VINF_EM_RAW_EMULATE_INSTR_GDT_FAULT;
228 }
229 }
230 if ( rc != VINF_EM_RAW_EMULATE_INSTR_LDT_FAULT
231 && rc != VINF_EM_RAW_EMULATE_INSTR_TSS_FAULT)
232 {
233 /* Not necessary when we need to go back to the host context to sync the LDT or TSS. */
234 VM_FF_SET(pVM, VM_FF_SELM_SYNC_GDT);
235 }
236 STAM_COUNTER_INC(&pVM->selm.s.StatGCWriteGuestGDTUnhandled);
237 return rc;
238}
239
240
241/**
242 * \#PF Virtual Handler callback for Guest write access to the Guest's own LDT.
243 *
244 * @returns VBox status code (appropriate for trap handling and GC return).
245 * @param pVM VM Handle.
246 * @param uErrorCode CPU Error code.
247 * @param pRegFrame Trap register frame.
248 * @param pvFault The fault address (cr2).
249 * @param pvRange The base address of the handled virtual range.
250 * @param offRange The offset of the access into this range.
251 * (If it's a EIP range this's the EIP, if not it's pvFault.)
252 */
253SELMGCDECL(int) selmgcGuestLDTWriteHandler(PVM pVM, RTGCUINT uErrorCode, PCPUMCTXCORE pRegFrame, void *pvFault, void *pvRange, uintptr_t offRange)
254{
255 /** @todo To be implemented. */
256 ////LogCom(("selmgcGuestLDTWriteHandler: eip=%08X pvFault=%08X pvRange=%08X\r\n", pRegFrame->eip, pvFault, pvRange));
257
258 VM_FF_SET(pVM, VM_FF_SELM_SYNC_LDT);
259 STAM_COUNTER_INC(&pVM->selm.s.StatGCWriteGuestLDT);
260 return VINF_EM_RAW_EMULATE_INSTR_LDT_FAULT;
261}
262
263
264/**
265 * \#PF Virtual Handler callback for Guest write access to the Guest's own current TSS.
266 *
267 * @returns VBox status code (appropriate for trap handling and GC return).
268 * @param pVM VM Handle.
269 * @param uErrorCode CPU Error code.
270 * @param pRegFrame Trap register frame.
271 * @param pvFault The fault address (cr2).
272 * @param pvRange The base address of the handled virtual range.
273 * @param offRange The offset of the access into this range.
274 * (If it's a EIP range this's the EIP, if not it's pvFault.)
275 */
276SELMGCDECL(int) selmgcGuestTSSWriteHandler(PVM pVM, RTGCUINT uErrorCode, PCPUMCTXCORE pRegFrame, void *pvFault, void *pvRange, uintptr_t offRange)
277{
278 LogFlow(("selmgcGuestTSSWriteHandler errcode=%x fault=%08x offRange=%08x\n", uErrorCode, pvFault, offRange));
279
280 /*
281 * Try emulate the access and compare the R0 ss:esp with the shadow tss values.
282 *
283 * Note, that it's safe to access the TSS after a successfull instruction emulation,
284 * even if the stuff that was changed wasn't the ss0 or esp0 bits. The CPU insists
285 * on the TSS being all one physical page, so ASSUMING that we're not trapping
286 * I/O map accesses this is safe.
287 */
288 uint32_t cb;
289 int rc = EMInterpretInstruction(pVM, pRegFrame, pvFault, &cb);
290 if (VBOX_SUCCESS(rc) && cb)
291 {
292 PCVBOXTSS pGuestTSS = (PVBOXTSS)pVM->selm.s.GCPtrGuestTss;
293 if ( pGuestTSS->esp0 != pVM->selm.s.Tss.esp1
294 || pGuestTSS->ss0 != (pVM->selm.s.Tss.ss1 & ~1)) /* undo raw-r0 */
295 {
296 Log(("selmgcGuestTSSWriteHandler: R0 stack: %RTsel:%VGv -> %RTsel:%VGv\n",
297 (RTSEL)(pVM->selm.s.Tss.ss1 & ~1), pVM->selm.s.Tss.esp1, (RTSEL)pGuestTSS->ss0, pGuestTSS->esp0));
298 pVM->selm.s.Tss.esp1 = pGuestTSS->esp0;
299 pVM->selm.s.Tss.ss1 = pGuestTSS->ss0 | 1;
300 STAM_COUNTER_INC(&pVM->selm.s.StatGCWriteGuestTSSHandledChanged);
301 }
302 if (CPUMGetGuestCR4(pVM) & X86_CR4_VME)
303 {
304 uint32_t offIntRedirBitmap = pGuestTSS->offIoBitmap - sizeof(pVM->selm.s.Tss.IntRedirBitmap);
305
306 /** @todo not sure how the partial case is handled; probably not allowed */
307 if ( offIntRedirBitmap <= offRange
308 && offIntRedirBitmap + sizeof(pVM->selm.s.Tss.IntRedirBitmap) >= offRange + cb
309 && offIntRedirBitmap + sizeof(pVM->selm.s.Tss.IntRedirBitmap) <= pVM->selm.s.cbGuestTss)
310 {
311 Log(("offIoBitmap=%x offIntRedirBitmap=%x cbTSS=%x\n", pGuestTSS->offIoBitmap, offIntRedirBitmap, pVM->selm.s.cbGuestTss));
312 /** @todo only update the changed part. */
313 for (uint32_t i = 0; i < sizeof(pVM->selm.s.Tss.IntRedirBitmap) / 8;i++)
314 {
315 rc = MMGCRamRead(pVM, &pVM->selm.s.Tss.IntRedirBitmap[i * 8], (uint8_t *)pGuestTSS + offIntRedirBitmap + i * 8, 8);
316 if (VBOX_FAILURE(rc))
317 {
318 /* Shadow page table might be out of sync */
319 rc = PGMPrefetchPage(pVM, (uint8_t *)pGuestTSS + offIntRedirBitmap + i*8);
320 if (VBOX_FAILURE(rc))
321 {
322 AssertMsg(rc == VINF_SUCCESS, ("PGMPrefetchPage %VGv failed with %Vrc\n", (uint8_t *)pGuestTSS + offIntRedirBitmap + i*8, rc));
323 break;
324 }
325 rc = MMGCRamRead(pVM, &pVM->selm.s.Tss.IntRedirBitmap[i * 8], (uint8_t *)pGuestTSS + offIntRedirBitmap + i * 8, 8);
326 }
327 AssertMsg(rc == VINF_SUCCESS, ("MMGCRamRead %VGv failed with %Vrc\n", (uint8_t *)pGuestTSS + offIntRedirBitmap + i * 8, rc));
328 }
329 STAM_COUNTER_INC(&pVM->selm.s.StatGCWriteGuestTSSRedir);
330 }
331 }
332 STAM_COUNTER_INC(&pVM->selm.s.StatGCWriteGuestTSSHandled);
333 }
334 else
335 {
336 Assert(VBOX_FAILURE(rc));
337 VM_FF_SET(pVM, VM_FF_SELM_SYNC_TSS);
338 STAM_COUNTER_INC(&pVM->selm.s.StatGCWriteGuestTSSUnhandled);
339 if (rc == VERR_EM_INTERPRETER)
340 rc = VINF_EM_RAW_EMULATE_INSTR_TSS_FAULT;
341 }
342 return rc;
343}
344
345
346
347/**
348 * \#PF Virtual Handler callback for Guest write access to the VBox shadow GDT.
349 *
350 * @returns VBox status code (appropriate for trap handling and GC return).
351 * @param pVM VM Handle.
352 * @param uErrorCode CPU Error code.
353 * @param pRegFrame Trap register frame.
354 * @param pvFault The fault address (cr2).
355 * @param pvRange The base address of the handled virtual range.
356 * @param offRange The offset of the access into this range.
357 * (If it's a EIP range this's the EIP, if not it's pvFault.)
358 */
359SELMGCDECL(int) selmgcShadowGDTWriteHandler(PVM pVM, RTGCUINT uErrorCode, PCPUMCTXCORE pRegFrame, void *pvFault, void *pvRange, uintptr_t offRange)
360{
361 LogRel(("FATAL ERROR: selmgcShadowGDTWriteHandler: eip=%08X pvFault=%08X pvRange=%08X\r\n", pRegFrame->eip, pvFault, pvRange));
362 return VERR_SELM_SHADOW_GDT_WRITE;
363}
364
365/**
366 * \#PF Virtual Handler callback for Guest write access to the VBox shadow LDT.
367 *
368 * @returns VBox status code (appropriate for trap handling and GC return).
369 * @param pVM VM Handle.
370 * @param uErrorCode CPU Error code.
371 * @param pRegFrame Trap register frame.
372 * @param pvFault The fault address (cr2).
373 * @param pvRange The base address of the handled virtual range.
374 * @param offRange The offset of the access into this range.
375 * (If it's a EIP range this's the EIP, if not it's pvFault.)
376 */
377SELMGCDECL(int) selmgcShadowLDTWriteHandler(PVM pVM, RTGCUINT uErrorCode, PCPUMCTXCORE pRegFrame, void *pvFault, void *pvRange, uintptr_t offRange)
378{
379 LogRel(("FATAL ERROR: selmgcShadowLDTWriteHandler: eip=%08X pvFault=%08X pvRange=%08X\r\n", pRegFrame->eip, pvFault, pvRange));
380 Assert(pvFault >= pVM->selm.s.GCPtrLdt && (uintptr_t)pvFault < (uintptr_t)pVM->selm.s.GCPtrLdt + 65536 + PAGE_SIZE);
381 return VERR_SELM_SHADOW_LDT_WRITE;
382}
383
384/**
385 * \#PF Virtual Handler callback for Guest write access to the VBox shadow TSS.
386 *
387 * @returns VBox status code (appropriate for trap handling and GC return).
388 * @param pVM VM Handle.
389 * @param uErrorCode CPU Error code.
390 * @param pRegFrame Trap register frame.
391 * @param pvFault The fault address (cr2).
392 * @param pvRange The base address of the handled virtual range.
393 * @param offRange The offset of the access into this range.
394 * (If it's a EIP range this's the EIP, if not it's pvFault.)
395 */
396SELMGCDECL(int) selmgcShadowTSSWriteHandler(PVM pVM, RTGCUINT uErrorCode, PCPUMCTXCORE pRegFrame, void *pvFault, void *pvRange, uintptr_t offRange)
397{
398 LogRel(("FATAL ERROR: selmgcShadowTSSWriteHandler: eip=%08X pvFault=%08X pvRange=%08X\r\n", pRegFrame->eip, pvFault, pvRange));
399 return VERR_SELM_SHADOW_TSS_WRITE;
400}
401
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette