Esempio n. 1
0
/**
 * 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;
}