VirtualBox

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

Last change on this file since 78229 was 76553, checked in by vboxsync, 6 years ago

scm --update-copyright-year

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