/** * \#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; }
/** * \#BP (Breakpoint) handler. * * @returns VBox status code. * VINF_SUCCESS means we completely handled this trap, * other codes are passed execution to host context. * * @param pVM The cross context VM structure. * @param pVCpu The cross context virtual CPU structure. * @param pRegFrame Pointer to the register frame for the trap. */ VMMRZ_INT_DECL(int) DBGFRZTrap03Handler(PVM pVM, PVMCPU pVCpu, PCPUMCTXCORE pRegFrame) { #ifdef IN_RC const bool fInHyper = !(pRegFrame->ss.Sel & X86_SEL_RPL) && !pRegFrame->eflags.Bits.u1VM; #else const bool fInHyper = false; #endif /* * Get the trap address and look it up in the breakpoint table. * Don't bother if we don't have any breakpoints. */ unsigned cToSearch = pVM->dbgf.s.Int3.cToSearch; if (cToSearch > 0) { RTGCPTR pPc; int rc = SELMValidateAndConvertCSAddr(pVCpu, pRegFrame->eflags, pRegFrame->ss.Sel, pRegFrame->cs.Sel, &pRegFrame->cs, #ifdef IN_RC pRegFrame->eip - 1, #else pRegFrame->rip /* no -1 in R0 */, #endif &pPc); AssertRCReturn(rc, rc); unsigned iBp = pVM->dbgf.s.Int3.iStartSearch; while (cToSearch-- > 0) { if ( pVM->dbgf.s.aBreakpoints[iBp].u.GCPtr == (RTGCUINTPTR)pPc && pVM->dbgf.s.aBreakpoints[iBp].enmType == DBGFBPTYPE_INT3) { pVM->dbgf.s.aBreakpoints[iBp].cHits++; pVCpu->dbgf.s.iActiveBp = pVM->dbgf.s.aBreakpoints[iBp].iBp; LogFlow(("DBGFRZTrap03Handler: hit breakpoint %d at %RGv (%04x:%RGv) cHits=0x%RX64\n", pVM->dbgf.s.aBreakpoints[iBp].iBp, pPc, pRegFrame->cs.Sel, pRegFrame->rip, pVM->dbgf.s.aBreakpoints[iBp].cHits)); return fInHyper ? VINF_EM_DBG_HYPER_BREAKPOINT : VINF_EM_DBG_BREAKPOINT; } iBp++; } } return fInHyper ? VINF_EM_DBG_HYPER_ASSERTION : VINF_EM_RAW_GUEST_TRAP; }
/** * \#NP ((segment) Not Present) handler. * * @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) TRPMGCTrap0bHandler(PTRPMCPU pTrpmCpu, PCPUMCTXCORE pRegFrame) { LogFlow(("TRPMGC0b: %04x:%08x\n", pRegFrame->cs.Sel, pRegFrame->eip)); PVM pVM = TRPMCPU_2_VM(pTrpmCpu); PVMCPU pVCpu = TRPMCPU_2_VMCPU(pTrpmCpu); PGMRZDynMapStartAutoSet(pVCpu); /* * Try to detect instruction by opcode which caused trap. * XXX note: this code may cause \#PF (trap e) or \#GP (trap d) while * accessing user code. need to handle it somehow in future! */ RTGCPTR GCPtr; if ( SELMValidateAndConvertCSAddr(pVCpu, pRegFrame->eflags, pRegFrame->ss.Sel, pRegFrame->cs.Sel, &pRegFrame->cs, (RTGCPTR)pRegFrame->eip, &GCPtr) == VINF_SUCCESS) { uint8_t *pu8Code = (uint8_t *)(uintptr_t)GCPtr; /* * First skip possible instruction prefixes, such as: * OS, AS * CS:, DS:, ES:, SS:, FS:, GS: * REPE, REPNE * * note: Currently we supports only up to 4 prefixes per opcode, more * prefixes (normally not used anyway) will cause trap d in guest. * note: Instruction length in IA-32 may be up to 15 bytes, we dont * check this issue, its too hard. */ for (unsigned i = 0; i < 4; i++) { if ( pu8Code[0] != 0xf2 /* REPNE/REPNZ */ && pu8Code[0] != 0xf3 /* REP/REPE/REPZ */ && pu8Code[0] != 0x2e /* CS: */ && pu8Code[0] != 0x36 /* SS: */ && pu8Code[0] != 0x3e /* DS: */ && pu8Code[0] != 0x26 /* ES: */ && pu8Code[0] != 0x64 /* FS: */ && pu8Code[0] != 0x65 /* GS: */ && pu8Code[0] != 0x66 /* OS */ && pu8Code[0] != 0x67 /* AS */ ) break; pu8Code++; } /* * Detect right switch using a callgate. * * We recognize the following causes for the trap 0b: * CALL FAR, CALL FAR [] * JMP FAR, JMP FAR [] * IRET (may cause a task switch) * * Note: we can't detect whether the trap was caused by a call to a * callgate descriptor or it is a real trap 0b due to a bad selector. * In both situations we'll pass execution to our recompiler so we don't * have to worry. * If we wanted to do better detection, we have set GDT entries to callgate * descriptors pointing to our own handlers. */ /** @todo not sure about IRET, may generate Trap 0d (\#GP), NEED TO CHECK! */ if ( pu8Code[0] == 0x9a /* CALL FAR */ || ( pu8Code[0] == 0xff /* CALL FAR [] */ && (pu8Code[1] & X86_OPCODE_MODRM_REG_MASK) == 0x18) || pu8Code[0] == 0xea /* JMP FAR */ || ( pu8Code[0] == 0xff /* JMP FAR [] */ && (pu8Code[1] & X86_OPCODE_MODRM_REG_MASK) == 0x28) || pu8Code[0] == 0xcf /* IRET */ ) { /* * Got potential call to callgate. * We simply return execution to the recompiler to do emulation * starting from the instruction which caused the trap. */ pTrpmCpu->uActiveVector = UINT32_MAX; Log6(("TRPMGC0b: %Rrc (%04x:%08x) (CG)\n", VINF_EM_RAW_RING_SWITCH, pRegFrame->cs.Sel, pRegFrame->eip)); PGMRZDynMapReleaseAutoSet(pVCpu); return VINF_EM_RAW_RING_SWITCH; } } /* * Pass trap 0b as is to the recompiler in all other cases. */ Log6(("TRPMGC0b: %Rrc (%04x:%08x)\n", VINF_EM_RAW_GUEST_TRAP, pRegFrame->cs.Sel, pRegFrame->eip)); PGMRZDynMapReleaseAutoSet(pVCpu); return VINF_EM_RAW_GUEST_TRAP; }
/** * 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; }
/** * \#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; int rc = SELMValidateAndConvertCSAddr(pVCpu, pRegFrame->eflags, pRegFrame->ss.Sel, pRegFrame->cs.Sel, &pRegFrame->cs, pRegFrame->rip, &PC); 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); }