SELMRC.cpp revision 4816371c73f5c7a1c2f7d301f1861556bff55e81
/* $Id$ */
/** @file
* SELM - The Selector Manager, Guest Context.
*/
/*
* Copyright (C) 2006-2012 Oracle Corporation
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
* General Public License (GPL) as published by the Free Software
* Foundation, in version 2 as it comes in the "COPYING" file of the
* VirtualBox OSE distribution. VirtualBox OSE is distributed in the
* hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
*/
/*******************************************************************************
* Header Files *
*******************************************************************************/
#define LOG_GROUP LOG_GROUP_SELM
#include "SELMInternal.h"
#include "SELMInline.h"
/*******************************************************************************
* Global Variables *
*******************************************************************************/
#ifdef LOG_ENABLED
/** Segment register names. */
#endif
#ifdef SELM_TRACK_GUEST_GDT_CHANGES
/**
* Synchronizes one GDT entry (guest -> shadow).
*
* @returns VBox strict status code (appropriate for trap handling and GC
* return).
* @retval VINF_EM_RAW_EMULATE_INSTR_GDT_FAULT
* @retval VINF_SELM_SYNC_GDT
* @retval VINF_EM_RESCHEDULE_REM
*
* @param pVM Pointer to the VM.
* @param pVCpu The current virtual CPU.
* @param pRegFrame Trap register frame.
* @param iGDTEntry The GDT entry to sync.
*
* @remarks Caller checks that this isn't the LDT entry!
*/
static VBOXSTRICTRC selmRCSyncGDTEntry(PVM pVM, PVMCPU pVCpu, PCPUMCTXCORE pRegFrame, unsigned iGDTEntry)
{
/*
* Validate the offset.
*/
if ( iGDTEntry >= SELM_GDT_ELEMENTS
return VINF_SUCCESS; /* ignore */
/*
* Read the guest descriptor.
*/
if (RT_FAILURE(rc))
{
if (RT_FAILURE(rc))
{
return VINF_EM_RESCHEDULE_REM;
}
}
/*
* Check for conflicts.
*/
{
{
return VINF_SELM_SYNC_GDT; /** @todo this status code is ignored, unfortunately. */
}
Log(("selmRCSyncGDTEntry: Sel=%d Desc=%.8Rhxs: potential conflict (still not present)!\n", Sel, &Desc));
/* Note: we can't continue below or else we'll change the shadow descriptor!! */
/* When the guest makes the selector present, then we'll do a GDT sync. */
return VINF_SUCCESS;
}
/*
* Convert the guest selector to a shadow selector and update the shadow GDT.
*/
//Log(("O: base=%08X limit=%08X attr=%04X\n", X86DESC_BASE(*pShwDescr)), X86DESC_LIMIT(*pShwDescr), (pShwDescr->au32[1] >> 8) & 0xFFFF ));
//Log(("N: base=%08X limit=%08X attr=%04X\n", X86DESC_BASE(Desc)), X86DESC_LIMIT(Desc), (Desc.au32[1] >> 8) & 0xFFFF ));
/*
* Detect and mark stale registers.
*/
{
{
{
{
Log(("GDT write to selector in %s register %04X (now stale)\n", g_aszSRegNms[iSReg], paSReg[iSReg].Sel));
/* rcStrict = VINF_EM_RESCHEDULE_REM; - bad idea if we're in a patch. */
}
{
Log(("GDT write to selector in %s register %04X (no longer stale)\n", g_aszSRegNms[iSReg], paSReg[iSReg].Sel));
}
else
Log(("GDT write to selector in %s register %04X (no important change)\n", g_aszSRegNms[iSReg], paSReg[iSReg].Sel));
}
else
}
}
/** @todo Detect stale LDTR as well? */
return rcStrict;
}
/**
* Synchronizes any segment registers refering to the given GDT entry.
*
* This is called before any changes performed and shadowed, so it's possible to
* look in both the shadow and guest descriptor table entries for hidden
* register content.
*
* @param pVM Pointer to the VM.
* @param pVCpu The current virtual CPU.
* @param pRegFrame Trap register frame.
* @param iGDTEntry The GDT entry to sync.
*/
{
/*
* Validate the offset.
*/
if ( iGDTEntry >= SELM_GDT_ELEMENTS
return;
/*
* Sync outdated segment registers using this entry.
*/
{
{
{
{
}
else
Log(("selmRCSyncGDTSegRegs: Bad shadow descriptor %#x (for %s): %.8Rhxs \n",
}
}
}
}
/**
* \#PF Virtual Handler callback for Guest write access to the Guest's own GDT.
*
* @returns VBox status code (appropriate for trap handling and GC return).
* @param pVM Pointer to the VM.
* @param uErrorCode CPU Error code.
* @param pRegFrame Trap register frame.
* @param pvFault The fault address (cr2).
* @param pvRange The base address of the handled virtual range.
* @param offRange The offset of the access into this range.
* (If it's a EIP range this is the EIP, if not it's pvFault.)
*/
VMMRCDECL(int) selmRCGuestGDTWriteHandler(PVM pVM, RTGCUINT uErrorCode, PCPUMCTXCORE pRegFrame, RTGCPTR pvFault, RTGCPTR pvRange, uintptr_t offRange)
{
LogFlow(("selmRCGuestGDTWriteHandler errcode=%x fault=%RGv offRange=%08x\n", (uint32_t)uErrorCode, pvFault, offRange));
/*
* Check if any selectors might be affected.
*/
/*
* Attempt to emulate the instruction and sync the affected entries.
*/
{
/* Check if the LDT was in any way affected. Do not sync the
shadow GDT if that's the case or we might have trouble in
the world switcher (or so they say). */
{
Log(("LDTR selector change -> fall back to HC!!\n"));
/** @todo Implement correct stale LDT handling. */
}
else
{
/* Sync the shadow GDT and continue provided the update didn't
cause any segment registers to go stale in any way. */
{
if (rc == VINF_SUCCESS)
{
if (rc == VINF_SUCCESS)
}
{
/* VINF_EM_RESCHEDULE_REM - bad idea if we're in a patch. */
if (rc2 == VINF_EM_RESCHEDULE_REM)
return rc;
}
}
/* sync failed, return to ring-3 and resync the GDT. */
}
}
else
{
if (rc == VERR_EM_INTERPRETER)
}
return rc;
}
#endif /* SELM_TRACK_GUEST_GDT_CHANGES */
#ifdef SELM_TRACK_GUEST_LDT_CHANGES
/**
* \#PF Virtual Handler callback for Guest write access to the Guest's own LDT.
*
* @returns VBox status code (appropriate for trap handling and GC return).
* @param pVM Pointer to the VM.
* @param uErrorCode CPU Error code.
* @param pRegFrame Trap register frame.
* @param pvFault The fault address (cr2).
* @param pvRange The base address of the handled virtual range.
* @param offRange The offset of the access into this range.
* (If it's a EIP range this is the EIP, if not it's pvFault.)
*/
VMMRCDECL(int) selmRCGuestLDTWriteHandler(PVM pVM, RTGCUINT uErrorCode, PCPUMCTXCORE pRegFrame, RTGCPTR pvFault, RTGCPTR pvRange, uintptr_t offRange)
{
/** @todo To be implemented. */
////LogCom(("selmRCGuestLDTWriteHandler: eip=%08X pvFault=%RGv pvRange=%RGv\r\n", pRegFrame->eip, pvFault, pvRange));
}
#endif
#ifdef SELM_TRACK_GUEST_TSS_CHANGES
/**
* Read wrapper used by selmRCGuestTSSWriteHandler.
* @returns VBox status code (appropriate for trap handling and GC return).
* @param pVM Pointer to the VM.
* @param pvDst Where to put the bits we read.
* @param pvSrc Guest address to read from.
* @param cb The number of bytes to read.
*/
{
if (RT_SUCCESS(rc))
return VINF_SUCCESS;
/** @todo use different fallback? */
if (rc == VINF_SUCCESS)
{
}
return rc;
}
/**
* \#PF Virtual Handler callback for Guest write access to the Guest's own current TSS.
*
* @returns VBox status code (appropriate for trap handling and GC return).
* @param pVM Pointer to the VM.
* @param uErrorCode CPU Error code.
* @param pRegFrame Trap register frame.
* @param pvFault The fault address (cr2).
* @param pvRange The base address of the handled virtual range.
* @param offRange The offset of the access into this range.
* (If it's a EIP range this is the EIP, if not it's pvFault.)
*/
VMMRCDECL(int) selmRCGuestTSSWriteHandler(PVM pVM, RTGCUINT uErrorCode, PCPUMCTXCORE pRegFrame, RTGCPTR pvFault, RTGCPTR pvRange, uintptr_t offRange)
{
LogFlow(("selmRCGuestTSSWriteHandler errcode=%x fault=%RGv offRange=%08x\n", (uint32_t)uErrorCode, pvFault, offRange));
/*
* Try emulate the access.
*/
if ( RT_SUCCESS(rc)
&& cb)
{
rc = VINF_SUCCESS;
/*
* If it's on the same page as the esp0 and ss0 fields or actually one of them,
* then check if any of these has changed.
*/
)
{
Log(("selmRCGuestTSSWriteHandler: R0 stack: %RTsel:%RGv -> %RTsel:%RGv\n",
(RTSEL)(pVM->selm.s.Tss.ss1 & ~1), (RTGCPTR)pVM->selm.s.Tss.esp1, (RTSEL)pGuestTss->ss0, (RTGCPTR)pGuestTss->esp0));
}
#ifdef VBOX_WITH_RAW_RING1
else if ( EMIsRawRing1Enabled(pVM)
)
{
Log(("selmRCGuestTSSWriteHandler: R1 stack: %RTsel:%RGv -> %RTsel:%RGv\n",
(RTSEL)((pVM->selm.s.Tss.ss2 & ~2) | 1), (RTGCPTR)pVM->selm.s.Tss.esp2, (RTSEL)pGuestTss->ss1, (RTGCPTR)pGuestTss->esp1));
}
#endif
/* Handle misaligned TSS in a safe manner (just in case). */
{
struct
{
} s;
AssertCompileSize(s, 8);
if ( rc == VINF_SUCCESS
)
{
Log(("selmRCGuestTSSWriteHandler: R0 stack: %RTsel:%RGv -> %RTsel:%RGv [x-page]\n",
}
}
/*
* If VME is enabled we need to check if the interrupt redirection bitmap
* needs updating.
*/
{
{
{
Log(("TSS offIoBitmap changed: old=%#x new=%#x -> resync in ring-3\n", pVM->selm.s.offGuestIoBitmap, offIoBitmap));
}
else
}
else
{
/** @todo not sure how the partial case is handled; probably not allowed */
if ( offIntRedirBitmap <= offRange
{
Log(("TSS IntRedirBitmap Changed: offIoBitmap=%x offIntRedirBitmap=%x cbTSS=%x offRange=%x cb=%x\n",
/** @todo only update the changed part. */
{
if (rc != VINF_SUCCESS)
break;
}
}
}
}
/* Return to ring-3 for a full resync if any of the above fails... (?) */
if (rc != VINF_SUCCESS)
{
if (RT_SUCCESS(rc))
rc = VINF_SUCCESS;
}
}
else
{
if (rc == VERR_EM_INTERPRETER)
}
return rc;
}
#endif /* SELM_TRACK_GUEST_TSS_CHANGES */
/**
* \#PF Virtual Handler callback for Guest write access to the VBox shadow GDT.
*
* @returns VBox status code (appropriate for trap handling and GC return).
* @param pVM Pointer to the VM.
* @param uErrorCode CPU Error code.
* @param pRegFrame Trap register frame.
* @param pvFault The fault address (cr2).
* @param pvRange The base address of the handled virtual range.
* @param offRange The offset of the access into this range.
* (If it's a EIP range this is the EIP, if not it's pvFault.)
*/
VMMRCDECL(int) selmRCShadowGDTWriteHandler(PVM pVM, RTGCUINT uErrorCode, PCPUMCTXCORE pRegFrame, RTGCPTR pvFault, RTGCPTR pvRange, uintptr_t offRange)
{
LogRel(("FATAL ERROR: selmRCShadowGDTWriteHandler: eip=%08X pvFault=%RGv pvRange=%RGv\r\n", pRegFrame->eip, pvFault, pvRange));
return VERR_SELM_SHADOW_GDT_WRITE;
}
#endif
/**
* \#PF Virtual Handler callback for Guest write access to the VBox shadow LDT.
*
* @returns VBox status code (appropriate for trap handling and GC return).
* @param pVM Pointer to the VM.
* @param uErrorCode CPU Error code.
* @param pRegFrame Trap register frame.
* @param pvFault The fault address (cr2).
* @param pvRange The base address of the handled virtual range.
* @param offRange The offset of the access into this range.
* (If it's a EIP range this is the EIP, if not it's pvFault.)
*/
VMMRCDECL(int) selmRCShadowLDTWriteHandler(PVM pVM, RTGCUINT uErrorCode, PCPUMCTXCORE pRegFrame, RTGCPTR pvFault, RTGCPTR pvRange, uintptr_t offRange)
{
LogRel(("FATAL ERROR: selmRCShadowLDTWriteHandler: eip=%08X pvFault=%RGv pvRange=%RGv\r\n", pRegFrame->eip, pvFault, pvRange));
return VERR_SELM_SHADOW_LDT_WRITE;
}
#endif
/**
* \#PF Virtual Handler callback for Guest write access to the VBox shadow TSS.
*
* @returns VBox status code (appropriate for trap handling and GC return).
* @param pVM Pointer to the VM.
* @param uErrorCode CPU Error code.
* @param pRegFrame Trap register frame.
* @param pvFault The fault address (cr2).
* @param pvRange The base address of the handled virtual range.
* @param offRange The offset of the access into this range.
* (If it's a EIP range this is the EIP, if not it's pvFault.)
*/
VMMRCDECL(int) selmRCShadowTSSWriteHandler(PVM pVM, RTGCUINT uErrorCode, PCPUMCTXCORE pRegFrame, RTGCPTR pvFault, RTGCPTR pvRange, uintptr_t offRange)
{
LogRel(("FATAL ERROR: selmRCShadowTSSWriteHandler: eip=%08X pvFault=%RGv pvRange=%RGv\r\n", pRegFrame->eip, pvFault, pvRange));
return VERR_SELM_SHADOW_TSS_WRITE;
}
#endif