/**
 * \#PF Virtual Handler callback for Guest write access to the Guest's own current 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) trpmRCGuestIDTWriteHandler(PVM pVM, RTGCUINT uErrorCode, PCPUMCTXCORE pRegFrame, RTGCPTR pvFault, RTGCPTR pvRange, uintptr_t offRange)
{
    PVMCPU      pVCpu = VMMGetCpu0(pVM);
    uint16_t    cbIDT;
    RTGCPTR     GCPtrIDT    = (RTGCPTR)CPUMGetGuestIDTR(pVCpu, &cbIDT);
#ifdef VBOX_STRICT
    RTGCPTR     GCPtrIDTEnd = (RTGCPTR)((RTGCUINTPTR)GCPtrIDT + cbIDT + 1);
#endif
    uint32_t    iGate       = ((RTGCUINTPTR)pvFault - (RTGCUINTPTR)GCPtrIDT)/sizeof(VBOXIDTE);

    AssertMsg(offRange < (uint32_t)cbIDT+1, ("pvFault=%RGv GCPtrIDT=%RGv-%RGv pvRange=%RGv\n", pvFault, GCPtrIDT, GCPtrIDTEnd, pvRange));
    Assert((RTGCPTR)(RTRCUINTPTR)pvRange == GCPtrIDT);

#if 0
    /* Note! this causes problems in Windows XP as instructions following the update can be dangerous (str eax has been seen) */
    /* Note! not going back to ring 3 could make the code scanner miss them. */
    /* Check if we can handle the write here. */
    if (     iGate != 3                                         /* Gate 3 is handled differently; could do it here as well, but let ring 3 handle this case for now. */
        &&  !ASMBitTest(&pVM->trpm.s.au32IdtPatched[0], iGate)) /* Passthru gates need special attention too. */
    {
        uint32_t cb;
        int rc = EMInterpretInstruction(pVM, pVCpu, pRegFrame, pvFault, &cb);
        if (RT_SUCCESS(rc) && cb)
        {
            uint32_t iGate1 = (offRange + cb - 1)/sizeof(VBOXIDTE);

            Log(("trpmRCGuestIDTWriteHandler: write to gate %x (%x) offset %x cb=%d\n", iGate, iGate1, offRange, cb));

            trpmClearGuestTrapHandler(pVM, iGate);
            if (iGate != iGate1)
                trpmClearGuestTrapHandler(pVM, iGate1);

            STAM_COUNTER_INC(&pVM->trpm.s.StatRCWriteGuestIDTHandled);
            return VINF_SUCCESS;
        }
    }
#else
    NOREF(iGate);
