/** * Executes one (or perhaps a few more) IO instruction(s). * * @returns VBox status code suitable for EM. * @param pVM The cross context VM structure. * @param pVCpu The cross context virtual CPU structure. */ static int emR3HmExecuteIOInstruction(PVM pVM, PVMCPU pVCpu) { PCPUMCTX pCtx = pVCpu->em.s.pCtx; STAM_PROFILE_START(&pVCpu->em.s.StatIOEmu, a); /* * Try to restart the io instruction that was refused in ring-0. */ VBOXSTRICTRC rcStrict = HMR3RestartPendingIOInstr(pVM, pVCpu, pCtx); if (IOM_SUCCESS(rcStrict)) { STAM_COUNTER_INC(&pVCpu->em.s.CTX_SUFF(pStats)->StatIoRestarted); STAM_PROFILE_STOP(&pVCpu->em.s.StatIOEmu, a); return VBOXSTRICTRC_TODO(rcStrict); /* rip already updated. */ } AssertMsgReturn(rcStrict == VERR_NOT_FOUND, ("%Rrc\n", VBOXSTRICTRC_VAL(rcStrict)), RT_SUCCESS_NP(rcStrict) ? VERR_IPE_UNEXPECTED_INFO_STATUS : VBOXSTRICTRC_TODO(rcStrict)); /* * Hand it over to the interpreter. */ rcStrict = IEMExecOne(pVCpu); LogFlow(("emR3HmExecuteIOInstruction: %Rrc\n", VBOXSTRICTRC_VAL(rcStrict))); STAM_COUNTER_INC(&pVCpu->em.s.CTX_SUFF(pStats)->StatIoIem); STAM_PROFILE_STOP(&pVCpu->em.s.StatIOEmu, a); return VBOXSTRICTRC_TODO(rcStrict); }
/** * Executes one (or perhaps a few more) IO instruction(s). * * @returns VBox status code suitable for EM. * @param pVM The cross context VM structure. * @param pVCpu The cross context virtual CPU structure. */ static int emR3HmExecuteIOInstruction(PVM pVM, PVMCPU pVCpu) { RT_NOREF(pVM); STAM_PROFILE_START(&pVCpu->em.s.StatIOEmu, a); VBOXSTRICTRC rcStrict; uint32_t idxContinueExitRec = pVCpu->em.s.idxContinueExitRec; RT_UNTRUSTED_NONVOLATILE_COPY_FENCE(); if (idxContinueExitRec >= RT_ELEMENTS(pVCpu->em.s.aExitRecords)) { /* * Hand it over to the interpreter. */ CPUM_IMPORT_EXTRN_RET(pVCpu, IEM_CPUMCTX_EXTRN_MUST_MASK); rcStrict = IEMExecOne(pVCpu); LogFlow(("emR3HmExecuteIOInstruction: %Rrc\n", VBOXSTRICTRC_VAL(rcStrict))); } else { RT_UNTRUSTED_VALIDATED_FENCE(); CPUM_IMPORT_EXTRN_RET(pVCpu, IEM_CPUMCTX_EXTRN_MUST_MASK); rcStrict = EMHistoryExec(pVCpu, &pVCpu->em.s.aExitRecords[idxContinueExitRec], 0); LogFlow(("emR3HmExecuteIOInstruction: %Rrc (EMHistoryExec)\n", VBOXSTRICTRC_VAL(rcStrict))); STAM_COUNTER_INC(&pVCpu->em.s.CTX_SUFF(pStats)->StatIoRestarted); } STAM_COUNTER_INC(&pVCpu->em.s.CTX_SUFF(pStats)->StatIoIem); STAM_PROFILE_STOP(&pVCpu->em.s.StatIOEmu, a); return VBOXSTRICTRC_TODO(rcStrict); }
/** @interface_method_impl{PDMDEVHLPR0,pfnPhysWrite} */ static DECLCALLBACK(int) pdmR0DevHlp_PhysWrite(PPDMDEVINS pDevIns, RTGCPHYS GCPhys, const void *pvBuf, size_t cbWrite) { PDMDEV_ASSERT_DEVINS(pDevIns); LogFlow(("pdmR0DevHlp_PhysWrite: caller=%p/%d: GCPhys=%RGp pvBuf=%p cbWrite=%#x\n", pDevIns, pDevIns->iInstance, GCPhys, pvBuf, cbWrite)); VBOXSTRICTRC rcStrict = PGMPhysWrite(pDevIns->Internal.s.pVMR0, GCPhys, pvBuf, cbWrite, PGMACCESSORIGIN_DEVICE); AssertMsg(rcStrict == VINF_SUCCESS, ("%Rrc\n", VBOXSTRICTRC_VAL(rcStrict))); /** @todo track down the users for this bugger. */ Log(("pdmR0DevHlp_PhysWrite: caller=%p/%d: returns %Rrc\n", pDevIns, pDevIns->iInstance, VBOXSTRICTRC_VAL(rcStrict) )); return VBOXSTRICTRC_VAL(rcStrict); }
static int emR3HmExecuteInstructionWorker(PVM pVM, PVMCPU pVCpu, int rcRC) #endif { NOREF(rcRC); #ifdef LOG_ENABLED /* * Log it. */ Log(("EMINS: %04x:%RGv RSP=%RGv\n", pVCpu->cpum.GstCtx.cs.Sel, (RTGCPTR)pVCpu->cpum.GstCtx.rip, (RTGCPTR)pVCpu->cpum.GstCtx.rsp)); if (pszPrefix) { DBGFR3_INFO_LOG(pVM, pVCpu, "cpumguest", pszPrefix); DBGFR3_DISAS_INSTR_CUR_LOG(pVCpu, pszPrefix); } #endif /* * Use IEM and fallback on REM if the functionality is missing. * Once IEM gets mature enough, nothing should ever fall back. */ STAM_PROFILE_START(&pVCpu->em.s.StatIEMEmu, a); VBOXSTRICTRC rcStrict; uint32_t idxContinueExitRec = pVCpu->em.s.idxContinueExitRec; RT_UNTRUSTED_NONVOLATILE_COPY_FENCE(); if (idxContinueExitRec >= RT_ELEMENTS(pVCpu->em.s.aExitRecords)) { CPUM_IMPORT_EXTRN_RET(pVCpu, IEM_CPUMCTX_EXTRN_MUST_MASK); rcStrict = VBOXSTRICTRC_TODO(IEMExecOne(pVCpu)); } else { RT_UNTRUSTED_VALIDATED_FENCE(); rcStrict = EMHistoryExec(pVCpu, &pVCpu->em.s.aExitRecords[idxContinueExitRec], 0); LogFlow(("emR3HmExecuteInstruction: %Rrc (EMHistoryExec)\n", VBOXSTRICTRC_VAL(rcStrict))); } STAM_PROFILE_STOP(&pVCpu->em.s.StatIEMEmu, a); if ( rcStrict == VERR_IEM_ASPECT_NOT_IMPLEMENTED || rcStrict == VERR_IEM_INSTR_NOT_IMPLEMENTED) { #ifdef VBOX_WITH_REM STAM_PROFILE_START(&pVCpu->em.s.StatREMEmu, b); EMRemLock(pVM); /* Flush the recompiler TLB if the VCPU has changed. */ if (pVM->em.s.idLastRemCpu != pVCpu->idCpu) CPUMSetChangedFlags(pVCpu, CPUM_CHANGED_ALL); pVM->em.s.idLastRemCpu = pVCpu->idCpu; rcStrict = REMR3EmulateInstruction(pVM, pVCpu); EMRemUnlock(pVM); STAM_PROFILE_STOP(&pVCpu->em.s.StatREMEmu, b); #else /* !VBOX_WITH_REM */ NOREF(pVM); #endif /* !VBOX_WITH_REM */ } return VBOXSTRICTRC_TODO(rcStrict); }
/** * Wrapper which does the read and updates range statistics when such are enabled. */ DECLINLINE(VBOXSTRICTRC) iomMMIODoRead(PVM pVM, PVMCPU pVCpu, PIOMMMIORANGE pRange, RTGCPHYS GCPhys, void *pvValue, unsigned cbValue) { #ifdef VBOX_WITH_STATISTICS int rcSem = IOM_LOCK_SHARED(pVM); if (rcSem == VERR_SEM_BUSY) return VINF_IOM_R3_MMIO_READ; PIOMMMIOSTATS pStats = iomMmioGetStats(pVM, pVCpu, GCPhys, pRange); if (!pStats) # ifdef IN_RING3 return VERR_NO_MEMORY; # else return VINF_IOM_R3_MMIO_READ; # endif STAM_PROFILE_START(&pStats->CTX_SUFF_Z(ProfRead), a); #else NOREF(pVCpu); #endif VBOXSTRICTRC rcStrict; if (RT_LIKELY(pRange->CTX_SUFF(pfnReadCallback))) { if ( ( cbValue == 4 && !(GCPhys & 3)) || (pRange->fFlags & IOMMMIO_FLAGS_READ_MODE) == IOMMMIO_FLAGS_READ_PASSTHRU || ( cbValue == 8 && !(GCPhys & 7) && (pRange->fFlags & IOMMMIO_FLAGS_READ_MODE) == IOMMMIO_FLAGS_READ_DWORD_QWORD ) ) rcStrict = pRange->CTX_SUFF(pfnReadCallback)(pRange->CTX_SUFF(pDevIns), pRange->CTX_SUFF(pvUser), GCPhys, pvValue, cbValue); else rcStrict = iomMMIODoComplicatedRead(pVM, pRange, GCPhys, pvValue, cbValue); } else rcStrict = VINF_IOM_MMIO_UNUSED_FF; if (rcStrict != VINF_SUCCESS) { switch (VBOXSTRICTRC_VAL(rcStrict)) { case VINF_IOM_MMIO_UNUSED_FF: rcStrict = iomMMIODoReadFFs(pvValue, cbValue); break; case VINF_IOM_MMIO_UNUSED_00: rcStrict = iomMMIODoRead00s(pvValue, cbValue); break; } } STAM_PROFILE_STOP(&pStats->CTX_SUFF_Z(ProfRead), a); STAM_COUNTER_INC(&pStats->Accesses); return rcStrict; }
/** * Physical access handler for MMIO ranges. * * @returns VBox status code (appropriate for GC return). * @param pVM The cross context VM structure. * @param pVCpu The cross context virtual CPU structure of the calling EMT. * @param uErrorCode CPU Error code. * @param pCtxCore Trap register frame. * @param GCPhysFault The GC physical address. */ VMMDECL(VBOXSTRICTRC) IOMMMIOPhysHandler(PVM pVM, PVMCPU pVCpu, RTGCUINT uErrorCode, PCPUMCTXCORE pCtxCore, RTGCPHYS GCPhysFault) { /* * We don't have a range here, so look it up before calling the common function. */ int rc2 = IOM_LOCK_SHARED(pVM); NOREF(rc2); #ifndef IN_RING3 if (rc2 == VERR_SEM_BUSY) return VINF_IOM_R3_MMIO_READ_WRITE; #endif PIOMMMIORANGE pRange = iomMmioGetRange(pVM, pVCpu, GCPhysFault); if (RT_UNLIKELY(!pRange)) { IOM_UNLOCK_SHARED(pVM); return VERR_IOM_MMIO_RANGE_NOT_FOUND; } iomMmioRetainRange(pRange); IOM_UNLOCK_SHARED(pVM); VBOXSTRICTRC rcStrict = iomMmioCommonPfHandler(pVM, pVCpu, (uint32_t)uErrorCode, pCtxCore, GCPhysFault, pRange); iomMmioReleaseRange(pVM, pRange); return VBOXSTRICTRC_VAL(rcStrict); }
/** * Executes instruction in HM mode if we can. * * This is somewhat comparable to REMR3EmulateInstruction. * * @returns VBox strict status code. * @retval VINF_EM_DBG_STEPPED on success. * @retval VERR_EM_CANNOT_EXEC_GUEST if we cannot execute guest instructions in * HM right now. * * @param pVM The cross context VM structure. * @param pVCpu The cross context virtual CPU structure for the calling EMT. * @param fFlags Combinations of EM_ONE_INS_FLAGS_XXX. * @thread EMT. */ VMMR3_INT_DECL(VBOXSTRICTRC) EMR3HmSingleInstruction(PVM pVM, PVMCPU pVCpu, uint32_t fFlags) { PCPUMCTX pCtx = pVCpu->em.s.pCtx; Assert(!(fFlags & ~EM_ONE_INS_FLAGS_MASK)); if (!HMR3CanExecuteGuest(pVM, pCtx)) return VINF_EM_RESCHEDULE; uint64_t const uOldRip = pCtx->rip; for (;;) { /* * Service necessary FFs before going into HM. */ if ( VM_FF_IS_PENDING(pVM, VM_FF_HIGH_PRIORITY_PRE_RAW_MASK) || VMCPU_FF_IS_PENDING(pVCpu, VMCPU_FF_HIGH_PRIORITY_PRE_RAW_MASK)) { VBOXSTRICTRC rcStrict = emR3HmForcedActions(pVM, pVCpu, pCtx); if (rcStrict != VINF_SUCCESS) { Log(("EMR3HmSingleInstruction: FFs before -> %Rrc\n", VBOXSTRICTRC_VAL(rcStrict))); return rcStrict; } } /* * Go execute it. */ bool fOld = HMSetSingleInstruction(pVM, pVCpu, true); VBOXSTRICTRC rcStrict = VMMR3HmRunGC(pVM, pVCpu); HMSetSingleInstruction(pVM, pVCpu, fOld); LogFlow(("EMR3HmSingleInstruction: %Rrc\n", VBOXSTRICTRC_VAL(rcStrict))); /* * Handle high priority FFs and informational status codes. We don't do * normal FF processing the caller or the next call can deal with them. */ VMCPU_FF_CLEAR(pVCpu, VMCPU_FF_RESUME_GUEST_MASK); if ( VM_FF_IS_PENDING(pVM, VM_FF_HIGH_PRIORITY_POST_MASK) || VMCPU_FF_IS_PENDING(pVCpu, VMCPU_FF_HIGH_PRIORITY_POST_MASK)) { rcStrict = emR3HighPriorityPostForcedActions(pVM, pVCpu, VBOXSTRICTRC_TODO(rcStrict)); LogFlow(("EMR3HmSingleInstruction: FFs after -> %Rrc\n", VBOXSTRICTRC_VAL(rcStrict))); } if (rcStrict != VINF_SUCCESS && (rcStrict < VINF_EM_FIRST || rcStrict > VINF_EM_LAST)) { rcStrict = emR3HmHandleRC(pVM, pVCpu, pCtx, VBOXSTRICTRC_TODO(rcStrict)); Log(("EMR3HmSingleInstruction: emR3HmHandleRC -> %Rrc\n", VBOXSTRICTRC_VAL(rcStrict))); } /* * Done? */ if ( (rcStrict != VINF_SUCCESS && rcStrict != VINF_EM_DBG_STEPPED) || !(fFlags & EM_ONE_INS_FLAGS_RIP_CHANGE) || pCtx->rip != uOldRip) { if (rcStrict == VINF_SUCCESS && pCtx->rip != uOldRip) rcStrict = VINF_EM_DBG_STEPPED; Log(("EMR3HmSingleInstruction: returns %Rrc (rip %llx -> %llx)\n", VBOXSTRICTRC_VAL(rcStrict), uOldRip, pCtx->rip)); return rcStrict; } } }
/** * Executes one (or perhaps a few more) IO instruction(s). * * @returns VBox status code suitable for EM. * @param pVM Pointer to the VM. * @param pVCpu Pointer to the VMCPU. */ static int emR3ExecuteIOInstruction(PVM pVM, PVMCPU pVCpu) { PCPUMCTX pCtx = pVCpu->em.s.pCtx; STAM_PROFILE_START(&pVCpu->em.s.StatIOEmu, a); /* Try to restart the io instruction that was refused in ring-0. */ VBOXSTRICTRC rcStrict = HWACCMR3RestartPendingIOInstr(pVM, pVCpu, pCtx); if (IOM_SUCCESS(rcStrict)) { STAM_COUNTER_INC(&pVCpu->em.s.CTX_SUFF(pStats)->StatIoRestarted); STAM_PROFILE_STOP(&pVCpu->em.s.StatIOEmu, a); return VBOXSTRICTRC_TODO(rcStrict); /* rip already updated. */ } AssertMsgReturn(rcStrict == VERR_NOT_FOUND, ("%Rrc\n", VBOXSTRICTRC_VAL(rcStrict)), RT_SUCCESS_NP(rcStrict) ? VERR_IPE_UNEXPECTED_INFO_STATUS : VBOXSTRICTRC_TODO(rcStrict)); /** @todo probably we should fall back to the recompiler; otherwise we'll go back and forth between HC & GC * as io instructions tend to come in packages of more than one */ DISCPUSTATE Cpu; int rc2 = CPUMR3DisasmInstrCPU(pVM, pVCpu, pCtx, pCtx->rip, &Cpu, "IO EMU"); if (RT_SUCCESS(rc2)) { rcStrict = VINF_EM_RAW_EMULATE_INSTR; if (!(Cpu.fPrefix & (DISPREFIX_REP | DISPREFIX_REPNE))) { switch (Cpu.pCurInstr->uOpcode) { case OP_IN: { STAM_COUNTER_INC(&pVCpu->em.s.CTX_SUFF(pStats)->StatIn); rcStrict = IOMInterpretIN(pVM, CPUMCTX2CORE(pCtx), &Cpu); break; } case OP_OUT: { STAM_COUNTER_INC(&pVCpu->em.s.CTX_SUFF(pStats)->StatOut); rcStrict = IOMInterpretOUT(pVM, CPUMCTX2CORE(pCtx), &Cpu); break; } } } else if (Cpu.fPrefix & DISPREFIX_REP) { switch (Cpu.pCurInstr->uOpcode) { case OP_INSB: case OP_INSWD: { STAM_COUNTER_INC(&pVCpu->em.s.CTX_SUFF(pStats)->StatIn); rcStrict = IOMInterpretINS(pVM, CPUMCTX2CORE(pCtx), &Cpu); break; } case OP_OUTSB: case OP_OUTSWD: { STAM_COUNTER_INC(&pVCpu->em.s.CTX_SUFF(pStats)->StatOut); rcStrict = IOMInterpretOUTS(pVM, CPUMCTX2CORE(pCtx), &Cpu); break; } } } /* * Handled the I/O return codes. * (The unhandled cases end up with rcStrict == VINF_EM_RAW_EMULATE_INSTR.) */ if (IOM_SUCCESS(rcStrict)) { pCtx->rip += Cpu.cbInstr; STAM_PROFILE_STOP(&pVCpu->em.s.StatIOEmu, a); return VBOXSTRICTRC_TODO(rcStrict); } if (rcStrict == VINF_EM_RAW_GUEST_TRAP) { /* The active trap will be dispatched. */ Assert(TRPMHasTrap(pVCpu)); STAM_PROFILE_STOP(&pVCpu->em.s.StatIOEmu, a); return VINF_SUCCESS; } AssertMsg(rcStrict != VINF_TRPM_XCPT_DISPATCHED, ("Handle VINF_TRPM_XCPT_DISPATCHED\n")); if (RT_FAILURE(rcStrict)) { STAM_PROFILE_STOP(&pVCpu->em.s.StatIOEmu, a); return VBOXSTRICTRC_TODO(rcStrict); } AssertMsg(rcStrict == VINF_EM_RAW_EMULATE_INSTR || rcStrict == VINF_EM_RESCHEDULE_REM, ("rcStrict=%Rrc\n", VBOXSTRICTRC_VAL(rcStrict))); } STAM_PROFILE_STOP(&pVCpu->em.s.StatIOEmu, a); return emR3ExecuteInstruction(pVM, pVCpu, "IO: "); }
/** * @callback_method_impl{FNPGMPHYSHANDLER, MMIO page accesses} * * @remarks The @a pvUser argument points to the MMIO range entry. */ PGM_ALL_CB2_DECL(VBOXSTRICTRC) iomMmioHandler(PVM pVM, PVMCPU pVCpu, RTGCPHYS GCPhysFault, void *pvPhys, void *pvBuf, size_t cbBuf, PGMACCESSTYPE enmAccessType, PGMACCESSORIGIN enmOrigin, void *pvUser) { PIOMMMIORANGE pRange = (PIOMMMIORANGE)pvUser; STAM_COUNTER_INC(&pVM->iom.s.StatR3MMIOHandler); NOREF(pvPhys); NOREF(enmOrigin); AssertPtr(pRange); AssertMsg(cbBuf >= 1, ("%zu\n", cbBuf)); #ifndef IN_RING3 /* * If someone is doing FXSAVE, FXRSTOR, XSAVE, XRSTOR or other stuff dealing with * large amounts of data, just go to ring-3 where we don't need to deal with partial * successes. No chance any of these will be problematic read-modify-write stuff. */ if (cbBuf > sizeof(pVCpu->iom.s.PendingMmioWrite.abValue)) return enmAccessType == PGMACCESSTYPE_WRITE ? VINF_IOM_R3_MMIO_WRITE : VINF_IOM_R3_MMIO_READ; #endif /* * Validate the range. */ int rc = IOM_LOCK_SHARED(pVM); #ifndef IN_RING3 if (rc == VERR_SEM_BUSY) { if (enmAccessType == PGMACCESSTYPE_READ) return VINF_IOM_R3_MMIO_READ; Assert(enmAccessType == PGMACCESSTYPE_WRITE); return iomMmioRing3WritePending(pVCpu, GCPhysFault, pvBuf, cbBuf, NULL /*pRange*/); } #endif AssertRC(rc); Assert(pRange == iomMmioGetRange(pVM, pVCpu, GCPhysFault)); /* * Perform locking. */ iomMmioRetainRange(pRange); PPDMDEVINS pDevIns = pRange->CTX_SUFF(pDevIns); IOM_UNLOCK_SHARED(pVM); VBOXSTRICTRC rcStrict = PDMCritSectEnter(pDevIns->CTX_SUFF(pCritSectRo), VINF_IOM_R3_MMIO_READ_WRITE); if (rcStrict == VINF_SUCCESS) { /* * Perform the access. */ if (enmAccessType == PGMACCESSTYPE_READ) rcStrict = iomMMIODoRead(pVM, pVCpu, pRange, GCPhysFault, pvBuf, (unsigned)cbBuf); else { rcStrict = iomMMIODoWrite(pVM, pVCpu, pRange, GCPhysFault, pvBuf, (unsigned)cbBuf); #ifndef IN_RING3 if (rcStrict == VINF_IOM_R3_MMIO_WRITE) rcStrict = iomMmioRing3WritePending(pVCpu, GCPhysFault, pvBuf, cbBuf, pRange); #endif } /* Check the return code. */ #ifdef IN_RING3 AssertMsg(rcStrict == VINF_SUCCESS, ("%Rrc - %RGp - %s\n", VBOXSTRICTRC_VAL(rcStrict), GCPhysFault, pRange->pszDesc)); #else AssertMsg( rcStrict == VINF_SUCCESS || rcStrict == (enmAccessType == PGMACCESSTYPE_READ ? VINF_IOM_R3_MMIO_READ : VINF_IOM_R3_MMIO_WRITE) || (rcStrict == VINF_IOM_R3_MMIO_COMMIT_WRITE && enmAccessType == PGMACCESSTYPE_WRITE) || rcStrict == VINF_IOM_R3_MMIO_READ_WRITE || rcStrict == VINF_EM_DBG_STOP || rcStrict == VINF_EM_DBG_EVENT || rcStrict == VINF_EM_DBG_BREAKPOINT || rcStrict == VINF_EM_OFF || rcStrict == VINF_EM_SUSPEND || rcStrict == VINF_EM_RESET || rcStrict == VINF_EM_RAW_EMULATE_IO_BLOCK //|| rcStrict == VINF_EM_HALT /* ?? */ //|| rcStrict == VINF_EM_NO_MEMORY /* ?? */ , ("%Rrc - %RGp - %p\n", VBOXSTRICTRC_VAL(rcStrict), GCPhysFault, pDevIns)); #endif iomMmioReleaseRange(pVM, pRange); PDMCritSectLeave(pDevIns->CTX_SUFF(pCritSectRo)); } #ifdef IN_RING3 else iomMmioReleaseRange(pVM, pRange); #else else { if (rcStrict == VINF_IOM_R3_MMIO_READ_WRITE)