SELM.cpp revision ff88d4153cd65650577e8c2d1a5a3fdfa0404a80
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync/* $Id$ */
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync/** @file
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync * SELM - The Selector Manager.
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync */
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync/*
e64031e20c39650a7bc902a3e1aba613b9415deevboxsync * Copyright (C) 2006-2007 Sun Microsystems, Inc.
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync *
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync * This file is part of VirtualBox Open Source Edition (OSE), as
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync * available from http://www.virtualbox.org. This file is free software;
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync * you can redistribute it and/or modify it under the terms of the GNU
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync * General Public License (GPL) as published by the Free Software
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync * Foundation, in version 2 as it comes in the "COPYING" file of the
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync *
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync * Clara, CA 95054 USA or visit http://www.sun.com if you need
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync * additional information or have any questions.
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync */
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync/** @page pg_selm SELM - The Selector Manager
43747b1f0bc8302a238fb35e55857a5e9aa1933dvboxsync *
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync * SELM takes care of GDT, LDT and TSS shadowing in raw-mode, and the injection
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync * of a few hyper selector for the raw-mode context. In the hardware assisted
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync * virtualization mode its only task is to decode entries in the guest GDT or
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync * LDT once in a while.
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync *
bf27591c0d413dd4d7ba41cf89f117171968d462vboxsync * @see grp_selm
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync *
3e88c818fff5c64b5eff43d5daf4596bd87230c5vboxsync *
3e88c818fff5c64b5eff43d5daf4596bd87230c5vboxsync * @section seg_selm_shadowing Shadowing
3eef103be36a8f4828d69cfec3cea9bcefc9fd92vboxsync *
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync * SELMR3UpdateFromCPUM() and SELMR3SyncTSS() does the bulk synchronization
f5e53763b0a581b0299e98028c6c52192eb06785vboxsync * work. The three structures (GDT, LDT, TSS) are all shadowed wholesale atm.
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync * The idea is to do it in a more on-demand fashion when we get time. There
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync * also a whole bunch of issues with the current synchronization of all three
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync * tables, see notes and todos in the code.
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync *
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync * When the guest makes changes to the GDT we will try update the shadow copy
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync * without involving SELMR3UpdateFromCPUM(), see selmGCSyncGDTEntry().
34b5a73592441cbfacb85c2f2261648c1d25ad31vboxsync *
34b5a73592441cbfacb85c2f2261648c1d25ad31vboxsync * When the guest make LDT changes we'll trigger a full resync of the LDT
34b5a73592441cbfacb85c2f2261648c1d25ad31vboxsync * (SELMR3UpdateFromCPUM()), which, needless to say, isn't optimal.
34b5a73592441cbfacb85c2f2261648c1d25ad31vboxsync *
34b5a73592441cbfacb85c2f2261648c1d25ad31vboxsync * The TSS shadowing is limited to the fields we need to care about, namely SS0
34b5a73592441cbfacb85c2f2261648c1d25ad31vboxsync * and ESP0. The Patch Manager makes use of these. We monitor updates to the
34b5a73592441cbfacb85c2f2261648c1d25ad31vboxsync * guest TSS and will try keep our SS0 and ESP0 copies up to date this way
34b5a73592441cbfacb85c2f2261648c1d25ad31vboxsync * rather than go the SELMR3SyncTSS() route.
34b5a73592441cbfacb85c2f2261648c1d25ad31vboxsync *
34b5a73592441cbfacb85c2f2261648c1d25ad31vboxsync * When in raw-mode SELM also injects a few extra GDT selectors which are used
34b5a73592441cbfacb85c2f2261648c1d25ad31vboxsync * by the raw-mode (hyper) context. These start their life at the high end of
34b5a73592441cbfacb85c2f2261648c1d25ad31vboxsync * the table and will be relocated when the guest tries to make use of them...
34b5a73592441cbfacb85c2f2261648c1d25ad31vboxsync * Well, that was that idea at least, only the code isn't quite there yet which
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync * is why we have trouble with guests which actually have a full sized GDT.
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync *
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync * So, the summary of the current GDT, LDT and TSS shadowing is that there is a
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync * lot of relatively simple and enjoyable work to be done, see @bugref{3267}.
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync *
34b5a73592441cbfacb85c2f2261648c1d25ad31vboxsync */
34b5a73592441cbfacb85c2f2261648c1d25ad31vboxsync
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync/*******************************************************************************
a98c2afd05b2949737f73ec954d227712f3b0146vboxsync* Header Files *
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync*******************************************************************************/
a98c2afd05b2949737f73ec954d227712f3b0146vboxsync#define LOG_GROUP LOG_GROUP_SELM
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync#include <VBox/selm.h>
a98c2afd05b2949737f73ec954d227712f3b0146vboxsync#include <VBox/cpum.h>
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync#include <VBox/stam.h>
a98c2afd05b2949737f73ec954d227712f3b0146vboxsync#include <VBox/mm.h>
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync#include <VBox/ssm.h>
a98c2afd05b2949737f73ec954d227712f3b0146vboxsync#include <VBox/pgm.h>
3e88c818fff5c64b5eff43d5daf4596bd87230c5vboxsync#include <VBox/trpm.h>
a98c2afd05b2949737f73ec954d227712f3b0146vboxsync#include <VBox/dbgf.h>
a98c2afd05b2949737f73ec954d227712f3b0146vboxsync#include "SELMInternal.h"
a98c2afd05b2949737f73ec954d227712f3b0146vboxsync#include <VBox/vm.h>
a98c2afd05b2949737f73ec954d227712f3b0146vboxsync#include <VBox/err.h>
a98c2afd05b2949737f73ec954d227712f3b0146vboxsync#include <VBox/param.h>
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync#include <iprt/assert.h>
bf27591c0d413dd4d7ba41cf89f117171968d462vboxsync#include <VBox/log.h>
bf27591c0d413dd4d7ba41cf89f117171968d462vboxsync#include <iprt/asm.h>
bf27591c0d413dd4d7ba41cf89f117171968d462vboxsync#include <iprt/string.h>
bf27591c0d413dd4d7ba41cf89f117171968d462vboxsync#include <iprt/thread.h>
bf27591c0d413dd4d7ba41cf89f117171968d462vboxsync#include <iprt/string.h>
bf27591c0d413dd4d7ba41cf89f117171968d462vboxsync
bf27591c0d413dd4d7ba41cf89f117171968d462vboxsync
bf27591c0d413dd4d7ba41cf89f117171968d462vboxsync/**
bf27591c0d413dd4d7ba41cf89f117171968d462vboxsync * Enable or disable tracking of Guest's GDT/LDT/TSS.
bf27591c0d413dd4d7ba41cf89f117171968d462vboxsync * @{
bf27591c0d413dd4d7ba41cf89f117171968d462vboxsync */
bf27591c0d413dd4d7ba41cf89f117171968d462vboxsync#define SELM_TRACK_GUEST_GDT_CHANGES
bf27591c0d413dd4d7ba41cf89f117171968d462vboxsync#define SELM_TRACK_GUEST_LDT_CHANGES
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync#define SELM_TRACK_GUEST_TSS_CHANGES
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync/** @} */
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync/**
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync * Enable or disable tracking of Shadow GDT/LDT/TSS.
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync * @{
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync */
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync#define SELM_TRACK_SHADOW_GDT_CHANGES
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync#define SELM_TRACK_SHADOW_LDT_CHANGES
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync#define SELM_TRACK_SHADOW_TSS_CHANGES
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync/** @} */
ad27e1d5e48ca41245120c331cc88b50464813cevboxsync
bf27591c0d413dd4d7ba41cf89f117171968d462vboxsync
bf27591c0d413dd4d7ba41cf89f117171968d462vboxsync/** SELM saved state version. */
bf27591c0d413dd4d7ba41cf89f117171968d462vboxsync#define SELM_SAVED_STATE_VERSION 5
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync
3e88c818fff5c64b5eff43d5daf4596bd87230c5vboxsync/*******************************************************************************
3e88c818fff5c64b5eff43d5daf4596bd87230c5vboxsync* Internal Functions *
3e88c818fff5c64b5eff43d5daf4596bd87230c5vboxsync*******************************************************************************/
3e88c818fff5c64b5eff43d5daf4596bd87230c5vboxsyncstatic DECLCALLBACK(int) selmR3Save(PVM pVM, PSSMHANDLE pSSM);
3e88c818fff5c64b5eff43d5daf4596bd87230c5vboxsyncstatic DECLCALLBACK(int) selmR3Load(PVM pVM, PSSMHANDLE pSSM, uint32_t u32Version);
3e88c818fff5c64b5eff43d5daf4596bd87230c5vboxsyncstatic DECLCALLBACK(int) selmR3LoadDone(PVM pVM, PSSMHANDLE pSSM);
3e88c818fff5c64b5eff43d5daf4596bd87230c5vboxsyncstatic DECLCALLBACK(int) selmR3GuestGDTWriteHandler(PVM pVM, RTGCPTR GCPtr, void *pvPhys, void *pvBuf, size_t cbBuf, PGMACCESSTYPE enmAccessType, void *pvUser);
3e88c818fff5c64b5eff43d5daf4596bd87230c5vboxsyncstatic DECLCALLBACK(int) selmR3GuestLDTWriteHandler(PVM pVM, RTGCPTR GCPtr, void *pvPhys, void *pvBuf, size_t cbBuf, PGMACCESSTYPE enmAccessType, void *pvUser);
3e88c818fff5c64b5eff43d5daf4596bd87230c5vboxsyncstatic DECLCALLBACK(int) selmR3GuestTSSWriteHandler(PVM pVM, RTGCPTR GCPtr, void *pvPhys, void *pvBuf, size_t cbBuf, PGMACCESSTYPE enmAccessType, void *pvUser);
3e88c818fff5c64b5eff43d5daf4596bd87230c5vboxsyncstatic DECLCALLBACK(void) selmR3InfoGdt(PVM pVM, PCDBGFINFOHLP pHlp, const char *pszArgs);
3e88c818fff5c64b5eff43d5daf4596bd87230c5vboxsyncstatic DECLCALLBACK(void) selmR3InfoGdtGuest(PVM pVM, PCDBGFINFOHLP pHlp, const char *pszArgs);
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsyncstatic DECLCALLBACK(void) selmR3InfoLdt(PVM pVM, PCDBGFINFOHLP pHlp, const char *pszArgs);
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsyncstatic DECLCALLBACK(void) selmR3InfoLdtGuest(PVM pVM, PCDBGFINFOHLP pHlp, const char *pszArgs);
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync//static DECLCALLBACK(void) selmR3InfoTss(PVM pVM, PCDBGFINFOHLP pHlp, const char *pszArgs);
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync//static DECLCALLBACK(void) selmR3InfoTssGuest(PVM pVM, PCDBGFINFOHLP pHlp, const char *pszArgs);
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync/**
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync * Initializes the SELM.
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync *
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync * @returns VBox status code.
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync * @param pVM The VM to operate on.
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync */
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsyncVMMR3DECL(int) SELMR3Init(PVM pVM)
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync{
cab115cfa31c584def7069312a1e23c3fc88533bvboxsync LogFlow(("SELMR3Init\n"));
cab115cfa31c584def7069312a1e23c3fc88533bvboxsync
cab115cfa31c584def7069312a1e23c3fc88533bvboxsync /*
cab115cfa31c584def7069312a1e23c3fc88533bvboxsync * Assert alignment and sizes.
cab115cfa31c584def7069312a1e23c3fc88533bvboxsync * (The TSS block requires contiguous back.)
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync */
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync AssertCompile(sizeof(pVM->selm.s) <= sizeof(pVM->selm.padding)); AssertRelease(sizeof(pVM->selm.s) <= sizeof(pVM->selm.padding));
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync AssertCompileMemberAlignment(VM, selm.s, 32); AssertRelease(!(RT_OFFSETOF(VM, selm.s) & 31));
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync#if 0 /* doesn't work */
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync AssertCompile((RT_OFFSETOF(VM, selm.s.Tss) & PAGE_OFFSET_MASK) <= PAGE_SIZE - sizeof(pVM->selm.s.Tss));
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync AssertCompile((RT_OFFSETOF(VM, selm.s.TssTrap08) & PAGE_OFFSET_MASK) <= PAGE_SIZE - sizeof(pVM->selm.s.TssTrap08));
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync#endif
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync AssertRelease((RT_OFFSETOF(VM, selm.s.Tss) & PAGE_OFFSET_MASK) <= PAGE_SIZE - sizeof(pVM->selm.s.Tss));
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync AssertRelease((RT_OFFSETOF(VM, selm.s.TssTrap08) & PAGE_OFFSET_MASK) <= PAGE_SIZE - sizeof(pVM->selm.s.TssTrap08));
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync AssertRelease(sizeof(pVM->selm.s.Tss.IntRedirBitmap) == 0x20);
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync
3e88c818fff5c64b5eff43d5daf4596bd87230c5vboxsync /*
3e88c818fff5c64b5eff43d5daf4596bd87230c5vboxsync * Init the structure.
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync */
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync pVM->selm.s.offVM = RT_OFFSETOF(VM, selm);
3e88c818fff5c64b5eff43d5daf4596bd87230c5vboxsync pVM->selm.s.aHyperSel[SELM_HYPER_SEL_CS] = (SELM_GDT_ELEMENTS - 0x1) << 3;
3e88c818fff5c64b5eff43d5daf4596bd87230c5vboxsync pVM->selm.s.aHyperSel[SELM_HYPER_SEL_DS] = (SELM_GDT_ELEMENTS - 0x2) << 3;
3e88c818fff5c64b5eff43d5daf4596bd87230c5vboxsync pVM->selm.s.aHyperSel[SELM_HYPER_SEL_CS64] = (SELM_GDT_ELEMENTS - 0x3) << 3;
3e88c818fff5c64b5eff43d5daf4596bd87230c5vboxsync pVM->selm.s.aHyperSel[SELM_HYPER_SEL_TSS] = (SELM_GDT_ELEMENTS - 0x4) << 3;
3e88c818fff5c64b5eff43d5daf4596bd87230c5vboxsync pVM->selm.s.aHyperSel[SELM_HYPER_SEL_TSS_TRAP08] = (SELM_GDT_ELEMENTS - 0x5) << 3;
3e88c818fff5c64b5eff43d5daf4596bd87230c5vboxsync
3e88c818fff5c64b5eff43d5daf4596bd87230c5vboxsync /*
3e88c818fff5c64b5eff43d5daf4596bd87230c5vboxsync * Allocate GDT table.
3e88c818fff5c64b5eff43d5daf4596bd87230c5vboxsync */
3e88c818fff5c64b5eff43d5daf4596bd87230c5vboxsync int rc = MMR3HyperAllocOnceNoRel(pVM, sizeof(pVM->selm.s.paGdtR3[0]) * SELM_GDT_ELEMENTS,
3e88c818fff5c64b5eff43d5daf4596bd87230c5vboxsync PAGE_SIZE, MM_TAG_SELM, (void **)&pVM->selm.s.paGdtR3);
3e88c818fff5c64b5eff43d5daf4596bd87230c5vboxsync AssertRCReturn(rc, rc);
3e88c818fff5c64b5eff43d5daf4596bd87230c5vboxsync
3e88c818fff5c64b5eff43d5daf4596bd87230c5vboxsync /*
3e88c818fff5c64b5eff43d5daf4596bd87230c5vboxsync * Allocate LDT area.
3e88c818fff5c64b5eff43d5daf4596bd87230c5vboxsync */
3e88c818fff5c64b5eff43d5daf4596bd87230c5vboxsync rc = MMR3HyperAllocOnceNoRel(pVM, _64K + PAGE_SIZE, PAGE_SIZE, MM_TAG_SELM, &pVM->selm.s.pvLdtR3);
a98c2afd05b2949737f73ec954d227712f3b0146vboxsync AssertRCReturn(rc, rc);
a98c2afd05b2949737f73ec954d227712f3b0146vboxsync
a98c2afd05b2949737f73ec954d227712f3b0146vboxsync /*
a98c2afd05b2949737f73ec954d227712f3b0146vboxsync * Init Guest's and Shadow GDT, LDT, TSS changes control variables.
a98c2afd05b2949737f73ec954d227712f3b0146vboxsync */
a98c2afd05b2949737f73ec954d227712f3b0146vboxsync pVM->selm.s.cbEffGuestGdtLimit = 0;
a98c2afd05b2949737f73ec954d227712f3b0146vboxsync pVM->selm.s.GuestGdtr.pGdt = RTRCPTR_MAX;
a98c2afd05b2949737f73ec954d227712f3b0146vboxsync pVM->selm.s.GCPtrGuestLdt = RTRCPTR_MAX;
a98c2afd05b2949737f73ec954d227712f3b0146vboxsync pVM->selm.s.GCPtrGuestTss = RTRCPTR_MAX;
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync pVM->selm.s.paGdtRC = NIL_RTRCPTR; /* Must be set in SELMR3Relocate because of monitoring. */
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync pVM->selm.s.pvLdtRC = RTRCPTR_MAX;
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync pVM->selm.s.pvMonShwTssRC = RTRCPTR_MAX;
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync pVM->selm.s.GCSelTss = RTSEL_MAX;
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync pVM->selm.s.fDisableMonitoring = false;
34b5a73592441cbfacb85c2f2261648c1d25ad31vboxsync pVM->selm.s.fSyncTSSRing0Stack = false;
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync /* The I/O bitmap starts right after the virtual interrupt redirection bitmap. Outside the TSS on purpose; the CPU will not check it
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync * for I/O operations. */
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync pVM->selm.s.Tss.offIoBitmap = sizeof(VBOXTSS);
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync /* bit set to 1 means no redirection */
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync memset(pVM->selm.s.Tss.IntRedirBitmap, 0xff, sizeof(pVM->selm.s.Tss.IntRedirBitmap));
34b5a73592441cbfacb85c2f2261648c1d25ad31vboxsync
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync /*
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync * Register the saved state data unit.
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync */
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync rc = SSMR3RegisterInternal(pVM, "selm", 1, SELM_SAVED_STATE_VERSION, sizeof(SELM),
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync NULL, selmR3Save, NULL,
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync NULL, selmR3Load, selmR3LoadDone);
34b5a73592441cbfacb85c2f2261648c1d25ad31vboxsync if (RT_FAILURE(rc))
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync return rc;
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync /*
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync * Statistics.
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync */
a98c2afd05b2949737f73ec954d227712f3b0146vboxsync STAM_REG(pVM, &pVM->selm.s.StatRCWriteGuestGDTHandled, STAMTYPE_COUNTER, "/SELM/GC/Write/Guest/GDTInt", STAMUNIT_OCCURENCES, "The number of handled writes to the Guest GDT.");
a98c2afd05b2949737f73ec954d227712f3b0146vboxsync STAM_REG(pVM, &pVM->selm.s.StatRCWriteGuestGDTUnhandled, STAMTYPE_COUNTER, "/SELM/GC/Write/Guest/GDTEmu", STAMUNIT_OCCURENCES, "The number of unhandled writes to the Guest GDT.");
a98c2afd05b2949737f73ec954d227712f3b0146vboxsync STAM_REG(pVM, &pVM->selm.s.StatRCWriteGuestLDT, STAMTYPE_COUNTER, "/SELM/GC/Write/Guest/LDT", STAMUNIT_OCCURENCES, "The number of writes to the Guest LDT was detected.");
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync STAM_REG(pVM, &pVM->selm.s.StatRCWriteGuestTSSHandled, STAMTYPE_COUNTER, "/SELM/GC/Write/Guest/TSSInt", STAMUNIT_OCCURENCES, "The number of handled writes to the Guest TSS.");
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync STAM_REG(pVM, &pVM->selm.s.StatRCWriteGuestTSSRedir, STAMTYPE_COUNTER, "/SELM/GC/Write/Guest/TSSRedir",STAMUNIT_OCCURENCES, "The number of handled redir bitmap writes to the Guest TSS.");
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync STAM_REG(pVM, &pVM->selm.s.StatRCWriteGuestTSSHandledChanged,STAMTYPE_COUNTER, "/SELM/GC/Write/Guest/TSSIntChg", STAMUNIT_OCCURENCES, "The number of handled writes to the Guest TSS where the R0 stack changed.");
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync STAM_REG(pVM, &pVM->selm.s.StatRCWriteGuestTSSUnhandled, STAMTYPE_COUNTER, "/SELM/GC/Write/Guest/TSSEmu", STAMUNIT_OCCURENCES, "The number of unhandled writes to the Guest TSS.");
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync STAM_REG(pVM, &pVM->selm.s.StatTSSSync, STAMTYPE_PROFILE, "/PROF/SELM/TSSSync", STAMUNIT_TICKS_PER_CALL, "Profiling of the SELMR3SyncTSS() body.");
a98c2afd05b2949737f73ec954d227712f3b0146vboxsync STAM_REG(pVM, &pVM->selm.s.StatUpdateFromCPUM, STAMTYPE_PROFILE, "/PROF/SELM/UpdateFromCPUM", STAMUNIT_TICKS_PER_CALL, "Profiling of the SELMR3UpdateFromCPUM() body.");
a98c2afd05b2949737f73ec954d227712f3b0146vboxsync
a98c2afd05b2949737f73ec954d227712f3b0146vboxsync 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.");
a98c2afd05b2949737f73ec954d227712f3b0146vboxsync STAM_REG(pVM, &pVM->selm.s.StatScanForHyperSels, STAMTYPE_COUNTER, "/SELM/HyperSels/Scan", STAMUNIT_OCCURENCES, "The number of times we had find free hypervisor selectors.");
a98c2afd05b2949737f73ec954d227712f3b0146vboxsync
a98c2afd05b2949737f73ec954d227712f3b0146vboxsync /*
a98c2afd05b2949737f73ec954d227712f3b0146vboxsync * Default action when entering raw mode for the first time
a98c2afd05b2949737f73ec954d227712f3b0146vboxsync */
a98c2afd05b2949737f73ec954d227712f3b0146vboxsync VM_FF_SET(pVM, VM_FF_SELM_SYNC_TSS);
a98c2afd05b2949737f73ec954d227712f3b0146vboxsync VM_FF_SET(pVM, VM_FF_SELM_SYNC_GDT);
a98c2afd05b2949737f73ec954d227712f3b0146vboxsync VM_FF_SET(pVM, VM_FF_SELM_SYNC_LDT);
a98c2afd05b2949737f73ec954d227712f3b0146vboxsync
a98c2afd05b2949737f73ec954d227712f3b0146vboxsync /*
a98c2afd05b2949737f73ec954d227712f3b0146vboxsync * Register info handlers.
a98c2afd05b2949737f73ec954d227712f3b0146vboxsync */
a98c2afd05b2949737f73ec954d227712f3b0146vboxsync DBGFR3InfoRegisterInternal(pVM, "gdt", "Displays the shadow GDT. No arguments.", &selmR3InfoGdt);
a98c2afd05b2949737f73ec954d227712f3b0146vboxsync DBGFR3InfoRegisterInternal(pVM, "gdtguest", "Displays the guest GDT. No arguments.", &selmR3InfoGdtGuest);
a98c2afd05b2949737f73ec954d227712f3b0146vboxsync DBGFR3InfoRegisterInternal(pVM, "ldt", "Displays the shadow LDT. No arguments.", &selmR3InfoLdt);
a98c2afd05b2949737f73ec954d227712f3b0146vboxsync DBGFR3InfoRegisterInternal(pVM, "ldtguest", "Displays the guest LDT. No arguments.", &selmR3InfoLdtGuest);
a98c2afd05b2949737f73ec954d227712f3b0146vboxsync //DBGFR3InfoRegisterInternal(pVM, "tss", "Displays the shadow TSS. No arguments.", &selmR3InfoTss);
a98c2afd05b2949737f73ec954d227712f3b0146vboxsync //DBGFR3InfoRegisterInternal(pVM, "tssguest", "Displays the guest TSS. No arguments.", &selmR3InfoTssGuest);
a98c2afd05b2949737f73ec954d227712f3b0146vboxsync
a98c2afd05b2949737f73ec954d227712f3b0146vboxsync return rc;
a98c2afd05b2949737f73ec954d227712f3b0146vboxsync}
a98c2afd05b2949737f73ec954d227712f3b0146vboxsync
a98c2afd05b2949737f73ec954d227712f3b0146vboxsync
a98c2afd05b2949737f73ec954d227712f3b0146vboxsync/**
a98c2afd05b2949737f73ec954d227712f3b0146vboxsync * Finalizes HMA page attributes.
a98c2afd05b2949737f73ec954d227712f3b0146vboxsync *
a98c2afd05b2949737f73ec954d227712f3b0146vboxsync * @returns VBox status code.
a98c2afd05b2949737f73ec954d227712f3b0146vboxsync * @param pVM The VM handle.
a98c2afd05b2949737f73ec954d227712f3b0146vboxsync */
a98c2afd05b2949737f73ec954d227712f3b0146vboxsyncVMMR3DECL(int) SELMR3InitFinalize(PVM pVM)
a98c2afd05b2949737f73ec954d227712f3b0146vboxsync{
a98c2afd05b2949737f73ec954d227712f3b0146vboxsync /** @cfgm{/DoubleFault,bool,false}
a98c2afd05b2949737f73ec954d227712f3b0146vboxsync * Enables catching of double faults in the raw-mode context VMM code. This can
a98c2afd05b2949737f73ec954d227712f3b0146vboxsync * be used when the tripple faults or hangs occure and one suspect an unhandled
a98c2afd05b2949737f73ec954d227712f3b0146vboxsync * double fault. This is not enabled by default because it means making the
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync * hyper selectors writeable for all supervisor code, including the guest's.
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync * The double fault is a task switch and thus requires write access to the GDT
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync * of the TSS (to set it busy), to the old TSS (to store state), and to the Trap
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync * 8 TSS for the back link.
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync */
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync bool f;
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync#if defined(DEBUG_bird)
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync int rc = CFGMR3QueryBoolDef(CFGMR3GetRoot(pVM), "DoubleFault", &f, true);
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync#else
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync int rc = CFGMR3QueryBoolDef(CFGMR3GetRoot(pVM), "DoubleFault", &f, false);
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync#endif
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync AssertLogRelRCReturn(rc, rc);
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync if (f)
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync {
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync PX86DESC paGdt = pVM->selm.s.paGdtR3;
6114ff5789db75ff28460a056ed422ee03d8f0ebvboxsync rc = PGMMapSetPage(pVM, MMHyperR3ToRC(pVM, &paGdt[pVM->selm.s.aHyperSel[SELM_HYPER_SEL_TSS_TRAP08] >> 3]), sizeof(paGdt[0]),
6114ff5789db75ff28460a056ed422ee03d8f0ebvboxsync X86_PTE_RW | X86_PTE_P | X86_PTE_A | X86_PTE_D);
6114ff5789db75ff28460a056ed422ee03d8f0ebvboxsync AssertRC(rc);
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync rc = PGMMapSetPage(pVM, MMHyperR3ToRC(pVM, &paGdt[pVM->selm.s.aHyperSel[SELM_HYPER_SEL_TSS] >> 3]), sizeof(paGdt[0]),
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync X86_PTE_RW | X86_PTE_P | X86_PTE_A | X86_PTE_D);
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync AssertRC(rc);
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync rc = PGMMapSetPage(pVM, VM_RC_ADDR(pVM, &pVM->selm.s.aHyperSel[SELM_HYPER_SEL_TSS]), sizeof(pVM->selm.s.aHyperSel[SELM_HYPER_SEL_TSS]),
bf27591c0d413dd4d7ba41cf89f117171968d462vboxsync X86_PTE_RW | X86_PTE_P | X86_PTE_A | X86_PTE_D);
bf27591c0d413dd4d7ba41cf89f117171968d462vboxsync AssertRC(rc);
bf27591c0d413dd4d7ba41cf89f117171968d462vboxsync rc = PGMMapSetPage(pVM, VM_RC_ADDR(pVM, &pVM->selm.s.aHyperSel[SELM_HYPER_SEL_TSS_TRAP08]), sizeof(pVM->selm.s.aHyperSel[SELM_HYPER_SEL_TSS_TRAP08]),
bf27591c0d413dd4d7ba41cf89f117171968d462vboxsync X86_PTE_RW | X86_PTE_P | X86_PTE_A | X86_PTE_D);
bf27591c0d413dd4d7ba41cf89f117171968d462vboxsync AssertRC(rc);
bf27591c0d413dd4d7ba41cf89f117171968d462vboxsync }
bf27591c0d413dd4d7ba41cf89f117171968d462vboxsync return VINF_SUCCESS;
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync}
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync/**
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync * Setup the hypervisor GDT selectors in our shadow table
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync *
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync * @param pVM The VM handle.
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync */
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsyncstatic void selmR3SetupHyperGDTSelectors(PVM pVM)
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync{
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync PX86DESC paGdt = pVM->selm.s.paGdtR3;
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync /*
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync * Set up global code and data descriptors for use in the guest context.
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync * Both are wide open (base 0, limit 4GB)
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync */
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync PX86DESC pDesc = &paGdt[pVM->selm.s.aHyperSel[SELM_HYPER_SEL_CS] >> 3];
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync pDesc->Gen.u16LimitLow = 0xffff;
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync pDesc->Gen.u4LimitHigh = 0xf;
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync pDesc->Gen.u16BaseLow = 0;
bf27591c0d413dd4d7ba41cf89f117171968d462vboxsync pDesc->Gen.u8BaseHigh1 = 0;
bf27591c0d413dd4d7ba41cf89f117171968d462vboxsync pDesc->Gen.u8BaseHigh2 = 0;
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync pDesc->Gen.u4Type = X86_SEL_TYPE_ER_ACC;
bf27591c0d413dd4d7ba41cf89f117171968d462vboxsync pDesc->Gen.u1DescType = 1; /* not system, but code/data */
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync pDesc->Gen.u2Dpl = 0; /* supervisor */
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync pDesc->Gen.u1Present = 1;
bf27591c0d413dd4d7ba41cf89f117171968d462vboxsync pDesc->Gen.u1Available = 0;
bf27591c0d413dd4d7ba41cf89f117171968d462vboxsync pDesc->Gen.u1Long = 0;
bf27591c0d413dd4d7ba41cf89f117171968d462vboxsync pDesc->Gen.u1DefBig = 1; /* def 32 bit */
bf27591c0d413dd4d7ba41cf89f117171968d462vboxsync pDesc->Gen.u1Granularity = 1; /* 4KB limit */
bf27591c0d413dd4d7ba41cf89f117171968d462vboxsync
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync /* data */
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync pDesc = &paGdt[pVM->selm.s.aHyperSel[SELM_HYPER_SEL_DS] >> 3];
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync pDesc->Gen.u16LimitLow = 0xffff;
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync pDesc->Gen.u4LimitHigh = 0xf;
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync pDesc->Gen.u16BaseLow = 0;
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync pDesc->Gen.u8BaseHigh1 = 0;
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync pDesc->Gen.u8BaseHigh2 = 0;
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync pDesc->Gen.u4Type = X86_SEL_TYPE_RW_ACC;
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync pDesc->Gen.u1DescType = 1; /* not system, but code/data */
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync pDesc->Gen.u2Dpl = 0; /* supervisor */
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync pDesc->Gen.u1Present = 1;
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync pDesc->Gen.u1Available = 0;
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync pDesc->Gen.u1Long = 0;
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync pDesc->Gen.u1DefBig = 1; /* big */
bf27591c0d413dd4d7ba41cf89f117171968d462vboxsync pDesc->Gen.u1Granularity = 1; /* 4KB limit */
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync /* 64-bit mode code (& data?) */
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync pDesc = &paGdt[pVM->selm.s.aHyperSel[SELM_HYPER_SEL_CS64] >> 3];
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync pDesc->Gen.u16LimitLow = 0xffff;
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync pDesc->Gen.u4LimitHigh = 0xf;
e93dfb5368911523b10cd2f506c44f5fbd47abf3vboxsync pDesc->Gen.u16BaseLow = 0;
e93dfb5368911523b10cd2f506c44f5fbd47abf3vboxsync pDesc->Gen.u8BaseHigh1 = 0;
bf27591c0d413dd4d7ba41cf89f117171968d462vboxsync pDesc->Gen.u8BaseHigh2 = 0;
bf27591c0d413dd4d7ba41cf89f117171968d462vboxsync pDesc->Gen.u4Type = X86_SEL_TYPE_ER_ACC;
bf27591c0d413dd4d7ba41cf89f117171968d462vboxsync pDesc->Gen.u1DescType = 1; /* not system, but code/data */
bf27591c0d413dd4d7ba41cf89f117171968d462vboxsync pDesc->Gen.u2Dpl = 0; /* supervisor */
bf27591c0d413dd4d7ba41cf89f117171968d462vboxsync pDesc->Gen.u1Present = 1;
bf27591c0d413dd4d7ba41cf89f117171968d462vboxsync pDesc->Gen.u1Available = 0;
bf27591c0d413dd4d7ba41cf89f117171968d462vboxsync pDesc->Gen.u1Long = 1; /* The Long (L) attribute bit. */
bf27591c0d413dd4d7ba41cf89f117171968d462vboxsync pDesc->Gen.u1DefBig = 0; /* With L=1 this must be 0. */
bf27591c0d413dd4d7ba41cf89f117171968d462vboxsync pDesc->Gen.u1Granularity = 1; /* 4KB limit */
bf27591c0d413dd4d7ba41cf89f117171968d462vboxsync
bf27591c0d413dd4d7ba41cf89f117171968d462vboxsync /*
bf27591c0d413dd4d7ba41cf89f117171968d462vboxsync * TSS descriptor
bf27591c0d413dd4d7ba41cf89f117171968d462vboxsync */
bf27591c0d413dd4d7ba41cf89f117171968d462vboxsync pDesc = &paGdt[pVM->selm.s.aHyperSel[SELM_HYPER_SEL_TSS] >> 3];
bf27591c0d413dd4d7ba41cf89f117171968d462vboxsync RTRCPTR RCPtrTSS = VM_RC_ADDR(pVM, &pVM->selm.s.Tss);
bf27591c0d413dd4d7ba41cf89f117171968d462vboxsync pDesc->Gen.u16BaseLow = RT_LOWORD(RCPtrTSS);
bf27591c0d413dd4d7ba41cf89f117171968d462vboxsync pDesc->Gen.u8BaseHigh1 = RT_BYTE3(RCPtrTSS);
bf27591c0d413dd4d7ba41cf89f117171968d462vboxsync pDesc->Gen.u8BaseHigh2 = RT_BYTE4(RCPtrTSS);
bf27591c0d413dd4d7ba41cf89f117171968d462vboxsync pDesc->Gen.u16LimitLow = sizeof(VBOXTSS) - 1;
bf27591c0d413dd4d7ba41cf89f117171968d462vboxsync pDesc->Gen.u4LimitHigh = 0;
bf27591c0d413dd4d7ba41cf89f117171968d462vboxsync pDesc->Gen.u4Type = X86_SEL_TYPE_SYS_386_TSS_AVAIL;
bf27591c0d413dd4d7ba41cf89f117171968d462vboxsync pDesc->Gen.u1DescType = 0; /* system */
bf27591c0d413dd4d7ba41cf89f117171968d462vboxsync pDesc->Gen.u2Dpl = 0; /* supervisor */
bf27591c0d413dd4d7ba41cf89f117171968d462vboxsync pDesc->Gen.u1Present = 1;
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync pDesc->Gen.u1Available = 0;
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync pDesc->Gen.u1Long = 0;
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync pDesc->Gen.u1DefBig = 0;
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync pDesc->Gen.u1Granularity = 0; /* byte limit */
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync /*
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync * TSS descriptor for trap 08
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync */
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync pDesc = &paGdt[pVM->selm.s.aHyperSel[SELM_HYPER_SEL_TSS_TRAP08] >> 3];
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync pDesc->Gen.u16LimitLow = sizeof(VBOXTSS) - 1;
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync pDesc->Gen.u4LimitHigh = 0;
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync RCPtrTSS = VM_RC_ADDR(pVM, &pVM->selm.s.TssTrap08);
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync pDesc->Gen.u16BaseLow = RT_LOWORD(RCPtrTSS);
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync pDesc->Gen.u8BaseHigh1 = RT_BYTE3(RCPtrTSS);
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync pDesc->Gen.u8BaseHigh2 = RT_BYTE4(RCPtrTSS);
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync pDesc->Gen.u4Type = X86_SEL_TYPE_SYS_386_TSS_AVAIL;
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync pDesc->Gen.u1DescType = 0; /* system */
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync pDesc->Gen.u2Dpl = 0; /* supervisor */
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync pDesc->Gen.u1Present = 1;
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync pDesc->Gen.u1Available = 0;
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync pDesc->Gen.u1Long = 0;
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync pDesc->Gen.u1DefBig = 0;
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync pDesc->Gen.u1Granularity = 0; /* byte limit */
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync}
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync/**
6114ff5789db75ff28460a056ed422ee03d8f0ebvboxsync * Applies relocations to data and code managed by this
6114ff5789db75ff28460a056ed422ee03d8f0ebvboxsync * component. This function will be called at init and
6114ff5789db75ff28460a056ed422ee03d8f0ebvboxsync * whenever the VMM need to relocate it self inside the GC.
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync *
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync * @param pVM The VM.
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync */
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsyncVMMR3DECL(void) SELMR3Relocate(PVM pVM)
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync{
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync PX86DESC paGdt = pVM->selm.s.paGdtR3;
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync LogFlow(("SELMR3Relocate\n"));
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync /*
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync * Update GDTR and selector.
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync */
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync CPUMSetHyperGDTR(pVM, MMHyperR3ToRC(pVM, paGdt), SELM_GDT_ELEMENTS * sizeof(paGdt[0]) - 1);
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync /** @todo selector relocations should be a seperate operation? */
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync CPUMSetHyperCS(pVM, pVM->selm.s.aHyperSel[SELM_HYPER_SEL_CS]);
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync CPUMSetHyperDS(pVM, pVM->selm.s.aHyperSel[SELM_HYPER_SEL_DS]);
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync CPUMSetHyperES(pVM, pVM->selm.s.aHyperSel[SELM_HYPER_SEL_DS]);
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync CPUMSetHyperSS(pVM, pVM->selm.s.aHyperSel[SELM_HYPER_SEL_DS]);
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync CPUMSetHyperTR(pVM, pVM->selm.s.aHyperSel[SELM_HYPER_SEL_TSS]);
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync selmR3SetupHyperGDTSelectors(pVM);
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync/** @todo SELM must be called when any of the CR3s changes during a cpu mode change. */
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync/** @todo PGM knows the proper CR3 values these days, not CPUM. */
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync /*
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync * Update the TSSes.
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync */
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync /* Current TSS */
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync pVM->selm.s.Tss.cr3 = PGMGetHyperCR3(pVM);
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync pVM->selm.s.Tss.ss0 = pVM->selm.s.aHyperSel[SELM_HYPER_SEL_DS];
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync pVM->selm.s.Tss.esp0 = VMMGetStackRC(pVM);
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync pVM->selm.s.Tss.cs = pVM->selm.s.aHyperSel[SELM_HYPER_SEL_CS];
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync pVM->selm.s.Tss.ds = pVM->selm.s.aHyperSel[SELM_HYPER_SEL_DS];
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync pVM->selm.s.Tss.es = pVM->selm.s.aHyperSel[SELM_HYPER_SEL_DS];
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync pVM->selm.s.Tss.offIoBitmap = sizeof(VBOXTSS);
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync /* trap 08 */
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync pVM->selm.s.TssTrap08.cr3 = PGMGetInterRCCR3(pVM); /* this should give use better survival chances. */
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync pVM->selm.s.TssTrap08.ss0 = pVM->selm.s.aHyperSel[SELM_HYPER_SEL_DS];
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync pVM->selm.s.TssTrap08.ss = pVM->selm.s.aHyperSel[SELM_HYPER_SEL_DS];
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync pVM->selm.s.TssTrap08.esp0 = VMMGetStackRC(pVM) - PAGE_SIZE / 2; /* upper half can be analysed this way. */
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync pVM->selm.s.TssTrap08.esp = pVM->selm.s.TssTrap08.esp0;
1426407ebd84624dd10808b452b19d1fc4f6835avboxsync pVM->selm.s.TssTrap08.ebp = pVM->selm.s.TssTrap08.esp0;
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync pVM->selm.s.TssTrap08.cs = pVM->selm.s.aHyperSel[SELM_HYPER_SEL_CS];
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync pVM->selm.s.TssTrap08.ds = pVM->selm.s.aHyperSel[SELM_HYPER_SEL_DS];
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync pVM->selm.s.TssTrap08.es = pVM->selm.s.aHyperSel[SELM_HYPER_SEL_DS];
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync pVM->selm.s.TssTrap08.fs = 0;
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync pVM->selm.s.TssTrap08.gs = 0;
1426407ebd84624dd10808b452b19d1fc4f6835avboxsync pVM->selm.s.TssTrap08.selLdt = 0;
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync pVM->selm.s.TssTrap08.eflags = 0x2; /* all cleared */
1426407ebd84624dd10808b452b19d1fc4f6835avboxsync pVM->selm.s.TssTrap08.ecx = VM_RC_ADDR(pVM, &pVM->selm.s.Tss); /* setup ecx to normal Hypervisor TSS address. */
1426407ebd84624dd10808b452b19d1fc4f6835avboxsync pVM->selm.s.TssTrap08.edi = pVM->selm.s.TssTrap08.ecx;
1426407ebd84624dd10808b452b19d1fc4f6835avboxsync pVM->selm.s.TssTrap08.eax = pVM->selm.s.TssTrap08.ecx;
1426407ebd84624dd10808b452b19d1fc4f6835avboxsync pVM->selm.s.TssTrap08.edx = VM_RC_ADDR(pVM, pVM); /* setup edx VM address. */
1426407ebd84624dd10808b452b19d1fc4f6835avboxsync pVM->selm.s.TssTrap08.edi = pVM->selm.s.TssTrap08.edx;
1426407ebd84624dd10808b452b19d1fc4f6835avboxsync pVM->selm.s.TssTrap08.ebx = pVM->selm.s.TssTrap08.edx;
1426407ebd84624dd10808b452b19d1fc4f6835avboxsync pVM->selm.s.TssTrap08.offIoBitmap = sizeof(VBOXTSS);
1426407ebd84624dd10808b452b19d1fc4f6835avboxsync /* TRPM will be updating the eip */
1426407ebd84624dd10808b452b19d1fc4f6835avboxsync
1426407ebd84624dd10808b452b19d1fc4f6835avboxsync if (!pVM->selm.s.fDisableMonitoring)
1426407ebd84624dd10808b452b19d1fc4f6835avboxsync {
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync /*
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync * Update shadow GDT/LDT/TSS write access handlers.
e93dfb5368911523b10cd2f506c44f5fbd47abf3vboxsync */
e93dfb5368911523b10cd2f506c44f5fbd47abf3vboxsync int rc;
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync#ifdef SELM_TRACK_SHADOW_GDT_CHANGES
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync if (pVM->selm.s.paGdtRC != NIL_RTRCPTR)
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync {
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync rc = PGMHandlerVirtualDeregister(pVM, pVM->selm.s.paGdtRC);
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync AssertRC(rc);
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync }
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync pVM->selm.s.paGdtRC = MMHyperR3ToRC(pVM, paGdt);
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync rc = PGMR3HandlerVirtualRegister(pVM, PGMVIRTHANDLERTYPE_HYPERVISOR, pVM->selm.s.paGdtRC,
3e88c818fff5c64b5eff43d5daf4596bd87230c5vboxsync pVM->selm.s.paGdtRC + SELM_GDT_ELEMENTS * sizeof(paGdt[0]) - 1,
3e88c818fff5c64b5eff43d5daf4596bd87230c5vboxsync 0, 0, "selmRCShadowGDTWriteHandler", 0, "Shadow GDT write access handler");
3e88c818fff5c64b5eff43d5daf4596bd87230c5vboxsync AssertRC(rc);
3e88c818fff5c64b5eff43d5daf4596bd87230c5vboxsync#endif
3e88c818fff5c64b5eff43d5daf4596bd87230c5vboxsync#ifdef SELM_TRACK_SHADOW_TSS_CHANGES
3e88c818fff5c64b5eff43d5daf4596bd87230c5vboxsync if (pVM->selm.s.pvMonShwTssRC != RTRCPTR_MAX)
3e88c818fff5c64b5eff43d5daf4596bd87230c5vboxsync {
3e88c818fff5c64b5eff43d5daf4596bd87230c5vboxsync rc = PGMHandlerVirtualDeregister(pVM, pVM->selm.s.pvMonShwTssRC);
3e88c818fff5c64b5eff43d5daf4596bd87230c5vboxsync AssertRC(rc);
3e88c818fff5c64b5eff43d5daf4596bd87230c5vboxsync }
3e88c818fff5c64b5eff43d5daf4596bd87230c5vboxsync pVM->selm.s.pvMonShwTssRC = VM_RC_ADDR(pVM, &pVM->selm.s.Tss);
3e88c818fff5c64b5eff43d5daf4596bd87230c5vboxsync rc = PGMR3HandlerVirtualRegister(pVM, PGMVIRTHANDLERTYPE_HYPERVISOR, pVM->selm.s.pvMonShwTssRC,
a98c2afd05b2949737f73ec954d227712f3b0146vboxsync pVM->selm.s.pvMonShwTssRC + sizeof(pVM->selm.s.Tss) - 1,
3e88c818fff5c64b5eff43d5daf4596bd87230c5vboxsync 0, 0, "selmRCShadowTSSWriteHandler", 0, "Shadow TSS write access handler");
3e88c818fff5c64b5eff43d5daf4596bd87230c5vboxsync AssertRC(rc);
3e88c818fff5c64b5eff43d5daf4596bd87230c5vboxsync#endif
3e88c818fff5c64b5eff43d5daf4596bd87230c5vboxsync
3e88c818fff5c64b5eff43d5daf4596bd87230c5vboxsync /*
7de81a482cdfc1bbf53600a4f6cdd4c892ee460cvboxsync * Update the GC LDT region handler and address.
3e88c818fff5c64b5eff43d5daf4596bd87230c5vboxsync */
3e88c818fff5c64b5eff43d5daf4596bd87230c5vboxsync#ifdef SELM_TRACK_SHADOW_LDT_CHANGES
3e88c818fff5c64b5eff43d5daf4596bd87230c5vboxsync if (pVM->selm.s.pvLdtRC != RTRCPTR_MAX)
3e88c818fff5c64b5eff43d5daf4596bd87230c5vboxsync {
3e88c818fff5c64b5eff43d5daf4596bd87230c5vboxsync rc = PGMHandlerVirtualDeregister(pVM, pVM->selm.s.pvLdtRC);
3e88c818fff5c64b5eff43d5daf4596bd87230c5vboxsync AssertRC(rc);
3e88c818fff5c64b5eff43d5daf4596bd87230c5vboxsync }
3e88c818fff5c64b5eff43d5daf4596bd87230c5vboxsync#endif
3e88c818fff5c64b5eff43d5daf4596bd87230c5vboxsync pVM->selm.s.pvLdtRC = MMHyperR3ToRC(pVM, pVM->selm.s.pvLdtR3);
3e88c818fff5c64b5eff43d5daf4596bd87230c5vboxsync#ifdef SELM_TRACK_SHADOW_LDT_CHANGES
3e88c818fff5c64b5eff43d5daf4596bd87230c5vboxsync rc = PGMR3HandlerVirtualRegister(pVM, PGMVIRTHANDLERTYPE_HYPERVISOR, pVM->selm.s.pvLdtRC,
3e88c818fff5c64b5eff43d5daf4596bd87230c5vboxsync pVM->selm.s.pvLdtRC + _64K + PAGE_SIZE - 1,
3e88c818fff5c64b5eff43d5daf4596bd87230c5vboxsync 0, 0, "selmRCShadowLDTWriteHandler", 0, "Shadow LDT write access handler");
3e88c818fff5c64b5eff43d5daf4596bd87230c5vboxsync AssertRC(rc);
3e88c818fff5c64b5eff43d5daf4596bd87230c5vboxsync#endif
060db742c6ecb43560beec8754fb5b9c13bd7856vboxsync }
3e88c818fff5c64b5eff43d5daf4596bd87230c5vboxsync}
3e88c818fff5c64b5eff43d5daf4596bd87230c5vboxsync
3e88c818fff5c64b5eff43d5daf4596bd87230c5vboxsync
3e88c818fff5c64b5eff43d5daf4596bd87230c5vboxsync/**
3e88c818fff5c64b5eff43d5daf4596bd87230c5vboxsync * Notification callback which is called whenever there is a chance that a CR3
3e88c818fff5c64b5eff43d5daf4596bd87230c5vboxsync * value might have changed.
3e88c818fff5c64b5eff43d5daf4596bd87230c5vboxsync * This is called by PGM.
3e88c818fff5c64b5eff43d5daf4596bd87230c5vboxsync *
3e88c818fff5c64b5eff43d5daf4596bd87230c5vboxsync * @param pVM The VM handle
3e88c818fff5c64b5eff43d5daf4596bd87230c5vboxsync */
3e88c818fff5c64b5eff43d5daf4596bd87230c5vboxsyncVMMR3DECL(void) SELMR3PagingModeChanged(PVM pVM)
3e88c818fff5c64b5eff43d5daf4596bd87230c5vboxsync{
3e88c818fff5c64b5eff43d5daf4596bd87230c5vboxsync pVM->selm.s.Tss.cr3 = PGMGetHyperCR3(pVM);
3e88c818fff5c64b5eff43d5daf4596bd87230c5vboxsync pVM->selm.s.TssTrap08.cr3 = PGMGetInterRCCR3(pVM);
3e88c818fff5c64b5eff43d5daf4596bd87230c5vboxsync}
3e88c818fff5c64b5eff43d5daf4596bd87230c5vboxsync
3e88c818fff5c64b5eff43d5daf4596bd87230c5vboxsync
3e88c818fff5c64b5eff43d5daf4596bd87230c5vboxsync/**
3e88c818fff5c64b5eff43d5daf4596bd87230c5vboxsync * Terminates the SELM.
3e88c818fff5c64b5eff43d5daf4596bd87230c5vboxsync *
3e88c818fff5c64b5eff43d5daf4596bd87230c5vboxsync * Termination means cleaning up and freeing all resources,
3e88c818fff5c64b5eff43d5daf4596bd87230c5vboxsync * the VM it self is at this point powered off or suspended.
3e88c818fff5c64b5eff43d5daf4596bd87230c5vboxsync *
3e88c818fff5c64b5eff43d5daf4596bd87230c5vboxsync * @returns VBox status code.
3e88c818fff5c64b5eff43d5daf4596bd87230c5vboxsync * @param pVM The VM to operate on.
3e88c818fff5c64b5eff43d5daf4596bd87230c5vboxsync */
3e88c818fff5c64b5eff43d5daf4596bd87230c5vboxsyncVMMR3DECL(int) SELMR3Term(PVM pVM)
3e88c818fff5c64b5eff43d5daf4596bd87230c5vboxsync{
3e88c818fff5c64b5eff43d5daf4596bd87230c5vboxsync return 0;
c7ff622115966b69b482bd2896662e40d823b22fvboxsync}
3e88c818fff5c64b5eff43d5daf4596bd87230c5vboxsync
3e88c818fff5c64b5eff43d5daf4596bd87230c5vboxsync
c5f009d280c87ab513356eb5585eff8b4cd26db7vboxsync/**
3e88c818fff5c64b5eff43d5daf4596bd87230c5vboxsync * The VM is being reset.
3e88c818fff5c64b5eff43d5daf4596bd87230c5vboxsync *
3e88c818fff5c64b5eff43d5daf4596bd87230c5vboxsync * For the SELM component this means that any GDT/LDT/TSS monitors
3e88c818fff5c64b5eff43d5daf4596bd87230c5vboxsync * needs to be removed.
3e88c818fff5c64b5eff43d5daf4596bd87230c5vboxsync *
3e88c818fff5c64b5eff43d5daf4596bd87230c5vboxsync * @param pVM VM handle.
3e88c818fff5c64b5eff43d5daf4596bd87230c5vboxsync */
3e88c818fff5c64b5eff43d5daf4596bd87230c5vboxsyncVMMR3DECL(void) SELMR3Reset(PVM pVM)
3e88c818fff5c64b5eff43d5daf4596bd87230c5vboxsync{
3e88c818fff5c64b5eff43d5daf4596bd87230c5vboxsync LogFlow(("SELMR3Reset:\n"));
3e88c818fff5c64b5eff43d5daf4596bd87230c5vboxsync VM_ASSERT_EMT(pVM);
3e88c818fff5c64b5eff43d5daf4596bd87230c5vboxsync
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync /*
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync * Uninstall guest GDT/LDT/TSS write access handlers.
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync */
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync int rc;
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync#ifdef SELM_TRACK_GUEST_GDT_CHANGES
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync if (pVM->selm.s.GuestGdtr.pGdt != RTRCPTR_MAX && pVM->selm.s.fGDTRangeRegistered)
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync {
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync rc = PGMHandlerVirtualDeregister(pVM, pVM->selm.s.GuestGdtr.pGdt);
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync AssertRC(rc);
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync pVM->selm.s.GuestGdtr.pGdt = RTRCPTR_MAX;
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync pVM->selm.s.GuestGdtr.cbGdt = 0;
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync }
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync pVM->selm.s.fGDTRangeRegistered = false;
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync#endif
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync#ifdef SELM_TRACK_GUEST_LDT_CHANGES
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync if (pVM->selm.s.GCPtrGuestLdt != RTRCPTR_MAX)
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync {
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync rc = PGMHandlerVirtualDeregister(pVM, pVM->selm.s.GCPtrGuestLdt);
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync AssertRC(rc);
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync pVM->selm.s.GCPtrGuestLdt = RTRCPTR_MAX;
3e88c818fff5c64b5eff43d5daf4596bd87230c5vboxsync }
3e88c818fff5c64b5eff43d5daf4596bd87230c5vboxsync#endif
3e88c818fff5c64b5eff43d5daf4596bd87230c5vboxsync#ifdef SELM_TRACK_GUEST_TSS_CHANGES
3e88c818fff5c64b5eff43d5daf4596bd87230c5vboxsync if (pVM->selm.s.GCPtrGuestTss != RTRCPTR_MAX)
3e88c818fff5c64b5eff43d5daf4596bd87230c5vboxsync {
3e88c818fff5c64b5eff43d5daf4596bd87230c5vboxsync rc = PGMHandlerVirtualDeregister(pVM, pVM->selm.s.GCPtrGuestTss);
3e88c818fff5c64b5eff43d5daf4596bd87230c5vboxsync AssertRC(rc);
3e88c818fff5c64b5eff43d5daf4596bd87230c5vboxsync pVM->selm.s.GCPtrGuestTss = RTRCPTR_MAX;
3e88c818fff5c64b5eff43d5daf4596bd87230c5vboxsync pVM->selm.s.GCSelTss = RTSEL_MAX;
3e88c818fff5c64b5eff43d5daf4596bd87230c5vboxsync }
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync#endif
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync /*
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync * Re-initialize other members.
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync */
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync pVM->selm.s.cbLdtLimit = 0;
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync pVM->selm.s.offLdtHyper = 0;
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync pVM->selm.s.cbMonitoredGuestTss = 0;
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync pVM->selm.s.fSyncTSSRing0Stack = false;
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync /*
3e88c818fff5c64b5eff43d5daf4596bd87230c5vboxsync * Default action when entering raw mode for the first time
3e88c818fff5c64b5eff43d5daf4596bd87230c5vboxsync */
3e88c818fff5c64b5eff43d5daf4596bd87230c5vboxsync VM_FF_SET(pVM, VM_FF_SELM_SYNC_TSS);
3e88c818fff5c64b5eff43d5daf4596bd87230c5vboxsync VM_FF_SET(pVM, VM_FF_SELM_SYNC_GDT);
3e88c818fff5c64b5eff43d5daf4596bd87230c5vboxsync VM_FF_SET(pVM, VM_FF_SELM_SYNC_LDT);
3e88c818fff5c64b5eff43d5daf4596bd87230c5vboxsync}
3e88c818fff5c64b5eff43d5daf4596bd87230c5vboxsync
3e88c818fff5c64b5eff43d5daf4596bd87230c5vboxsync/**
3e88c818fff5c64b5eff43d5daf4596bd87230c5vboxsync * Disable GDT/LDT/TSS monitoring and syncing
3e88c818fff5c64b5eff43d5daf4596bd87230c5vboxsync *
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync * @param pVM The VM to operate on.
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync */
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsyncVMMR3DECL(void) SELMR3DisableMonitoring(PVM pVM)
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync{
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync /*
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync * Uninstall guest GDT/LDT/TSS write access handlers.
7de81a482cdfc1bbf53600a4f6cdd4c892ee460cvboxsync */
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync int rc;
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync#ifdef SELM_TRACK_GUEST_GDT_CHANGES
34b5a73592441cbfacb85c2f2261648c1d25ad31vboxsync if (pVM->selm.s.GuestGdtr.pGdt != RTRCPTR_MAX && pVM->selm.s.fGDTRangeRegistered)
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync {
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync rc = PGMHandlerVirtualDeregister(pVM, pVM->selm.s.GuestGdtr.pGdt);
3e88c818fff5c64b5eff43d5daf4596bd87230c5vboxsync AssertRC(rc);
3e88c818fff5c64b5eff43d5daf4596bd87230c5vboxsync pVM->selm.s.GuestGdtr.pGdt = RTRCPTR_MAX;
3e88c818fff5c64b5eff43d5daf4596bd87230c5vboxsync pVM->selm.s.GuestGdtr.cbGdt = 0;
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync }
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync pVM->selm.s.fGDTRangeRegistered = false;
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync#endif
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync#ifdef SELM_TRACK_GUEST_LDT_CHANGES
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync if (pVM->selm.s.GCPtrGuestLdt != RTRCPTR_MAX)
3e88c818fff5c64b5eff43d5daf4596bd87230c5vboxsync {
3e88c818fff5c64b5eff43d5daf4596bd87230c5vboxsync rc = PGMHandlerVirtualDeregister(pVM, pVM->selm.s.GCPtrGuestLdt);
3e88c818fff5c64b5eff43d5daf4596bd87230c5vboxsync AssertRC(rc);
3e88c818fff5c64b5eff43d5daf4596bd87230c5vboxsync pVM->selm.s.GCPtrGuestLdt = RTRCPTR_MAX;
3e88c818fff5c64b5eff43d5daf4596bd87230c5vboxsync }
3e88c818fff5c64b5eff43d5daf4596bd87230c5vboxsync#endif
3e88c818fff5c64b5eff43d5daf4596bd87230c5vboxsync#ifdef SELM_TRACK_GUEST_TSS_CHANGES
3e88c818fff5c64b5eff43d5daf4596bd87230c5vboxsync if (pVM->selm.s.GCPtrGuestTss != RTRCPTR_MAX)
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync {
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync rc = PGMHandlerVirtualDeregister(pVM, pVM->selm.s.GCPtrGuestTss);
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync AssertRC(rc);
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync pVM->selm.s.GCPtrGuestTss = RTRCPTR_MAX;
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync pVM->selm.s.GCSelTss = RTSEL_MAX;
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync }
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync#endif
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync /*
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync * Unregister shadow GDT/LDT/TSS write access handlers.
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync */
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync#ifdef SELM_TRACK_SHADOW_GDT_CHANGES
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync if (pVM->selm.s.paGdtRC != NIL_RTRCPTR)
7de81a482cdfc1bbf53600a4f6cdd4c892ee460cvboxsync {
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync rc = PGMHandlerVirtualDeregister(pVM, pVM->selm.s.paGdtRC);
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync AssertRC(rc);
34b5a73592441cbfacb85c2f2261648c1d25ad31vboxsync pVM->selm.s.paGdtRC = NIL_RTRCPTR;
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync }
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync#endif
3e88c818fff5c64b5eff43d5daf4596bd87230c5vboxsync#ifdef SELM_TRACK_SHADOW_TSS_CHANGES
3e88c818fff5c64b5eff43d5daf4596bd87230c5vboxsync if (pVM->selm.s.pvMonShwTssRC != RTRCPTR_MAX)
3e88c818fff5c64b5eff43d5daf4596bd87230c5vboxsync {
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync rc = PGMHandlerVirtualDeregister(pVM, pVM->selm.s.pvMonShwTssRC);
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync AssertRC(rc);
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync pVM->selm.s.pvMonShwTssRC = RTRCPTR_MAX;
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync }
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync#endif
3e88c818fff5c64b5eff43d5daf4596bd87230c5vboxsync#ifdef SELM_TRACK_SHADOW_LDT_CHANGES
3e88c818fff5c64b5eff43d5daf4596bd87230c5vboxsync if (pVM->selm.s.pvLdtRC != RTRCPTR_MAX)
3e88c818fff5c64b5eff43d5daf4596bd87230c5vboxsync {
3e88c818fff5c64b5eff43d5daf4596bd87230c5vboxsync rc = PGMHandlerVirtualDeregister(pVM, pVM->selm.s.pvLdtRC);
3e88c818fff5c64b5eff43d5daf4596bd87230c5vboxsync AssertRC(rc);
3e88c818fff5c64b5eff43d5daf4596bd87230c5vboxsync pVM->selm.s.pvLdtRC = RTRCPTR_MAX;
3e88c818fff5c64b5eff43d5daf4596bd87230c5vboxsync }
3e88c818fff5c64b5eff43d5daf4596bd87230c5vboxsync#endif
3e88c818fff5c64b5eff43d5daf4596bd87230c5vboxsync
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync VM_FF_CLEAR(pVM, VM_FF_SELM_SYNC_TSS);
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync VM_FF_CLEAR(pVM, VM_FF_SELM_SYNC_GDT);
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync VM_FF_CLEAR(pVM, VM_FF_SELM_SYNC_LDT);
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync pVM->selm.s.fDisableMonitoring = true;
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync}
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync/**
34b5a73592441cbfacb85c2f2261648c1d25ad31vboxsync * Execute state save operation.
34b5a73592441cbfacb85c2f2261648c1d25ad31vboxsync *
34b5a73592441cbfacb85c2f2261648c1d25ad31vboxsync * @returns VBox status code.
34b5a73592441cbfacb85c2f2261648c1d25ad31vboxsync * @param pVM VM Handle.
34b5a73592441cbfacb85c2f2261648c1d25ad31vboxsync * @param pSSM SSM operation handle.
34b5a73592441cbfacb85c2f2261648c1d25ad31vboxsync */
34b5a73592441cbfacb85c2f2261648c1d25ad31vboxsyncstatic DECLCALLBACK(int) selmR3Save(PVM pVM, PSSMHANDLE pSSM)
54597c35ab7fe470ca07560969c437982854e5devboxsync{
54597c35ab7fe470ca07560969c437982854e5devboxsync LogFlow(("selmR3Save:\n"));
54597c35ab7fe470ca07560969c437982854e5devboxsync
34b5a73592441cbfacb85c2f2261648c1d25ad31vboxsync /*
34b5a73592441cbfacb85c2f2261648c1d25ad31vboxsync * Save the basic bits - fortunately all the other things can be resynced on load.
34b5a73592441cbfacb85c2f2261648c1d25ad31vboxsync */
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync PSELM pSelm = &pVM->selm.s;
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync SSMR3PutBool(pSSM, pSelm->fDisableMonitoring);
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync SSMR3PutBool(pSSM, pSelm->fSyncTSSRing0Stack);
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync SSMR3PutSel(pSSM, pSelm->aHyperSel[SELM_HYPER_SEL_CS]);
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync SSMR3PutSel(pSSM, pSelm->aHyperSel[SELM_HYPER_SEL_DS]);
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync SSMR3PutSel(pSSM, pSelm->aHyperSel[SELM_HYPER_SEL_CS64]);
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync SSMR3PutSel(pSSM, pSelm->aHyperSel[SELM_HYPER_SEL_CS64]); /* reserved for DS64. */
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync SSMR3PutSel(pSSM, pSelm->aHyperSel[SELM_HYPER_SEL_TSS]);
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync return SSMR3PutSel(pSSM, pSelm->aHyperSel[SELM_HYPER_SEL_TSS_TRAP08]);
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync}
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync/**
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync * Execute state load operation.
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync *
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync * @returns VBox status code.
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync * @param pVM VM Handle.
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync * @param pSSM SSM operation handle.
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync * @param u32Version Data layout version.
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync */
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsyncstatic DECLCALLBACK(int) selmR3Load(PVM pVM, PSSMHANDLE pSSM, uint32_t u32Version)
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync{
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync LogFlow(("selmR3Load:\n"));
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync /*
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync * Validate version.
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync */
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync if (u32Version != SELM_SAVED_STATE_VERSION)
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync {
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync AssertMsgFailed(("selmR3Load: Invalid version u32Version=%d!\n", u32Version));
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync return VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION;
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync }
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync /*
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync * Do a reset.
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync */
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync SELMR3Reset(pVM);
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync /* Get the monitoring flag. */
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync SSMR3GetBool(pSSM, &pVM->selm.s.fDisableMonitoring);
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync /* Get the TSS state flag. */
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync SSMR3GetBool(pSSM, &pVM->selm.s.fSyncTSSRing0Stack);
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync /*
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync * Get the selectors.
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync */
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync RTSEL SelCS;
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync SSMR3GetSel(pSSM, &SelCS);
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync RTSEL SelDS;
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync SSMR3GetSel(pSSM, &SelDS);
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync RTSEL SelCS64;
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync SSMR3GetSel(pSSM, &SelCS64);
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync RTSEL SelDS64;
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync SSMR3GetSel(pSSM, &SelDS64);
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync RTSEL SelTSS;
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync SSMR3GetSel(pSSM, &SelTSS);
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync RTSEL SelTSSTrap08;
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync SSMR3GetSel(pSSM, &SelTSSTrap08);
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync /* Copy the selectors; they will be checked during relocation. */
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync PSELM pSelm = &pVM->selm.s;
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync pSelm->aHyperSel[SELM_HYPER_SEL_CS] = SelCS;
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync pSelm->aHyperSel[SELM_HYPER_SEL_DS] = SelDS;
f687f34bd232be13744edbc0cc5155fa5d4540edvboxsync pSelm->aHyperSel[SELM_HYPER_SEL_CS64] = SelCS64;
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync pSelm->aHyperSel[SELM_HYPER_SEL_TSS] = SelTSS;
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync pSelm->aHyperSel[SELM_HYPER_SEL_TSS_TRAP08] = SelTSSTrap08;
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync return VINF_SUCCESS;
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync}
6114ff5789db75ff28460a056ed422ee03d8f0ebvboxsync
6114ff5789db75ff28460a056ed422ee03d8f0ebvboxsync
3e88c818fff5c64b5eff43d5daf4596bd87230c5vboxsync/**
3e88c818fff5c64b5eff43d5daf4596bd87230c5vboxsync * Sync the GDT, LDT and TSS after loading the state.
3e88c818fff5c64b5eff43d5daf4596bd87230c5vboxsync *
3e88c818fff5c64b5eff43d5daf4596bd87230c5vboxsync * Just to play save, we set the FFs to force syncing before
3e88c818fff5c64b5eff43d5daf4596bd87230c5vboxsync * executing GC code.
f687f34bd232be13744edbc0cc5155fa5d4540edvboxsync *
34b5a73592441cbfacb85c2f2261648c1d25ad31vboxsync * @returns VBox status code.
f687f34bd232be13744edbc0cc5155fa5d4540edvboxsync * @param pVM VM Handle.
34b5a73592441cbfacb85c2f2261648c1d25ad31vboxsync * @param pSSM SSM operation handle.
f687f34bd232be13744edbc0cc5155fa5d4540edvboxsync */
34b5a73592441cbfacb85c2f2261648c1d25ad31vboxsyncstatic DECLCALLBACK(int) selmR3LoadDone(PVM pVM, PSSMHANDLE pSSM)
34b5a73592441cbfacb85c2f2261648c1d25ad31vboxsync{
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync LogFlow(("selmR3LoadDone:\n"));
f687f34bd232be13744edbc0cc5155fa5d4540edvboxsync
f687f34bd232be13744edbc0cc5155fa5d4540edvboxsync /*
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync * Don't do anything if it's a load failure.
a98c2afd05b2949737f73ec954d227712f3b0146vboxsync */
a98c2afd05b2949737f73ec954d227712f3b0146vboxsync int rc = SSMR3HandleGetStatus(pSSM);
a98c2afd05b2949737f73ec954d227712f3b0146vboxsync if (RT_FAILURE(rc))
a98c2afd05b2949737f73ec954d227712f3b0146vboxsync return VINF_SUCCESS;
a98c2afd05b2949737f73ec954d227712f3b0146vboxsync
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync /*
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync * Do the syncing if we're in protected mode.
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync */
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync if (PGMGetGuestMode(pVM) != PGMMODE_REAL)
cab115cfa31c584def7069312a1e23c3fc88533bvboxsync {
cab115cfa31c584def7069312a1e23c3fc88533bvboxsync VM_FF_SET(pVM, VM_FF_SELM_SYNC_GDT);
cab115cfa31c584def7069312a1e23c3fc88533bvboxsync VM_FF_SET(pVM, VM_FF_SELM_SYNC_LDT);
cab115cfa31c584def7069312a1e23c3fc88533bvboxsync VM_FF_SET(pVM, VM_FF_SELM_SYNC_TSS);
cab115cfa31c584def7069312a1e23c3fc88533bvboxsync SELMR3UpdateFromCPUM(pVM);
cab115cfa31c584def7069312a1e23c3fc88533bvboxsync }
cab115cfa31c584def7069312a1e23c3fc88533bvboxsync
cab115cfa31c584def7069312a1e23c3fc88533bvboxsync /*
cab115cfa31c584def7069312a1e23c3fc88533bvboxsync * Flag everything for resync on next raw mode entry.
cab115cfa31c584def7069312a1e23c3fc88533bvboxsync */
cab115cfa31c584def7069312a1e23c3fc88533bvboxsync VM_FF_SET(pVM, VM_FF_SELM_SYNC_GDT);
cab115cfa31c584def7069312a1e23c3fc88533bvboxsync VM_FF_SET(pVM, VM_FF_SELM_SYNC_LDT);
cab115cfa31c584def7069312a1e23c3fc88533bvboxsync VM_FF_SET(pVM, VM_FF_SELM_SYNC_TSS);
cab115cfa31c584def7069312a1e23c3fc88533bvboxsync
cab115cfa31c584def7069312a1e23c3fc88533bvboxsync return VINF_SUCCESS;
cab115cfa31c584def7069312a1e23c3fc88533bvboxsync}
cab115cfa31c584def7069312a1e23c3fc88533bvboxsync
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync/**
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync * Updates the Guest GDT & LDT virtualization based on current CPU state.
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync *
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync * @returns VBox status code.
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync * @param pVM The VM to operate on.
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync */
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsyncVMMR3DECL(int) SELMR3UpdateFromCPUM(PVM pVM)
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync{
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync int rc = VINF_SUCCESS;
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync if (pVM->selm.s.fDisableMonitoring)
6114ff5789db75ff28460a056ed422ee03d8f0ebvboxsync {
6114ff5789db75ff28460a056ed422ee03d8f0ebvboxsync VM_FF_CLEAR(pVM, VM_FF_SELM_SYNC_GDT);
cab115cfa31c584def7069312a1e23c3fc88533bvboxsync VM_FF_CLEAR(pVM, VM_FF_SELM_SYNC_LDT);
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync VM_FF_CLEAR(pVM, VM_FF_SELM_SYNC_TSS);
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync return VINF_SUCCESS;
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync }
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync STAM_PROFILE_START(&pVM->selm.s.StatUpdateFromCPUM, a);
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync /*
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync * GDT sync
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync */
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync if (VM_FF_ISSET(pVM, VM_FF_SELM_SYNC_GDT))
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync {
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync /*
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync * Always assume the best
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync */
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync VM_FF_CLEAR(pVM, VM_FF_SELM_SYNC_GDT);
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync /* If the GDT was changed, then make sure the LDT is checked too */
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync /** @todo only do this if the actual ldtr selector was changed; this is a bit excessive */
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync VM_FF_SET(pVM, VM_FF_SELM_SYNC_LDT);
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync /* Same goes for the TSS selector */
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync VM_FF_SET(pVM, VM_FF_SELM_SYNC_TSS);
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync /*
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync * Get the GDTR and check if there is anything to do (there usually is).
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync */
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync VBOXGDTR GDTR;
3e88c818fff5c64b5eff43d5daf4596bd87230c5vboxsync CPUMGetGuestGDTR(pVM, &GDTR);
3e88c818fff5c64b5eff43d5daf4596bd87230c5vboxsync if (GDTR.cbGdt < sizeof(X86DESC))
3e88c818fff5c64b5eff43d5daf4596bd87230c5vboxsync {
3e88c818fff5c64b5eff43d5daf4596bd87230c5vboxsync Log(("No GDT entries...\n"));
3e88c818fff5c64b5eff43d5daf4596bd87230c5vboxsync STAM_PROFILE_STOP(&pVM->selm.s.StatUpdateFromCPUM, a);
3e88c818fff5c64b5eff43d5daf4596bd87230c5vboxsync return VINF_SUCCESS;
3e88c818fff5c64b5eff43d5daf4596bd87230c5vboxsync }
a98c2afd05b2949737f73ec954d227712f3b0146vboxsync
a98c2afd05b2949737f73ec954d227712f3b0146vboxsync /*
a98c2afd05b2949737f73ec954d227712f3b0146vboxsync * Read the Guest GDT.
a98c2afd05b2949737f73ec954d227712f3b0146vboxsync * ASSUMES that the entire GDT is in memory.
a98c2afd05b2949737f73ec954d227712f3b0146vboxsync */
a98c2afd05b2949737f73ec954d227712f3b0146vboxsync RTUINT cbEffLimit = GDTR.cbGdt;
a98c2afd05b2949737f73ec954d227712f3b0146vboxsync PX86DESC pGDTE = &pVM->selm.s.paGdtR3[1];
a98c2afd05b2949737f73ec954d227712f3b0146vboxsync rc = PGMPhysSimpleReadGCPtr(pVM, pGDTE, GDTR.pGdt + sizeof(X86DESC), cbEffLimit + 1 - sizeof(X86DESC));
a98c2afd05b2949737f73ec954d227712f3b0146vboxsync if (RT_FAILURE(rc))
a98c2afd05b2949737f73ec954d227712f3b0146vboxsync {
a98c2afd05b2949737f73ec954d227712f3b0146vboxsync /*
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync * Read it page by page.
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync *
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync * Keep track of the last valid page and delay memsets and
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync * adjust cbEffLimit to reflect the effective size. The latter
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync * is something we do in the belief that the guest will probably
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync * never actually commit the last page, thus allowing us to keep
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync * our selectors in the high end of the GDT.
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync */
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync RTUINT cbLeft = cbEffLimit + 1 - sizeof(X86DESC);
3e88c818fff5c64b5eff43d5daf4596bd87230c5vboxsync RTGCPTR GCPtrSrc = (RTGCPTR)GDTR.pGdt + sizeof(X86DESC);
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync uint8_t *pu8Dst = (uint8_t *)&pVM->selm.s.paGdtR3[1];
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync uint8_t *pu8DstInvalid = pu8Dst;
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync while (cbLeft)
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync {
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync RTUINT cb = PAGE_SIZE - (GCPtrSrc & PAGE_OFFSET_MASK);
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync cb = RT_MIN(cb, cbLeft);
3e88c818fff5c64b5eff43d5daf4596bd87230c5vboxsync rc = PGMPhysSimpleReadGCPtr(pVM, pu8Dst, GCPtrSrc, cb);
3e88c818fff5c64b5eff43d5daf4596bd87230c5vboxsync if (RT_SUCCESS(rc))
3e88c818fff5c64b5eff43d5daf4596bd87230c5vboxsync {
a98c2afd05b2949737f73ec954d227712f3b0146vboxsync if (pu8DstInvalid != pu8Dst)
a98c2afd05b2949737f73ec954d227712f3b0146vboxsync memset(pu8DstInvalid, 0, pu8Dst - pu8DstInvalid);
a98c2afd05b2949737f73ec954d227712f3b0146vboxsync GCPtrSrc += cb;
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync pu8Dst += cb;
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync pu8DstInvalid = pu8Dst;
3e88c818fff5c64b5eff43d5daf4596bd87230c5vboxsync }
3e88c818fff5c64b5eff43d5daf4596bd87230c5vboxsync else if ( rc == VERR_PAGE_NOT_PRESENT
3e88c818fff5c64b5eff43d5daf4596bd87230c5vboxsync || rc == VERR_PAGE_TABLE_NOT_PRESENT)
3e88c818fff5c64b5eff43d5daf4596bd87230c5vboxsync {
3e88c818fff5c64b5eff43d5daf4596bd87230c5vboxsync GCPtrSrc += cb;
3e88c818fff5c64b5eff43d5daf4596bd87230c5vboxsync pu8Dst += cb;
3e88c818fff5c64b5eff43d5daf4596bd87230c5vboxsync }
3e88c818fff5c64b5eff43d5daf4596bd87230c5vboxsync else
a98c2afd05b2949737f73ec954d227712f3b0146vboxsync {
a98c2afd05b2949737f73ec954d227712f3b0146vboxsync AssertReleaseMsgFailed(("Couldn't read GDT at %016RX64, rc=%Rrc!\n", GDTR.pGdt, rc));
a98c2afd05b2949737f73ec954d227712f3b0146vboxsync STAM_PROFILE_STOP(&pVM->selm.s.StatUpdateFromCPUM, a);
a98c2afd05b2949737f73ec954d227712f3b0146vboxsync return VERR_NOT_IMPLEMENTED;
3e88c818fff5c64b5eff43d5daf4596bd87230c5vboxsync }
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync cbLeft -= cb;
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync }
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync /* any invalid pages at the end? */
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync if (pu8DstInvalid != pu8Dst)
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync {
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync cbEffLimit = pu8DstInvalid - (uint8_t *)pVM->selm.s.paGdtR3 - 1;
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync /* If any GDTEs was invalidated, zero them. */
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync if (cbEffLimit < pVM->selm.s.cbEffGuestGdtLimit)
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync memset(pu8DstInvalid + cbEffLimit + 1, 0, pVM->selm.s.cbEffGuestGdtLimit - cbEffLimit);
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync }
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync /* keep track of the effective limit. */
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync if (cbEffLimit != pVM->selm.s.cbEffGuestGdtLimit)
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync {
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync Log(("SELMR3UpdateFromCPUM: cbEffGuestGdtLimit=%#x -> %#x (actual %#x)\n",
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync pVM->selm.s.cbEffGuestGdtLimit, cbEffLimit, GDTR.cbGdt));
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync pVM->selm.s.cbEffGuestGdtLimit = cbEffLimit;
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync }
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync }
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync /*
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync * Check if the Guest GDT intrudes on our GDT entries.
34b5a73592441cbfacb85c2f2261648c1d25ad31vboxsync */
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync /** @todo we should try to minimize relocations by making sure our current selectors can be reused. */
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync RTSEL aHyperSel[SELM_HYPER_SEL_MAX];
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync if (cbEffLimit >= SELM_HYPER_DEFAULT_BASE)
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync {
cab115cfa31c584def7069312a1e23c3fc88533bvboxsync PX86DESC pGDTEStart = pVM->selm.s.paGdtR3;
cab115cfa31c584def7069312a1e23c3fc88533bvboxsync PX86DESC pGDTE = (PX86DESC)((char *)pGDTEStart + GDTR.cbGdt + 1 - sizeof(X86DESC));
cab115cfa31c584def7069312a1e23c3fc88533bvboxsync int iGDT = 0;
cab115cfa31c584def7069312a1e23c3fc88533bvboxsync
cab115cfa31c584def7069312a1e23c3fc88533bvboxsync Log(("Internal SELM GDT conflict: use non-present entries\n"));
cab115cfa31c584def7069312a1e23c3fc88533bvboxsync STAM_COUNTER_INC(&pVM->selm.s.StatScanForHyperSels);
cab115cfa31c584def7069312a1e23c3fc88533bvboxsync while (pGDTE > pGDTEStart)
cab115cfa31c584def7069312a1e23c3fc88533bvboxsync {
cab115cfa31c584def7069312a1e23c3fc88533bvboxsync /* We can reuse non-present entries */
cab115cfa31c584def7069312a1e23c3fc88533bvboxsync if (!pGDTE->Gen.u1Present)
cab115cfa31c584def7069312a1e23c3fc88533bvboxsync {
cab115cfa31c584def7069312a1e23c3fc88533bvboxsync aHyperSel[iGDT] = ((uintptr_t)pGDTE - (uintptr_t)pVM->selm.s.paGdtR3) / sizeof(X86DESC);
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync aHyperSel[iGDT] = aHyperSel[iGDT] << X86_SEL_SHIFT;
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync Log(("SELM: Found unused GDT %04X\n", aHyperSel[iGDT]));
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync iGDT++;
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync if (iGDT >= SELM_HYPER_SEL_MAX)
3e88c818fff5c64b5eff43d5daf4596bd87230c5vboxsync break;
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync }
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync
3e88c818fff5c64b5eff43d5daf4596bd87230c5vboxsync pGDTE--;
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync }
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync if (iGDT != SELM_HYPER_SEL_MAX)
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync {
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync AssertReleaseMsgFailed(("Internal SELM GDT conflict.\n"));
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync STAM_PROFILE_STOP(&pVM->selm.s.StatUpdateFromCPUM, a);
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync return VERR_NOT_IMPLEMENTED;
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync }
b26de2ddb274b0e52de6652ad8b45259be2b9913vboxsync }
3e88c818fff5c64b5eff43d5daf4596bd87230c5vboxsync else
3e88c818fff5c64b5eff43d5daf4596bd87230c5vboxsync {
3e88c818fff5c64b5eff43d5daf4596bd87230c5vboxsync aHyperSel[SELM_HYPER_SEL_CS] = SELM_HYPER_DEFAULT_SEL_CS;
3e88c818fff5c64b5eff43d5daf4596bd87230c5vboxsync aHyperSel[SELM_HYPER_SEL_DS] = SELM_HYPER_DEFAULT_SEL_DS;
3e88c818fff5c64b5eff43d5daf4596bd87230c5vboxsync aHyperSel[SELM_HYPER_SEL_CS64] = SELM_HYPER_DEFAULT_SEL_CS64;
3e88c818fff5c64b5eff43d5daf4596bd87230c5vboxsync aHyperSel[SELM_HYPER_SEL_TSS] = SELM_HYPER_DEFAULT_SEL_TSS;
3e88c818fff5c64b5eff43d5daf4596bd87230c5vboxsync aHyperSel[SELM_HYPER_SEL_TSS_TRAP08] = SELM_HYPER_DEFAULT_SEL_TSS_TRAP08;
3e88c818fff5c64b5eff43d5daf4596bd87230c5vboxsync }
3e88c818fff5c64b5eff43d5daf4596bd87230c5vboxsync
3e88c818fff5c64b5eff43d5daf4596bd87230c5vboxsync /*
3e88c818fff5c64b5eff43d5daf4596bd87230c5vboxsync * Work thru the copied GDT entries adjusting them for correct virtualization.
3e88c818fff5c64b5eff43d5daf4596bd87230c5vboxsync */
3e88c818fff5c64b5eff43d5daf4596bd87230c5vboxsync PX86DESC pGDTEEnd = (PX86DESC)((char *)pGDTE + cbEffLimit + 1 - sizeof(X86DESC));
3e88c818fff5c64b5eff43d5daf4596bd87230c5vboxsync while (pGDTE < pGDTEEnd)
3e88c818fff5c64b5eff43d5daf4596bd87230c5vboxsync {
3e88c818fff5c64b5eff43d5daf4596bd87230c5vboxsync if (pGDTE->Gen.u1Present)
3e88c818fff5c64b5eff43d5daf4596bd87230c5vboxsync {
3e88c818fff5c64b5eff43d5daf4596bd87230c5vboxsync /*
3e88c818fff5c64b5eff43d5daf4596bd87230c5vboxsync * Code and data selectors are generally 1:1, with the
3e88c818fff5c64b5eff43d5daf4596bd87230c5vboxsync * 'little' adjustment we do for DPL 0 selectors.
3e88c818fff5c64b5eff43d5daf4596bd87230c5vboxsync */
3e88c818fff5c64b5eff43d5daf4596bd87230c5vboxsync if (pGDTE->Gen.u1DescType)
3e88c818fff5c64b5eff43d5daf4596bd87230c5vboxsync {
3e88c818fff5c64b5eff43d5daf4596bd87230c5vboxsync /*
3e88c818fff5c64b5eff43d5daf4596bd87230c5vboxsync * Hack for A-bit against Trap E on read-only GDT.
3e88c818fff5c64b5eff43d5daf4596bd87230c5vboxsync */
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync /** @todo Fix this by loading ds and cs before turning off WP. */
a98c2afd05b2949737f73ec954d227712f3b0146vboxsync pGDTE->Gen.u4Type |= X86_SEL_TYPE_ACCESSED;
a98c2afd05b2949737f73ec954d227712f3b0146vboxsync
a98c2afd05b2949737f73ec954d227712f3b0146vboxsync /*
a98c2afd05b2949737f73ec954d227712f3b0146vboxsync * All DPL 0 code and data segments are squeezed into DPL 1.
a98c2afd05b2949737f73ec954d227712f3b0146vboxsync *
a98c2afd05b2949737f73ec954d227712f3b0146vboxsync * We're skipping conforming segments here because those
a98c2afd05b2949737f73ec954d227712f3b0146vboxsync * cannot give us any trouble.
bf27591c0d413dd4d7ba41cf89f117171968d462vboxsync */
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync if ( pGDTE->Gen.u2Dpl == 0
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync && (pGDTE->Gen.u4Type & (X86_SEL_TYPE_CODE | X86_SEL_TYPE_CONF))
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync != (X86_SEL_TYPE_CODE | X86_SEL_TYPE_CONF) )
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync pGDTE->Gen.u2Dpl = 1;
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync }
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync else
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync {
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync /*
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync * System type selectors are marked not present.
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync * Recompiler or special handling is required for these.
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync */
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync /** @todo what about interrupt gates and rawr0? */
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync pGDTE->Gen.u1Present = 0;
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync }
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync }
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync /* Next GDT entry. */
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync pGDTE++;
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync }
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync /*
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync * Check if our hypervisor selectors were changed.
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync */
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync if ( aHyperSel[SELM_HYPER_SEL_CS] != pVM->selm.s.aHyperSel[SELM_HYPER_SEL_CS]
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync || aHyperSel[SELM_HYPER_SEL_DS] != pVM->selm.s.aHyperSel[SELM_HYPER_SEL_DS]
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync || aHyperSel[SELM_HYPER_SEL_CS64] != pVM->selm.s.aHyperSel[SELM_HYPER_SEL_CS64]
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync || aHyperSel[SELM_HYPER_SEL_TSS] != pVM->selm.s.aHyperSel[SELM_HYPER_SEL_TSS]
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync || aHyperSel[SELM_HYPER_SEL_TSS_TRAP08] != pVM->selm.s.aHyperSel[SELM_HYPER_SEL_TSS_TRAP08])
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync {
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync /* Reinitialize our hypervisor GDTs */
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync pVM->selm.s.aHyperSel[SELM_HYPER_SEL_CS] = aHyperSel[SELM_HYPER_SEL_CS];
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync pVM->selm.s.aHyperSel[SELM_HYPER_SEL_DS] = aHyperSel[SELM_HYPER_SEL_DS];
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync pVM->selm.s.aHyperSel[SELM_HYPER_SEL_CS64] = aHyperSel[SELM_HYPER_SEL_CS64];
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync pVM->selm.s.aHyperSel[SELM_HYPER_SEL_TSS] = aHyperSel[SELM_HYPER_SEL_TSS];
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync pVM->selm.s.aHyperSel[SELM_HYPER_SEL_TSS_TRAP08] = aHyperSel[SELM_HYPER_SEL_TSS_TRAP08];
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync STAM_COUNTER_INC(&pVM->selm.s.StatHyperSelsChanged);
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync /*
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync * Do the relocation callbacks to let everyone update their hyper selector dependencies.
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync * (SELMR3Relocate will call selmR3SetupHyperGDTSelectors() for us.)
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync */
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync VMR3Relocate(pVM, 0);
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync }
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync else if (cbEffLimit >= SELM_HYPER_DEFAULT_BASE)
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync /* We overwrote all entries above, so we have to save them again. */
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync selmR3SetupHyperGDTSelectors(pVM);
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync /*
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync * Adjust the cached GDT limit.
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync * Any GDT entries which have been removed must be cleared.
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync */
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync if (pVM->selm.s.GuestGdtr.cbGdt != GDTR.cbGdt)
fb7b8c126ea3bc0adf9dd2b2b6a43870ee41853avboxsync {
if (pVM->selm.s.GuestGdtr.cbGdt > GDTR.cbGdt)
memset(pGDTE, 0, pVM->selm.s.GuestGdtr.cbGdt - GDTR.cbGdt);
#ifndef SELM_TRACK_GUEST_GDT_CHANGES
pVM->selm.s.GuestGdtr.cbGdt = GDTR.cbGdt;
#endif
}
#ifdef SELM_TRACK_GUEST_GDT_CHANGES
/*
* Check if Guest's GDTR is changed.
*/
if ( GDTR.pGdt != pVM->selm.s.GuestGdtr.pGdt
|| GDTR.cbGdt != pVM->selm.s.GuestGdtr.cbGdt)
{
Log(("SELMR3UpdateFromCPUM: Guest's GDT is changed to pGdt=%016RX64 cbGdt=%08X\n", GDTR.pGdt, GDTR.cbGdt));
/*
* [Re]Register write virtual handler for guest's GDT.
*/
if (pVM->selm.s.GuestGdtr.pGdt != RTRCPTR_MAX && pVM->selm.s.fGDTRangeRegistered)
{
rc = PGMHandlerVirtualDeregister(pVM, pVM->selm.s.GuestGdtr.pGdt);
AssertRC(rc);
}
rc = PGMR3HandlerVirtualRegister(pVM, PGMVIRTHANDLERTYPE_WRITE, GDTR.pGdt, GDTR.pGdt + GDTR.cbGdt /* already inclusive */,
0, selmR3GuestGDTWriteHandler, "selmRCGuestGDTWriteHandler", 0, "Guest GDT write access handler");
if (RT_FAILURE(rc))
return rc;
/* Update saved Guest GDTR. */
pVM->selm.s.GuestGdtr = GDTR;
pVM->selm.s.fGDTRangeRegistered = true;
}
#endif
}
/*
* TSS sync
*/
if (VM_FF_ISSET(pVM, VM_FF_SELM_SYNC_TSS))
{
SELMR3SyncTSS(pVM);
}
/*
* LDT sync
*/
if (VM_FF_ISSET(pVM, VM_FF_SELM_SYNC_LDT))
{
/*
* Always assume the best
*/
VM_FF_CLEAR(pVM, VM_FF_SELM_SYNC_LDT);
/*
* 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.
*/
RTSEL SelLdt = CPUMGetGuestLDTR(pVM);
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
if (pVM->selm.s.GCPtrGuestLdt != RTRCPTR_MAX)
{
rc = PGMHandlerVirtualDeregister(pVM, pVM->selm.s.GCPtrGuestLdt);
AssertRC(rc);
pVM->selm.s.GCPtrGuestLdt = RTRCPTR_MAX;
}
#endif
STAM_PROFILE_STOP(&pVM->selm.s.StatUpdateFromCPUM, a);
return VINF_SUCCESS;
}
/*
* Get the LDT selector.
*/
PX86DESC pDesc = &pVM->selm.s.paGdtR3[SelLdt >> X86_SEL_SHIFT];
RTGCPTR GCPtrLdt = X86DESC_BASE(*pDesc);
unsigned cbLdt = X86DESC_LIMIT(*pDesc);
if (pDesc->Gen.u1Granularity)
cbLdt = (cbLdt << PAGE_SHIFT) | PAGE_OFFSET_MASK;
/*
* Validate it.
*/
if ( !cbLdt
|| SelLdt >= pVM->selm.s.GuestGdtr.cbGdt
|| pDesc->Gen.u1DescType
|| pDesc->Gen.u4Type != X86_SEL_TYPE_SYS_LDT)
{
AssertMsg(!cbLdt, ("Invalid LDT %04x!\n", SelLdt));
/* 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
if (pVM->selm.s.GCPtrGuestLdt != RTRCPTR_MAX)
{
rc = PGMHandlerVirtualDeregister(pVM, pVM->selm.s.GCPtrGuestLdt);
AssertRC(rc);
pVM->selm.s.GCPtrGuestLdt = RTRCPTR_MAX;
}
#endif
STAM_PROFILE_STOP(&pVM->selm.s.StatUpdateFromCPUM, a);
return VINF_SUCCESS;
}
/** @todo check what intel does about odd limits. */
AssertMsg(RT_ALIGN(cbLdt + 1, sizeof(X86DESC)) == 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)
*/
if (MMHyperIsInsideArea(pVM, GCPtrLdt))
GCPtrLdt = pVM->selm.s.GCPtrGuestLdt; /* use the old one */
#ifdef SELM_TRACK_GUEST_LDT_CHANGES
/** @todo Handle only present LDT segments. */
// if (pDesc->Gen.u1Present)
{
/*
* Check if Guest's LDT address/limit is changed.
*/
if ( GCPtrLdt != pVM->selm.s.GCPtrGuestLdt
|| cbLdt != pVM->selm.s.cbLdtLimit)
{
Log(("SELMR3UpdateFromCPUM: Guest LDT changed to from %RGv:%04x to %RGv:%04x. (GDTR=%016RX64:%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.
*/
if (pVM->selm.s.GCPtrGuestLdt != RTRCPTR_MAX)
{
rc = PGMHandlerVirtualDeregister(pVM, pVM->selm.s.GCPtrGuestLdt);
AssertRC(rc);
}
#ifdef DEBUG
if (pDesc->Gen.u1Present)
Log(("LDT selector marked not present!!\n"));
#endif
rc = PGMR3HandlerVirtualRegister(pVM, PGMVIRTHANDLERTYPE_WRITE, GCPtrLdt, GCPtrLdt + cbLdt /* already inclusive */,
0, selmR3GuestLDTWriteHandler, "selmRCGuestLDTWriteHandler", 0, "Guest LDT write access handler");
if (rc == VERR_PGM_HANDLER_VIRTUAL_CONFLICT)
{
/** @todo investigate the various cases where conflicts happen and try avoid them by enh. the instruction emulation. */
pVM->selm.s.GCPtrGuestLdt = RTRCPTR_MAX;
Log(("WARNING: Guest LDT (%RGv:%04x) conflicted with existing access range!! Assumes LDT is begin updated. (GDTR=%016RX64:%04x)\n",
GCPtrLdt, cbLdt, pVM->selm.s.GuestGdtr.pGdt, pVM->selm.s.GuestGdtr.cbGdt));
}
else if (RT_SUCCESS(rc))
pVM->selm.s.GCPtrGuestLdt = GCPtrLdt;
else
{
CPUMSetHyperLDTR(pVM, 0);
STAM_PROFILE_STOP(&pVM->selm.s.StatUpdateFromCPUM, a);
return rc;
}
pVM->selm.s.cbLdtLimit = cbLdt;
}
}
#else
pVM->selm.s.cbLdtLimit = cbLdt;
#endif
/*
* Calc Shadow LDT base.
*/
unsigned off;
pVM->selm.s.offLdtHyper = off = (GCPtrLdt & PAGE_OFFSET_MASK);
RTGCPTR GCPtrShadowLDT = (RTGCPTR)((RTGCUINTPTR)pVM->selm.s.pvLdtRC + off);
PX86DESC pShadowLDT = (PX86DESC)((uintptr_t)pVM->selm.s.pvLdtR3 + off);
/*
* Enable the LDT selector in the shadow GDT.
*/
pDesc->Gen.u1Present = 1;
pDesc->Gen.u16BaseLow = RT_LOWORD(GCPtrShadowLDT);
pDesc->Gen.u8BaseHigh1 = RT_BYTE3(GCPtrShadowLDT);
pDesc->Gen.u8BaseHigh2 = RT_BYTE4(GCPtrShadowLDT);
pDesc->Gen.u1Available = 0;
pDesc->Gen.u1Long = 0;
if (cbLdt > 0xffff)
{
cbLdt = 0xffff;
pDesc->Gen.u4LimitHigh = 0;
pDesc->Gen.u16LimitLow = pDesc->Gen.u1Granularity ? 0xf : 0xffff;
}
/*
* Set Hyper LDTR and notify TRPM.
*/
CPUMSetHyperLDTR(pVM, SelLdt);
/*
* Loop synchronising the LDT page by page.
*/
/** @todo investigate how intel handle various operations on half present cross page entries. */
off = GCPtrLdt & (sizeof(X86DESC) - 1);
AssertMsg(!off, ("LDT is not aligned on entry size! GCPtrLdt=%08x\n", GCPtrLdt));
/* Note: Do not skip the first selector; unlike the GDT, a zero LDT selector is perfectly valid. */
unsigned cbLeft = cbLdt + 1;
PX86DESC pLDTE = pShadowLDT;
while (cbLeft)
{
/*
* Read a chunk.
*/
unsigned cbChunk = PAGE_SIZE - ((RTGCUINTPTR)GCPtrLdt & PAGE_OFFSET_MASK);
if (cbChunk > cbLeft)
cbChunk = cbLeft;
rc = PGMPhysSimpleReadGCPtr(pVM, pShadowLDT, GCPtrLdt, cbChunk);
if (RT_SUCCESS(rc))
{
/*
* Mark page
*/
rc = PGMMapSetPage(pVM, GCPtrShadowLDT & PAGE_BASE_GC_MASK, PAGE_SIZE, X86_PTE_P | X86_PTE_A | X86_PTE_D);
AssertRC(rc);
/*
* 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.
*/
PX86DESC pLDTEEnd = (PX86DESC)((uintptr_t)pShadowLDT + cbChunk) - 1;
if (pLDTE + 1 < pShadowLDT)
pLDTE = (PX86DESC)((uintptr_t)pShadowLDT + off);
while (pLDTE <= pLDTEEnd)
{
if (pLDTE->Gen.u1Present)
{
/*
* Code and data selectors are generally 1:1, with the
* 'little' adjustment we do for DPL 0 selectors.
*/
if (pLDTE->Gen.u1DescType)
{
/*
* Hack for A-bit against Trap E on read-only GDT.
*/
/** @todo Fix this by loading ds and cs before turning off WP. */
if (!(pLDTE->Gen.u4Type & X86_SEL_TYPE_ACCESSED))
pLDTE->Gen.u4Type |= X86_SEL_TYPE_ACCESSED;
/*
* 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.
*/
if ( pLDTE->Gen.u2Dpl == 0
&& (pLDTE->Gen.u4Type & (X86_SEL_TYPE_CODE | X86_SEL_TYPE_CONF))
!= (X86_SEL_TYPE_CODE | X86_SEL_TYPE_CONF) )
pLDTE->Gen.u2Dpl = 1;
}
else
{
/*
* System type selectors are marked not present.
* Recompiler or special handling is required for these.
*/
/** @todo what about interrupt gates and rawr0? */
pLDTE->Gen.u1Present = 0;
}
}
/* Next LDT entry. */
pLDTE++;
}
}
else
{
AssertMsg(rc == VERR_PAGE_NOT_PRESENT || rc == VERR_PAGE_TABLE_NOT_PRESENT, ("rc=%Rrc\n", rc));
rc = PGMMapSetPage(pVM, GCPtrShadowLDT & PAGE_BASE_GC_MASK, PAGE_SIZE, 0);
AssertRC(rc);
}
/*
* Advance to the next page.
*/
cbLeft -= cbChunk;
GCPtrShadowLDT += cbChunk;
pShadowLDT = (PX86DESC)((char *)pShadowLDT + cbChunk);
GCPtrLdt += cbChunk;
}
}
STAM_PROFILE_STOP(&pVM->selm.s.StatUpdateFromCPUM, a);
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 pvBuf What the guest is reading/writing.
* @param cbBuf How much it's reading/writing.
* @param enmAccessType The access type.
* @param pvUser User argument.
*/
static DECLCALLBACK(int) selmR3GuestGDTWriteHandler(PVM pVM, RTGCPTR GCPtr, void *pvPtr, void *pvBuf, size_t cbBuf, PGMACCESSTYPE enmAccessType, void *pvUser)
{
Assert(enmAccessType == PGMACCESSTYPE_WRITE);
Log(("selmR3GuestGDTWriteHandler: write to %RGv size %d\n", GCPtr, cbBuf));
VM_FF_SET(pVM, VM_FF_SELM_SYNC_GDT);
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 pvBuf What the guest is reading/writing.
* @param cbBuf How much it's reading/writing.
* @param enmAccessType The access type.
* @param pvUser User argument.
*/
static DECLCALLBACK(int) selmR3GuestLDTWriteHandler(PVM pVM, RTGCPTR GCPtr, void *pvPtr, void *pvBuf, size_t cbBuf, PGMACCESSTYPE enmAccessType, void *pvUser)
{
Assert(enmAccessType == PGMACCESSTYPE_WRITE);
Log(("selmR3GuestLDTWriteHandler: write to %RGv size %d\n", GCPtr, cbBuf));
VM_FF_SET(pVM, VM_FF_SELM_SYNC_LDT);
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 pvBuf What the guest is reading/writing.
* @param cbBuf How much it's reading/writing.
* @param enmAccessType The access type.
* @param pvUser User argument.
*/
static DECLCALLBACK(int) selmR3GuestTSSWriteHandler(PVM pVM, RTGCPTR GCPtr, void *pvPtr, void *pvBuf, size_t cbBuf, PGMACCESSTYPE enmAccessType, void *pvUser)
{
Assert(enmAccessType == PGMACCESSTYPE_WRITE);
Log(("selmR3GuestTSSWriteHandler: write to %RGv size %d\n", GCPtr, cbBuf));
VM_FF_SET(pVM, VM_FF_SELM_SYNC_TSS);
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.
*/
VMMR3DECL(int) SELMR3SyncTSS(PVM pVM)
{
int rc;
if (pVM->selm.s.fDisableMonitoring)
{
VM_FF_CLEAR(pVM, VM_FF_SELM_SYNC_TSS);
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.
*/
STAM_PROFILE_START(&pVM->selm.s.StatTSSSync, a);
Assert(!VM_FF_ISSET(pVM, VM_FF_SELM_SYNC_GDT));
Assert(VM_FF_ISSET(pVM, VM_FF_SELM_SYNC_TSS));
/*
* TSS sync
*/
RTSEL SelTss = CPUMGetGuestTR(pVM);
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.
*/
PX86DESC pDesc = &pVM->selm.s.paGdtR3[SelTss >> X86_SEL_SHIFT];
RTGCPTR GCPtrTss = X86DESC_BASE(*pDesc);
unsigned cbTss = X86DESC_LIMIT(*pDesc);
if (pDesc->Gen.u1Granularity)
cbTss = (cbTss << PAGE_SHIFT) | PAGE_OFFSET_MASK;
cbTss++;
pVM->selm.s.cbGuestTss = cbTss;
pVM->selm.s.fGuestTss32Bit = pDesc->Gen.u4Type == X86_SEL_TYPE_SYS_386_TSS_AVAIL
|| pDesc->Gen.u4Type == X86_SEL_TYPE_SYS_386_TSS_BUSY;
/* 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.) */
if (cbTss > sizeof(VBOXTSS))
cbTss = sizeof(VBOXTSS);
#endif
/* The guest's TSS can span multiple pages now. We will monitor the whole thing. */
AssertMsg((GCPtrTss >> PAGE_SHIFT) == ((GCPtrTss + sizeof(VBOXTSS) - 1) >> PAGE_SHIFT),
("GCPtrTss=%RGv cbTss=%#x - We assume everything is inside one page!\n", GCPtrTss, cbTss));
// 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.
*/
if ( GCPtrTss != pVM->selm.s.GCPtrGuestTss
|| cbTss != pVM->selm.s.cbMonitoredGuestTss)
{
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
|| SelTss >= pVM->selm.s.GuestGdtr.cbGdt
|| pDesc->Gen.u1DescType
|| ( pDesc->Gen.u4Type != X86_SEL_TYPE_SYS_286_TSS_AVAIL
&& pDesc->Gen.u4Type != X86_SEL_TYPE_SYS_286_TSS_BUSY
&& pDesc->Gen.u4Type != X86_SEL_TYPE_SYS_386_TSS_AVAIL
&& pDesc->Gen.u4Type != X86_SEL_TYPE_SYS_386_TSS_BUSY) )
{
AssertMsgFailed(("Invalid Guest TSS %04x!\n", SelTss));
}
else
{
/*
* [Re]Register write virtual handler for guest's TSS.
*/
if (pVM->selm.s.GCPtrGuestTss != RTRCPTR_MAX)
{
rc = PGMHandlerVirtualDeregister(pVM, pVM->selm.s.GCPtrGuestTss);
AssertRC(rc);
}
rc = PGMR3HandlerVirtualRegister(pVM, PGMVIRTHANDLERTYPE_WRITE, GCPtrTss, GCPtrTss + cbTss - 1,
0, selmR3GuestTSSWriteHandler, "selmRCGuestTSSWriteHandler", 0, "Guest TSS write access handler");
if (RT_FAILURE(rc))
{
STAM_PROFILE_STOP(&pVM->selm.s.StatUpdateFromCPUM, a);
return rc;
}
/* Update saved Guest TSS info. */
pVM->selm.s.GCPtrGuestTss = GCPtrTss;
pVM->selm.s.cbMonitoredGuestTss = cbTss;
pVM->selm.s.GCSelTss = SelTss;
}
}
/* Update the ring 0 stack selector and base address */
/* feeling very lazy; reading too much */
VBOXTSS tss;
rc = PGMPhysSimpleReadGCPtr(pVM, &tss, GCPtrTss, RT_OFFSETOF(VBOXTSS, offIoBitmap) + sizeof(tss.offIoBitmap));
if (RT_SUCCESS(rc))
{
#ifdef LOG_ENABLED
uint32_t ssr0, espr0;
SELMGetRing1Stack(pVM, &ssr0, &espr0);
ssr0 &= ~1;
if (ssr0 != tss.ss0 || espr0 != tss.esp0)
Log(("SELMR3SyncTSS: Updating TSS ring 0 stack to %04X:%08X\n", tss.ss0, tss.esp0));
Log(("offIoBitmap=%#x\n", tss.offIoBitmap));
#endif /* LOG_ENABLED */
/* Update our TSS structure for the guest's ring 1 stack */
SELMSetRing1Stack(pVM, tss.ss0 | 1, tss.esp0);
/* Should we sync the virtual interrupt redirection bitmap as well? */
if (CPUMGetGuestCR4(pVM) & X86_CR4_VME)
{
uint32_t offRedirBitmap = tss.offIoBitmap - sizeof(tss.IntRedirBitmap);
/** @todo not sure how the partial case is handled; probably not allowed */
if (offRedirBitmap + sizeof(tss.IntRedirBitmap) <= pVM->selm.s.cbGuestTss)
{
rc = PGMPhysSimpleReadGCPtr(pVM, &pVM->selm.s.Tss.IntRedirBitmap, GCPtrTss + offRedirBitmap, sizeof(tss.IntRedirBitmap));
AssertRC(rc);
Log2(("Redirection bitmap:\n"));
Log2(("%.*Rhxd\n", sizeof(tss.IntRedirBitmap), &pVM->selm.s.Tss.IntRedirBitmap));
}
}
}
else
{
/* Note: the ring 0 stack selector and base address are updated on demand in this case. */
/** @todo handle these dependencies better! */
TRPMR3SetGuestTrapHandler(pVM, 0x2E, TRPM_INVALID_HANDLER);
TRPMR3SetGuestTrapHandler(pVM, 0x80, TRPM_INVALID_HANDLER);
pVM->selm.s.fSyncTSSRing0Stack = true;
}
VM_FF_CLEAR(pVM, VM_FF_SELM_SYNC_TSS);
}
}
else /* Null TR means there's no TSS, has to be reloaded first, so clear the forced action. */
VM_FF_CLEAR(pVM, VM_FF_SELM_SYNC_TSS);
STAM_PROFILE_STOP(&pVM->selm.s.StatTSSSync, a);
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.
*/
VMMR3DECL(int) SELMR3DebugCheck(PVM pVM)
{
#ifdef VBOX_STRICT
/*
* Get GDTR and check for conflict.
*/
VBOXGDTR GDTR;
CPUMGetGuestGDTR(pVM, &GDTR);
if (GDTR.cbGdt == 0)
return VINF_SUCCESS;
if (GDTR.cbGdt >= (unsigned)(pVM->selm.s.aHyperSel[SELM_HYPER_SEL_TSS_TRAP08] >> X86_SEL_SHIFT))
Log(("SELMR3DebugCheck: guest GDT size forced us to look for unused selectors.\n"));
if (GDTR.cbGdt != pVM->selm.s.GuestGdtr.cbGdt)
Log(("SELMR3DebugCheck: limits have changed! new=%d old=%d\n", GDTR.cbGdt, pVM->selm.s.GuestGdtr.cbGdt));
/*
* Loop thru the GDT checking each entry.
*/
RTGCPTR GCPtrGDTEGuest = GDTR.pGdt;
PX86DESC pGDTE = pVM->selm.s.paGdtR3;
PX86DESC pGDTEEnd = (PX86DESC)((uintptr_t)pGDTE + GDTR.cbGdt);
while (pGDTE < pGDTEEnd)
{
X86DESC GDTEGuest;
int rc = PGMPhysSimpleReadGCPtr(pVM, &GDTEGuest, GCPtrGDTEGuest, sizeof(GDTEGuest));
if (RT_SUCCESS(rc))
{
if (pGDTE->Gen.u1DescType || pGDTE->Gen.u4Type != X86_SEL_TYPE_SYS_LDT)
{
if ( pGDTE->Gen.u16LimitLow != GDTEGuest.Gen.u16LimitLow
|| pGDTE->Gen.u4LimitHigh != GDTEGuest.Gen.u4LimitHigh
|| pGDTE->Gen.u16BaseLow != GDTEGuest.Gen.u16BaseLow
|| pGDTE->Gen.u8BaseHigh1 != GDTEGuest.Gen.u8BaseHigh1
|| pGDTE->Gen.u8BaseHigh2 != GDTEGuest.Gen.u8BaseHigh2
|| pGDTE->Gen.u1DefBig != GDTEGuest.Gen.u1DefBig
|| pGDTE->Gen.u1DescType != GDTEGuest.Gen.u1DescType)
{
unsigned iGDT = pGDTE - pVM->selm.s.paGdtR3;
SELMR3DumpDescriptor(*pGDTE, iGDT << 3, "SELMR3DebugCheck: GDT mismatch, shadow");
SELMR3DumpDescriptor(GDTEGuest, iGDT << 3, "SELMR3DebugCheck: GDT mismatch, guest");
}
}
}
/* Advance to the next descriptor. */
GCPtrGDTEGuest += sizeof(X86DESC);
pGDTE++;
}
/*
* LDT?
*/
RTSEL SelLdt = CPUMGetGuestLDTR(pVM);
if ((SelLdt & X86_SEL_MASK) == 0)
return VINF_SUCCESS;
if (SelLdt > GDTR.cbGdt)
{
Log(("SELMR3DebugCheck: ldt is out of bound SelLdt=%#x\n", SelLdt));
return VERR_INTERNAL_ERROR;
}
X86DESC LDTDesc;
int rc = PGMPhysSimpleReadGCPtr(pVM, &LDTDesc, GDTR.pGdt + (SelLdt & X86_SEL_MASK), sizeof(LDTDesc));
if (RT_FAILURE(rc))
{
Log(("SELMR3DebugCheck: Failed to read LDT descriptor. rc=%d\n", rc));
return rc;
}
RTGCPTR GCPtrLDTEGuest = X86DESC_BASE(LDTDesc);
unsigned cbLdt = X86DESC_LIMIT(LDTDesc);
if (LDTDesc.Gen.u1Granularity)
cbLdt = (cbLdt << PAGE_SHIFT) | PAGE_OFFSET_MASK;
/*
* Validate it.
*/
if (!cbLdt)
return VINF_SUCCESS;
/** @todo check what intel does about odd limits. */
AssertMsg(RT_ALIGN(cbLdt + 1, sizeof(X86DESC)) == cbLdt + 1 && cbLdt <= 0xffff, ("cbLdt=%d\n", cbLdt));
if ( LDTDesc.Gen.u1DescType
|| LDTDesc.Gen.u4Type != X86_SEL_TYPE_SYS_LDT
|| SelLdt >= pVM->selm.s.GuestGdtr.cbGdt)
{
Log(("SELmR3DebugCheck: Invalid LDT %04x!\n", SelLdt));
return VERR_INTERNAL_ERROR;
}
/*
* Loop thru the LDT checking each entry.
*/
unsigned off = (GCPtrLDTEGuest & PAGE_OFFSET_MASK);
PX86DESC pLDTE = (PX86DESC)((uintptr_t)pVM->selm.s.pvLdtR3 + off);
PX86DESC pLDTEEnd = (PX86DESC)((uintptr_t)pGDTE + cbLdt);
while (pLDTE < pLDTEEnd)
{
X86DESC LDTEGuest;
int rc = PGMPhysSimpleReadGCPtr(pVM, &LDTEGuest, GCPtrLDTEGuest, sizeof(LDTEGuest));
if (RT_SUCCESS(rc))
{
if ( pLDTE->Gen.u16LimitLow != LDTEGuest.Gen.u16LimitLow
|| pLDTE->Gen.u4LimitHigh != LDTEGuest.Gen.u4LimitHigh
|| pLDTE->Gen.u16BaseLow != LDTEGuest.Gen.u16BaseLow
|| pLDTE->Gen.u8BaseHigh1 != LDTEGuest.Gen.u8BaseHigh1
|| pLDTE->Gen.u8BaseHigh2 != LDTEGuest.Gen.u8BaseHigh2
|| pLDTE->Gen.u1DefBig != LDTEGuest.Gen.u1DefBig
|| pLDTE->Gen.u1DescType != LDTEGuest.Gen.u1DescType)
{
unsigned iLDT = pLDTE - (PX86DESC)((uintptr_t)pVM->selm.s.pvLdtR3 + off);
SELMR3DumpDescriptor(*pLDTE, iLDT << 3, "SELMR3DebugCheck: LDT mismatch, shadow");
SELMR3DumpDescriptor(LDTEGuest, iLDT << 3, "SELMR3DebugCheck: LDT mismatch, guest");
}
}
/* Advance to the next descriptor. */
GCPtrLDTEGuest += sizeof(X86DESC);
pLDTE++;
}
#else /* !VBOX_STRICT */
NOREF(pVM);
#endif /* !VBOX_STRICT */
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.
*/
VMMR3DECL(bool) SELMR3CheckTSS(PVM pVM)
{
#ifdef VBOX_STRICT
RTSEL SelTss = CPUMGetGuestTR(pVM);
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.
*/
PX86DESC pDesc = &pVM->selm.s.paGdtR3[SelTss >> X86_SEL_SHIFT];
RTGCPTR GCPtrTss = X86DESC_BASE(*pDesc);
unsigned cbTss = X86DESC_LIMIT(*pDesc);
if (pDesc->Gen.u1Granularity)
cbTss = (cbTss << PAGE_SHIFT) | PAGE_OFFSET_MASK;
cbTss++;
# if 1
/* Don't bother with anything but the core structure. (Actually all we care for is the r0 ss.) */
if (cbTss > sizeof(VBOXTSS))
cbTss = sizeof(VBOXTSS);
# endif
AssertMsg((GCPtrTss >> PAGE_SHIFT) == ((GCPtrTss + sizeof(VBOXTSS) - 1) >> PAGE_SHIFT),
("GCPtrTss=%RGv cbTss=%#x - We assume everything is inside one page!\n", GCPtrTss, cbTss));
// 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.
*/
if ( GCPtrTss != pVM->selm.s.GCPtrGuestTss
|| cbTss != pVM->selm.s.cbMonitoredGuestTss)
{
AssertMsgFailed(("Guest's TSS (Sel 0x%X) is changed from %RGv:%04x to %RGv:%04x\n",
SelTss, pVM->selm.s.GCPtrGuestTss, pVM->selm.s.cbMonitoredGuestTss,
GCPtrTss, cbTss));
}
}
if (!pVM->selm.s.fSyncTSSRing0Stack)
{
RTGCPTR GCPtrGuestTSS = pVM->selm.s.GCPtrGuestTss;
uint32_t ESPR0;
int rc = PGMPhysSimpleReadGCPtr(pVM, &ESPR0, GCPtrGuestTSS + RT_OFFSETOF(VBOXTSS, esp0), sizeof(ESPR0));
if (RT_SUCCESS(rc))
{
RTSEL SelSS0;
rc = PGMPhysSimpleReadGCPtr(pVM, &SelSS0, GCPtrGuestTSS + RT_OFFSETOF(VBOXTSS, ss0), sizeof(SelSS0));
if (RT_SUCCESS(rc))
{
if ( ESPR0 == pVM->selm.s.Tss.esp1
&& SelSS0 == (pVM->selm.s.Tss.ss1 & ~1))
return true;
RTGCPHYS GCPhys;
uint64_t fFlags;
rc = PGMGstGetPage(pVM, GCPtrGuestTSS, &fFlags, &GCPhys);
AssertRC(rc);
AssertMsgFailed(("TSS out of sync!! (%04X:%08X vs %04X:%08X (guest)) Tss=%RGv Phys=%RGp\n",
(pVM->selm.s.Tss.ss1 & ~1), pVM->selm.s.Tss.esp1, SelSS0, ESPR0, GCPtrGuestTSS, GCPhys));
}
else
AssertRC(rc);
}
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 /* !VBOX_STRICT */
NOREF(pVM);
return true;
#endif /* !VBOX_STRICT */
}
/**
* 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.
*/
VMMDECL(int) SELMGetLDTFromSel(PVM pVM, RTSEL SelLdt, PRTGCPTR ppvLdt, unsigned *pcbLimit)
{
/* Get guest GDTR. */
VBOXGDTR GDTR;
CPUMGetGuestGDTR(pVM, &GDTR);
/* Check selector TI and GDT limit. */
if ( SelLdt & X86_SEL_LDT
|| (SelLdt > GDTR.cbGdt))
return VERR_INVALID_SELECTOR;
/* Read descriptor from GC. */
X86DESC Desc;
int rc = PGMPhysSimpleReadGCPtr(pVM, (void *)&Desc, (RTGCPTR)(GDTR.pGdt + (SelLdt & X86_SEL_MASK)), sizeof(Desc));
if (RT_FAILURE(rc))
{
/* fatal */
AssertMsgFailed(("Can't read LDT descriptor for selector=%04X\n", SelLdt));
return VERR_SELECTOR_NOT_PRESENT;
}
/* Check if LDT descriptor is not present. */
if (Desc.Gen.u1Present == 0)
return VERR_SELECTOR_NOT_PRESENT;
/* Check LDT descriptor type. */
if ( Desc.Gen.u1DescType == 1
|| Desc.Gen.u4Type != X86_SEL_TYPE_SYS_LDT)
return VERR_INVALID_SELECTOR;
/* LDT descriptor is ok. */
if (ppvLdt)
{
*ppvLdt = (RTGCPTR)X86DESC_BASE(Desc);
*pcbLimit = X86DESC_LIMIT(Desc);
}
return VINF_SUCCESS;
}
/**
* Gets information about a 64-bit selector, SELMR3GetSelectorInfo helper.
*
* See SELMR3GetSelectorInfo for details.
*
* @returns VBox status code, see SELMR3GetSelectorInfo for details.
*
* @param pVM VM handle.
* @param Sel The selector to get info about.
* @param pSelInfo Where to store the information.
*/
static int selmR3GetSelectorInfo64(PVM pVM, RTSEL Sel, PSELMSELINFO pSelInfo)
{
pSelInfo->fHyper = false;
/*
* Read it from the guest descriptor table.
*/
X86DESC64 Desc;
VBOXGDTR Gdtr;
RTGCPTR GCPtrDesc;
CPUMGetGuestGDTR(pVM, &Gdtr);
if (!(Sel & X86_SEL_LDT))
{
/* GDT */
if ((unsigned)(Sel & X86_SEL_MASK) + sizeof(X86DESC) - 1 > (unsigned)Gdtr.cbGdt)
return VERR_INVALID_SELECTOR;
GCPtrDesc = Gdtr.pGdt + (Sel & X86_SEL_MASK);
}
else
{
/*
* LDT - must locate the LDT first...
*/
RTSEL SelLdt = CPUMGetGuestLDTR(pVM);
if ( (unsigned)(SelLdt & X86_SEL_MASK) < sizeof(X86DESC) /* the first selector is invalid, right? */
|| (unsigned)(SelLdt & X86_SEL_MASK) + sizeof(X86DESC) - 1 > (unsigned)Gdtr.cbGdt)
return VERR_INVALID_SELECTOR;
GCPtrDesc = Gdtr.pGdt + (SelLdt & X86_SEL_MASK);
int rc = PGMPhysSimpleReadGCPtr(pVM, &Desc, GCPtrDesc, sizeof(Desc));
if (RT_FAILURE(rc))
return rc;
/* validate the LDT descriptor. */
if (Desc.Gen.u1Present == 0)
return VERR_SELECTOR_NOT_PRESENT;
if ( Desc.Gen.u1DescType == 1
|| Desc.Gen.u4Type != X86_SEL_TYPE_SYS_LDT)
return VERR_INVALID_SELECTOR;
unsigned cbLimit = X86DESC_LIMIT(Desc);
if (Desc.Gen.u1Granularity)
cbLimit = (cbLimit << PAGE_SHIFT) | PAGE_OFFSET_MASK;
if ((unsigned)(Sel & X86_SEL_MASK) + sizeof(X86DESC) - 1 > cbLimit)
return VERR_INVALID_SELECTOR;
/* calc the descriptor location. */
GCPtrDesc = X86DESC64_BASE(Desc);
GCPtrDesc += (Sel & X86_SEL_MASK);
}
/* read the descriptor. */
int rc = PGMPhysSimpleReadGCPtr(pVM, &Desc, GCPtrDesc, sizeof(Desc));
if (RT_FAILURE(rc))
return rc;
/*
* Extract the base and limit
*/
pSelInfo->Sel = Sel;
pSelInfo->Raw64 = Desc;
pSelInfo->cbLimit = X86DESC_LIMIT(Desc);
if (Desc.Gen.u1Granularity)
pSelInfo->cbLimit = (pSelInfo->cbLimit << PAGE_SHIFT) | PAGE_OFFSET_MASK;
pSelInfo->GCPtrBase = X86DESC64_BASE(Desc);
pSelInfo->fRealMode = false;
return VINF_SUCCESS;
}
/**
* Gets information about a 64-bit selector, SELMR3GetSelectorInfo helper.
*
* See SELMR3GetSelectorInfo for details.
*
* @returns VBox status code, see SELMR3GetSelectorInfo for details.
*
* @param pVM VM handle.
* @param Sel The selector to get info about.
* @param pSelInfo Where to store the information.
*/
static int selmR3GetSelectorInfo32(PVM pVM, RTSEL Sel, PSELMSELINFO pSelInfo)
{
/*
* Read the descriptor entry
*/
X86DESC Desc;
if ( !(Sel & X86_SEL_LDT)
&& ( pVM->selm.s.aHyperSel[SELM_HYPER_SEL_CS] == (Sel & X86_SEL_MASK)
|| pVM->selm.s.aHyperSel[SELM_HYPER_SEL_DS] == (Sel & X86_SEL_MASK)
|| pVM->selm.s.aHyperSel[SELM_HYPER_SEL_CS64] == (Sel & X86_SEL_MASK)
|| pVM->selm.s.aHyperSel[SELM_HYPER_SEL_TSS] == (Sel & X86_SEL_MASK)
|| pVM->selm.s.aHyperSel[SELM_HYPER_SEL_TSS_TRAP08] == (Sel & X86_SEL_MASK))
)
{
/*
* Hypervisor descriptor.
*/
pSelInfo->fHyper = true;
Desc = pVM->selm.s.paGdtR3[Sel >> X86_SEL_SHIFT];
}
else if (CPUMIsGuestInProtectedMode(pVM))
{
/*
* Read it from the guest descriptor table.
*/
pSelInfo->fHyper = false;
VBOXGDTR Gdtr;
RTGCPTR GCPtrDesc;
CPUMGetGuestGDTR(pVM, &Gdtr);
if (!(Sel & X86_SEL_LDT))
{
/* GDT */
if ((unsigned)(Sel & X86_SEL_MASK) + sizeof(X86DESC) - 1 > (unsigned)Gdtr.cbGdt)
return VERR_INVALID_SELECTOR;
GCPtrDesc = Gdtr.pGdt + (Sel & X86_SEL_MASK);
}
else
{
/*
* LDT - must locate the LDT first...
*/
RTSEL SelLdt = CPUMGetGuestLDTR(pVM);
if ( (unsigned)(SelLdt & X86_SEL_MASK) < sizeof(X86DESC) /* the first selector is invalid, right? */
|| (unsigned)(SelLdt & X86_SEL_MASK) + sizeof(X86DESC) - 1 > (unsigned)Gdtr.cbGdt)
return VERR_INVALID_SELECTOR;
GCPtrDesc = Gdtr.pGdt + (SelLdt & X86_SEL_MASK);
int rc = PGMPhysSimpleReadGCPtr(pVM, &Desc, GCPtrDesc, sizeof(Desc));
if (RT_FAILURE(rc))
return rc;
/* validate the LDT descriptor. */
if (Desc.Gen.u1Present == 0)
return VERR_SELECTOR_NOT_PRESENT;
if ( Desc.Gen.u1DescType == 1
|| Desc.Gen.u4Type != X86_SEL_TYPE_SYS_LDT)
return VERR_INVALID_SELECTOR;
unsigned cbLimit = X86DESC_LIMIT(Desc);
if (Desc.Gen.u1Granularity)
cbLimit = (cbLimit << PAGE_SHIFT) | PAGE_OFFSET_MASK;
if ((unsigned)(Sel & X86_SEL_MASK) + sizeof(X86DESC) - 1 > cbLimit)
return VERR_INVALID_SELECTOR;
/* calc the descriptor location. */
GCPtrDesc = X86DESC_BASE(Desc);
GCPtrDesc += (Sel & X86_SEL_MASK);
}
/* read the descriptor. */
int rc = PGMPhysSimpleReadGCPtr(pVM, &Desc, GCPtrDesc, sizeof(Desc));
if (RT_FAILURE(rc))
return rc;
}
else
{
/*
* We're in real mode.
*/
pSelInfo->Sel = Sel;
pSelInfo->GCPtrBase = Sel << 4;
pSelInfo->cbLimit = 0xffff;
pSelInfo->fHyper = false;
pSelInfo->fRealMode = true;
memset(&pSelInfo->Raw, 0, sizeof(pSelInfo->Raw));
return VINF_SUCCESS;
}
/*
* Extract the base and limit
*/
pSelInfo->Sel = Sel;
pSelInfo->Raw = Desc;
pSelInfo->cbLimit = X86DESC_LIMIT(Desc);
if (Desc.Gen.u1Granularity)
pSelInfo->cbLimit = (pSelInfo->cbLimit << PAGE_SHIFT) | PAGE_OFFSET_MASK;
pSelInfo->GCPtrBase = X86DESC_BASE(Desc);
pSelInfo->fRealMode = false;
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.
*/
VMMR3DECL(int) SELMR3GetSelectorInfo(PVM pVM, RTSEL Sel, PSELMSELINFO pSelInfo)
{
AssertPtr(pSelInfo);
if (CPUMIsGuestInLongMode(pVM))
return selmR3GetSelectorInfo64(pVM, Sel, pSelInfo);
return selmR3GetSelectorInfo32(pVM, Sel, pSelInfo);
}
/**
* 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.
*/
VMMR3DECL(int) SELMR3GetShadowSelectorInfo(PVM pVM, RTSEL Sel, PSELMSELINFO pSelInfo)
{
Assert(pSelInfo);
/*
* Read the descriptor entry
*/
X86DESC Desc;
if (!(Sel & X86_SEL_LDT))
{
/*
* Global descriptor.
*/
Desc = pVM->selm.s.paGdtR3[Sel >> X86_SEL_SHIFT];
pSelInfo->fHyper = pVM->selm.s.aHyperSel[SELM_HYPER_SEL_CS] == (Sel & X86_SEL_MASK)
|| pVM->selm.s.aHyperSel[SELM_HYPER_SEL_DS] == (Sel & X86_SEL_MASK)
|| pVM->selm.s.aHyperSel[SELM_HYPER_SEL_CS64] == (Sel & X86_SEL_MASK)
|| pVM->selm.s.aHyperSel[SELM_HYPER_SEL_TSS] == (Sel & X86_SEL_MASK)
|| pVM->selm.s.aHyperSel[SELM_HYPER_SEL_TSS_TRAP08] == (Sel & X86_SEL_MASK);
/** @todo check that the GDT offset is valid. */
}
else
{
/*
* Local Descriptor.
*/
PX86DESC paLDT = (PX86DESC)((char *)pVM->selm.s.pvLdtR3 + pVM->selm.s.offLdtHyper);
Desc = paLDT[Sel >> X86_SEL_SHIFT];
/** @todo check if the LDT page is actually available. */
/** @todo check that the LDT offset is valid. */
pSelInfo->fHyper = false;
}
/*
* Extract the base and limit
*/
pSelInfo->Sel = Sel;
pSelInfo->Raw = Desc;
pSelInfo->cbLimit = X86DESC_LIMIT(Desc);
if (Desc.Gen.u1Granularity)
pSelInfo->cbLimit = (pSelInfo->cbLimit << PAGE_SHIFT) | PAGE_OFFSET_MASK;
pSelInfo->GCPtrBase = X86DESC_BASE(Desc);
pSelInfo->fRealMode = false;
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.
*/
static void selmR3FormatDescriptor(X86DESC Desc, RTSEL Sel, char *pszOutput, size_t cchOutput)
{
/*
* Make variable description string.
*/
static struct
{
unsigned cch;
const char *psz;
} const aTypes[32] =
{
#define STRENTRY(str) { sizeof(str) - 1, str }
/* system */
STRENTRY("Reserved0 "), /* 0x00 */
STRENTRY("TSS16Avail "), /* 0x01 */
STRENTRY("LDT "), /* 0x02 */
STRENTRY("TSS16Busy "), /* 0x03 */
STRENTRY("Call16 "), /* 0x04 */
STRENTRY("Task "), /* 0x05 */
STRENTRY("Int16 "), /* 0x06 */
STRENTRY("Trap16 "), /* 0x07 */
STRENTRY("Reserved8 "), /* 0x08 */
STRENTRY("TSS32Avail "), /* 0x09 */
STRENTRY("ReservedA "), /* 0x0a */
STRENTRY("TSS32Busy "), /* 0x0b */
STRENTRY("Call32 "), /* 0x0c */
STRENTRY("ReservedD "), /* 0x0d */
STRENTRY("Int32 "), /* 0x0e */
STRENTRY("Trap32 "), /* 0x0f */
/* non system */
STRENTRY("DataRO "), /* 0x10 */
STRENTRY("DataRO Accessed "), /* 0x11 */
STRENTRY("DataRW "), /* 0x12 */
STRENTRY("DataRW Accessed "), /* 0x13 */
STRENTRY("DataDownRO "), /* 0x14 */
STRENTRY("DataDownRO Accessed "), /* 0x15 */
STRENTRY("DataDownRW "), /* 0x16 */
STRENTRY("DataDownRW Accessed "), /* 0x17 */
STRENTRY("CodeEO "), /* 0x18 */
STRENTRY("CodeEO Accessed "), /* 0x19 */
STRENTRY("CodeER "), /* 0x1a */
STRENTRY("CodeER Accessed "), /* 0x1b */
STRENTRY("CodeConfEO "), /* 0x1c */
STRENTRY("CodeConfEO Accessed "), /* 0x1d */
STRENTRY("CodeConfER "), /* 0x1e */
STRENTRY("CodeConfER Accessed ") /* 0x1f */
#undef SYSENTRY
};
#define ADD_STR(psz, pszAdd) do { strcpy(psz, pszAdd); psz += strlen(pszAdd); } while (0)
char szMsg[128];
char *psz = &szMsg[0];
unsigned i = Desc.Gen.u1DescType << 4 | Desc.Gen.u4Type;
memcpy(psz, aTypes[i].psz, aTypes[i].cch);
psz += aTypes[i].cch;
if (Desc.Gen.u1Present)
ADD_STR(psz, "Present ");
else
ADD_STR(psz, "Not-Present ");
if (Desc.Gen.u1Granularity)
ADD_STR(psz, "Page ");
if (Desc.Gen.u1DefBig)
ADD_STR(psz, "32-bit ");
else
ADD_STR(psz, "16-bit ");
#undef ADD_STR
*psz = '\0';
/*
* Limit and Base and format the output.
*/
uint32_t u32Limit = X86DESC_LIMIT(Desc);
if (Desc.Gen.u1Granularity)
u32Limit = u32Limit << PAGE_SHIFT | PAGE_OFFSET_MASK;
uint32_t u32Base = X86DESC_BASE(Desc);
RTStrPrintf(pszOutput, cchOutput, "%04x - %08x %08x - base=%08x limit=%08x dpl=%d %s",
Sel, Desc.au32[0], Desc.au32[1], u32Base, u32Limit, Desc.Gen.u2Dpl, szMsg);
}
/**
* Dumps a descriptor.
*
* @param Desc Descriptor to dump.
* @param Sel Selector number.
* @param pszMsg Message to prepend the log entry with.
*/
VMMR3DECL(void) SELMR3DumpDescriptor(X86DESC Desc, RTSEL Sel, const char *pszMsg)
{
char szOutput[128];
selmR3FormatDescriptor(Desc, Sel, &szOutput[0], sizeof(szOutput));
Log(("%s: %s\n", pszMsg, szOutput));
NOREF(szOutput[0]);
}
/**
* Display the shadow gdt.
*
* @param pVM VM Handle.
* @param pHlp The info helpers.
* @param pszArgs Arguments, ignored.
*/
static DECLCALLBACK(void) selmR3InfoGdt(PVM pVM, PCDBGFINFOHLP pHlp, const char *pszArgs)
{
pHlp->pfnPrintf(pHlp, "Shadow GDT (GCAddr=%RRv):\n", MMHyperR3ToRC(pVM, pVM->selm.s.paGdtR3));
for (unsigned iGDT = 0; iGDT < SELM_GDT_ELEMENTS; iGDT++)
{
if (pVM->selm.s.paGdtR3[iGDT].Gen.u1Present)
{
char szOutput[128];
selmR3FormatDescriptor(pVM->selm.s.paGdtR3[iGDT], iGDT << X86_SEL_SHIFT, &szOutput[0], sizeof(szOutput));
const char *psz = "";
if (iGDT == ((unsigned)pVM->selm.s.aHyperSel[SELM_HYPER_SEL_CS] >> X86_SEL_SHIFT))
psz = " HyperCS";
else if (iGDT == ((unsigned)pVM->selm.s.aHyperSel[SELM_HYPER_SEL_DS] >> X86_SEL_SHIFT))
psz = " HyperDS";
else if (iGDT == ((unsigned)pVM->selm.s.aHyperSel[SELM_HYPER_SEL_CS64] >> X86_SEL_SHIFT))
psz = " HyperCS64";
else if (iGDT == ((unsigned)pVM->selm.s.aHyperSel[SELM_HYPER_SEL_TSS] >> X86_SEL_SHIFT))
psz = " HyperTSS";
else if (iGDT == ((unsigned)pVM->selm.s.aHyperSel[SELM_HYPER_SEL_TSS_TRAP08] >> X86_SEL_SHIFT))
psz = " HyperTSSTrap08";
pHlp->pfnPrintf(pHlp, "%s%s\n", szOutput, psz);
}
}
}
/**
* Display the guest gdt.
*
* @param pVM VM Handle.
* @param pHlp The info helpers.
* @param pszArgs Arguments, ignored.
*/
static DECLCALLBACK(void) selmR3InfoGdtGuest(PVM pVM, PCDBGFINFOHLP pHlp, const char *pszArgs)
{
VBOXGDTR GDTR;
CPUMGetGuestGDTR(pVM, &GDTR);
RTGCPTR GCPtrGDT = GDTR.pGdt;
unsigned cGDTs = ((unsigned)GDTR.cbGdt + 1) / sizeof(X86DESC);
pHlp->pfnPrintf(pHlp, "Guest GDT (GCAddr=%RGv limit=%x):\n", GCPtrGDT, GDTR.cbGdt);
for (unsigned iGDT = 0; iGDT < cGDTs; iGDT++, GCPtrGDT += sizeof(X86DESC))
{
X86DESC GDTE;
int rc = PGMPhysSimpleReadGCPtr(pVM, &GDTE, GCPtrGDT, sizeof(GDTE));
if (RT_SUCCESS(rc))
{
if (GDTE.Gen.u1Present)
{
char szOutput[128];
selmR3FormatDescriptor(GDTE, iGDT << X86_SEL_SHIFT, &szOutput[0], sizeof(szOutput));
pHlp->pfnPrintf(pHlp, "%s\n", szOutput);
}
}
else if (rc == VERR_PAGE_NOT_PRESENT)
{
if ((GCPtrGDT & PAGE_OFFSET_MASK) + sizeof(X86DESC) - 1 < sizeof(X86DESC))
pHlp->pfnPrintf(pHlp, "%04x - page not present (GCAddr=%RGv)\n", iGDT << X86_SEL_SHIFT, GCPtrGDT);
}
else
pHlp->pfnPrintf(pHlp, "%04x - read error rc=%Rrc GCAddr=%RGv\n", iGDT << X86_SEL_SHIFT, rc, GCPtrGDT);
}
}
/**
* Display the shadow ldt.
*
* @param pVM VM Handle.
* @param pHlp The info helpers.
* @param pszArgs Arguments, ignored.
*/
static DECLCALLBACK(void) selmR3InfoLdt(PVM pVM, PCDBGFINFOHLP pHlp, const char *pszArgs)
{
unsigned cLDTs = ((unsigned)pVM->selm.s.cbLdtLimit + 1) >> X86_SEL_SHIFT;
PX86DESC paLDT = (PX86DESC)((char *)pVM->selm.s.pvLdtR3 + pVM->selm.s.offLdtHyper);
pHlp->pfnPrintf(pHlp, "Shadow LDT (GCAddr=%RRv limit=%#x):\n", pVM->selm.s.pvLdtRC + pVM->selm.s.offLdtHyper, pVM->selm.s.cbLdtLimit);
for (unsigned iLDT = 0; iLDT < cLDTs; iLDT++)
{
if (paLDT[iLDT].Gen.u1Present)
{
char szOutput[128];
selmR3FormatDescriptor(paLDT[iLDT], (iLDT << X86_SEL_SHIFT) | X86_SEL_LDT, &szOutput[0], sizeof(szOutput));
pHlp->pfnPrintf(pHlp, "%s\n", szOutput);
}
}
}
/**
* Display the guest ldt.
*
* @param pVM VM Handle.
* @param pHlp The info helpers.
* @param pszArgs Arguments, ignored.
*/
static DECLCALLBACK(void) selmR3InfoLdtGuest(PVM pVM, PCDBGFINFOHLP pHlp, const char *pszArgs)
{
RTSEL SelLdt = CPUMGetGuestLDTR(pVM);
if (!(SelLdt & X86_SEL_MASK))
{
pHlp->pfnPrintf(pHlp, "Guest LDT (Sel=%x): Null-Selector\n", SelLdt);
return;
}
RTGCPTR GCPtrLdt;
unsigned cbLdt;
int rc = SELMGetLDTFromSel(pVM, SelLdt, &GCPtrLdt, &cbLdt);
if (RT_FAILURE(rc))
{
pHlp->pfnPrintf(pHlp, "Guest LDT (Sel=%x): rc=%Rrc\n", SelLdt, rc);
return;
}
pHlp->pfnPrintf(pHlp, "Guest LDT (Sel=%x GCAddr=%RGv limit=%x):\n", SelLdt, GCPtrLdt, cbLdt);
unsigned cLdts = (cbLdt + 1) >> X86_SEL_SHIFT;
for (unsigned iLdt = 0; iLdt < cLdts; iLdt++, GCPtrLdt += sizeof(X86DESC))
{
X86DESC LdtE;
int rc = PGMPhysSimpleReadGCPtr(pVM, &LdtE, GCPtrLdt, sizeof(LdtE));
if (RT_SUCCESS(rc))
{
if (LdtE.Gen.u1Present)
{
char szOutput[128];
selmR3FormatDescriptor(LdtE, (iLdt << X86_SEL_SHIFT) | X86_SEL_LDT, &szOutput[0], sizeof(szOutput));
pHlp->pfnPrintf(pHlp, "%s\n", szOutput);
}
}
else if (rc == VERR_PAGE_NOT_PRESENT)
{
if ((GCPtrLdt & PAGE_OFFSET_MASK) + sizeof(X86DESC) - 1 < sizeof(X86DESC))
pHlp->pfnPrintf(pHlp, "%04x - page not present (GCAddr=%RGv)\n", (iLdt << X86_SEL_SHIFT) | X86_SEL_LDT, GCPtrLdt);
}
else
pHlp->pfnPrintf(pHlp, "%04x - read error rc=%Rrc GCAddr=%RGv\n", (iLdt << X86_SEL_SHIFT) | X86_SEL_LDT, rc, GCPtrLdt);
}
}
/**
* Dumps the hypervisor GDT
*
* @param pVM VM handle.
*/
VMMR3DECL(void) SELMR3DumpHyperGDT(PVM pVM)
{
DBGFR3Info(pVM, "gdt", NULL, NULL);
}
/**
* Dumps the hypervisor LDT
*
* @param pVM VM handle.
*/
VMMR3DECL(void) SELMR3DumpHyperLDT(PVM pVM)
{
DBGFR3Info(pVM, "ldt", NULL, NULL);
}
/**
* Dumps the guest GDT
*
* @param pVM VM handle.
*/
VMMR3DECL(void) SELMR3DumpGuestGDT(PVM pVM)
{
DBGFR3Info(pVM, "gdtguest", NULL, NULL);
}
/**
* Dumps the guest LDT
*
* @param pVM VM handle.
*/
VMMR3DECL(void) SELMR3DumpGuestLDT(PVM pVM)
{
DBGFR3Info(pVM, "ldtguest", NULL, NULL);
}