VirtualBox

source: vbox/trunk/src/VBox/VMM/VMMRC/SELMRC.cpp@ 52934

Last change on this file since 52934 was 49914, checked in by vboxsync, 11 years ago

SELM: Fixed stale selector handling issue (raw-mode only). Returning VINF_EM_RESCHEDULE_REM isn't OK for PATM should the GDT write monitoring trigger in patch code. Using VINF_EM_RAW_EMULATE_INSTR_GDT_FAULT instead gives the PATM related code in TRPM and EM a chance to clean up before trying IEM and REM. (Seen booting Ubuntu 8.04 live CD.)

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 24.4 KB
Line 
1/* $Id: SELMRC.cpp 49914 2013-12-15 20:03:06Z vboxsync $ */
2/** @file
3 * SELM - The Selector Manager, Guest Context.
4 */
5
6/*
7 * Copyright (C) 2006-2012 Oracle Corporation
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/vmm/selm.h>
23#include <VBox/vmm/mm.h>
24#include <VBox/vmm/em.h>
25#include <VBox/vmm/trpm.h>
26#include "SELMInternal.h"
27#include <VBox/vmm/vm.h>
28#include <VBox/vmm/vmm.h>
29#include <VBox/vmm/pgm.h>
30
31#include <VBox/param.h>
32#include <VBox/err.h>
33#include <VBox/log.h>
34#include <iprt/assert.h>
35#include <iprt/asm.h>
36
37#include "SELMInline.h"
38
39
40/*******************************************************************************
41* Global Variables *
42*******************************************************************************/
43#ifdef LOG_ENABLED
44/** Segment register names. */
45static char const g_aszSRegNms[X86_SREG_COUNT][4] = { "ES", "CS", "SS", "DS", "FS", "GS" };
46#endif
47
48
49#ifdef SELM_TRACK_GUEST_GDT_CHANGES
50/**
51 * Synchronizes one GDT entry (guest -> shadow).
52 *
53 * @returns VBox strict status code (appropriate for trap handling and GC
54 * return).
55 * @retval VINF_EM_RAW_EMULATE_INSTR_GDT_FAULT
56 * @retval VINF_SELM_SYNC_GDT
57 * @retval VINF_EM_RESCHEDULE_REM
58 *
59 * @param pVM Pointer to the VM.
60 * @param pVCpu The current virtual CPU.
61 * @param pRegFrame Trap register frame.
62 * @param iGDTEntry The GDT entry to sync.
63 *
64 * @remarks Caller checks that this isn't the LDT entry!
65 */
66static VBOXSTRICTRC selmRCSyncGDTEntry(PVM pVM, PVMCPU pVCpu, PCPUMCTXCORE pRegFrame, unsigned iGDTEntry)
67{
68 Log2(("GDT %04X LDTR=%04X\n", iGDTEntry, CPUMGetGuestLDTR(pVCpu)));
69
70 /*
71 * Validate the offset.
72 */
73 VBOXGDTR GdtrGuest;
74 CPUMGetGuestGDTR(pVCpu, &GdtrGuest);
75 unsigned offEntry = iGDTEntry * sizeof(X86DESC);
76 if ( iGDTEntry >= SELM_GDT_ELEMENTS
77 || offEntry > GdtrGuest.cbGdt)
78 return VINF_SUCCESS; /* ignore */
79
80 /*
81 * Read the guest descriptor.
82 */
83 X86DESC Desc;
84 int rc = MMGCRamRead(pVM, &Desc, (uint8_t *)(uintptr_t)GdtrGuest.pGdt + offEntry, sizeof(X86DESC));
85 if (RT_FAILURE(rc))
86 {
87 rc = PGMPhysSimpleReadGCPtr(pVCpu, &Desc, (uintptr_t)GdtrGuest.pGdt + offEntry, sizeof(X86DESC));
88 if (RT_FAILURE(rc))
89 {
90 VMCPU_FF_SET(pVCpu, VMCPU_FF_SELM_SYNC_GDT);
91 VMCPU_FF_SET(pVCpu, VMCPU_FF_TO_R3); /* paranoia */
92 return VINF_EM_RESCHEDULE_REM;
93 }
94 }
95
96 /*
97 * Check for conflicts.
98 */
99 RTSEL Sel = iGDTEntry << X86_SEL_SHIFT;
100 Assert( !(pVM->selm.s.aHyperSel[SELM_HYPER_SEL_CS] & ~X86_SEL_MASK_OFF_RPL)
101 && !(pVM->selm.s.aHyperSel[SELM_HYPER_SEL_DS] & ~X86_SEL_MASK_OFF_RPL)
102 && !(pVM->selm.s.aHyperSel[SELM_HYPER_SEL_CS64] & ~X86_SEL_MASK_OFF_RPL)
103 && !(pVM->selm.s.aHyperSel[SELM_HYPER_SEL_TSS] & ~X86_SEL_MASK_OFF_RPL)
104 && !(pVM->selm.s.aHyperSel[SELM_HYPER_SEL_TSS_TRAP08] & ~X86_SEL_MASK_OFF_RPL));
105 if ( pVM->selm.s.aHyperSel[SELM_HYPER_SEL_CS] == Sel
106 || pVM->selm.s.aHyperSel[SELM_HYPER_SEL_DS] == Sel
107 || pVM->selm.s.aHyperSel[SELM_HYPER_SEL_CS64] == Sel
108 || pVM->selm.s.aHyperSel[SELM_HYPER_SEL_TSS] == Sel
109 || pVM->selm.s.aHyperSel[SELM_HYPER_SEL_TSS_TRAP08] == Sel)
110 {
111 if (Desc.Gen.u1Present)
112 {
113 Log(("selmRCSyncGDTEntry: Sel=%d Desc=%.8Rhxs: detected conflict!!\n", Sel, &Desc));
114 VMCPU_FF_SET(pVCpu, VMCPU_FF_SELM_SYNC_GDT);
115 VMCPU_FF_SET(pVCpu, VMCPU_FF_TO_R3);
116 return VINF_SELM_SYNC_GDT; /** @todo this status code is ignored, unfortunately. */
117 }
118 Log(("selmRCSyncGDTEntry: Sel=%d Desc=%.8Rhxs: potential conflict (still not present)!\n", Sel, &Desc));
119
120 /* Note: we can't continue below or else we'll change the shadow descriptor!! */
121 /* When the guest makes the selector present, then we'll do a GDT sync. */
122 return VINF_SUCCESS;
123 }
124
125 /*
126 * Convert the guest selector to a shadow selector and update the shadow GDT.
127 */
128 selmGuestToShadowDesc(pVM, &Desc);
129 PX86DESC pShwDescr = &pVM->selm.s.paGdtRC[iGDTEntry];
130 //Log(("O: base=%08X limit=%08X attr=%04X\n", X86DESC_BASE(*pShwDescr)), X86DESC_LIMIT(*pShwDescr), (pShwDescr->au32[1] >> 8) & 0xFFFF ));
131 //Log(("N: base=%08X limit=%08X attr=%04X\n", X86DESC_BASE(Desc)), X86DESC_LIMIT(Desc), (Desc.au32[1] >> 8) & 0xFFFF ));
132 *pShwDescr = Desc;
133
134 /*
135 * Detect and mark stale registers.
136 */
137 VBOXSTRICTRC rcStrict = VINF_SUCCESS;
138 PCPUMCTX pCtx = CPUMQueryGuestCtxPtr(pVCpu); Assert(CPUMCTX2CORE(pCtx) == pRegFrame);
139 PCPUMSELREG paSReg = CPUMCTX_FIRST_SREG(pCtx);
140 for (unsigned iSReg = 0; iSReg <= X86_SREG_COUNT; iSReg++)
141 {
142 if (Sel == (paSReg[iSReg].Sel & X86_SEL_MASK_OFF_RPL))
143 {
144 if (CPUMSELREG_ARE_HIDDEN_PARTS_VALID(pVCpu, &paSReg[iSReg]))
145 {
146 if (selmIsSRegStale32(&paSReg[iSReg], &Desc, iSReg))
147 {
148 Log(("GDT write to selector in %s register %04X (now stale)\n", g_aszSRegNms[iSReg], paSReg[iSReg].Sel));
149 paSReg[iSReg].fFlags |= CPUMSELREG_FLAGS_STALE;
150 VMCPU_FF_SET(pVCpu, VMCPU_FF_TO_R3); /* paranoia */
151 /* rcStrict = VINF_EM_RESCHEDULE_REM; - bad idea if we're in a patch. */
152 rcStrict = VINF_EM_RAW_EMULATE_INSTR_GDT_FAULT;
153 }
154 else if (paSReg[iSReg].fFlags & CPUMSELREG_FLAGS_STALE)
155 {
156 Log(("GDT write to selector in %s register %04X (no longer stale)\n", g_aszSRegNms[iSReg], paSReg[iSReg].Sel));
157 paSReg[iSReg].fFlags &= ~CPUMSELREG_FLAGS_STALE;
158 }
159 else
160 Log(("GDT write to selector in %s register %04X (no important change)\n", g_aszSRegNms[iSReg], paSReg[iSReg].Sel));
161 }
162 else
163 Log(("GDT write to selector in %s register %04X (out of sync)\n", paSReg[iSReg].Sel));
164 }
165 }
166
167 /** @todo Detect stale LDTR as well? */
168
169 return rcStrict;
170}
171
172
173/**
174 * Synchronizes any segment registers refering to the given GDT entry.
175 *
176 * This is called before any changes performed and shadowed, so it's possible to
177 * look in both the shadow and guest descriptor table entries for hidden
178 * register content.
179 *
180 * @param pVM Pointer to the VM.
181 * @param pVCpu The current virtual CPU.
182 * @param pRegFrame Trap register frame.
183 * @param iGDTEntry The GDT entry to sync.
184 */
185static void selmRCSyncGDTSegRegs(PVM pVM, PVMCPU pVCpu, PCPUMCTXCORE pRegFrame, unsigned iGDTEntry)
186{
187 /*
188 * Validate the offset.
189 */
190 VBOXGDTR GdtrGuest;
191 CPUMGetGuestGDTR(pVCpu, &GdtrGuest);
192 unsigned offEntry = iGDTEntry * sizeof(X86DESC);
193 if ( iGDTEntry >= SELM_GDT_ELEMENTS
194 || offEntry > GdtrGuest.cbGdt)
195 return;
196
197 /*
198 * Sync outdated segment registers using this entry.
199 */
200 PCX86DESC pDesc = &pVM->selm.s.CTX_SUFF(paGdt)[iGDTEntry];
201 uint32_t uCpl = CPUMGetGuestCPL(pVCpu);
202 PCPUMCTX pCtx = CPUMQueryGuestCtxPtr(pVCpu); Assert(CPUMCTX2CORE(pCtx) == pRegFrame);
203 PCPUMSELREG paSReg = CPUMCTX_FIRST_SREG(pCtx);
204 for (unsigned iSReg = 0; iSReg <= X86_SREG_COUNT; iSReg++)
205 {
206 if (iGDTEntry == (paSReg[iSReg].Sel & X86_SEL_MASK_OFF_RPL))
207 {
208 if (!CPUMSELREG_ARE_HIDDEN_PARTS_VALID(pVCpu, &paSReg[iSReg]))
209 {
210 if (selmIsShwDescGoodForSReg(&paSReg[iSReg], pDesc, iSReg, uCpl))
211 {
212 selmLoadHiddenSRegFromShadowDesc(&paSReg[iSReg], pDesc);
213 Log(("selmRCSyncGDTSegRegs: Updated %s\n", g_aszSRegNms[iSReg]));
214 }
215 else
216 Log(("selmRCSyncGDTSegRegs: Bad shadow descriptor %#x (for %s): %.8Rhxs \n",
217 iGDTEntry, g_aszSRegNms[iSReg], pDesc));
218 }
219 }
220 }
221
222}
223
224
225
226/**
227 * \#PF Virtual Handler callback for Guest write access to the Guest's own GDT.
228 *
229 * @returns VBox status code (appropriate for trap handling and GC return).
230 * @param pVM Pointer to the VM.
231 * @param uErrorCode CPU Error code.
232 * @param pRegFrame Trap register frame.
233 * @param pvFault The fault address (cr2).
234 * @param pvRange The base address of the handled virtual range.
235 * @param offRange The offset of the access into this range.
236 * (If it's a EIP range this is the EIP, if not it's pvFault.)
237 */
238VMMRCDECL(int) selmRCGuestGDTWriteHandler(PVM pVM, RTGCUINT uErrorCode, PCPUMCTXCORE pRegFrame, RTGCPTR pvFault, RTGCPTR pvRange, uintptr_t offRange)
239{
240 PVMCPU pVCpu = VMMGetCpu0(pVM);
241 LogFlow(("selmRCGuestGDTWriteHandler errcode=%x fault=%RGv offRange=%08x\n", (uint32_t)uErrorCode, pvFault, offRange));
242 NOREF(pvRange);
243
244 /*
245 * Check if any selectors might be affected.
246 */
247 unsigned const iGDTE1 = offRange >> X86_SEL_SHIFT;
248 selmRCSyncGDTSegRegs(pVM, pVCpu, pRegFrame, iGDTE1);
249 if (((offRange + 8) >> X86_SEL_SHIFT) != iGDTE1)
250 selmRCSyncGDTSegRegs(pVM, pVCpu, pRegFrame, iGDTE1 + 1);
251
252 /*
253 * Attempt to emulate the instruction and sync the affected entries.
254 */
255 uint32_t cb;
256 int rc = EMInterpretInstructionEx(pVCpu, pRegFrame, (RTGCPTR)(RTRCUINTPTR)pvFault, &cb);
257 if (RT_SUCCESS(rc) && cb)
258 {
259 /* Check if the LDT was in any way affected. Do not sync the
260 shadow GDT if that's the case or we might have trouble in
261 the world switcher (or so they say). */
262 unsigned const iLdt = CPUMGetGuestLDTR(pVCpu) >> X86_SEL_SHIFT;
263 unsigned const iGDTE2 = (offRange + cb - 1) >> X86_SEL_SHIFT;
264 if ( iGDTE1 == iLdt
265 || iGDTE2 == iLdt)
266 {
267 Log(("LDTR selector change -> fall back to HC!!\n"));
268 VMCPU_FF_SET(pVCpu, VMCPU_FF_SELM_SYNC_GDT);
269 rc = VINF_SELM_SYNC_GDT;
270 /** @todo Implement correct stale LDT handling. */
271 }
272 else
273 {
274 /* Sync the shadow GDT and continue provided the update didn't
275 cause any segment registers to go stale in any way. */
276 int rc2 = selmRCSyncGDTEntry(pVM, pVCpu, pRegFrame, iGDTE1);
277 if (rc2 == VINF_SUCCESS || rc2 == VINF_EM_RESCHEDULE_REM)
278 {
279 if (rc == VINF_SUCCESS)
280 rc = rc2;
281
282 if (iGDTE1 != iGDTE2)
283 {
284 rc2 = selmRCSyncGDTEntry(pVM, pVCpu, pRegFrame, iGDTE2);
285 if (rc == VINF_SUCCESS)
286 rc = rc2;
287 }
288
289 if (rc2 == VINF_SUCCESS || rc2 == VINF_EM_RESCHEDULE_REM)
290 {
291 /* VINF_EM_RESCHEDULE_REM - bad idea if we're in a patch. */
292 if (rc2 == VINF_EM_RESCHEDULE_REM)
293 rc = VINF_EM_RAW_EMULATE_INSTR_GDT_FAULT;
294 STAM_COUNTER_INC(&pVM->selm.s.StatRCWriteGuestGDTHandled);
295 return rc;
296 }
297 }
298
299 /* sync failed, return to ring-3 and resync the GDT. */
300 if (rc == VINF_SUCCESS || RT_FAILURE(rc2))
301 rc = rc2;
302 }
303 }
304 else
305 {
306 Assert(RT_FAILURE(rc));
307 if (rc == VERR_EM_INTERPRETER)
308 rc = VINF_EM_RAW_EMULATE_INSTR_GDT_FAULT;
309 VMCPU_FF_SET(pVCpu, VMCPU_FF_SELM_SYNC_GDT);
310 }
311
312 STAM_COUNTER_INC(&pVM->selm.s.StatRCWriteGuestGDTUnhandled);
313 return rc;
314}
315#endif /* SELM_TRACK_GUEST_GDT_CHANGES */
316
317
318#ifdef SELM_TRACK_GUEST_LDT_CHANGES
319/**
320 * \#PF Virtual Handler callback for Guest write access to the Guest's own LDT.
321 *
322 * @returns VBox status code (appropriate for trap handling and GC return).
323 * @param pVM Pointer to the VM.
324 * @param uErrorCode CPU Error code.
325 * @param pRegFrame Trap register frame.
326 * @param pvFault The fault address (cr2).
327 * @param pvRange The base address of the handled virtual range.
328 * @param offRange The offset of the access into this range.
329 * (If it's a EIP range this is the EIP, if not it's pvFault.)
330 */
331VMMRCDECL(int) selmRCGuestLDTWriteHandler(PVM pVM, RTGCUINT uErrorCode, PCPUMCTXCORE pRegFrame, RTGCPTR pvFault, RTGCPTR pvRange, uintptr_t offRange)
332{
333 /** @todo To be implemented. */
334 ////LogCom(("selmRCGuestLDTWriteHandler: eip=%08X pvFault=%RGv pvRange=%RGv\r\n", pRegFrame->eip, pvFault, pvRange));
335 NOREF(uErrorCode); NOREF(pRegFrame); NOREF(pvFault); NOREF(pvRange); NOREF(offRange);
336
337 VMCPU_FF_SET(VMMGetCpu0(pVM), VMCPU_FF_SELM_SYNC_LDT);
338 STAM_COUNTER_INC(&pVM->selm.s.StatRCWriteGuestLDT);
339 return VINF_EM_RAW_EMULATE_INSTR_LDT_FAULT;
340}
341#endif
342
343
344#ifdef SELM_TRACK_GUEST_TSS_CHANGES
345/**
346 * Read wrapper used by selmRCGuestTSSWriteHandler.
347 * @returns VBox status code (appropriate for trap handling and GC return).
348 * @param pVM Pointer to the VM.
349 * @param pvDst Where to put the bits we read.
350 * @param pvSrc Guest address to read from.
351 * @param cb The number of bytes to read.
352 */
353DECLINLINE(int) selmRCReadTssBits(PVM pVM, void *pvDst, void const *pvSrc, size_t cb)
354{
355 PVMCPU pVCpu = VMMGetCpu0(pVM);
356
357 int rc = MMGCRamRead(pVM, pvDst, (void *)pvSrc, cb);
358 if (RT_SUCCESS(rc))
359 return VINF_SUCCESS;
360
361 /** @todo use different fallback? */
362 rc = PGMPrefetchPage(pVCpu, (uintptr_t)pvSrc);
363 AssertMsg(rc == VINF_SUCCESS, ("PGMPrefetchPage %p failed with %Rrc\n", &pvSrc, rc));
364 if (rc == VINF_SUCCESS)
365 {
366 rc = MMGCRamRead(pVM, pvDst, (void *)pvSrc, cb);
367 AssertMsg(rc == VINF_SUCCESS, ("MMGCRamRead %p failed with %Rrc\n", &pvSrc, rc));
368 }
369 return rc;
370}
371
372/**
373 * \#PF Virtual Handler callback for Guest write access to the Guest's own current TSS.
374 *
375 * @returns VBox status code (appropriate for trap handling and GC return).
376 * @param pVM Pointer to the VM.
377 * @param uErrorCode CPU Error code.
378 * @param pRegFrame Trap register frame.
379 * @param pvFault The fault address (cr2).
380 * @param pvRange The base address of the handled virtual range.
381 * @param offRange The offset of the access into this range.
382 * (If it's a EIP range this is the EIP, if not it's pvFault.)
383 */
384VMMRCDECL(int) selmRCGuestTSSWriteHandler(PVM pVM, RTGCUINT uErrorCode, PCPUMCTXCORE pRegFrame, RTGCPTR pvFault, RTGCPTR pvRange, uintptr_t offRange)
385{
386 PVMCPU pVCpu = VMMGetCpu0(pVM);
387 LogFlow(("selmRCGuestTSSWriteHandler errcode=%x fault=%RGv offRange=%08x\n", (uint32_t)uErrorCode, pvFault, offRange));
388 NOREF(pvRange);
389
390 /*
391 * Try emulate the access.
392 */
393 uint32_t cb;
394 int rc = EMInterpretInstructionEx(pVCpu, pRegFrame, (RTGCPTR)(RTRCUINTPTR)pvFault, &cb);
395 if ( RT_SUCCESS(rc)
396 && cb)
397 {
398 rc = VINF_SUCCESS;
399
400 /*
401 * If it's on the same page as the esp0 and ss0 fields or actually one of them,
402 * then check if any of these has changed.
403 */
404 PCVBOXTSS pGuestTss = (PVBOXTSS)(uintptr_t)pVM->selm.s.GCPtrGuestTss;
405 if ( PAGE_ADDRESS(&pGuestTss->esp0) == PAGE_ADDRESS(&pGuestTss->padding_ss0)
406 && PAGE_ADDRESS(&pGuestTss->esp0) == PAGE_ADDRESS((uint8_t *)pGuestTss + offRange)
407 && ( pGuestTss->esp0 != pVM->selm.s.Tss.esp1
408 || pGuestTss->ss0 != (pVM->selm.s.Tss.ss1 & ~1)) /* undo raw-r0 */
409 )
410 {
411 Log(("selmRCGuestTSSWriteHandler: R0 stack: %RTsel:%RGv -> %RTsel:%RGv\n",
412 (RTSEL)(pVM->selm.s.Tss.ss1 & ~1), (RTGCPTR)pVM->selm.s.Tss.esp1, (RTSEL)pGuestTss->ss0, (RTGCPTR)pGuestTss->esp0));
413 pVM->selm.s.Tss.esp1 = pGuestTss->esp0;
414 pVM->selm.s.Tss.ss1 = pGuestTss->ss0 | 1;
415 STAM_COUNTER_INC(&pVM->selm.s.StatRCWriteGuestTSSHandledChanged);
416 }
417#ifdef VBOX_WITH_RAW_RING1
418 else if ( EMIsRawRing1Enabled(pVM)
419 && PAGE_ADDRESS(&pGuestTss->esp1) == PAGE_ADDRESS(&pGuestTss->padding_ss1)
420 && PAGE_ADDRESS(&pGuestTss->esp1) == PAGE_ADDRESS((uint8_t *)pGuestTss + offRange)
421 && ( pGuestTss->esp1 != pVM->selm.s.Tss.esp2
422 || pGuestTss->ss1 != ((pVM->selm.s.Tss.ss2 & ~2) | 1)) /* undo raw-r1 */
423 )
424 {
425 Log(("selmRCGuestTSSWriteHandler: R1 stack: %RTsel:%RGv -> %RTsel:%RGv\n",
426 (RTSEL)((pVM->selm.s.Tss.ss2 & ~2) | 1), (RTGCPTR)pVM->selm.s.Tss.esp2, (RTSEL)pGuestTss->ss1, (RTGCPTR)pGuestTss->esp1));
427 pVM->selm.s.Tss.esp2 = pGuestTss->esp1;
428 pVM->selm.s.Tss.ss2 = (pGuestTss->ss1 & ~1) | 2;
429 STAM_COUNTER_INC(&pVM->selm.s.StatRCWriteGuestTSSHandledChanged);
430 }
431#endif
432 /* Handle misaligned TSS in a safe manner (just in case). */
433 else if ( offRange >= RT_UOFFSETOF(VBOXTSS, esp0)
434 && offRange < RT_UOFFSETOF(VBOXTSS, padding_ss0))
435 {
436 struct
437 {
438 uint32_t esp0;
439 uint16_t ss0;
440 uint16_t padding_ss0;
441 } s;
442 AssertCompileSize(s, 8);
443 rc = selmRCReadTssBits(pVM, &s, &pGuestTss->esp0, sizeof(s));
444 if ( rc == VINF_SUCCESS
445 && ( s.esp0 != pVM->selm.s.Tss.esp1
446 || s.ss0 != (pVM->selm.s.Tss.ss1 & ~1)) /* undo raw-r0 */
447 )
448 {
449 Log(("selmRCGuestTSSWriteHandler: R0 stack: %RTsel:%RGv -> %RTsel:%RGv [x-page]\n",
450 (RTSEL)(pVM->selm.s.Tss.ss1 & ~1), (RTGCPTR)pVM->selm.s.Tss.esp1, (RTSEL)s.ss0, (RTGCPTR)s.esp0));
451 pVM->selm.s.Tss.esp1 = s.esp0;
452 pVM->selm.s.Tss.ss1 = s.ss0 | 1;
453 STAM_COUNTER_INC(&pVM->selm.s.StatRCWriteGuestTSSHandledChanged);
454 }
455 }
456
457 /*
458 * If VME is enabled we need to check if the interrupt redirection bitmap
459 * needs updating.
460 */
461 if ( offRange >= RT_UOFFSETOF(VBOXTSS, offIoBitmap)
462 && (CPUMGetGuestCR4(pVCpu) & X86_CR4_VME))
463 {
464 if (offRange - RT_UOFFSETOF(VBOXTSS, offIoBitmap) < sizeof(pGuestTss->offIoBitmap))
465 {
466 uint16_t offIoBitmap = pGuestTss->offIoBitmap;
467 if (offIoBitmap != pVM->selm.s.offGuestIoBitmap)
468 {
469 Log(("TSS offIoBitmap changed: old=%#x new=%#x -> resync in ring-3\n", pVM->selm.s.offGuestIoBitmap, offIoBitmap));
470 VMCPU_FF_SET(pVCpu, VMCPU_FF_SELM_SYNC_TSS);
471 VMCPU_FF_SET(pVCpu, VMCPU_FF_TO_R3);
472 }
473 else
474 Log(("TSS offIoBitmap: old=%#x new=%#x [unchanged]\n", pVM->selm.s.offGuestIoBitmap, offIoBitmap));
475 }
476 else
477 {
478 /** @todo not sure how the partial case is handled; probably not allowed */
479 uint32_t offIntRedirBitmap = pVM->selm.s.offGuestIoBitmap - sizeof(pVM->selm.s.Tss.IntRedirBitmap);
480 if ( offIntRedirBitmap <= offRange
481 && offIntRedirBitmap + sizeof(pVM->selm.s.Tss.IntRedirBitmap) >= offRange + cb
482 && offIntRedirBitmap + sizeof(pVM->selm.s.Tss.IntRedirBitmap) <= pVM->selm.s.cbGuestTss)
483 {
484 Log(("TSS IntRedirBitmap Changed: offIoBitmap=%x offIntRedirBitmap=%x cbTSS=%x offRange=%x cb=%x\n",
485 pVM->selm.s.offGuestIoBitmap, offIntRedirBitmap, pVM->selm.s.cbGuestTss, offRange, cb));
486
487 /** @todo only update the changed part. */
488 for (uint32_t i = 0; i < sizeof(pVM->selm.s.Tss.IntRedirBitmap) / 8; i++)
489 {
490 rc = selmRCReadTssBits(pVM, &pVM->selm.s.Tss.IntRedirBitmap[i * 8],
491 (uint8_t *)pGuestTss + offIntRedirBitmap + i * 8, 8);
492 if (rc != VINF_SUCCESS)
493 break;
494 }
495 STAM_COUNTER_INC(&pVM->selm.s.StatRCWriteGuestTSSRedir);
496 }
497 }
498 }
499
500 /* Return to ring-3 for a full resync if any of the above fails... (?) */
501 if (rc != VINF_SUCCESS)
502 {
503 VMCPU_FF_SET(pVCpu, VMCPU_FF_SELM_SYNC_TSS);
504 VMCPU_FF_SET(pVCpu, VMCPU_FF_TO_R3);
505 if (RT_SUCCESS(rc))
506 rc = VINF_SUCCESS;
507 }
508
509 STAM_COUNTER_INC(&pVM->selm.s.StatRCWriteGuestTSSHandled);
510 }
511 else
512 {
513 AssertMsg(RT_FAILURE(rc), ("cb=%u rc=%#x\n", cb, rc));
514 VMCPU_FF_SET(pVCpu, VMCPU_FF_SELM_SYNC_TSS);
515 STAM_COUNTER_INC(&pVM->selm.s.StatRCWriteGuestTSSUnhandled);
516 if (rc == VERR_EM_INTERPRETER)
517 rc = VINF_EM_RAW_EMULATE_INSTR_TSS_FAULT;
518 }
519 return rc;
520}
521#endif /* SELM_TRACK_GUEST_TSS_CHANGES */
522
523
524#ifdef SELM_TRACK_SHADOW_GDT_CHANGES
525/**
526 * \#PF Virtual Handler callback for Guest write access to the VBox shadow GDT.
527 *
528 * @returns VBox status code (appropriate for trap handling and GC return).
529 * @param pVM Pointer to the VM.
530 * @param uErrorCode CPU Error code.
531 * @param pRegFrame Trap register frame.
532 * @param pvFault The fault address (cr2).
533 * @param pvRange The base address of the handled virtual range.
534 * @param offRange The offset of the access into this range.
535 * (If it's a EIP range this is the EIP, if not it's pvFault.)
536 */
537VMMRCDECL(int) selmRCShadowGDTWriteHandler(PVM pVM, RTGCUINT uErrorCode, PCPUMCTXCORE pRegFrame, RTGCPTR pvFault, RTGCPTR pvRange, uintptr_t offRange)
538{
539 LogRel(("FATAL ERROR: selmRCShadowGDTWriteHandler: eip=%08X pvFault=%RGv pvRange=%RGv\r\n", pRegFrame->eip, pvFault, pvRange));
540 NOREF(pVM); NOREF(uErrorCode); NOREF(pRegFrame); NOREF(pvFault); NOREF(pvRange); NOREF(offRange);
541 return VERR_SELM_SHADOW_GDT_WRITE;
542}
543#endif
544
545
546#ifdef SELM_TRACK_SHADOW_LDT_CHANGES
547/**
548 * \#PF Virtual Handler callback for Guest write access to the VBox shadow LDT.
549 *
550 * @returns VBox status code (appropriate for trap handling and GC return).
551 * @param pVM Pointer to the VM.
552 * @param uErrorCode CPU Error code.
553 * @param pRegFrame Trap register frame.
554 * @param pvFault The fault address (cr2).
555 * @param pvRange The base address of the handled virtual range.
556 * @param offRange The offset of the access into this range.
557 * (If it's a EIP range this is the EIP, if not it's pvFault.)
558 */
559VMMRCDECL(int) selmRCShadowLDTWriteHandler(PVM pVM, RTGCUINT uErrorCode, PCPUMCTXCORE pRegFrame, RTGCPTR pvFault, RTGCPTR pvRange, uintptr_t offRange)
560{
561 LogRel(("FATAL ERROR: selmRCShadowLDTWriteHandler: eip=%08X pvFault=%RGv pvRange=%RGv\r\n", pRegFrame->eip, pvFault, pvRange));
562 Assert(pvFault - (uintptr_t)pVM->selm.s.pvLdtRC < (unsigned)(65536U + PAGE_SIZE));
563 NOREF(pVM); NOREF(uErrorCode); NOREF(pRegFrame); NOREF(pvFault); NOREF(pvRange); NOREF(offRange);
564 return VERR_SELM_SHADOW_LDT_WRITE;
565}
566#endif
567
568
569#ifdef SELM_TRACK_SHADOW_TSS_CHANGES
570/**
571 * \#PF Virtual Handler callback for Guest write access to the VBox shadow TSS.
572 *
573 * @returns VBox status code (appropriate for trap handling and GC return).
574 * @param pVM Pointer to the VM.
575 * @param uErrorCode CPU Error code.
576 * @param pRegFrame Trap register frame.
577 * @param pvFault The fault address (cr2).
578 * @param pvRange The base address of the handled virtual range.
579 * @param offRange The offset of the access into this range.
580 * (If it's a EIP range this is the EIP, if not it's pvFault.)
581 */
582VMMRCDECL(int) selmRCShadowTSSWriteHandler(PVM pVM, RTGCUINT uErrorCode, PCPUMCTXCORE pRegFrame, RTGCPTR pvFault, RTGCPTR pvRange, uintptr_t offRange)
583{
584 LogRel(("FATAL ERROR: selmRCShadowTSSWriteHandler: eip=%08X pvFault=%RGv pvRange=%RGv\r\n", pRegFrame->eip, pvFault, pvRange));
585 NOREF(pVM); NOREF(uErrorCode); NOREF(pRegFrame); NOREF(pvFault); NOREF(pvRange); NOREF(offRange);
586 return VERR_SELM_SHADOW_TSS_WRITE;
587}
588#endif
589
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