/** * 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 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: "); }
/** * \#GP (General Protection Fault) handler. * * @returns VBox status code. * VINF_SUCCESS means we completely handled this trap, * other codes are passed execution to host context. * * @param pVM Pointer to the VM. * @param pTrpmCpu Pointer to TRPMCPU data (within VM). * @param pRegFrame Pointer to the register frame for the trap. */ static int trpmGCTrap0dHandler(PVM pVM, PTRPMCPU pTrpmCpu, PCPUMCTXCORE pRegFrame) { LogFlow(("trpmGCTrap0dHandler: cs:eip=%RTsel:%08RX32 uErr=%RGv\n", pRegFrame->cs.Sel, pRegFrame->eip, pTrpmCpu->uActiveErrorCode)); PVMCPU pVCpu = TRPMCPU_2_VMCPU(pTrpmCpu); /* * Convert and validate CS. */ STAM_PROFILE_START(&pVM->trpm.s.StatTrap0dDisasm, a); RTGCPTR PC; uint32_t cBits; int rc = SELMValidateAndConvertCSAddrGCTrap(pVCpu, pRegFrame->eflags, pRegFrame->ss.Sel, pRegFrame->cs.Sel, pRegFrame->rip, &PC, &cBits); if (RT_FAILURE(rc)) { Log(("trpmGCTrap0dHandler: Failed to convert %RTsel:%RX32 (cpl=%d) - rc=%Rrc !!\n", pRegFrame->cs.Sel, pRegFrame->eip, pRegFrame->ss.Sel & X86_SEL_RPL, rc)); STAM_PROFILE_STOP(&pVM->trpm.s.StatTrap0dDisasm, a); return trpmGCExitTrap(pVM, pVCpu, VINF_EM_RAW_EMULATE_INSTR, pRegFrame); } /* * Disassemble the instruction. */ DISCPUSTATE Cpu; uint32_t cbOp; rc = EMInterpretDisasOneEx(pVM, pVCpu, PC, pRegFrame, &Cpu, &cbOp); if (RT_FAILURE(rc)) { AssertMsgFailed(("DISCoreOneEx failed to PC=%RGv rc=%Rrc\n", PC, rc)); STAM_PROFILE_STOP(&pVM->trpm.s.StatTrap0dDisasm, a); return trpmGCExitTrap(pVM, pVCpu, VINF_EM_RAW_EMULATE_INSTR, pRegFrame); } STAM_PROFILE_STOP(&pVM->trpm.s.StatTrap0dDisasm, a); /* * Optimize RDTSC traps. * Some guests (like Solaris) are using RDTSC all over the place and * will end up trapping a *lot* because of that. * * Note: it's no longer safe to access the instruction opcode directly due to possible stale code TLB entries */ if (Cpu.pCurInstr->uOpcode == OP_RDTSC) return trpmGCTrap0dHandlerRdTsc(pVM, pVCpu, pRegFrame); /* * Deal with I/O port access. */ if ( pVCpu->trpm.s.uActiveErrorCode == 0 && (Cpu.pCurInstr->fOpType & DISOPTYPE_PORTIO)) { VBOXSTRICTRC rcStrict = IOMRCIOPortHandler(pVM, pRegFrame, &Cpu); if (IOM_SUCCESS(rcStrict)) pRegFrame->rip += cbOp; rc = VBOXSTRICTRC_TODO(rcStrict); return trpmGCExitTrap(pVM, pVCpu, rc, pRegFrame); } /* * Deal with Ring-0 (privileged instructions) */ if ( (pRegFrame->ss.Sel & X86_SEL_RPL) <= 1 && !pRegFrame->eflags.Bits.u1VM) return trpmGCTrap0dHandlerRing0(pVM, pVCpu, pRegFrame, &Cpu, PC); /* * Deal with Ring-3 GPs. */ if (!pRegFrame->eflags.Bits.u1VM) return trpmGCTrap0dHandlerRing3(pVM, pVCpu, pRegFrame, &Cpu, PC); /* * Deal with v86 code. * * We always set IOPL to zero which makes e.g. pushf fault in V86 * mode. The guest might use IOPL=3 and therefore not expect a #GP. * Simply fall back to the recompiler to emulate this instruction if * that's the case. To get the correct we must use CPUMRawGetEFlags. */ X86EFLAGS eflags; eflags.u32 = CPUMRawGetEFlags(pVCpu); /* Get the correct value. */ Log3(("TRPM #GP V86: cs:eip=%04x:%08x IOPL=%d efl=%08x\n", pRegFrame->cs.Sel, pRegFrame->eip, eflags.Bits.u2IOPL, eflags.u)); if (eflags.Bits.u2IOPL != 3) { Assert(eflags.Bits.u2IOPL == 0); rc = TRPMForwardTrap(pVCpu, pRegFrame, 0xD, 0, TRPM_TRAP_HAS_ERRORCODE, TRPM_TRAP, 0xd); Assert(rc == VINF_EM_RAW_GUEST_TRAP); return trpmGCExitTrap(pVM, pVCpu, rc, pRegFrame); } return trpmGCExitTrap(pVM, pVCpu, VINF_EM_RAW_EMULATE_INSTR, pRegFrame); }