SELM.cpp revision 96be842aaed828957a6671643c1558f2f2c07df7
/* $Id$ */
/** @file
* SELM - The Selector manager.
*/
/*
* Copyright (C) 2006 InnoTek Systemberatung GmbH
*
* 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 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.
*
* If you received this file as part of a commercial VirtualBox
* distribution, then only the terms of your commercial VirtualBox
* license agreement apply instead of the previous paragraph.
*/
/*******************************************************************************
* Header Files *
*******************************************************************************/
#define LOG_GROUP LOG_GROUP_SELM
#include "SELMInternal.h"
#include "x86context.h"
/**
* @{
*/
#define SELM_TRACK_GUEST_GDT_CHANGES
#define SELM_TRACK_GUEST_LDT_CHANGES
#define SELM_TRACK_GUEST_TSS_CHANGES
/** @} */
/**
* @{
*/
#define SELM_TRACK_SHADOW_GDT_CHANGES
#define SELM_TRACK_SHADOW_LDT_CHANGES
#define SELM_TRACK_SHADOW_TSS_CHANGES
/** @} */
/** SELM saved state version. */
#define SELM_SAVED_STATE_VERSION 5
/*******************************************************************************
* Internal Functions *
*******************************************************************************/
//static DECLCALLBACK(void) selmR3InfoTss(PVM pVM, PCDBGFINFOHLP pHlp, const char *pszArgs);
//static DECLCALLBACK(void) selmR3InfoTssGuest(PVM pVM, PCDBGFINFOHLP pHlp, const char *pszArgs);
static DECLCALLBACK(int) selmGuestGDTWriteHandler(PVM pVM, RTGCPTR GCPtr, void *pvPhys, void *pvBuf, size_t cbBuf, PGMACCESSTYPE enmAccessType, void *pvUser);
static DECLCALLBACK(int) selmGuestLDTWriteHandler(PVM pVM, RTGCPTR GCPtr, void *pvPhys, void *pvBuf, size_t cbBuf, PGMACCESSTYPE enmAccessType, void *pvUser);
static DECLCALLBACK(int) selmGuestTSSWriteHandler(PVM pVM, RTGCPTR GCPtr, void *pvPhys, void *pvBuf, size_t cbBuf, PGMACCESSTYPE enmAccessType, void *pvUser);
/**
* Initializes the SELM.
*
* @returns VBox status code.
* @param pVM The VM to operate on.
*/
{
LogFlow(("SELMR3Init\n"));
/*
* Assert alignment and sizes.
* (The TSS block requires contiguous back.)
*/
AssertCompile(sizeof(pVM->selm.s) <= sizeof(pVM->selm.padding)); AssertRelease(sizeof(pVM->selm.s) <= sizeof(pVM->selm.padding));
#if 0 /* doesn't work */
AssertCompile((RT_OFFSETOF(VM, selm.s.Tss) & PAGE_OFFSET_MASK) <= PAGE_SIZE - sizeof(pVM->selm.s.Tss));
AssertCompile((RT_OFFSETOF(VM, selm.s.TssTrap08) & PAGE_OFFSET_MASK) <= PAGE_SIZE - sizeof(pVM->selm.s.TssTrap08));
#endif
AssertRelease((RT_OFFSETOF(VM, selm.s.Tss) & PAGE_OFFSET_MASK) <= PAGE_SIZE - sizeof(pVM->selm.s.Tss));
AssertRelease((RT_OFFSETOF(VM, selm.s.TssTrap08) & PAGE_OFFSET_MASK) <= PAGE_SIZE - sizeof(pVM->selm.s.TssTrap08));
/*
* Init the structure.
*/
/*
* Allocate GDT table.
*/
/*
* Allocate LDT area.
*/
/*
* Init Guest's and Shadow GDT, LDT, TSS changes control variables.
*/
/* The I/O bitmap starts right after the virtual interrupt redirection bitmap. Outside the TSS on purpose; the CPU will not check it
* for I/O operations. */
/* bit set to 1 means no redirection */
/*
* Register the saved state data unit.
*/
if (VBOX_FAILURE(rc))
return rc;
/*
* Statistics.
*/
STAM_REG(pVM, &pVM->selm.s.StatGCWriteGuestGDTHandled, STAMTYPE_COUNTER, "/SELM/GC/Write/Guest/GDTInt", STAMUNIT_OCCURENCES, "The number of handled writes to the Guest GDT.");
STAM_REG(pVM, &pVM->selm.s.StatGCWriteGuestGDTUnhandled, STAMTYPE_COUNTER, "/SELM/GC/Write/Guest/GDTEmu", STAMUNIT_OCCURENCES, "The number of unhandled writes to the Guest GDT.");
STAM_REG(pVM, &pVM->selm.s.StatGCWriteGuestLDT, STAMTYPE_COUNTER, "/SELM/GC/Write/Guest/LDT", STAMUNIT_OCCURENCES, "The number of writes to the Guest LDT was detected.");
STAM_REG(pVM, &pVM->selm.s.StatGCWriteGuestTSSHandled, STAMTYPE_COUNTER, "/SELM/GC/Write/Guest/TSSInt", STAMUNIT_OCCURENCES, "The number of handled writes to the Guest TSS.");
STAM_REG(pVM, &pVM->selm.s.StatGCWriteGuestTSSRedir, STAMTYPE_COUNTER, "/SELM/GC/Write/Guest/TSSRedir",STAMUNIT_OCCURENCES, "The number of handled redir bitmap writes to the Guest TSS.");
STAM_REG(pVM, &pVM->selm.s.StatGCWriteGuestTSSHandledChanged,STAMTYPE_COUNTER, "/SELM/GC/Write/Guest/TSSIntChg", STAMUNIT_OCCURENCES, "The number of handled writes to the Guest TSS where the R0 stack changed.");
STAM_REG(pVM, &pVM->selm.s.StatGCWriteGuestTSSUnhandled, STAMTYPE_COUNTER, "/SELM/GC/Write/Guest/TSSEmu", STAMUNIT_OCCURENCES, "The number of unhandled writes to the Guest TSS.");
STAM_REG(pVM, &pVM->selm.s.StatTSSSync, STAMTYPE_PROFILE, "/PROF/SELM/TSSSync", STAMUNIT_TICKS_PER_CALL, "Profiling of the SELMR3SyncTSS() body.");
STAM_REG(pVM, &pVM->selm.s.StatUpdateFromCPUM, STAMTYPE_PROFILE, "/PROF/SELM/UpdateFromCPUM", STAMUNIT_TICKS_PER_CALL, "Profiling of the SELMR3UpdateFromCPUM() body.");
STAM_REG(pVM, &pVM->selm.s.StatHyperSelsChanged, STAMTYPE_COUNTER, "/SELM/HyperSels/Changed", STAMUNIT_OCCURENCES, "The number of times we had to relocate our hypervisor selectors.");
STAM_REG(pVM, &pVM->selm.s.StatScanForHyperSels, STAMTYPE_COUNTER, "/SELM/HyperSels/Scan", STAMUNIT_OCCURENCES, "The number of times we had find free hypervisor selectors.");
/*
* Default action when entering raw mode for the first time
*/
/*
* Register info handlers.
*/
DBGFR3InfoRegisterInternal(pVM, "gdtguest", "Displays the guest GDT. No arguments.", &selmR3InfoGdtGuest);
DBGFR3InfoRegisterInternal(pVM, "ldtguest", "Displays the guest LDT. No arguments.", &selmR3InfoLdtGuest);
//DBGFR3InfoRegisterInternal(pVM, "tss", "Displays the shadow TSS. No arguments.", &selmR3InfoTss);
//DBGFR3InfoRegisterInternal(pVM, "tssguest", "Displays the guest TSS. No arguments.", &selmR3InfoTssGuest);
return rc;
}
/**
* Finalizes HMA page attributes.
*
* @returns VBox status code.
* @param pVM The VM handle.
*/
{
/*
* Make Double Fault work with WP enabled?
*
* The double fault is a task switch and thus requires write access to the GDT of the TSS
* (to set it busy), to the old TSS (to store state), and to the Trap 8 TSS for the back link.
*
* Since we in enabling write access to these pages make ourself vulnerable to attacks,
* it is not possible to do this by default.
*/
bool f;
#if !defined(DEBUG_bird)
if (VBOX_SUCCESS(rc) && f)
#endif
{
rc = PGMMapSetPage(pVM, MMHyperHC2GC(pVM, &paGdt[pVM->selm.s.aHyperSel[SELM_HYPER_SEL_TSS_TRAP08] >> 3]), sizeof(paGdt[0]),
rc = PGMMapSetPage(pVM, MMHyperHC2GC(pVM, &paGdt[pVM->selm.s.aHyperSel[SELM_HYPER_SEL_TSS] >> 3]), sizeof(paGdt[0]),
rc = PGMMapSetPage(pVM, VM_GUEST_ADDR(pVM, &pVM->selm.s.aHyperSel[SELM_HYPER_SEL_TSS]), sizeof(pVM->selm.s.aHyperSel[SELM_HYPER_SEL_TSS]),
rc = PGMMapSetPage(pVM, VM_GUEST_ADDR(pVM, &pVM->selm.s.aHyperSel[SELM_HYPER_SEL_TSS_TRAP08]), sizeof(pVM->selm.s.aHyperSel[SELM_HYPER_SEL_TSS_TRAP08]),
}
return VINF_SUCCESS;
}
/**
* Setup the hypervisor GDT selectors in our shadow table
*
* @param pVM The VM handle.
*/
{
/*
* Set up global code and data descriptors for use in the guest context.
* Both are wide open (base 0, limit 4GB)
*/
/* data */
/* 64-bit mode code (& data?) */
/*
* TSS descriptor
*/
/*
* TSS descriptor for trap 08
*/
}
/**
* Applies relocations to data and code managed by this
* component. This function will be called at init and
* whenever the VMM need to relocate it self inside the GC.
*
* @param pVM The VM.
*/
{
LogFlow(("SELMR3Relocate\n"));
/*
* Update GDTR and selector.
*/
/** @todo selector relocations should be a seperate operation? */
/** @todo SELM must be called when any of the CR3s changes during a cpu mode change. */
/** @todo PGM knows the proper CR3 values these days, not CPUM. */
/*
* Update the TSSes.
*/
/* Current TSS */
/* trap 08 */
pVM->selm.s.TssTrap08.cr3 = PGMGetInterGCCR3(pVM); /* this should give use better survival chances. */
pVM->selm.s.TssTrap08.esp0 = VMMGetStackGC(pVM) - PAGE_SIZE / 2; /* upper half can be analysed this way. */
pVM->selm.s.TssTrap08.ecx = VM_GUEST_ADDR(pVM, &pVM->selm.s.Tss); /* setup ecx to normal Hypervisor TSS address. */
/* TRPM will be updating the eip */
{
/*
*/
int rc;
{
}
0, 0, "selmgcShadowGDTWriteHandler", 0, "Shadow GDT write access handler");
#endif
{
}
0, 0, "selmgcShadowTSSWriteHandler", 0, "Shadow TSS write access handler");
#endif
/*
* Update the GC LDT region handler and address.
*/
{
}
#endif
0, 0, "selmgcShadowLDTWriteHandler", 0, "Shadow LDT write access handler");
#endif
}
}
/**
* Notification callback which is called whenever there is a chance that a CR3
* value might have changed.
* This is called by PGM.
*
* @param pVM The VM handle
*/
{
}
/**
* Terminates the SELM.
*
* Termination means cleaning up and freeing all resources,
* the VM it self is at this point powered off or suspended.
*
* @returns VBox status code.
* @param pVM The VM to operate on.
*/
{
return 0;
}
/**
* The VM is being reset.
*
* needs to be removed.
*
* @param pVM VM handle.
*/
{
LogFlow(("SELMR3Reset:\n"));
/*
*/
int rc;
#ifdef SELM_TRACK_GUEST_GDT_CHANGES
{
}
#endif
#ifdef SELM_TRACK_GUEST_LDT_CHANGES
{
}
#endif
#ifdef SELM_TRACK_GUEST_TSS_CHANGES
{
}
#endif
/*
* Re-initialize other members.
*/
/*
* Default action when entering raw mode for the first time
*/
}
/**
*
* @param pVM The VM to operate on.
*/
{
/*
*/
int rc;
#ifdef SELM_TRACK_GUEST_GDT_CHANGES
{
}
#endif
#ifdef SELM_TRACK_GUEST_LDT_CHANGES
{
}
#endif
#ifdef SELM_TRACK_GUEST_TSS_CHANGES
{
}
#endif
/*
*/
{
}
#endif
{
}
#endif
{
}
#endif
}
/**
* Execute state save operation.
*
* @returns VBox status code.
* @param pVM VM Handle.
* @param pSSM SSM operation handle.
*/
{
LogFlow(("selmR3Save:\n"));
/*
* Save the basic bits - fortunately all the other things can be resynced on load.
*/
}
/**
* Execute state load operation.
*
* @returns VBox status code.
* @param pVM VM Handle.
* @param pSSM SSM operation handle.
* @param u32Version Data layout version.
*/
{
LogFlow(("selmR3Load:\n"));
/*
* Validate version.
*/
if (u32Version != SELM_SAVED_STATE_VERSION)
{
}
/*
* Do a reset.
*/
/* Get the monitoring flag. */
/* Get the TSS state flag. */
/*
* Get the selectors.
*/
/* Copy the selectors; they will be checked during relocation. */
return VINF_SUCCESS;
}
/**
* Sync the GDT, LDT and TSS after loading the state.
*
* Just to play save, we set the FFs to force syncing before
* executing GC code.
*
* @returns VBox status code.
* @param pVM VM Handle.
* @param pSSM SSM operation handle.
*/
{
LogFlow(("selmR3LoadDone:\n"));
/*
* Don't do anything if it's a load failure.
*/
if (VBOX_FAILURE(rc))
return VINF_SUCCESS;
/*
* Do the syncing if we're in protected mode.
*/
{
}
/*
* Flag everything for resync on next raw mode entry.
*/
return VINF_SUCCESS;
}
/**
* Updates the Guest GDT & LDT virtualization based on current CPU state.
*
* @returns VBox status code.
* @param pVM The VM to operate on.
*/
{
int rc = VINF_SUCCESS;
{
return VINF_SUCCESS;
}
/*
* GDT sync
*/
{
/*
* Always assume the best
*/
/* If the GDT was changed, then make sure the LDT is checked too */
/** @todo only do this if the actual ldtr selector was changed; this is a bit excessive */
/* Same goes for the TSS selector */
/*
* Get the GDTR and check if there is anything to do (there usually is).
*/
{
Log(("No GDT entries...\n"));
return VINF_SUCCESS;
}
/*
* Read the Guest GDT.
* ASSUMES that the entire GDT is in memory.
*/
if (VBOX_FAILURE(rc))
{
/*
* Read it page by page.
*
* Keep track of the last valid page and delay memsets and
* adjust cbEffLimit to reflect the effective size. The latter
* is something we do in the belief that the guest will probably
* never actually commit the last page, thus allowing us to keep
* our selectors in the high end of the GDT.
*/
while (cbLeft)
{
if (VBOX_SUCCESS(rc))
{
if (pu8DstInvalid != pu8Dst)
}
else if ( rc == VERR_PAGE_NOT_PRESENT
|| rc == VERR_PAGE_TABLE_NOT_PRESENT)
{
}
else
{
return VERR_NOT_IMPLEMENTED;
}
}
/* any invalid pages at the end? */
if (pu8DstInvalid != pu8Dst)
{
/* If any GDTEs was invalidated, zero them. */
}
/* keep track of the effective limit. */
{
Log(("SELMR3UpdateFromCPUM: cbEffGuestGdtLimit=%#x -> %#x (actual %#x)\n",
}
}
/*
* Check if the Guest GDT intrudes on our GDT entries.
*/
/** @todo we should try to minimize relocations by making sure our current selectors can be reused. */
if (cbEffLimit >= SELM_HYPER_DEFAULT_BASE)
{
int iGDT = 0;
Log(("Internal SELM GDT conflict: use non-present entries\n"));
while (pGDTE > pGDTEStart)
{
/* We can reuse non-present entries */
{
iGDT++;
if (iGDT >= SELM_HYPER_SEL_MAX)
break;
}
pGDTE--;
}
if (iGDT != SELM_HYPER_SEL_MAX)
{
AssertReleaseMsgFailed(("Internal SELM GDT conflict.\n"));
return VERR_NOT_IMPLEMENTED;
}
}
else
{
}
/*
* Work thru the copied GDT entries adjusting them for correct virtualization.
*/
{
{
/*
* Code and data selectors are generally 1:1, with the
* 'little' adjustment we do for DPL 0 selectors.
*/
{
/*
* Hack for A-bit against Trap E on read-only GDT.
*/
/** @todo Fix this by loading ds and cs before turning off WP. */
/*
* All DPL 0 code and data segments are squeezed into DPL 1.
*
* We're skipping conforming segments here because those
* cannot give us any trouble.
*/
!= (X86_SEL_TYPE_CODE | X86_SEL_TYPE_CONF) )
}
else
{
/*
* System type selectors are marked not present.
* Recompiler or special handling is required for these.
*/
/** @todo what about interrupt gates and rawr0? */
}
}
/* Next GDT entry. */
pGDTE++;
}
/*
* Check if our hypervisor selectors were changed.
*/
{
/* Reinitialize our hypervisor GDTs */
/*
* Do the relocation callbacks to let everyone update their hyper selector dependencies.
* (SELMR3Relocate will call selmR3SetupHyperGDTSelectors() for us.)
*/
VMR3Relocate(pVM, 0);
}
else if (cbEffLimit >= SELM_HYPER_DEFAULT_BASE)
/* We overwrote all entries above, so we have to save them again. */
/*
* Adjust the cached GDT limit.
* Any GDT entries which have been removed must be cleared.
*/
{
#ifndef SELM_TRACK_GUEST_GDT_CHANGES
#endif
}
#ifdef SELM_TRACK_GUEST_GDT_CHANGES
/*
* Check if Guest's GDTR is changed.
*/
{
Log(("SELMR3UpdateFromCPUM: Guest's GDT is changed to pGdt=%08X cbGdt=%08X\n", GDTR.pGdt, GDTR.cbGdt));
/*
* [Re]Register write virtual handler for guest's GDT.
*/
{
}
rc = PGMR3HandlerVirtualRegister(pVM, PGMVIRTHANDLERTYPE_WRITE, GDTR.pGdt, GDTR.pGdt + GDTR.cbGdt /* already inclusive */,
if (VBOX_FAILURE(rc))
return rc;
/* Update saved Guest GDTR. */
}
#endif
}
/*
* TSS sync
*/
{
}
/*
* LDT sync
*/
{
/*
* Always assume the best
*/
/*
* LDT handling is done similarly to the GDT handling with a shadow
* array. However, since the LDT is expected to be swappable (at least
* some ancient OSes makes it swappable) it must be floating and
* synced on a per-page basis.
*
* Eventually we will change this to be fully on demand. Meaning that
* we will only sync pages containing LDT selectors actually used and
* let the #PF handler lazily sync pages as they are used.
* (This applies to GDT too, when we start making OS/2 fast.)
*/
/*
* First, determin the current LDT selector.
*/
if ((SelLdt & X86_SEL_MASK) == 0)
{
/* ldtr = 0 - update hyper LDTR and deregister any active handler. */
CPUMSetHyperLDTR(pVM, 0);
#ifdef SELM_TRACK_GUEST_LDT_CHANGES
{
}
#endif
return VINF_SUCCESS;
}
/*
* Get the LDT selector.
*/
RTGCPTR GCPtrLdt = pDesc->Gen.u16BaseLow | (pDesc->Gen.u8BaseHigh1 << 16) | (pDesc->Gen.u8BaseHigh2 << 24);
/*
* Validate it.
*/
if ( !cbLdt
{
/* cbLdt > 0:
* This is quite impossible, so we do as most people do when faced with
* the impossible, we simply ignore it.
*/
CPUMSetHyperLDTR(pVM, 0);
#ifdef SELM_TRACK_GUEST_LDT_CHANGES
{
}
#endif
return VINF_SUCCESS;
}
/** @todo check what intel does about odd limits. */
AssertMsg(RT_ALIGN(cbLdt + 1, sizeof(VBOXDESC)) == cbLdt + 1 && cbLdt <= 0xffff, ("cbLdt=%d\n", cbLdt));
/*
* Use the cached guest ldt address if the descriptor has already been modified (see below)
* (this is necessary due to redundant LDT updates; see todo above at GDT sync)
*/
#ifdef SELM_TRACK_GUEST_LDT_CHANGES
/** @todo Handle only present LDT segments. */
// if (pDesc->Gen.u1Present)
{
/*
*/
{
Log(("SELMR3UpdateFromCPUM: Guest LDT changed to from %VGv:%04x to %VGv:%04x. (GDTR=%VGv:%04x)\n",
pVM->selm.s.GCPtrGuestLdt, pVM->selm.s.cbLdtLimit, GCPtrLdt, cbLdt, pVM->selm.s.GuestGdtr.pGdt, pVM->selm.s.GuestGdtr.cbGdt));
/*
* [Re]Register write virtual handler for guest's GDT.
* In the event of LDT overlapping something, don't install it just assume it's being updated.
*/
{
}
#ifdef DEBUG
Log(("LDT selector marked not present!!\n"));
#endif
rc = PGMR3HandlerVirtualRegister(pVM, PGMVIRTHANDLERTYPE_WRITE, GCPtrLdt, GCPtrLdt + cbLdt /* already inclusive */,
if (rc == VERR_PGM_HANDLER_VIRTUAL_CONFLICT)
{
/** @todo investigate the various cases where conflicts happen and try avoid them by enh. the instruction emulation. */
Log(("WARNING: Guest LDT (%VGv:%04x) conflicted with existing access range!! Assumes LDT is begin updated. (GDTR=%VGv:%04x)\n",
}
else if (VBOX_SUCCESS(rc))
else
{
CPUMSetHyperLDTR(pVM, 0);
return rc;
}
}
}
#else
#endif
/*
* Calc Shadow LDT base.
*/
unsigned off;
/*
* Enable the LDT selector in the shadow GDT.
*/
if (cbLdt > 0xffff)
{
cbLdt = 0xffff;
}
/*
* Set Hyper LDTR and notify TRPM.
*/
/*
* Loop synchronising the LDT page by page.
*/
/** @todo investigate how intel handle various operations on half present cross page entries. */
/* Note: Do not skip the first selector; unlike the GDT, a zero LDT selector is perfectly valid. */
while (cbLeft)
{
/*
* Read a chunk.
*/
if (VBOX_SUCCESS(rc))
{
/*
* Mark page
*/
rc = PGMMapSetPage(pVM, GCPtrShadowLDT & PAGE_BASE_GC_MASK, PAGE_SIZE, X86_PTE_P | X86_PTE_A | X86_PTE_D);
/*
* Loop thru the available LDT entries.
* Figure out where to start and end and the potential cross pageness of
* things adds a little complexity. pLDTE is updated there and not in the
* 'next' part of the loop. The pLDTEEnd is inclusive.
*/
{
{
/*
* Code and data selectors are generally 1:1, with the
* 'little' adjustment we do for DPL 0 selectors.
*/
{
/*
* Hack for A-bit against Trap E on read-only GDT.
*/
/** @todo Fix this by loading ds and cs before turning off WP. */
/*
* All DPL 0 code and data segments are squeezed into DPL 1.
*
* We're skipping conforming segments here because those
* cannot give us any trouble.
*/
!= (X86_SEL_TYPE_CODE | X86_SEL_TYPE_CONF) )
}
else
{
/*
* System type selectors are marked not present.
* Recompiler or special handling is required for these.
*/
/** @todo what about interrupt gates and rawr0? */
}
}
/* Next LDT entry. */
pLDTE++;
}
}
else
{
}
/*
* Advance to the next page.
*/
}
}
return VINF_SUCCESS;
}
/**
* \#PF Handler callback for virtual access handler ranges.
*
* Important to realize that a physical page in a range can have aliases, and
* for ALL and WRITE handlers these will also trigger.
*
* @returns VINF_SUCCESS if the handler have carried out the operation.
* @returns VINF_PGM_HANDLER_DO_DEFAULT if the caller should carry out the access operation.
* @param pVM VM Handle.
* @param GCPtr The virtual address the guest is writing to. (not correct if it's an alias!)
* @param pvPtr The HC mapping of that address.
* @param enmAccessType The access type.
* @param pvUser User argument.
*/
static DECLCALLBACK(int) selmGuestGDTWriteHandler(PVM pVM, RTGCPTR GCPtr, void *pvPtr, void *pvBuf, size_t cbBuf, PGMACCESSTYPE enmAccessType, void *pvUser)
{
return VINF_PGM_HANDLER_DO_DEFAULT;
}
/**
* \#PF Handler callback for virtual access handler ranges.
*
* Important to realize that a physical page in a range can have aliases, and
* for ALL and WRITE handlers these will also trigger.
*
* @returns VINF_SUCCESS if the handler have carried out the operation.
* @returns VINF_PGM_HANDLER_DO_DEFAULT if the caller should carry out the access operation.
* @param pVM VM Handle.
* @param GCPtr The virtual address the guest is writing to. (not correct if it's an alias!)
* @param pvPtr The HC mapping of that address.
* @param enmAccessType The access type.
* @param pvUser User argument.
*/
static DECLCALLBACK(int) selmGuestLDTWriteHandler(PVM pVM, RTGCPTR GCPtr, void *pvPtr, void *pvBuf, size_t cbBuf, PGMACCESSTYPE enmAccessType, void *pvUser)
{
return VINF_PGM_HANDLER_DO_DEFAULT;
}
/**
* \#PF Handler callback for virtual access handler ranges.
*
* Important to realize that a physical page in a range can have aliases, and
* for ALL and WRITE handlers these will also trigger.
*
* @returns VINF_SUCCESS if the handler have carried out the operation.
* @returns VINF_PGM_HANDLER_DO_DEFAULT if the caller should carry out the access operation.
* @param pVM VM Handle.
* @param GCPtr The virtual address the guest is writing to. (not correct if it's an alias!)
* @param pvPtr The HC mapping of that address.
* @param enmAccessType The access type.
* @param pvUser User argument.
*/
static DECLCALLBACK(int) selmGuestTSSWriteHandler(PVM pVM, RTGCPTR GCPtr, void *pvPtr, void *pvBuf, size_t cbBuf, PGMACCESSTYPE enmAccessType, void *pvUser)
{
return VINF_PGM_HANDLER_DO_DEFAULT;
}
/**
* Check if the TSS ring 0 stack selector and pointer were updated (for now)
*
* @returns VBox status code.
* @param pVM The VM to operate on.
*/
{
int rc;
{
return VINF_SUCCESS;
}
/** @todo r=bird: SELMR3SyncTSS should be VMMAll code.
* All the base, size, flags and stuff must be kept up to date in the CPUM tr register.
*/
/*
* TSS sync
*/
if (SelTss & X86_SEL_MASK)
{
/** @todo r=bird: strictly speaking, this is wrong as we shouldn't bother with changes to
* the TSS selector once its loaded. There are a bunch of this kind of problems (see Sander's
* comment in the unzip defect)
* The first part here should only be done when we're loading TR. The latter part which is
* updating of the ss0:esp0 pair can be done by the access handler now since we can trap all
* accesses, also REM ones. */
/*
* Guest TR is not NULL.
*/
RTGCPTR GCPtrTss = pDesc->Gen.u16BaseLow | (pDesc->Gen.u8BaseHigh1 << 16) | (pDesc->Gen.u8BaseHigh2 << 24);
cbTss++;
/* Note: We should monitor the whole TSS to catch accesses to the virtual interrupt redirection bitmap, but
* that causes some problems and with Windows guests some overhead as the entire TSS is rather big (3 pages).
* We'll assume for now that the bitmap is static.
*/
#if 1
/* Don't bother with anything but the core structure. (Actually all we care for is the r0 ss.) */
#endif
/* The guest's TSS can span multiple pages now. We will monitor the whole thing. */
// All system GDTs are marked not present above. That explains why this check fails.
//if (pDesc->Gen.u1Present)
/** @todo Handle only present TSS segments. */
{
/*
* Check if Guest's TSS is changed.
*/
{
Log(("SELMR3UpdateFromCPUM: Guest's TSS is changed to pTss=%08X cbTss=%08X cbGuestTss\n", GCPtrTss, cbTss, pVM->selm.s.cbGuestTss));
/*
* Validate it.
*/
if ( SelTss & X86_SEL_LDT
|| !cbTss
{
}
else
{
/*
* [Re]Register write virtual handler for guest's TSS.
*/
{
}
if (VBOX_FAILURE(rc))
{
return rc;
}
/* Update saved Guest TSS info. */
}
}
/* Update the ring 0 stack selector and base address */
/* feeling very lazy; reading too much */
rc = PGMPhysReadGCPtr(pVM, &tss, GCPtrTss, RT_OFFSETOF(VBOXTSS, offIoBitmap) + sizeof(tss.offIoBitmap));
if (VBOX_SUCCESS(rc))
{
#ifdef DEBUG
ssr0 &= ~1;
#endif
/* Update our TSS structure for the guest's ring 1 stack */
/* Should we sync the virtual interrupt redirection bitmap as well? */
{
/** @todo not sure how the partial case is handled; probably not allowed */
{
rc = PGMPhysReadGCPtr(pVM, &pVM->selm.s.Tss.IntRedirBitmap, GCPtrTss + offRedirBitmap, sizeof(tss.IntRedirBitmap));
Log2(("Redirection bitmap:\n"));
}
}
}
else
{
/* Note: the ring 0 stack selector and base address are updated on demand in this case. */
/** @todo handle these dependencies better! */
}
}
}
return VINF_SUCCESS;
}
/**
* Compares the Guest GDT and LDT with the shadow tables.
* This is a VBOX_STRICT only function.
*
* @returns VBox status code.
* @param pVM The VM Handle.
*/
{
#ifdef VBOX_STRICT
/*
* Get GDTR and check for conflict.
*/
return VINF_SUCCESS;
Log(("SELMR3DebugCheck: guest GDT size forced us to look for unused selectors.\n"));
Log(("SELMR3DebugCheck: limits have changed! new=%d old=%d\n", GDTR.cbGdt, pVM->selm.s.GuestGdtr.cbGdt));
/*
* Loop thru the GDT checking each entry.
*/
{
if (VBOX_SUCCESS(rc))
{
{
{
}
}
}
/* Advance to the next descriptor. */
GCPtrGDTEGuest += sizeof(VBOXDESC);
pGDTE++;
}
/*
* LDT?
*/
if ((SelLdt & X86_SEL_MASK) == 0)
return VINF_SUCCESS;
{
return VERR_INTERNAL_ERROR;
}
if (VBOX_FAILURE(rc))
{
return rc;
}
RTGCPTR GCPtrLDTEGuest = LDTDesc.Gen.u16BaseLow | (LDTDesc.Gen.u8BaseHigh1 << 16) | (LDTDesc.Gen.u8BaseHigh2 << 24);
/*
* Validate it.
*/
if (!cbLdt)
return VINF_SUCCESS;
/** @todo check what intel does about odd limits. */
AssertMsg(RT_ALIGN(cbLdt + 1, sizeof(VBOXDESC)) == cbLdt + 1 && cbLdt <= 0xffff, ("cbLdt=%d\n", cbLdt));
{
return VERR_INTERNAL_ERROR;
}
/*
* Loop thru the LDT checking each entry.
*/
{
if (VBOX_SUCCESS(rc))
{
{
}
}
/* Advance to the next descriptor. */
GCPtrLDTEGuest += sizeof(VBOXDESC);
pLDTE++;
}
#else
#endif
return VINF_SUCCESS;
}
/**
* Validates the RawR0 TSS values against the one in the Guest TSS.
*
* @returns true if it matches.
* @returns false and assertions on mismatch..
* @param pVM VM Handle.
*/
{
#ifdef VBOX_STRICT
if (SelTss & X86_SEL_MASK)
{
AssertMsg((SelTss & X86_SEL_MASK) == (pVM->selm.s.GCSelTss & X86_SEL_MASK), ("New TSS selector = %04X, old TSS selector = %04X\n", SelTss, pVM->selm.s.GCSelTss));
/*
* Guest TR is not NULL.
*/
RTGCPTR GCPtrTss = pDesc->Gen.u16BaseLow | (pDesc->Gen.u8BaseHigh1 << 16) | (pDesc->Gen.u8BaseHigh2 << 24);
cbTss++;
#if 1
/* Don't bother with anything but the core structure. (Actually all we care for is the r0 ss.) */
#endif
// All system GDTs are marked not present above. That explains why this check fails.
//if (pDesc->Gen.u1Present)
/** @todo Handle only present TSS segments. */
{
/*
* Check if Guest's TSS was changed.
*/
{
AssertMsgFailed(("Guest's TSS (Sel 0x%X) is changed from %RGv:%04x to %RGv:%04x\n",
}
}
}
{
if (VBOX_SUCCESS(rc))
{
if (VBOX_SUCCESS(rc))
{
return true;
AssertMsgFailed(("TSS out of sync!! (%04X:%08X vs %04X:%08X (guest)) Tss=%VGv Phys=%VGp\n",
}
else
}
else
/* Happens during early Windows XP boot when it is switching page tables. */
Assert(rc == VINF_SUCCESS || ((rc == VERR_PAGE_TABLE_NOT_PRESENT || rc == VERR_PAGE_NOT_PRESENT) && !(CPUMGetGuestEFlags(pVM) & X86_EFL_IF)));
}
return false;
#else
return true;
#endif
}
/**
* Returns flat address and limit of LDT by LDT selector from guest GDTR.
*
* Fully validate selector.
*
* @returns VBox status.
* @param pVM VM Handle.
* @param SelLdt LDT selector.
* @param ppvLdt Where to store the flat address of LDT.
* @param pcbLimit Where to store LDT limit.
*/
{
/* Get guest GDTR. */
/* Check selector TI and GDT limit. */
if ( SelLdt & X86_SEL_LDT
return VERR_INVALID_SELECTOR;
/* Read descriptor from GC. */
int rc = PGMPhysReadGCPtr(pVM, (void *)&Desc, (RTGCPTR)(GDTR.pGdt + (SelLdt & X86_SEL_MASK)), sizeof(Desc));
if (VBOX_FAILURE(rc))
{
/* fatal */
return VERR_SELECTOR_NOT_PRESENT;
}
/* Check if LDT descriptor is not present. */
return VERR_SELECTOR_NOT_PRESENT;
/* Check LDT descriptor type. */
return VERR_INVALID_SELECTOR;
/* LDT descriptor is ok. */
if (ppvLdt)
{
}
return VINF_SUCCESS;
}
/**
* Gets information about a selector.
* Intended for the debugger mostly and will prefer the guest
* descriptor tables over the shadow ones.
*
* @returns VINF_SUCCESS on success.
* @returns VERR_INVALID_SELECTOR if the selector isn't fully inside the descriptor table.
* @returns VERR_SELECTOR_NOT_PRESENT if the selector wasn't present.
* @returns VERR_PAGE_TABLE_NOT_PRESENT or VERR_PAGE_NOT_PRESENT if the pagetable or page
* backing the selector table wasn't present.
* @returns Other VBox status code on other errors.
*
* @param pVM VM handle.
* @param Sel The selector to get info about.
* @param pSelInfo Where to store the information.
*/
{
/*
* Read the descriptor entry
*/
if ( !(Sel & X86_SEL_LDT)
)
{
/*
* Hypervisor descriptor.
*/
}
else if (CPUMIsGuestInProtectedMode(pVM))
{
/*
* Read it from the guest descriptor table.
*/
if (!(Sel & X86_SEL_LDT))
{
/* GDT */
return VERR_INVALID_SELECTOR;
}
else
{
/*
* LDT - must locate the LDT first...
*/
if ( (unsigned)(SelLdt & X86_SEL_MASK) < sizeof(VBOXDESC) /* the first selector is invalid, right? */
return VERR_INVALID_SELECTOR;
if (VBOX_FAILURE(rc))
return rc;
/* validate the LDT descriptor. */
return VERR_SELECTOR_NOT_PRESENT;
return VERR_INVALID_SELECTOR;
return VERR_INVALID_SELECTOR;
/* calc the descriptor location. */
}
/* read the descriptor. */
if (VBOX_FAILURE(rc))
return rc;
}
else
{
/*
* We're in real mode.
*/
return VINF_SUCCESS;
}
/*
* Extract the base and limit
*/
return VINF_SUCCESS;
}
/**
* Gets information about a selector from the shadow tables.
*
* This is intended to be faster than the SELMR3GetSelectorInfo() method, but requires
* that the caller ensures that the shadow tables are up to date.
*
* @returns VINF_SUCCESS on success.
* @returns VERR_INVALID_SELECTOR if the selector isn't fully inside the descriptor table.
* @returns VERR_SELECTOR_NOT_PRESENT if the selector wasn't present.
* @returns VERR_PAGE_TABLE_NOT_PRESENT or VERR_PAGE_NOT_PRESENT if the pagetable or page
* backing the selector table wasn't present.
* @returns Other VBox status code on other errors.
*
* @param pVM VM handle.
* @param Sel The selector to get info about.
* @param pSelInfo Where to store the information.
*/
{
/*
* Read the descriptor entry
*/
if (!(Sel & X86_SEL_LDT))
{
/*
* Global descriptor.
*/
/** @todo check that the GDT offset is valid. */
}
else
{
/*
* Local Descriptor.
*/
/** @todo check if the LDT page is actually available. */
/** @todo check that the LDT offset is valid. */
}
/*
* Extract the base and limit
*/
return VINF_SUCCESS;
}
/**
* Formats a descriptor.
*
* @param Desc Descriptor to format.
* @param Sel Selector number.
* @param pszOutput Output buffer.
* @param cchOutput Size of output buffer.
*/
{
/*
* Make variable description string.
*/
static struct
{
unsigned cch;
const char *psz;
} const aTypes[32] =
{
/* system */
/* non system */
};
char szMsg[128];
else
else
*psz = '\0';
/*
* Limit and Base and format the output.
*/
}
/**
* Dumps a descriptor.
*
* @param Desc Descriptor to dump.
* @param Sel Selector number.
* @param pszMsg Message to prepend the log entry with.
*/
{
char szOutput[128];
}
/**
* Display the shadow gdt.
*
* @param pVM VM Handle.
* @param pHlp The info helpers.
* @param pszArgs Arguments, ignored.
*/
{
{
{
char szOutput[128];
selmR3FormatDescriptor(pVM->selm.s.paGdtHC[iGDT], iGDT << X86_SEL_SHIFT, &szOutput[0], sizeof(szOutput));
const char *psz = "";
psz = " HyperCS";
psz = " HyperDS";
psz = " HyperCS64";
psz = " HyperTSS";
psz = " HyperTSSTrap08";
}
}
}
/**
* Display the guest gdt.
*
* @param pVM VM Handle.
* @param pHlp The info helpers.
* @param pszArgs Arguments, ignored.
*/
{
{
if (VBOX_SUCCESS(rc))
{
{
char szOutput[128];
}
}
else if (rc == VERR_PAGE_NOT_PRESENT)
{
}
else
}
}
/**
* Display the shadow ldt.
*
* @param pVM VM Handle.
* @param pHlp The info helpers.
* @param pszArgs Arguments, ignored.
*/
{
pHlp->pfnPrintf(pHlp, "Shadow LDT (GCAddr=%VGv limit=%d):\n", pVM->selm.s.GCPtrLdt + pVM->selm.s.offLdtHyper, pVM->selm.s.cbLdtLimit);
{
{
char szOutput[128];
selmR3FormatDescriptor(paLDT[iLDT], (iLDT << X86_SEL_SHIFT) | X86_SEL_LDT, &szOutput[0], sizeof(szOutput));
}
}
}
/**
* Display the guest ldt.
*
* @param pVM VM Handle.
* @param pHlp The info helpers.
* @param pszArgs Arguments, ignored.
*/
{
if (!(SelLdt & X86_SEL_MASK))
{
return;
}
unsigned cbLdt;
if (VBOX_FAILURE(rc))
{
return;
}
{
if (VBOX_SUCCESS(rc))
{
{
char szOutput[128];
selmR3FormatDescriptor(LdtE, (iLdt << X86_SEL_SHIFT) | X86_SEL_LDT, &szOutput[0], sizeof(szOutput));
}
}
else if (rc == VERR_PAGE_NOT_PRESENT)
{
pHlp->pfnPrintf(pHlp, "%04 - page not present (GCAddr=%VGv)\n", (iLdt << X86_SEL_SHIFT) | X86_SEL_LDT, pLdtGC);
}
else
pHlp->pfnPrintf(pHlp, "%04 - read error rc=%Vrc GCAddr=%VGv\n", (iLdt << X86_SEL_SHIFT) | X86_SEL_LDT, rc, pLdtGC);
}
}
/**
* Dumps the hypervisor GDT
*
* @param pVM VM handle.
*/
{
}
/**
* Dumps the hypervisor LDT
*
* @param pVM VM handle.
*/
{
}
/**
* Dumps the guest GDT
*
* @param pVM VM handle.
*/
{
}
/**
* Dumps the guest LDT
*
* @param pVM VM handle.
*/
{
}