#endif

    Log(("trpmRCGuestIDTWriteHandler: eip=%RGv write to gate %x offset %x\n", pRegFrame->eip, iGate, offRange));

    /** @todo Check which IDT entry and keep the update cost low in TRPMR3SyncIDT() and CSAMCheckGates(). */
    VMCPU_FF_SET(pVCpu, VMCPU_FF_TRPM_SYNC_IDT);

    STAM_COUNTER_INC(&pVM->trpm.s.StatRCWriteGuestIDTFault);
    return VINF_EM_RAW_EMULATE_INSTR_IDT_FAULT;
}
Exemple #2
0
/**
 * \#PF Virtual Handler callback for Guest write access to the Guest's own current TSS.
 *
 * @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) selmRCGuestTSSWriteHandler(PVM pVM, RTGCUINT uErrorCode, PCPUMCTXCORE pRegFrame, RTGCPTR pvFault, RTGCPTR pvRange, uintptr_t offRange)
{
    PVMCPU pVCpu = VMMGetCpu0(pVM);

    LogFlow(("selmRCGuestTSSWriteHandler errcode=%x fault=%RGv offRange=%08x\n", (uint32_t)uErrorCode, pvFault, offRange));

    /*
     * Try emulate the access.
     */
    uint32_t cb;
    int rc = EMInterpretInstruction(pVM, pVCpu, pRegFrame, (RTGCPTR)(RTRCUINTPTR)pvFault, &cb);
    if (RT_SUCCESS(rc) && cb)
    {
        rc = VINF_SUCCESS;

        /*
         * If it's on the same page as the esp0 and ss0 fields or actually one of them,
         * then check if any of these has changed.
         */
        PCVBOXTSS pGuestTss = (PVBOXTSS)(uintptr_t)pVM->selm.s.GCPtrGuestTss;
        if (    PAGE_ADDRESS(&pGuestTss->esp0) == PAGE_ADDRESS(&pGuestTss->padding_ss0)
            &&  PAGE_ADDRESS(&pGuestTss->esp0) == PAGE_ADDRESS((uint8_t *)pGuestTss + offRange)
            &&  (    pGuestTss->esp0 !=  pVM->selm.s.Tss.esp1
                 ||  pGuestTss->ss0  != (pVM->selm.s.Tss.ss1 & ~1)) /* undo raw-r0 */
           )
        {
            Log(("selmRCGuestTSSWriteHandler: R0 stack: %RTsel:%RGv -> %RTsel:%RGv\n",
                 (RTSEL)(pVM->selm.s.Tss.ss1 & ~1), (RTGCPTR)pVM->selm.s.Tss.esp1, (RTSEL)pGuestTss->ss0, (RTGCPTR)pGuestTss->esp0));
            pVM->selm.s.Tss.esp1 = pGuestTss->esp0;
            pVM->selm.s.Tss.ss1  = pGuestTss->ss0 | 1;
            STAM_COUNTER_INC(&pVM->selm.s.StatRCWriteGuestTSSHandledChanged);
        }
        /* Handle misaligned TSS in a safe manner (just in case). */
        else if (   offRange >= RT_UOFFSETOF(VBOXTSS, esp0)
                 && offRange < RT_UOFFSETOF(VBOXTSS, padding_ss0))
        {
            struct
            {
                uint32_t esp0;
                uint16_t ss0;
                uint16_t padding_ss0;
            } s;
            AssertCompileSize(s, 8);
            rc = selmRCReadTssBits(pVM, &s, &pGuestTss->esp0, sizeof(s));
            if (    rc == VINF_SUCCESS
                &&  (    s.esp0 !=  pVM->selm.s.Tss.esp1
                     ||  s.ss0  != (pVM->selm.s.Tss.ss1 & ~1)) /* undo raw-r0 */
               )
            {
                Log(("selmRCGuestTSSWriteHandler: R0 stack: %RTsel:%RGv -> %RTsel:%RGv [x-page]\n",
                     (RTSEL)(pVM->selm.s.Tss.ss1 & ~1), (RTGCPTR)pVM->selm.s.Tss.esp1, (RTSEL)s.ss0, (RTGCPTR)s.esp0));
                pVM->selm.s.Tss.esp1 = s.esp0;
                pVM->selm.s.Tss.ss1  = s.ss0 | 1;
                STAM_COUNTER_INC(&pVM->selm.s.StatRCWriteGuestTSSHandledChanged);
            }
        }

        /*
         * If VME is enabled we need to check if the interrupt redirection bitmap
         * needs updating.
         */
        if (    offRange >= RT_UOFFSETOF(VBOXTSS, offIoBitmap)
            &&  (CPUMGetGuestCR4(pVCpu) & X86_CR4_VME))
        {
            if (offRange - RT_UOFFSETOF(VBOXTSS, offIoBitmap) < sizeof(pGuestTss->offIoBitmap))
            {
                uint16_t offIoBitmap = pGuestTss->offIoBitmap;
                if (offIoBitmap != pVM->selm.s.offGuestIoBitmap)
                {
                    Log(("TSS offIoBitmap changed: old=%#x new=%#x -> resync in ring-3\n", pVM->selm.s.offGuestIoBitmap, offIoBitmap));
                    VMCPU_FF_SET(pVCpu, VMCPU_FF_SELM_SYNC_TSS);
                    VMCPU_FF_SET(pVCpu, VMCPU_FF_TO_R3);
                }
                else
                    Log(("TSS offIoBitmap: old=%#x new=%#x [unchanged]\n", pVM->selm.s.offGuestIoBitmap, offIoBitmap));
            }
            else
            {
                /** @todo not sure how the partial case is handled; probably not allowed */
                uint32_t offIntRedirBitmap = pVM->selm.s.offGuestIoBitmap - sizeof(pVM->selm.s.Tss.IntRedirBitmap);
                if (   offIntRedirBitmap <= offRange
                    && offIntRedirBitmap + sizeof(pVM->selm.s.Tss.IntRedirBitmap) >= offRange + cb
                    && offIntRedirBitmap + sizeof(pVM->selm.s.Tss.IntRedirBitmap) <= pVM->selm.s.cbGuestTss)
                {
                    Log(("TSS IntRedirBitmap Changed: offIoBitmap=%x offIntRedirBitmap=%x cbTSS=%x offRange=%x cb=%x\n",
                         pVM->selm.s.offGuestIoBitmap, offIntRedirBitmap, pVM->selm.s.cbGuestTss, offRange, cb));

                    /** @todo only update the changed part. */
                    for (uint32_t i = 0; i < sizeof(pVM->selm.s.Tss.IntRedirBitmap) / 8; i++)
                    {
                        rc = selmRCReadTssBits(pVM, &pVM->selm.s.Tss.IntRedirBitmap[i * 8],
                                               (uint8_t *)pGuestTss + offIntRedirBitmap + i * 8, 8);
                        if (rc != VINF_SUCCESS)
                            break;
                    }
                    STAM_COUNTER_INC(&pVM->selm.s.StatRCWriteGuestTSSRedir);
                }
            }
        }

        /* Return to ring-3 for a full resync if any of the above fails... (?) */
        if (rc != VINF_SUCCESS)
        {
            VMCPU_FF_SET(pVCpu, VMCPU_FF_SELM_SYNC_TSS);
            VMCPU_FF_SET(pVCpu, VMCPU_FF_TO_R3);
            if (RT_SUCCESS(rc))
                rc = VINF_SUCCESS;
        }

        STAM_COUNTER_INC(&pVM->selm.s.StatRCWriteGuestTSSHandled);
    }
    else
    {
        Assert(RT_FAILURE(rc));
        VMCPU_FF_SET(pVCpu, VMCPU_FF_SELM_SYNC_TSS);
        STAM_COUNTER_INC(&pVM->selm.s.StatRCWriteGuestTSSUnhandled);
        if (rc == VERR_EM_INTERPRETER)
            rc = VINF_EM_RAW_EMULATE_INSTR_TSS_FAULT;
    }
    return rc;
}
Exemple #3
0
/**
 * \#PF Virtual Handler callback for Guest write access to the Guest's own GDT.
 *
 * @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) selmRCGuestGDTWriteHandler(PVM pVM, RTGCUINT uErrorCode, PCPUMCTXCORE pRegFrame, RTGCPTR pvFault, RTGCPTR pvRange, uintptr_t offRange)
{
    PVMCPU pVCpu = VMMGetCpu0(pVM);

    LogFlow(("selmRCGuestGDTWriteHandler errcode=%x fault=%RGv offRange=%08x\n", (uint32_t)uErrorCode, pvFault, offRange));

    /*
     * First check if this is the LDT entry.
     * LDT updates are problems since an invalid LDT entry will cause trouble during worldswitch.
     */
    int rc;
    if (CPUMGetGuestLDTR(pVCpu) / sizeof(X86DESC) == offRange / sizeof(X86DESC))
    {
        Log(("LDTR selector change -> fall back to HC!!\n"));
        rc = VINF_EM_RAW_EMULATE_INSTR_GDT_FAULT;
        /** @todo We're not handling changed to the selectors in LDTR and TR correctly at all.
         * We should ignore any changes to those and sync them only when they are loaded by the guest! */
    }
    else
    {
        /*
         * Attempt to emulate the instruction and sync the affected entries.
         */
        /** @todo should check if any affected selectors are loaded. */
        uint32_t cb;
        rc = EMInterpretInstruction(pVM, pVCpu, pRegFrame, (RTGCPTR)(RTRCUINTPTR)pvFault, &cb);
        if (RT_SUCCESS(rc) && cb)
        {
            unsigned iGDTE1 = offRange / sizeof(X86DESC);
            int rc2 = selmGCSyncGDTEntry(pVM, pRegFrame, iGDTE1);
            if (rc2 == VINF_SUCCESS)
            {
                Assert(cb);
                unsigned iGDTE2 = (offRange + cb - 1) / sizeof(X86DESC);
                if (iGDTE1 != iGDTE2)
                    rc2 = selmGCSyncGDTEntry(pVM, pRegFrame, iGDTE2);
                if (rc2 == VINF_SUCCESS)
                {
                    STAM_COUNTER_INC(&pVM->selm.s.StatRCWriteGuestGDTHandled);
                    return rc;
                }
            }
            if (rc == VINF_SUCCESS || RT_FAILURE(rc2))
                rc = rc2;
        }
        else
        {
            Assert(RT_FAILURE(rc));
            if (rc == VERR_EM_INTERPRETER)
                rc = VINF_EM_RAW_EMULATE_INSTR_GDT_FAULT;
        }
    }
    if (    rc != VINF_EM_RAW_EMULATE_INSTR_LDT_FAULT
        &&  rc != VINF_EM_RAW_EMULATE_INSTR_TSS_FAULT)
    {
        /* Not necessary when we need to go back to the host context to sync the LDT or TSS. */
        VMCPU_FF_SET(pVCpu, VMCPU_FF_SELM_SYNC_GDT);
    }
    STAM_COUNTER_INC(&pVM->selm.s.StatRCWriteGuestGDTUnhandled);
    return rc;
}