VirtualBox

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

Last change on this file since 3481 was 2981, checked in by vboxsync, 17 years ago

InnoTek -> innotek: all the headers and comments.

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