/** * \#PF Virtual Handler callback for Guest write access to the VBox shadow IDT. * * @returns VBox status code (appropriate for trap handling and GC return). * @param pVM VM Handle. * @param uErrorCode CPU Error code. * @param pRegFrame Trap register frame. * @param pvFault The fault address (cr2). * @param pvRange The base address of the handled virtual range. * @param offRange The offset of the access into this range. * (If it's a EIP range this is the EIP, if not it's pvFault.) */ VMMRCDECL(int) trpmRCShadowIDTWriteHandler(PVM pVM, RTGCUINT uErrorCode, PCPUMCTXCORE pRegFrame, RTGCPTR pvFault, RTGCPTR pvRange, uintptr_t offRange) { PVMCPU pVCpu = VMMGetCpu0(pVM); LogRel(("FATAL ERROR: trpmRCShadowIDTWriteHandler: eip=%08X pvFault=%RGv pvRange=%08X\r\n", pRegFrame->eip, pvFault, pvRange)); /* If we ever get here, then the guest has executed an sidt instruction that we failed to patch. In theory this could be very bad, but * there are nasty applications out there that install device drivers that mess with the guest's IDT. In those cases, it's quite ok * to simply ignore the writes and pretend success. */ RTGCPTR PC; int rc = SELMValidateAndConvertCSAddr(pVM, pRegFrame->eflags, pRegFrame->ss, pRegFrame->cs, &pRegFrame->csHid, (RTGCPTR)pRegFrame->eip, &PC); if (rc == VINF_SUCCESS) { DISCPUSTATE Cpu; uint32_t cbOp; rc = EMInterpretDisasOneEx(pVM, pVCpu, (RTGCUINTPTR)PC, pRegFrame, &Cpu, &cbOp); if (rc == VINF_SUCCESS) { /* Just ignore the write. */ pRegFrame->eip += Cpu.opsize; return VINF_SUCCESS; } } return VERR_TRPM_SHADOW_IDT_WRITE; }
/** * \#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); }
/** * Trap handler for illegal opcode fault (\#UD). * * @returns VBox status code. * VINF_SUCCESS means we completely handled this trap, * other codes are passed execution to host context. * * @param pTrpmCpu Pointer to TRPMCPU data (within VM). * @param pRegFrame Pointer to the register frame for the trap. * @internal */ DECLASM(int) TRPMGCTrap06Handler(PTRPMCPU pTrpmCpu, PCPUMCTXCORE pRegFrame) { LogFlow(("TRPMGC06: %04x:%08x efl=%x\n", pRegFrame->cs.Sel, pRegFrame->eip, pRegFrame->eflags.u32)); PVM pVM = TRPMCPU_2_VM(pTrpmCpu); PVMCPU pVCpu = TRPMCPU_2_VMCPU(pTrpmCpu); int rc; PGMRZDynMapStartAutoSet(pVCpu); if (CPUMGetGuestCPL(pVCpu) == 0) { /* * Decode the instruction. */ RTGCPTR PC; rc = SELMValidateAndConvertCSAddr(pVCpu, pRegFrame->eflags, pRegFrame->ss.Sel, pRegFrame->cs.Sel, &pRegFrame->cs, pRegFrame->rip, &PC); if (RT_FAILURE(rc)) { Log(("TRPMGCTrap06Handler: Failed to convert %RTsel:%RX32 (cpl=%d) - rc=%Rrc !!\n", pRegFrame->cs.Sel, pRegFrame->eip, pRegFrame->ss.Sel & X86_SEL_RPL, rc)); rc = trpmGCExitTrap(pVM, pVCpu, VINF_EM_RAW_GUEST_TRAP, pRegFrame); Log6(("TRPMGC06: %Rrc (%04x:%08x) (SELM)\n", rc, pRegFrame->cs.Sel, pRegFrame->eip)); return rc; } DISCPUSTATE Cpu; uint32_t cbOp; rc = EMInterpretDisasOneEx(pVM, pVCpu, (RTGCUINTPTR)PC, pRegFrame, &Cpu, &cbOp); if (RT_FAILURE(rc)) { rc = trpmGCExitTrap(pVM, pVCpu, VINF_EM_RAW_EMULATE_INSTR, pRegFrame); Log6(("TRPMGC06: %Rrc (%04x:%08x) (EM)\n", rc, pRegFrame->cs.Sel, pRegFrame->eip)); return rc; } /* * UD2 in a patch? * Note! PATMGCHandleIllegalInstrTrap doesn't always return. */ if ( Cpu.pCurInstr->uOpcode == OP_ILLUD2 && PATMIsPatchGCAddr(pVM, pRegFrame->eip)) { LogFlow(("TRPMGCTrap06Handler: -> PATMRCHandleIllegalInstrTrap\n")); rc = PATMRCHandleIllegalInstrTrap(pVM, pRegFrame); /** @todo These tests are completely unnecessary, should just follow the * flow and return at the end of the function. */ if ( rc == VINF_SUCCESS || rc == VINF_EM_RAW_EMULATE_INSTR || rc == VINF_PATM_DUPLICATE_FUNCTION || rc == VINF_PATM_PENDING_IRQ_AFTER_IRET || rc == VINF_EM_RESCHEDULE) { rc = trpmGCExitTrap(pVM, pVCpu, rc, pRegFrame); Log6(("TRPMGC06: %Rrc (%04x:%08x) (PATM)\n", rc, pRegFrame->cs.Sel, pRegFrame->eip)); return rc; } } /* * Speed up dtrace and don't entrust invalid lock sequences to the recompiler. */ else if (Cpu.fPrefix & DISPREFIX_LOCK) { Log(("TRPMGCTrap06Handler: pc=%08x op=%d\n", pRegFrame->eip, Cpu.pCurInstr->uOpcode)); #ifdef DTRACE_EXPERIMENT /** @todo fix/remove/permanent-enable this when DIS/PATM handles invalid lock sequences. */ Assert(!PATMIsPatchGCAddr(pVM, pRegFrame->eip)); rc = TRPMForwardTrap(pVCpu, pRegFrame, 0x6, 0, TRPM_TRAP_NO_ERRORCODE, TRPM_TRAP, 0x6); Assert(rc == VINF_EM_RAW_GUEST_TRAP); #else rc = VINF_EM_RAW_EMULATE_INSTR; #endif } /* * Handle MONITOR - it causes an #UD exception instead of #GP when not executed in ring 0. */ else if (Cpu.pCurInstr->uOpcode == OP_MONITOR) { LogFlow(("TRPMGCTrap06Handler: -> EMInterpretInstructionCPU\n")); rc = EMInterpretInstructionDisasState(pVCpu, &Cpu, pRegFrame, PC, EMCODETYPE_SUPERVISOR); } /* Never generate a raw trap here; it might be an instruction, that requires emulation. */ else { LogFlow(("TRPMGCTrap06Handler: -> VINF_EM_RAW_EMULATE_INSTR\n")); rc = VINF_EM_RAW_EMULATE_INSTR; } } else { LogFlow(("TRPMGCTrap06Handler: -> TRPMForwardTrap\n")); rc = TRPMForwardTrap(pVCpu, pRegFrame, 0x6, 0, TRPM_TRAP_NO_ERRORCODE, TRPM_TRAP, 0x6); Assert(rc == VINF_EM_RAW_GUEST_TRAP); } rc = trpmGCExitTrap(pVM, pVCpu, rc, pRegFrame); Log6(("TRPMGC06: %Rrc (%04x:%08x)\n", rc, pRegFrame->cs.Sel, pRegFrame->eip)); return rc; }