/** * Writes guest memory. * * @returns VBox status code. * * @param pUVM The user mode VM handle. * @param idCpu The ID of the target CPU context (for the address). * @param pAddress Where to start writing. * @param pvBuf The data to write. * @param cbWrite The number of bytes to write. */ static DECLCALLBACK(int) dbgfR3MemWrite(PUVM pUVM, VMCPUID idCpu, PCDBGFADDRESS pAddress, void const *pvBuf, size_t cbWrite) { /* * Validate the input we use, PGM does the rest. */ if (!DBGFR3AddrIsValid(pUVM, pAddress)) return VERR_INVALID_POINTER; if (!VALID_PTR(pvBuf)) return VERR_INVALID_POINTER; PVM pVM = pUVM->pVM; VM_ASSERT_VALID_EXT_RETURN(pVM, VERR_INVALID_VM_HANDLE); /* * HMA is always special. */ int rc; if (DBGFADDRESS_IS_HMA(pAddress)) { /** @todo write to HMA. */ rc = VERR_ACCESS_DENIED; } else { /* * Select PGM function by addressing mode. */ PVMCPU pVCpu = VMMGetCpuById(pVM, idCpu); PGMMODE enmMode = PGMGetGuestMode(pVCpu); if ( enmMode == PGMMODE_REAL || enmMode == PGMMODE_PROTECTED || DBGFADDRESS_IS_PHYS(pAddress) ) rc = PGMPhysSimpleWriteGCPhys(pVM, pAddress->FlatPtr, pvBuf, cbWrite); else { #if GC_ARCH_BITS > 32 if ( ( pAddress->FlatPtr >= _4G || pAddress->FlatPtr + cbWrite > _4G) && enmMode != PGMMODE_AMD64 && enmMode != PGMMODE_AMD64_NX) return VERR_PAGE_TABLE_NOT_PRESENT; #endif rc = PGMPhysSimpleWriteGCPtr(pVCpu, pAddress->FlatPtr, pvBuf, cbWrite); } } return rc; }
/** * Exception handler for #UD. * * @param pVCpu Pointer to the VMCPU. * @param pCtx Pointer to the guest-CPU context. * @param pDis Pointer to the disassembled instruction state at RIP. * Optional, can be NULL. */ VMM_INT_DECL(int) gimKvmXcptUD(PVMCPU pVCpu, PCPUMCTX pCtx, PDISCPUSTATE pDis) { /* * If we didn't ask for #UD to be trapped, bail. */ PVM pVM = pVCpu->CTX_SUFF(pVM); PGIMKVM pKvm = &pVM->gim.s.u.Kvm; if (RT_UNLIKELY(!pVM->gim.s.u.Kvm.fTrapXcptUD)) return VERR_GIM_OPERATION_FAILED; /* * Make sure guest ring-0 is the one making the hypercall. */ if (CPUMGetGuestCPL(pVCpu)) return VERR_GIM_HYPERCALL_ACCESS_DENIED; int rc = VINF_SUCCESS; if (!pDis) { /* * Disassemble the instruction at RIP to figure out if it's the Intel VMCALL instruction * or the AMD VMMCALL instruction and if so, handle it as a hypercall. */ DISCPUSTATE Dis; rc = EMInterpretDisasCurrent(pVM, pVCpu, &Dis, NULL /* pcbInstr */); pDis = &Dis; } if (RT_SUCCESS(rc)) { /* * Patch the instruction to so we don't have to spend time disassembling it each time. * Makes sense only for HM as with raw-mode we will be getting a #UD regardless. */ if ( pDis->pCurInstr->uOpcode == OP_VMCALL || pDis->pCurInstr->uOpcode == OP_VMMCALL) { if ( pDis->pCurInstr->uOpcode != pKvm->uOpCodeNative && HMIsEnabled(pVM)) { uint8_t abHypercall[3]; size_t cbWritten = 0; rc = VMMPatchHypercall(pVM, &abHypercall, sizeof(abHypercall), &cbWritten); AssertRC(rc); Assert(sizeof(abHypercall) == pDis->cbInstr); Assert(sizeof(abHypercall) == cbWritten); rc = PGMPhysSimpleWriteGCPtr(pVCpu, pCtx->rip, &abHypercall, sizeof(abHypercall)); } /* * Perform the hypercall and update RIP. * * For HM, we can simply resume guest execution without performing the hypercall now and * do it on the next VMCALL/VMMCALL exit handler on the patched instruction. * * For raw-mode we need to do this now anyway. So we do it here regardless with an added * advantage is that it saves one world-switch for the HM case. */ if (RT_SUCCESS(rc)) { int rc2 = gimKvmHypercall(pVCpu, pCtx); AssertRC(rc2); pCtx->rip += pDis->cbInstr; } return rc; } } return VERR_GIM_OPERATION_FAILED; }