VirtualBox

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

Last change on this file since 71983 was 69111, checked in by vboxsync, 7 years ago

(C) year

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 24.6 KB
Line 
1/* $Id: SELMRC.cpp 69111 2017-10-17 14:26:02Z vboxsync $ */
2/** @file
3 * SELM - The Selector Manager, Guest Context.
4 */
5
6/*
7 * Copyright (C) 2006-2017 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_PENDING(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 * If it's on the same page as the esp0 and ss0 fields or actually one of them,
405 * then check if any of these has changed.
406 */
407/** @todo just read the darn fields and put them on the stack. */
408 PCVBOXTSS pGuestTss = (PVBOXTSS)(uintptr_t)pVM->selm.s.GCPtrGuestTss;
409 if ( PAGE_ADDRESS(&pGuestTss->esp0) == PAGE_ADDRESS(&pGuestTss->padding_ss0)
410 && PAGE_ADDRESS(&pGuestTss->esp0) == PAGE_ADDRESS((uint8_t *)pGuestTss + offGuestTss)
411 && ( pGuestTss->esp0 != pVM->selm.s.Tss.esp1
412 || pGuestTss->ss0 != (pVM->selm.s.Tss.ss1 & ~1)) /* undo raw-r0 */
413 )
414 {
415 Log(("selmRCGuestTSSWritePfHandler: R0 stack: %RTsel:%RGv -> %RTsel:%RGv\n",
416 (RTSEL)(pVM->selm.s.Tss.ss1 & ~1), (RTGCPTR)pVM->selm.s.Tss.esp1, (RTSEL)pGuestTss->ss0, (RTGCPTR)pGuestTss->esp0));
417 pVM->selm.s.Tss.esp1 = pGuestTss->esp0;
418 pVM->selm.s.Tss.ss1 = pGuestTss->ss0 | 1;
419 STAM_COUNTER_INC(&pVM->selm.s.StatRCWriteGuestTSSHandledChanged);
420 }
421# ifdef VBOX_WITH_RAW_RING1
422 else if ( EMIsRawRing1Enabled(pVM)
423 && PAGE_ADDRESS(&pGuestTss->esp1) == PAGE_ADDRESS(&pGuestTss->padding_ss1)
424 && PAGE_ADDRESS(&pGuestTss->esp1) == PAGE_ADDRESS((uint8_t *)pGuestTss + offGuestTss)
425 && ( pGuestTss->esp1 != pVM->selm.s.Tss.esp2
426 || pGuestTss->ss1 != ((pVM->selm.s.Tss.ss2 & ~2) | 1)) /* undo raw-r1 */
427 )
428 {
429 Log(("selmRCGuestTSSWritePfHandler: R1 stack: %RTsel:%RGv -> %RTsel:%RGv\n",
430 (RTSEL)((pVM->selm.s.Tss.ss2 & ~2) | 1), (RTGCPTR)pVM->selm.s.Tss.esp2, (RTSEL)pGuestTss->ss1, (RTGCPTR)pGuestTss->esp1));
431 pVM->selm.s.Tss.esp2 = pGuestTss->esp1;
432 pVM->selm.s.Tss.ss2 = (pGuestTss->ss1 & ~1) | 2;
433 STAM_COUNTER_INC(&pVM->selm.s.StatRCWriteGuestTSSHandledChanged);
434 }
435# endif
436 /* Handle misaligned TSS in a safe manner (just in case). */
437 else if ( offGuestTss >= RT_UOFFSETOF(VBOXTSS, esp0)
438 && offGuestTss < RT_UOFFSETOF(VBOXTSS, padding_ss0))
439 {
440 struct
441 {
442 uint32_t esp0;
443 uint16_t ss0;
444 uint16_t padding_ss0;
445 } s;
446 AssertCompileSize(s, 8);
447 rcStrict = selmRCReadTssBits(pVM, pVCpu, &s, &pGuestTss->esp0, sizeof(s));
448 if ( rcStrict == VINF_SUCCESS
449 && ( s.esp0 != pVM->selm.s.Tss.esp1
450 || s.ss0 != (pVM->selm.s.Tss.ss1 & ~1)) /* undo raw-r0 */
451 )
452 {
453 Log(("selmRCGuestTSSWritePfHandler: R0 stack: %RTsel:%RGv -> %RTsel:%RGv [x-page]\n",
454 (RTSEL)(pVM->selm.s.Tss.ss1 & ~1), (RTGCPTR)pVM->selm.s.Tss.esp1, (RTSEL)s.ss0, (RTGCPTR)s.esp0));
455 pVM->selm.s.Tss.esp1 = s.esp0;
456 pVM->selm.s.Tss.ss1 = s.ss0 | 1;
457 STAM_COUNTER_INC(&pVM->selm.s.StatRCWriteGuestTSSHandledChanged);
458 }
459 }
460
461 /*
462 * If VME is enabled we need to check if the interrupt redirection bitmap
463 * needs updating.
464 */
465 if ( offGuestTss >= RT_UOFFSETOF(VBOXTSS, offIoBitmap)
466 && (CPUMGetGuestCR4(pVCpu) & X86_CR4_VME))
467 {
468 if (offGuestTss - RT_UOFFSETOF(VBOXTSS, offIoBitmap) < sizeof(pGuestTss->offIoBitmap))
469 {
470 uint16_t offIoBitmap = pGuestTss->offIoBitmap;
471 if (offIoBitmap != pVM->selm.s.offGuestIoBitmap)
472 {
473 Log(("TSS offIoBitmap changed: old=%#x new=%#x -> resync in ring-3\n", pVM->selm.s.offGuestIoBitmap, offIoBitmap));
474 VMCPU_FF_SET(pVCpu, VMCPU_FF_SELM_SYNC_TSS);
475 VMCPU_FF_SET(pVCpu, VMCPU_FF_TO_R3);
476 }
477 else
478 Log(("TSS offIoBitmap: old=%#x new=%#x [unchanged]\n", pVM->selm.s.offGuestIoBitmap, offIoBitmap));
479 }
480 else
481 {
482 /** @todo not sure how the partial case is handled; probably not allowed */
483 uint32_t offIntRedirBitmap = pVM->selm.s.offGuestIoBitmap - sizeof(pVM->selm.s.Tss.IntRedirBitmap);
484 if ( offIntRedirBitmap <= offGuestTss
485 && offIntRedirBitmap + sizeof(pVM->selm.s.Tss.IntRedirBitmap) >= offGuestTss + cbWrite
486 && offIntRedirBitmap + sizeof(pVM->selm.s.Tss.IntRedirBitmap) <= pVM->selm.s.cbGuestTss)
487 {
488 Log(("TSS IntRedirBitmap Changed: offIoBitmap=%x offIntRedirBitmap=%x cbTSS=%x offGuestTss=%x cbWrite=%x\n",
489 pVM->selm.s.offGuestIoBitmap, offIntRedirBitmap, pVM->selm.s.cbGuestTss, offGuestTss, cbWrite));
490
491 /** @todo only update the changed part. */
492 for (uint32_t i = 0; rcStrict == VINF_SUCCESS && i < sizeof(pVM->selm.s.Tss.IntRedirBitmap) / 8; i++)
493 rcStrict = selmRCReadTssBits(pVM, pVCpu, &pVM->selm.s.Tss.IntRedirBitmap[i * 8],
494 (uint8_t *)pGuestTss + offIntRedirBitmap + i * 8, 8);
495 STAM_COUNTER_INC(&pVM->selm.s.StatRCWriteGuestTSSRedir);
496 }
497 }
498 }
499
500 /*
501 * Return to ring-3 for a full resync if any of the above fails... (?)
502 */
503 if (rcStrict != VINF_SUCCESS)
504 {
505 VMCPU_FF_SET(pVCpu, VMCPU_FF_SELM_SYNC_TSS);
506 VMCPU_FF_SET(pVCpu, VMCPU_FF_TO_R3);
507 if (RT_SUCCESS(rcStrict))
508 rcStrict = VINF_SUCCESS;
509 }
510
511 STAM_COUNTER_INC(&pVM->selm.s.StatRCWriteGuestTSSHandled);
512 return rcStrict;
513}
514
515
516/**
517 * @callback_method_impl{FNPGMVIRTHANDLER, Guest TSS write access \#PF handler}
518 */
519DECLEXPORT(VBOXSTRICTRC) selmRCGuestTSSWritePfHandler(PVM pVM, PVMCPU pVCpu, RTGCUINT uErrorCode, PCPUMCTXCORE pRegFrame,
520 RTGCPTR pvFault, RTGCPTR pvRange, uintptr_t offRange, void *pvUser)
521{
522 LogFlow(("selmRCGuestTSSWritePfHandler errcode=%x fault=%RGv offRange=%08x\n", (uint32_t)uErrorCode, pvFault, offRange));
523 NOREF(pvRange); NOREF(pvUser); RT_NOREF_PV(uErrorCode);
524
525 /*
526 * Try emulate the access.
527 */
528 uint32_t cb;
529 VBOXSTRICTRC rcStrict = EMInterpretInstructionEx(pVCpu, pRegFrame, (RTGCPTR)(RTRCUINTPTR)pvFault, &cb);
530 if ( RT_SUCCESS(rcStrict)
531 && cb)
532 rcStrict = selmRCGuestTssPostWriteCheck(pVM, pVCpu, offRange, cb);
533 else
534 {
535 AssertMsg(RT_FAILURE(rcStrict), ("cb=%u rcStrict=%#x\n", cb, VBOXSTRICTRC_VAL(rcStrict)));
536 VMCPU_FF_SET(pVCpu, VMCPU_FF_SELM_SYNC_TSS);
537 STAM_COUNTER_INC(&pVM->selm.s.StatRCWriteGuestTSSUnhandled);
538 if (rcStrict == VERR_EM_INTERPRETER)
539 rcStrict = VINF_EM_RAW_EMULATE_INSTR_TSS_FAULT;
540 }
541 return rcStrict;
542}
543
544#endif /* SELM_TRACK_GUEST_TSS_CHANGES */
545
546#ifdef SELM_TRACK_SHADOW_GDT_CHANGES
547/**
548 * @callback_method_impl{FNPGMRCVIRTPFHANDLER,
549 * \#PF Virtual Handler callback for Guest write access to the VBox shadow GDT.}
550 */
551DECLEXPORT(VBOXSTRICTRC) selmRCShadowGDTWritePfHandler(PVM pVM, PVMCPU pVCpu, RTGCUINT uErrorCode, PCPUMCTXCORE pRegFrame,
552 RTGCPTR pvFault, RTGCPTR pvRange, uintptr_t offRange, void *pvUser)
553{
554 LogRel(("FATAL ERROR: selmRCShadowGDTWritePfHandler: eip=%08X pvFault=%RGv pvRange=%RGv\r\n", pRegFrame->eip, pvFault, pvRange));
555 NOREF(pVM); NOREF(pVCpu); NOREF(uErrorCode); NOREF(pRegFrame); NOREF(pvFault); NOREF(pvRange); NOREF(offRange); NOREF(pvUser);
556 return VERR_SELM_SHADOW_GDT_WRITE;
557}
558#endif
559
560
561#ifdef SELM_TRACK_SHADOW_LDT_CHANGES
562/**
563 * @callback_method_impl{FNPGMRCVIRTPFHANDLER,
564 * \#PF Virtual Handler callback for Guest write access to the VBox shadow LDT.}
565 */
566DECLEXPORT(VBOXSTRICTRC) selmRCShadowLDTWritePfHandler(PVM pVM, PVMCPU pVCpu, RTGCUINT uErrorCode, PCPUMCTXCORE pRegFrame,
567 RTGCPTR pvFault, RTGCPTR pvRange, uintptr_t offRange, void *pvUser)
568{
569 LogRel(("FATAL ERROR: selmRCShadowLDTWritePfHandler: eip=%08X pvFault=%RGv pvRange=%RGv\r\n", pRegFrame->eip, pvFault, pvRange));
570 Assert(pvFault - (uintptr_t)pVM->selm.s.pvLdtRC < (unsigned)(65536U + PAGE_SIZE));
571 NOREF(pVM); NOREF(pVCpu); NOREF(uErrorCode); NOREF(pRegFrame); NOREF(pvFault); NOREF(pvRange); NOREF(offRange); NOREF(pvUser);
572 return VERR_SELM_SHADOW_LDT_WRITE;
573}
574#endif
575
576
577#ifdef SELM_TRACK_SHADOW_TSS_CHANGES
578/**
579 * @callback_method_impl{FNPGMRCVIRTPFHANDLER,
580 * \#PF Virtual Handler callback for Guest write access to the VBox shadow TSS.}
581 */
582DECLEXPORT(VBOXSTRICTRC) selmRCShadowTSSWritePfHandler(PVM pVM, PVMCPU pVCpu, RTGCUINT uErrorCode, PCPUMCTXCORE pRegFrame,
583 RTGCPTR pvFault, RTGCPTR pvRange, uintptr_t offRange, void *pvUser)
584{
585 LogRel(("FATAL ERROR: selmRCShadowTSSWritePfHandler: eip=%08X pvFault=%RGv pvRange=%RGv\r\n", pRegFrame->eip, pvFault, pvRange));
586 NOREF(pVM); NOREF(pVCpu); NOREF(uErrorCode); NOREF(pRegFrame); NOREF(pvFault); NOREF(pvRange); NOREF(offRange); NOREF(pvUser);
587 return VERR_SELM_SHADOW_TSS_WRITE;
588}
589#endif
590
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