/* $Id: SELMAll.cpp 70948 2018-02-10 15:38:12Z vboxsync $ */ /** @file * SELM All contexts. */ /* * Copyright (C) 2006-2017 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; * you can redistribute it and/or modify it under the terms of the GNU * General Public License (GPL) as published by the Free Software * Foundation, in version 2 as it comes in the "COPYING" file of the * VirtualBox OSE distribution. VirtualBox OSE is distributed in the * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. */ /********************************************************************************************************************************* * Header Files * *********************************************************************************************************************************/ #define LOG_GROUP LOG_GROUP_SELM #include #include #include #include #include #include #include #include "SELMInternal.h" #include #include #include #include #include #include #include #include "SELMInline.h" /********************************************************************************************************************************* * Global Variables * *********************************************************************************************************************************/ #if defined(LOG_ENABLED) && defined(VBOX_WITH_RAW_MODE_NOT_R0) /** Segment register names. */ static char const g_aszSRegNms[X86_SREG_COUNT][4] = { "ES", "CS", "SS", "DS", "FS", "GS" }; #endif #ifndef IN_RING0 # ifdef SELM_TRACK_GUEST_GDT_CHANGES /** * @callback_method_impl{FNPGMVIRTHANDLER} */ PGM_ALL_CB2_DECL(VBOXSTRICTRC) selmGuestGDTWriteHandler(PVM pVM, PVMCPU pVCpu, RTGCPTR GCPtr, void *pvPtr, void *pvBuf, size_t cbBuf, PGMACCESSTYPE enmAccessType, PGMACCESSORIGIN enmOrigin, void *pvUser) { Assert(enmAccessType == PGMACCESSTYPE_WRITE); NOREF(enmAccessType); Log(("selmGuestGDTWriteHandler: write to %RGv size %d\n", GCPtr, cbBuf)); NOREF(GCPtr); NOREF(cbBuf); NOREF(pvPtr); NOREF(pvBuf); NOREF(enmOrigin); NOREF(pvUser); # ifdef IN_RING3 RT_NOREF_PV(pVM); VMCPU_FF_SET(pVCpu, VMCPU_FF_SELM_SYNC_GDT); return VINF_PGM_HANDLER_DO_DEFAULT; # else /* IN_RC: */ /* * Execute the write, doing necessary pre and post shadow GDT checks. */ PCPUMCTX pCtx = CPUMQueryGuestCtxPtr(pVCpu); uint32_t offGuestGdt = pCtx->gdtr.pGdt - GCPtr; selmRCGuestGdtPreWriteCheck(pVM, pVCpu, offGuestGdt, cbBuf, pCtx); memcpy(pvBuf, pvPtr, cbBuf); VBOXSTRICTRC rcStrict = selmRCGuestGdtPostWriteCheck(pVM, pVCpu, offGuestGdt, cbBuf, pCtx); if (!VMCPU_FF_IS_PENDING(pVCpu, VMCPU_FF_SELM_SYNC_GDT)) STAM_COUNTER_INC(&pVM->selm.s.StatRCWriteGuestGDTHandled); else STAM_COUNTER_INC(&pVM->selm.s.StatRCWriteGuestGDTUnhandled); return rcStrict; # endif } # endif # ifdef SELM_TRACK_GUEST_LDT_CHANGES /** * @callback_method_impl{FNPGMVIRTHANDLER} */ PGM_ALL_CB2_DECL(VBOXSTRICTRC) selmGuestLDTWriteHandler(PVM pVM, PVMCPU pVCpu, RTGCPTR GCPtr, void *pvPtr, void *pvBuf, size_t cbBuf, PGMACCESSTYPE enmAccessType, PGMACCESSORIGIN enmOrigin, void *pvUser) { Assert(enmAccessType == PGMACCESSTYPE_WRITE); NOREF(enmAccessType); Log(("selmGuestLDTWriteHandler: write to %RGv size %d\n", GCPtr, cbBuf)); NOREF(GCPtr); NOREF(cbBuf); NOREF(pvPtr); NOREF(pvBuf); NOREF(enmOrigin); NOREF(pvUser); RT_NOREF_PV(pVM); VMCPU_FF_SET(pVCpu, VMCPU_FF_SELM_SYNC_LDT); # ifdef IN_RING3 return VINF_PGM_HANDLER_DO_DEFAULT; # else STAM_COUNTER_INC(&pVM->selm.s.StatRCWriteGuestLDT); return VINF_EM_RAW_EMULATE_INSTR_LDT_FAULT; # endif } # endif # ifdef SELM_TRACK_GUEST_TSS_CHANGES /** * @callback_method_impl{FNPGMVIRTHANDLER} */ PGM_ALL_CB2_DECL(VBOXSTRICTRC) selmGuestTSSWriteHandler(PVM pVM, PVMCPU pVCpu, RTGCPTR GCPtr, void *pvPtr, void *pvBuf, size_t cbBuf, PGMACCESSTYPE enmAccessType, PGMACCESSORIGIN enmOrigin, void *pvUser) { Assert(enmAccessType == PGMACCESSTYPE_WRITE); NOREF(enmAccessType); Log(("selmGuestTSSWriteHandler: write %.*Rhxs to %RGv size %d\n", RT_MIN(8, cbBuf), pvBuf, GCPtr, cbBuf)); NOREF(pvBuf); NOREF(GCPtr); NOREF(cbBuf); NOREF(enmOrigin); NOREF(pvUser); NOREF(pvPtr); # ifdef IN_RING3 RT_NOREF_PV(pVM); /** @todo This can be optimized by checking for the ESP0 offset and tracking TR * reloads in REM (setting VM_FF_SELM_SYNC_TSS if TR is reloaded). We * should probably also deregister the virtual handler if TR.base/size * changes while we're in REM. May also share * selmRCGuestTssPostWriteCheck code. */ VMCPU_FF_SET(pVCpu, VMCPU_FF_SELM_SYNC_TSS); return VINF_PGM_HANDLER_DO_DEFAULT; # else /* IN_RC */ /* * Do the write and check if anything relevant changed. */ Assert(pVM->selm.s.GCPtrGuestTss != (uintptr_t)RTRCPTR_MAX); memcpy(pvPtr, pvBuf, cbBuf); return selmRCGuestTssPostWriteCheck(pVM, pVCpu, GCPtr - pVM->selm.s.GCPtrGuestTss, cbBuf); # endif } # endif #endif /* IN_RING0 */ #ifdef VBOX_WITH_RAW_MODE_NOT_R0 /** * Converts a GC selector based address to a flat address. * * No limit checks are done. Use the SELMToFlat*() or SELMValidate*() functions * for that. * * @returns Flat address. * @param pVM The cross context VM structure. * @param Sel Selector part. * @param Addr Address part. * @remarks Don't use when in long mode. */ VMMDECL(RTGCPTR) SELMToFlatBySel(PVM pVM, RTSEL Sel, RTGCPTR Addr) { Assert(pVM->cCpus == 1 && !CPUMIsGuestInLongMode(VMMGetCpu(pVM))); /* DON'T USE! */ Assert(VM_IS_RAW_MODE_ENABLED(pVM)); /** @todo check the limit. */ X86DESC Desc; if (!(Sel & X86_SEL_LDT)) Desc = pVM->selm.s.CTX_SUFF(paGdt)[Sel >> X86_SEL_SHIFT]; else { /** @todo handle LDT pages not present! */ PX86DESC paLDT = (PX86DESC)((char *)pVM->selm.s.CTX_SUFF(pvLdt) + pVM->selm.s.offLdtHyper); Desc = paLDT[Sel >> X86_SEL_SHIFT]; } return (RTGCPTR)(((RTGCUINTPTR)Addr + X86DESC_BASE(&Desc)) & 0xffffffff); } #endif /* VBOX_WITH_RAW_MODE_NOT_R0 */ /** * Converts a GC selector based address to a flat address. * * No limit checks are done. Use the SELMToFlat*() or SELMValidate*() functions * for that. * * @returns Flat address. * @param pVM The cross context VM structure. * @param SelReg Selector register * @param pCtxCore CPU context * @param Addr Address part. */ VMMDECL(RTGCPTR) SELMToFlat(PVM pVM, DISSELREG SelReg, PCPUMCTXCORE pCtxCore, RTGCPTR Addr) { PCPUMSELREG pSReg; PVMCPU pVCpu = VMMGetCpu(pVM); int rc = DISFetchRegSegEx(pCtxCore, SelReg, &pSReg); AssertRC(rc); /* * Deal with real & v86 mode first. */ if ( pCtxCore->eflags.Bits.u1VM || CPUMIsGuestInRealMode(pVCpu)) { uint32_t uFlat = (uint32_t)Addr & 0xffff; if (CPUMSELREG_ARE_HIDDEN_PARTS_VALID(pVCpu, pSReg)) uFlat += (uint32_t)pSReg->u64Base; else uFlat += (uint32_t)pSReg->Sel << 4; return (RTGCPTR)uFlat; } #ifdef VBOX_WITH_RAW_MODE_NOT_R0 /** @todo when we're in 16 bits mode, we should cut off the address as well?? */ if (!CPUMSELREG_ARE_HIDDEN_PARTS_VALID(pVCpu, pSReg)) CPUMGuestLazyLoadHiddenSelectorReg(pVCpu, pSReg); if (!CPUMSELREG_ARE_HIDDEN_PARTS_VALID(pVCpu, &pCtxCore->cs)) CPUMGuestLazyLoadHiddenSelectorReg(pVCpu, &pCtxCore->cs); #else Assert(CPUMSELREG_ARE_HIDDEN_PARTS_VALID(pVCpu, pSReg)); Assert(CPUMSELREG_ARE_HIDDEN_PARTS_VALID(pVCpu, &pCtxCore->cs)); #endif /* 64 bits mode: CS, DS, ES and SS are treated as if each segment base is 0 (Intel® 64 and IA-32 Architectures Software Developer's Manual: 3.4.2.1). */ if ( pCtxCore->cs.Attr.n.u1Long && CPUMIsGuestInLongMode(pVCpu)) { switch (SelReg) { case DISSELREG_FS: case DISSELREG_GS: return (RTGCPTR)(pSReg->u64Base + Addr); default: return Addr; /* base 0 */ } } /* AMD64 manual: compatibility mode ignores the high 32 bits when calculating an effective address. */ Assert(pSReg->u64Base <= 0xffffffff); return (uint32_t)pSReg->u64Base + (uint32_t)Addr; } /** * Converts a GC selector based address to a flat address. * * Some basic checking is done, but not all kinds yet. * * @returns VBox status * @param pVCpu The cross context virtual CPU structure. * @param SelReg Selector register. * @param pCtxCore CPU context. * @param Addr Address part. * @param fFlags SELMTOFLAT_FLAGS_* * GDT entires are valid. * @param ppvGC Where to store the GC flat address. */ VMMDECL(int) SELMToFlatEx(PVMCPU pVCpu, DISSELREG SelReg, PCPUMCTXCORE pCtxCore, RTGCPTR Addr, uint32_t fFlags, PRTGCPTR ppvGC) { /* * Fetch the selector first. */ PCPUMSELREG pSReg; int rc = DISFetchRegSegEx(pCtxCore, SelReg, &pSReg); AssertRCReturn(rc, rc); AssertPtr(pSReg); /* * Deal with real & v86 mode first. */ if ( pCtxCore->eflags.Bits.u1VM || CPUMIsGuestInRealMode(pVCpu)) { if (ppvGC) { uint32_t uFlat = (uint32_t)Addr & 0xffff; if (CPUMSELREG_ARE_HIDDEN_PARTS_VALID(pVCpu, pSReg)) *ppvGC = (uint32_t)pSReg->u64Base + uFlat; else *ppvGC = ((uint32_t)pSReg->Sel << 4) + uFlat; } return VINF_SUCCESS; } #ifdef VBOX_WITH_RAW_MODE_NOT_R0 if (!CPUMSELREG_ARE_HIDDEN_PARTS_VALID(pVCpu, pSReg)) CPUMGuestLazyLoadHiddenSelectorReg(pVCpu, pSReg); if (!CPUMSELREG_ARE_HIDDEN_PARTS_VALID(pVCpu, &pCtxCore->cs)) CPUMGuestLazyLoadHiddenSelectorReg(pVCpu, &pCtxCore->cs); #else Assert(CPUMSELREG_ARE_HIDDEN_PARTS_VALID(pVCpu, pSReg)); Assert(CPUMSELREG_ARE_HIDDEN_PARTS_VALID(pVCpu, &pCtxCore->cs)); #endif /* 64 bits mode: CS, DS, ES and SS are treated as if each segment base is 0 (Intel® 64 and IA-32 Architectures Software Developer's Manual: 3.4.2.1). */ RTGCPTR pvFlat; bool fCheckLimit = true; if ( pCtxCore->cs.Attr.n.u1Long && CPUMIsGuestInLongMode(pVCpu)) { fCheckLimit = false; switch (SelReg) { case DISSELREG_FS: case DISSELREG_GS: pvFlat = pSReg->u64Base + Addr; break; default: pvFlat = Addr; break; } } else { /* AMD64 manual: compatibility mode ignores the high 32 bits when calculating an effective address. */ Assert(pSReg->u64Base <= UINT32_C(0xffffffff)); pvFlat = (uint32_t)pSReg->u64Base + (uint32_t)Addr; Assert(pvFlat <= UINT32_MAX); } /* * Check type if present. */ if (pSReg->Attr.n.u1Present) { switch (pSReg->Attr.n.u4Type) { /* Read only selector type. */ case X86_SEL_TYPE_RO: case X86_SEL_TYPE_RO_ACC: case X86_SEL_TYPE_RW: case X86_SEL_TYPE_RW_ACC: case X86_SEL_TYPE_EO: case X86_SEL_TYPE_EO_ACC: case X86_SEL_TYPE_ER: case X86_SEL_TYPE_ER_ACC: if (!(fFlags & SELMTOFLAT_FLAGS_NO_PL)) { /** @todo fix this mess */ } /* check limit. */ if (fCheckLimit && Addr > pSReg->u32Limit) return VERR_OUT_OF_SELECTOR_BOUNDS; /* ok */ if (ppvGC) *ppvGC = pvFlat; return VINF_SUCCESS; case X86_SEL_TYPE_EO_CONF: case X86_SEL_TYPE_EO_CONF_ACC: case X86_SEL_TYPE_ER_CONF: case X86_SEL_TYPE_ER_CONF_ACC: if (!(fFlags & SELMTOFLAT_FLAGS_NO_PL)) { /** @todo fix this mess */ } /* check limit. */ if (fCheckLimit && Addr > pSReg->u32Limit) return VERR_OUT_OF_SELECTOR_BOUNDS; /* ok */ if (ppvGC) *ppvGC = pvFlat; return VINF_SUCCESS; case X86_SEL_TYPE_RO_DOWN: case X86_SEL_TYPE_RO_DOWN_ACC: case X86_SEL_TYPE_RW_DOWN: case X86_SEL_TYPE_RW_DOWN_ACC: if (!(fFlags & SELMTOFLAT_FLAGS_NO_PL)) { /** @todo fix this mess */ } /* check limit. */ if (fCheckLimit) { if (!pSReg->Attr.n.u1Granularity && Addr > UINT32_C(0xffff)) return VERR_OUT_OF_SELECTOR_BOUNDS; if (Addr <= pSReg->u32Limit) return VERR_OUT_OF_SELECTOR_BOUNDS; } /* ok */ if (ppvGC) *ppvGC = pvFlat; return VINF_SUCCESS; default: return VERR_INVALID_SELECTOR; } } return VERR_SELECTOR_NOT_PRESENT; } #ifdef VBOX_WITH_RAW_MODE_NOT_R0 /** * Converts a GC selector based address to a flat address. * * Some basic checking is done, but not all kinds yet. * * @returns VBox status * @param pVCpu The cross context virtual CPU structure. * @param eflags Current eflags * @param Sel Selector part. * @param Addr Address part. * @param fFlags SELMTOFLAT_FLAGS_* * GDT entires are valid. * @param ppvGC Where to store the GC flat address. * @param pcb Where to store the bytes from *ppvGC which can be accessed according to * the selector. NULL is allowed. * @remarks Don't use when in long mode. */ VMMDECL(int) SELMToFlatBySelEx(PVMCPU pVCpu, X86EFLAGS eflags, RTSEL Sel, RTGCPTR Addr, uint32_t fFlags, PRTGCPTR ppvGC, uint32_t *pcb) { Assert(!CPUMIsGuestInLongMode(pVCpu)); /* DON'T USE! (Accessing shadow GDT/LDT.) */ Assert(VM_IS_RAW_MODE_ENABLED(pVCpu->CTX_SUFF(pVM))); /* * Deal with real & v86 mode first. */ if ( eflags.Bits.u1VM || CPUMIsGuestInRealMode(pVCpu)) { RTGCUINTPTR uFlat = (RTGCUINTPTR)Addr & 0xffff; if (ppvGC) *ppvGC = ((RTGCUINTPTR)Sel << 4) + uFlat; if (pcb) *pcb = 0x10000 - uFlat; return VINF_SUCCESS; } /** @todo when we're in 16 bits mode, we should cut off the address as well?? */ X86DESC Desc; PVM pVM = pVCpu->CTX_SUFF(pVM); if (!(Sel & X86_SEL_LDT)) { if ( !(fFlags & SELMTOFLAT_FLAGS_HYPER) && (Sel | X86_SEL_RPL_LDT) > pVM->selm.s.GuestGdtr.cbGdt) return VERR_INVALID_SELECTOR; Desc = pVM->selm.s.CTX_SUFF(paGdt)[Sel >> X86_SEL_SHIFT]; } else { if ((Sel | X86_SEL_RPL_LDT) > pVM->selm.s.cbLdtLimit) return VERR_INVALID_SELECTOR; /** @todo handle LDT page(s) not present! */ PX86DESC paLDT = (PX86DESC)((char *)pVM->selm.s.CTX_SUFF(pvLdt) + pVM->selm.s.offLdtHyper); Desc = paLDT[Sel >> X86_SEL_SHIFT]; } /* calc limit. */ uint32_t u32Limit = X86DESC_LIMIT_G(&Desc); /* calc address assuming straight stuff. */ RTGCPTR pvFlat = Addr + X86DESC_BASE(&Desc); /* Cut the address to 32 bits. */ Assert(!CPUMIsGuestInLongMode(pVCpu)); pvFlat &= 0xffffffff; uint8_t u1Present = Desc.Gen.u1Present; uint8_t u1Granularity = Desc.Gen.u1Granularity; uint8_t u1DescType = Desc.Gen.u1DescType; uint8_t u4Type = Desc.Gen.u4Type; /* * Check if present. */ if (u1Present) { /* * Type check. */ #define BOTH(a, b) ((a << 16) | b) switch (BOTH(u1DescType, u4Type)) { /** Read only selector type. */ case BOTH(1,X86_SEL_TYPE_RO): case BOTH(1,X86_SEL_TYPE_RO_ACC): case BOTH(1,X86_SEL_TYPE_RW): case BOTH(1,X86_SEL_TYPE_RW_ACC): case BOTH(1,X86_SEL_TYPE_EO): case BOTH(1,X86_SEL_TYPE_EO_ACC): case BOTH(1,X86_SEL_TYPE_ER): case BOTH(1,X86_SEL_TYPE_ER_ACC): if (!(fFlags & SELMTOFLAT_FLAGS_NO_PL)) { /** @todo fix this mess */ } /* check limit. */ if ((RTGCUINTPTR)Addr > u32Limit) return VERR_OUT_OF_SELECTOR_BOUNDS; /* ok */ if (ppvGC) *ppvGC = pvFlat; if (pcb) *pcb = u32Limit - (uint32_t)Addr + 1; return VINF_SUCCESS; case BOTH(1,X86_SEL_TYPE_EO_CONF): case BOTH(1,X86_SEL_TYPE_EO_CONF_ACC): case BOTH(1,X86_SEL_TYPE_ER_CONF): case BOTH(1,X86_SEL_TYPE_ER_CONF_ACC): if (!(fFlags & SELMTOFLAT_FLAGS_NO_PL)) { /** @todo fix this mess */ } /* check limit. */ if ((RTGCUINTPTR)Addr > u32Limit) return VERR_OUT_OF_SELECTOR_BOUNDS; /* ok */ if (ppvGC) *ppvGC = pvFlat; if (pcb) *pcb = u32Limit - (uint32_t)Addr + 1; return VINF_SUCCESS; case BOTH(1,X86_SEL_TYPE_RO_DOWN): case BOTH(1,X86_SEL_TYPE_RO_DOWN_ACC): case BOTH(1,X86_SEL_TYPE_RW_DOWN): case BOTH(1,X86_SEL_TYPE_RW_DOWN_ACC): if (!(fFlags & SELMTOFLAT_FLAGS_NO_PL)) { /** @todo fix this mess */ } /* check limit. */ if (!u1Granularity && (RTGCUINTPTR)Addr > (RTGCUINTPTR)0xffff) return VERR_OUT_OF_SELECTOR_BOUNDS; if ((RTGCUINTPTR)Addr <= u32Limit) return VERR_OUT_OF_SELECTOR_BOUNDS; /* ok */ if (ppvGC) *ppvGC = pvFlat; if (pcb) *pcb = (RTGCUINTPTR)(u1Granularity ? 0xffffffff : 0xffff) - (RTGCUINTPTR)Addr + 1; return VINF_SUCCESS; case BOTH(0,X86_SEL_TYPE_SYS_286_TSS_AVAIL): case BOTH(0,X86_SEL_TYPE_SYS_LDT): case BOTH(0,X86_SEL_TYPE_SYS_286_TSS_BUSY): case BOTH(0,X86_SEL_TYPE_SYS_286_CALL_GATE): case BOTH(0,X86_SEL_TYPE_SYS_TASK_GATE): case BOTH(0,X86_SEL_TYPE_SYS_286_INT_GATE): case BOTH(0,X86_SEL_TYPE_SYS_286_TRAP_GATE): case BOTH(0,X86_SEL_TYPE_SYS_386_TSS_AVAIL): case BOTH(0,X86_SEL_TYPE_SYS_386_TSS_BUSY): case BOTH(0,X86_SEL_TYPE_SYS_386_CALL_GATE): case BOTH(0,X86_SEL_TYPE_SYS_386_INT_GATE): case BOTH(0,X86_SEL_TYPE_SYS_386_TRAP_GATE): if (!(fFlags & SELMTOFLAT_FLAGS_NO_PL)) { /** @todo fix this mess */ } /* check limit. */ if ((RTGCUINTPTR)Addr > u32Limit) return VERR_OUT_OF_SELECTOR_BOUNDS; /* ok */ if (ppvGC) *ppvGC = pvFlat; if (pcb) *pcb = 0xffffffff - (RTGCUINTPTR)pvFlat + 1; /* Depends on the type.. fixme if we care. */ return VINF_SUCCESS; default: return VERR_INVALID_SELECTOR; } #undef BOTH } return VERR_SELECTOR_NOT_PRESENT; } #endif /* VBOX_WITH_RAW_MODE_NOT_R0 */ #ifdef VBOX_WITH_RAW_MODE_NOT_R0 static void selLoadHiddenSelectorRegFromGuestTable(PVMCPU pVCpu, PCCPUMCTX pCtx, PCPUMSELREG pSReg, RTGCPTR GCPtrDesc, RTSEL const Sel, uint32_t const iSReg) { Assert(VM_IS_RAW_MODE_ENABLED(pVCpu->CTX_SUFF(pVM))); RT_NOREF_PV(pCtx); RT_NOREF_PV(Sel); /* * Try read the entry. */ X86DESC GstDesc; VBOXSTRICTRC rcStrict = PGMPhysReadGCPtr(pVCpu, &GstDesc, GCPtrDesc, sizeof(GstDesc), PGMACCESSORIGIN_SELM); if (rcStrict == VINF_SUCCESS) { /* * Validate it and load it. */ if (selmIsGstDescGoodForSReg(pVCpu, pSReg, &GstDesc, iSReg, CPUMGetGuestCPL(pVCpu))) { selmLoadHiddenSRegFromGuestDesc(pVCpu, pSReg, &GstDesc); Log(("SELMLoadHiddenSelectorReg: loaded %s=%#x:{b=%llx, l=%x, a=%x, vs=%x} (gst)\n", g_aszSRegNms[iSReg], Sel, pSReg->u64Base, pSReg->u32Limit, pSReg->Attr.u, pSReg->ValidSel)); STAM_COUNTER_INC(&pVCpu->CTX_SUFF(pVM)->selm.s.StatLoadHidSelGst); } else { Log(("SELMLoadHiddenSelectorReg: Guest table entry is no good (%s=%#x): %.8Rhxs\n", g_aszSRegNms[iSReg], Sel, &GstDesc)); STAM_REL_COUNTER_INC(&pVCpu->CTX_SUFF(pVM)->selm.s.StatLoadHidSelGstNoGood); } } else { AssertMsg(RT_FAILURE_NP(rcStrict), ("%Rrc\n", VBOXSTRICTRC_VAL(rcStrict))); Log(("SELMLoadHiddenSelectorReg: Error reading descriptor %s=%#x: %Rrc\n", g_aszSRegNms[iSReg], Sel, VBOXSTRICTRC_VAL(rcStrict) )); STAM_REL_COUNTER_INC(&pVCpu->CTX_SUFF(pVM)->selm.s.StatLoadHidSelReadErrors); } } /** * CPUM helper that loads the hidden selector register from the descriptor table * when executing with raw-mode. * * @remarks This is only used when in legacy protected mode! * * @param pVCpu The cross context virtual CPU structure of the calling EMT. * @param pCtx The guest CPU context. * @param pSReg The selector register. * * @todo Deal 100% correctly with stale selectors. What's more evil is * invalid page table entries, which isn't impossible to imagine for * LDT entries for instance, though unlikely. Currently, we turn a * blind eye to these issues and return the old hidden registers, * though we don't set the valid flag, so that we'll try loading them * over and over again till we succeed loading something. */ VMM_INT_DECL(void) SELMLoadHiddenSelectorReg(PVMCPU pVCpu, PCCPUMCTX pCtx, PCPUMSELREG pSReg) { Assert(pCtx->cr0 & X86_CR0_PE); Assert(!(pCtx->msrEFER & MSR_K6_EFER_LMA)); PVM pVM = pVCpu->CTX_SUFF(pVM); Assert(pVM->cCpus == 1); Assert(VM_IS_RAW_MODE_ENABLED(pVM)); /* * Get the shadow descriptor table entry and validate it. * Should something go amiss, try the guest table. */ RTSEL const Sel = pSReg->Sel; uint32_t const iSReg = pSReg - CPUMCTX_FIRST_SREG(pCtx); Assert(iSReg < X86_SREG_COUNT); PCX86DESC pShwDesc; if (!(Sel & X86_SEL_LDT)) { /** @todo this shall not happen, we shall check for these things when executing * LGDT */ AssertReturnVoid((Sel | X86_SEL_RPL | X86_SEL_LDT) <= pCtx->gdtr.cbGdt); pShwDesc = &pVM->selm.s.CTX_SUFF(paGdt)[Sel >> X86_SEL_SHIFT]; if ( VMCPU_FF_IS_SET(pVCpu, VMCPU_FF_SELM_SYNC_GDT) || !selmIsShwDescGoodForSReg(pSReg, pShwDesc, iSReg, CPUMGetGuestCPL(pVCpu))) { selLoadHiddenSelectorRegFromGuestTable(pVCpu, pCtx, pSReg, pCtx->gdtr.pGdt + (Sel & X86_SEL_MASK), Sel, iSReg); return; } } else { /** @todo this shall not happen, we shall check for these things when executing * LLDT */ AssertReturnVoid((Sel | X86_SEL_RPL | X86_SEL_LDT) <= pCtx->ldtr.u32Limit); pShwDesc = (PCX86DESC)((uintptr_t)pVM->selm.s.CTX_SUFF(pvLdt) + pVM->selm.s.offLdtHyper + (Sel & X86_SEL_MASK)); if ( VMCPU_FF_IS_SET(pVCpu, VMCPU_FF_SELM_SYNC_LDT) || !selmIsShwDescGoodForSReg(pSReg, pShwDesc, iSReg, CPUMGetGuestCPL(pVCpu))) { selLoadHiddenSelectorRegFromGuestTable(pVCpu, pCtx, pSReg, pCtx->ldtr.u64Base + (Sel & X86_SEL_MASK), Sel, iSReg); return; } } /* * All fine, load it. */ selmLoadHiddenSRegFromShadowDesc(pSReg, pShwDesc); STAM_COUNTER_INC(&pVCpu->CTX_SUFF(pVM)->selm.s.StatLoadHidSelShw); Log(("SELMLoadHiddenSelectorReg: loaded %s=%#x:{b=%llx, l=%x, a=%x, vs=%x} (shw)\n", g_aszSRegNms[iSReg], Sel, pSReg->u64Base, pSReg->u32Limit, pSReg->Attr.u, pSReg->ValidSel)); } #endif /* VBOX_WITH_RAW_MODE_NOT_R0 */ /** * Validates and converts a GC selector based code address to a flat * address when in real or v8086 mode. * * @returns VINF_SUCCESS. * @param pVCpu The cross context virtual CPU structure. * @param SelCS Selector part. * @param pSReg The hidden CS register part. Optional. * @param Addr Address part. * @param ppvFlat Where to store the flat address. */ DECLINLINE(int) selmValidateAndConvertCSAddrRealMode(PVMCPU pVCpu, RTSEL SelCS, PCCPUMSELREGHID pSReg, RTGCPTR Addr, PRTGCPTR ppvFlat) { NOREF(pVCpu); uint32_t uFlat = Addr & 0xffff; if (!pSReg || !CPUMSELREG_ARE_HIDDEN_PARTS_VALID(pVCpu, pSReg)) uFlat += (uint32_t)SelCS << 4; else uFlat += (uint32_t)pSReg->u64Base; *ppvFlat = uFlat; return VINF_SUCCESS; } #ifdef VBOX_WITH_RAW_MODE_NOT_R0 /** * Validates and converts a GC selector based code address to a flat address * when in protected/long mode using the raw-mode algorithm. * * @returns VBox status code. * @param pVM The cross context VM structure. * @param pVCpu The cross context virtual CPU structure. * @param SelCPL Current privilege level. Get this from SS - CS might be * conforming! A full selector can be passed, we'll only * use the RPL part. * @param SelCS Selector part. * @param Addr Address part. * @param ppvFlat Where to store the flat address. * @param pcBits Where to store the segment bitness (16/32/64). Optional. */ DECLINLINE(int) selmValidateAndConvertCSAddrRawMode(PVM pVM, PVMCPU pVCpu, RTSEL SelCPL, RTSEL SelCS, RTGCPTR Addr, PRTGCPTR ppvFlat, uint32_t *pcBits) { NOREF(pVCpu); Assert(VM_IS_RAW_MODE_ENABLED(pVM)); /** @todo validate limit! */ X86DESC Desc; if (!(SelCS & X86_SEL_LDT)) Desc = pVM->selm.s.CTX_SUFF(paGdt)[SelCS >> X86_SEL_SHIFT]; else { /** @todo handle LDT page(s) not present! */ PX86DESC paLDT = (PX86DESC)((char *)pVM->selm.s.CTX_SUFF(pvLdt) + pVM->selm.s.offLdtHyper); Desc = paLDT[SelCS >> X86_SEL_SHIFT]; } /* * Check if present. */ if (Desc.Gen.u1Present) { /* * Type check. */ if ( Desc.Gen.u1DescType == 1 && (Desc.Gen.u4Type & X86_SEL_TYPE_CODE)) { /* * Check level. */ unsigned uLevel = RT_MAX(SelCPL & X86_SEL_RPL, SelCS & X86_SEL_RPL); if ( !(Desc.Gen.u4Type & X86_SEL_TYPE_CONF) ? uLevel <= Desc.Gen.u2Dpl : uLevel >= Desc.Gen.u2Dpl /* hope I got this right now... */ ) { /* * Limit check. */ uint32_t u32Limit = X86DESC_LIMIT_G(&Desc); if ((RTGCUINTPTR)Addr <= u32Limit) { *ppvFlat = (RTGCPTR)((RTGCUINTPTR)Addr + X86DESC_BASE(&Desc)); /* Cut the address to 32 bits. */ *ppvFlat &= 0xffffffff; if (pcBits) *pcBits = Desc.Gen.u1DefBig ? 32 : 16; /** @todo GUEST64 */ return VINF_SUCCESS; } return VERR_OUT_OF_SELECTOR_BOUNDS; } return VERR_INVALID_RPL; } return VERR_NOT_CODE_SELECTOR; } return VERR_SELECTOR_NOT_PRESENT; } #endif /* VBOX_WITH_RAW_MODE_NOT_R0 */ /** * Validates and converts a GC selector based code address to a flat address * when in protected/long mode using the standard hidden selector registers * * @returns VBox status code. * @param pVCpu The cross context virtual CPU structure. * @param SelCPL Current privilege level. Get this from SS - CS might be * conforming! A full selector can be passed, we'll only * use the RPL part. * @param SelCS Selector part. * @param pSRegCS The full CS selector register. * @param Addr The address (think IP/EIP/RIP). * @param ppvFlat Where to store the flat address upon successful return. */ DECLINLINE(int) selmValidateAndConvertCSAddrHidden(PVMCPU pVCpu, RTSEL SelCPL, RTSEL SelCS, PCCPUMSELREGHID pSRegCS, RTGCPTR Addr, PRTGCPTR ppvFlat) { NOREF(SelCPL); NOREF(SelCS); /* * Check if present. */ if (pSRegCS->Attr.n.u1Present) { /* * Type check. */ if ( pSRegCS->Attr.n.u1DescType == 1 && (pSRegCS->Attr.n.u4Type & X86_SEL_TYPE_CODE)) { /* 64 bits mode: CS, DS, ES and SS are treated as if each segment base is 0 (Intel® 64 and IA-32 Architectures Software Developer's Manual: 3.4.2.1). */ if ( pSRegCS->Attr.n.u1Long && CPUMIsGuestInLongMode(pVCpu)) { *ppvFlat = Addr; return VINF_SUCCESS; } /* * Limit check. Note that the limit in the hidden register is the * final value. The granularity bit was included in its calculation. */ uint32_t u32Limit = pSRegCS->u32Limit; if ((uint32_t)Addr <= u32Limit) { *ppvFlat = (uint32_t)Addr + (uint32_t)pSRegCS->u64Base; return VINF_SUCCESS; } return VERR_OUT_OF_SELECTOR_BOUNDS; } return VERR_NOT_CODE_SELECTOR; } return VERR_SELECTOR_NOT_PRESENT; } /** * Validates and converts a GC selector based code address to a flat address. * * @returns VBox status code. * @param pVCpu The cross context virtual CPU structure. * @param Efl Current EFLAGS. * @param SelCPL Current privilege level. Get this from SS - CS might be * conforming! A full selector can be passed, we'll only * use the RPL part. * @param SelCS Selector part. * @param pSRegCS The full CS selector register. * @param Addr The address (think IP/EIP/RIP). * @param ppvFlat Where to store the flat address upon successful return. */ VMMDECL(int) SELMValidateAndConvertCSAddr(PVMCPU pVCpu, X86EFLAGS Efl, RTSEL SelCPL, RTSEL SelCS, PCPUMSELREG pSRegCS, RTGCPTR Addr, PRTGCPTR ppvFlat) { if ( Efl.Bits.u1VM || CPUMIsGuestInRealMode(pVCpu)) return selmValidateAndConvertCSAddrRealMode(pVCpu, SelCS, pSRegCS, Addr, ppvFlat); #ifdef VBOX_WITH_RAW_MODE_NOT_R0 /* Use the hidden registers when possible, updating them if outdate. */ if (!pSRegCS) return selmValidateAndConvertCSAddrRawMode(pVCpu->CTX_SUFF(pVM), pVCpu, SelCPL, SelCS, Addr, ppvFlat, NULL); if (!CPUMSELREG_ARE_HIDDEN_PARTS_VALID(pVCpu, pSRegCS)) CPUMGuestLazyLoadHiddenSelectorReg(pVCpu, pSRegCS); /* Undo ring compression. */ if ((SelCPL & X86_SEL_RPL) == 1 && VM_IS_RAW_MODE_ENABLED(pVCpu->CTX_SUFF(pVM))) SelCPL &= ~X86_SEL_RPL; Assert(pSRegCS->Sel == SelCS); if ((SelCS & X86_SEL_RPL) == 1 && VM_IS_RAW_MODE_ENABLED(pVCpu->CTX_SUFF(pVM))) SelCS &= ~X86_SEL_RPL; #else Assert(CPUMSELREG_ARE_HIDDEN_PARTS_VALID(pVCpu, pSRegCS)); Assert(pSRegCS->Sel == SelCS); #endif return selmValidateAndConvertCSAddrHidden(pVCpu, SelCPL, SelCS, pSRegCS, Addr, ppvFlat); } /** * Returns Hypervisor's Trap 08 (\#DF) selector. * * @returns Hypervisor's Trap 08 (\#DF) selector. * @param pVM The cross context VM structure. */ VMMDECL(RTSEL) SELMGetTrap8Selector(PVM pVM) { return pVM->selm.s.aHyperSel[SELM_HYPER_SEL_TSS_TRAP08]; } /** * Sets EIP of Hypervisor's Trap 08 (\#DF) TSS. * * @param pVM The cross context VM structure. * @param u32EIP EIP of Trap 08 handler. */ VMMDECL(void) SELMSetTrap8EIP(PVM pVM, uint32_t u32EIP) { pVM->selm.s.TssTrap08.eip = u32EIP; } /** * Sets ss:esp for ring1 in main Hypervisor's TSS. * * @param pVM The cross context VM structure. * @param ss Ring1 SS register value. Pass 0 if invalid. * @param esp Ring1 ESP register value. */ void selmSetRing1Stack(PVM pVM, uint32_t ss, RTGCPTR32 esp) { Assert(VM_IS_RAW_MODE_ENABLED(pVM)); Assert((ss & 1) || esp == 0); pVM->selm.s.Tss.ss1 = ss; pVM->selm.s.Tss.esp1 = (uint32_t)esp; } #ifdef VBOX_WITH_RAW_RING1 /** * Sets ss:esp for ring1 in main Hypervisor's TSS. * * @param pVM The cross context VM structure. * @param ss Ring2 SS register value. Pass 0 if invalid. * @param esp Ring2 ESP register value. */ void selmSetRing2Stack(PVM pVM, uint32_t ss, RTGCPTR32 esp) { Assert(VM_IS_RAW_MODE_ENABLED(pVM)); Assert((ss & 3) == 2 || esp == 0); pVM->selm.s.Tss.ss2 = ss; pVM->selm.s.Tss.esp2 = (uint32_t)esp; } #endif #ifdef VBOX_WITH_RAW_MODE_NOT_R0 /** * Gets ss:esp for ring1 in main Hypervisor's TSS. * * Returns SS=0 if the ring-1 stack isn't valid. * * @returns VBox status code. * @param pVM The cross context VM structure. * @param pSS Ring1 SS register value. * @param pEsp Ring1 ESP register value. */ VMMDECL(int) SELMGetRing1Stack(PVM pVM, uint32_t *pSS, PRTGCPTR32 pEsp) { Assert(VM_IS_RAW_MODE_ENABLED(pVM)); Assert(pVM->cCpus == 1); PVMCPU pVCpu = &pVM->aCpus[0]; #ifdef SELM_TRACK_GUEST_TSS_CHANGES if (pVM->selm.s.fSyncTSSRing0Stack) { #endif RTGCPTR GCPtrTss = pVM->selm.s.GCPtrGuestTss; int rc; VBOXTSS tss; Assert(pVM->selm.s.GCPtrGuestTss && pVM->selm.s.cbMonitoredGuestTss); # ifdef IN_RC bool fTriedAlready = false; l_tryagain: PVBOXTSS pTss = (PVBOXTSS)(uintptr_t)GCPtrTss; rc = MMGCRamRead(pVM, &tss.ss0, &pTss->ss0, sizeof(tss.ss0)); rc |= MMGCRamRead(pVM, &tss.esp0, &pTss->esp0, sizeof(tss.esp0)); # ifdef DEBUG rc |= MMGCRamRead(pVM, &tss.offIoBitmap, &pTss->offIoBitmap, sizeof(tss.offIoBitmap)); # endif if (RT_FAILURE(rc)) { if (!fTriedAlready) { /* Shadow page might be out of sync. Sync and try again */ /** @todo might cross page boundary */ fTriedAlready = true; rc = PGMPrefetchPage(pVCpu, (RTGCPTR)GCPtrTss); if (rc != VINF_SUCCESS) return rc; goto l_tryagain; } AssertMsgFailed(("Unable to read TSS structure at %08X\n", GCPtrTss)); return rc; } # else /* !IN_RC */ /* Reading too much. Could be cheaper than two separate calls though. */ rc = PGMPhysSimpleReadGCPtr(pVCpu, &tss, GCPtrTss, sizeof(VBOXTSS)); if (RT_FAILURE(rc)) { AssertReleaseMsgFailed(("Unable to read TSS structure at %08X\n", GCPtrTss)); return rc; } # endif /* !IN_RC */ # ifdef LOG_ENABLED uint32_t ssr0 = pVM->selm.s.Tss.ss1; uint32_t espr0 = pVM->selm.s.Tss.esp1; ssr0 &= ~1; if (ssr0 != tss.ss0 || espr0 != tss.esp0) Log(("SELMGetRing1Stack: Updating TSS ring 0 stack to %04X:%08X\n", tss.ss0, tss.esp0)); Log(("offIoBitmap=%#x\n", tss.offIoBitmap)); # endif /* Update our TSS structure for the guest's ring 1 stack */ selmSetRing1Stack(pVM, tss.ss0 | 1, (RTGCPTR32)tss.esp0); pVM->selm.s.fSyncTSSRing0Stack = false; #ifdef SELM_TRACK_GUEST_TSS_CHANGES } #endif *pSS = pVM->selm.s.Tss.ss1; *pEsp = (RTGCPTR32)pVM->selm.s.Tss.esp1; return VINF_SUCCESS; } #endif /* VBOX_WITH_RAW_MODE_NOT_R0 */ #if defined(VBOX_WITH_RAW_MODE) || (HC_ARCH_BITS != 64 && defined(VBOX_WITH_64_BITS_GUESTS)) /** * Gets the hypervisor code selector (CS). * @returns CS selector. * @param pVM The cross context VM structure. */ VMMDECL(RTSEL) SELMGetHyperCS(PVM pVM) { return pVM->selm.s.aHyperSel[SELM_HYPER_SEL_CS]; } /** * Gets the 64-mode hypervisor code selector (CS64). * @returns CS selector. * @param pVM The cross context VM structure. */ VMMDECL(RTSEL) SELMGetHyperCS64(PVM pVM) { return pVM->selm.s.aHyperSel[SELM_HYPER_SEL_CS64]; } /** * Gets the hypervisor data selector (DS). * @returns DS selector. * @param pVM The cross context VM structure. */ VMMDECL(RTSEL) SELMGetHyperDS(PVM pVM) { return pVM->selm.s.aHyperSel[SELM_HYPER_SEL_DS]; } /** * Gets the hypervisor TSS selector. * @returns TSS selector. * @param pVM The cross context VM structure. */ VMMDECL(RTSEL) SELMGetHyperTSS(PVM pVM) { return pVM->selm.s.aHyperSel[SELM_HYPER_SEL_TSS]; } /** * Gets the hypervisor TSS Trap 8 selector. * @returns TSS Trap 8 selector. * @param pVM The cross context VM structure. */ VMMDECL(RTSEL) SELMGetHyperTSSTrap08(PVM pVM) { return pVM->selm.s.aHyperSel[SELM_HYPER_SEL_TSS_TRAP08]; } /** * Gets the address for the hypervisor GDT. * * @returns The GDT address. * @param pVM The cross context VM structure. * @remark This is intended only for very special use, like in the world * switchers. Don't exploit this API! */ VMMDECL(RTRCPTR) SELMGetHyperGDT(PVM pVM) { /* * Always convert this from the HC pointer since we can be * called before the first relocation and have to work correctly * without having dependencies on the relocation order. */ return (RTRCPTR)MMHyperR3ToRC(pVM, pVM->selm.s.paGdtR3); } #endif /* defined(VBOX_WITH_RAW_MODE) || (HC_ARCH_BITS != 64 && defined(VBOX_WITH_64_BITS_GUESTS)) */ /** * Gets info about the current TSS. * * @returns VBox status code. * @retval VINF_SUCCESS if we've got a TSS loaded. * @retval VERR_SELM_NO_TSS if we haven't got a TSS (rather unlikely). * * @param pVM The cross context VM structure. * @param pVCpu The cross context virtual CPU structure. * @param pGCPtrTss Where to store the TSS address. * @param pcbTss Where to store the TSS size limit. * @param pfCanHaveIOBitmap Where to store the can-have-I/O-bitmap indicator. (optional) */ VMMDECL(int) SELMGetTSSInfo(PVM pVM, PVMCPU pVCpu, PRTGCUINTPTR pGCPtrTss, PRTGCUINTPTR pcbTss, bool *pfCanHaveIOBitmap) { NOREF(pVM); /* * The TR hidden register is always valid. */ CPUMSELREGHID trHid; RTSEL tr = CPUMGetGuestTR(pVCpu, &trHid); if (!(tr & X86_SEL_MASK_OFF_RPL)) return VERR_SELM_NO_TSS; *pGCPtrTss = trHid.u64Base; *pcbTss = trHid.u32Limit + (trHid.u32Limit != UINT32_MAX); /* be careful. */ if (pfCanHaveIOBitmap) *pfCanHaveIOBitmap = trHid.Attr.n.u4Type == X86_SEL_TYPE_SYS_386_TSS_AVAIL || trHid.Attr.n.u4Type == X86_SEL_TYPE_SYS_386_TSS_BUSY; return VINF_SUCCESS; } /** * Notification callback which is called whenever there is a chance that a CR3 * value might have changed. * This is called by PGM. * * @param pVM The cross context VM structure. * @param pVCpu The cross context virtual CPU structure. */ VMMDECL(void) SELMShadowCR3Changed(PVM pVM, PVMCPU pVCpu) { /** @todo SMP support!! (64-bit guest scenario, primarily) */ pVM->selm.s.Tss.cr3 = PGMGetHyperCR3(pVCpu); pVM->selm.s.TssTrap08.cr3 = PGMGetInterRCCR3(pVM, pVCpu); }