VirtualBox

Changeset 47432 in vbox


Ignore:
Timestamp:
Jul 27, 2013 12:35:49 AM (11 years ago)
Author:
vboxsync
Message:

HMR0VMX.cpp: Attempt to fix incorrect DR7 and DR[0-3] checks in I/O port path.

Location:
trunk
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • trunk/include/iprt/x86.h

    r47406 r47432  
    738738/** Bit 3 - B3 - Breakpoint 3 condition detected. */
    739739#define X86_DR6_B3                          RT_BIT(3)
     740/** Mask of all the Bx bits. */
     741#define X86_DR6_B_MASK                      UINT64_C(0x0000000f)
    740742/** Bit 13 - BD - Debug register access detected. Corresponds to the X86_DR7_GD bit. */
    741743#define X86_DR6_BD                          RT_BIT(13)
     
    753755#define X86_DR6_MBZ_MASK                    UINT64_C(0xffffffff00000000)
    754756/** @} */
     757
     758/** Get the DR6.Bx bit for a the given breakpoint. */
     759#define X86_DR6_B(iBp)                      RT_BIT_64(iBp)
    755760
    756761
     
    815820#define X86_DR7_G(iBp)                      ( UINT32_C(1) << (iBp * 2 + 1) )
    816821
     822/** Calcs the L and G bits of Nth breakpoint.
     823 * @param   iBp     The breakpoint number [0..3].
     824 */
     825#define X86_DR7_L_G(iBp)                    ( UINT32_C(3) << (iBp * 2) )
     826
    817827/** @name Read/Write values.
    818828 * @{ */
     
    833843#define X86_DR7_RW(iBp, fRw)                ( (fRw) << ((iBp) * 4 + 16) )
    834844
     845/** Fetch the the R/Wx bits for a given breakpoint (so it can be compared with
     846 * one of the X86_DR7_RW_XXX constants).
     847 *
     848 * @returns X86_DR7_RW_XXX
     849 * @param   uDR7    DR7 value
     850 * @param   iBp     The breakpoint number [0..3].
     851 */
     852#define X86_DR7_GET_RW(uDR7, iBp)            ( ( (uDR7) >> ((iBp) * 4 + 16) ) & UINT32_C(3) )
     853
     854/** R/W0, R/W1, R/W2, and R/W3. */
     855#define X86_DR7_RW_ALL_MASKS                UINT32_C(0x33330000)
     856
     857/** Checks if there are any I/O breakpoint types configured in the RW
     858 * registers.  Does NOT check if these are enabled, sorry. */
     859#define X86_DR7_ANY_RW_IO(uDR7) \
     860    (   (    UINT32_C(0x22220000) & (uDR7) ) /* any candidates? */ \
     861     && ( ( (UINT32_C(0x22220000) & (uDR7) ) >> 1 )  &  ~(uDR7) ) )
     862AssertCompile(X86_DR7_ANY_RW_IO(UINT32_C(0x33330000)) == 0);
     863AssertCompile(X86_DR7_ANY_RW_IO(UINT32_C(0x22220000)) == 1);
     864AssertCompile(X86_DR7_ANY_RW_IO(UINT32_C(0x32320000)) == 1);
     865AssertCompile(X86_DR7_ANY_RW_IO(UINT32_C(0x23230000)) == 1);
     866AssertCompile(X86_DR7_ANY_RW_IO(UINT32_C(0x00000000)) == 0);
     867AssertCompile(X86_DR7_ANY_RW_IO(UINT32_C(0x00010000)) == 0);
     868AssertCompile(X86_DR7_ANY_RW_IO(UINT32_C(0x00020000)) == 1);
     869AssertCompile(X86_DR7_ANY_RW_IO(UINT32_C(0x00030000)) == 0);
     870AssertCompile(X86_DR7_ANY_RW_IO(UINT32_C(0x00040000)) == 0);
     871
    835872/** @name Length values.
    836873 * @{ */
     
    851888 * @param   iBp     The breakpoint number [0..3].
    852889 */
    853 #define X86_DR7_GET_LEN(uDR7, iBp)          ( ( (uDR7) >> ((iBp) * 4 + 18) ) & 0x3U)
     890#define X86_DR7_GET_LEN(uDR7, iBp)          ( ( (uDR7) >> ((iBp) * 4 + 18) ) & UINT32_C(0x3) )
    854891
    855892/** Mask used to check if any breakpoints are enabled. */
    856 #define X86_DR7_ENABLED_MASK                (RT_BIT(0) | RT_BIT(1) | RT_BIT(2) | RT_BIT(3) | RT_BIT(4) | RT_BIT(5) | RT_BIT(6) | RT_BIT(7))
    857 
    858 /** Mask used to check if any io breakpoints are set. */
    859 #define X86_DR7_IO_ENABLED_MASK             (X86_DR7_RW(0, X86_DR7_RW_IO) | X86_DR7_RW(1, X86_DR7_RW_IO) | X86_DR7_RW(2, X86_DR7_RW_IO) | X86_DR7_RW(3, X86_DR7_RW_IO))
     893#define X86_DR7_ENABLED_MASK                UINT32_C(0x000000ff)
    860894
    861895/** Value of DR7 after powerup/reset. */
  • trunk/src/VBox/VMM/VMMR0/HMVMXR0.cpp

    r47378 r47432  
    55845584 *
    55855585 * @remarks No-long-jump zone!!!
     5586 * @todo r=bird: Why is this plural when it only saves DR7?  I almost jumped to
     5587 *       the wrong conclusions looking at the I/O code just now (it most likely
     5588 *       only needs DR7).
    55865589 */
    55875590static int hmR0VmxSaveGuestDebugRegs(PVMCPU pVCpu, PCPUMCTX pMixedCtx)
    55885591{
    5589     int rc = VINF_SUCCESS;
    55905592    if (!(pVCpu->hm.s.vmx.fUpdatedGuestState & HMVMX_UPDATED_GUEST_DEBUG))
    55915593    {
    55925594        /* Upper 32-bits are always zero. See Intel spec. 2.7.3 "Loading and Storing Debug Registers". */
    55935595        uint32_t u32Val;
    5594         rc = VMXReadVmcs32(VMX_VMCS_GUEST_DR7, &u32Val);        AssertRCReturn(rc, rc);
     5596        int rc = VMXReadVmcs32(VMX_VMCS_GUEST_DR7, &u32Val);    AssertRCReturn(rc, rc);
    55955597        pMixedCtx->dr[7] = u32Val;
    55965598
    55975599        pVCpu->hm.s.vmx.fUpdatedGuestState |= HMVMX_UPDATED_GUEST_DEBUG;
    55985600    }
    5599     return rc;
     5601    return VINF_SUCCESS;
    56005602}
    56015603
     
    83378339    /* Refer Intel spec. 27-5. "Exit Qualifications for I/O Instructions" for the format. */
    83388340    uint32_t uIOPort   = VMX_EXIT_QUALIFICATION_IO_PORT(pVmxTransient->uExitQualification);
    8339     uint32_t uIOWidth  = VMX_EXIT_QUALIFICATION_IO_WIDTH(pVmxTransient->uExitQualification);
    8340     bool     fIOWrite  = (VMX_EXIT_QUALIFICATION_IO_DIRECTION(pVmxTransient->uExitQualification)
     8341    uint8_t uIOWidth  = VMX_EXIT_QUALIFICATION_IO_WIDTH(pVmxTransient->uExitQualification);
     8342    bool     fIOWrite  = (   VMX_EXIT_QUALIFICATION_IO_DIRECTION(pVmxTransient->uExitQualification)
    83418343                          == VMX_EXIT_QUALIFICATION_IO_DIRECTION_OUT);
    83428344    bool     fIOString = (VMX_EXIT_QUALIFICATION_IO_STRING(pVmxTransient->uExitQualification) == 1);
    8343     Assert(uIOWidth == 0 || uIOWidth == 1 || uIOWidth == 3);
     8345    AssertReturn(uIOWidth <= 3 && uIOWidth != 2, VERR_HM_IPE_3);
    83448346
    83458347    /* I/O operation lookup arrays. */
    8346     static const uint32_t s_aIOSize[4]  = { 1, 2, 0, 4 };                   /* Size of the I/O accesses. */
     8348    static const uint32_t s_aIOSizes[4]  = { 1, 2, 0, 4 };                   /* Size of the I/O accesses. */
    83478349    static const uint32_t s_aIOOpAnd[4] = { 0xff, 0xffff, 0, 0xffffffff };  /* AND masks for saving the result (in AL/AX/EAX). */
    83488350
    8349     const uint32_t cbSize  = s_aIOSize[uIOWidth];
    8350     const uint32_t cbInstr = pVmxTransient->cbInstr;
    8351     PVM pVM                = pVCpu->CTX_SUFF(pVM);
     8351    const uint32_t cbValue  = s_aIOSizes[uIOWidth];
     8352    const uint32_t cbInstr  = pVmxTransient->cbInstr;
     8353    PVM pVM                 = pVCpu->CTX_SUFF(pVM);
    83528354    if (fIOString)
    83538355    {
    83548356        /* INS/OUTS - I/O String instruction. */
    8355         PDISCPUSTATE pDis = &pVCpu->hm.s.DisState;
    83568357        /** @todo for now manually disassemble later optimize by getting the fields from
    83578358         *        the VMCS. VMX_VMCS_RO_EXIT_GUEST_LINEAR_ADDR contains the flat pointer
    83588359         *        operand of the instruction. VMX_VMCS32_RO_EXIT_INSTR_INFO contains
    83598360         *        segment prefix info. */
     8361        PDISCPUSTATE pDis = &pVCpu->hm.s.DisState;
    83608362        rc = EMInterpretDisasCurrent(pVM, pVCpu, pDis, NULL);
    83618363        if (RT_SUCCESS(rc))
     
    83648366            {
    83658367                VBOXSTRICTRC rc2 = IOMInterpretOUTSEx(pVM, pVCpu, CPUMCTX2CORE(pMixedCtx), uIOPort, pDis->fPrefix,
    8366                                                       (DISCPUMODE)pDis->uAddrMode, cbSize);
     8368                                                      (DISCPUMODE)pDis->uAddrMode, cbValue);
    83678369                rc = VBOXSTRICTRC_VAL(rc2);
    83688370                STAM_COUNTER_INC(&pVCpu->hm.s.StatExitIOStringWrite);
     
    83718373            {
    83728374                VBOXSTRICTRC rc2 = IOMInterpretINSEx(pVM, pVCpu, CPUMCTX2CORE(pMixedCtx), uIOPort, pDis->fPrefix,
    8373                                                      (DISCPUMODE)pDis->uAddrMode, cbSize);
     8375                                                     (DISCPUMODE)pDis->uAddrMode, cbValue);
    83748376                rc = VBOXSTRICTRC_VAL(rc2);
    83758377                STAM_COUNTER_INC(&pVCpu->hm.s.StatExitIOStringRead);
     
    83898391        if (fIOWrite)
    83908392        {
    8391             VBOXSTRICTRC rc2 = IOMIOPortWrite(pVM, pVCpu, uIOPort, pMixedCtx->eax & uAndVal, cbSize);
     8393            VBOXSTRICTRC rc2 = IOMIOPortWrite(pVM, pVCpu, uIOPort, pMixedCtx->eax & uAndVal, cbValue);
    83928394            rc = VBOXSTRICTRC_VAL(rc2);
    83938395            if (rc == VINF_IOM_R3_IOPORT_WRITE)
    8394                 HMR0SavePendingIOPortWrite(pVCpu, pMixedCtx->rip, pMixedCtx->rip + cbInstr, uIOPort, uAndVal, cbSize);
     8396                HMR0SavePendingIOPortWrite(pVCpu, pMixedCtx->rip, pMixedCtx->rip + cbInstr, uIOPort, uAndVal, cbValue);
    83958397            STAM_COUNTER_INC(&pVCpu->hm.s.StatExitIOWrite);
    83968398        }
     
    83988400        {
    83998401            uint32_t u32Result = 0;
    8400             VBOXSTRICTRC rc2 = IOMIOPortRead(pVM, pVCpu, uIOPort, &u32Result, cbSize);
     8402            VBOXSTRICTRC rc2 = IOMIOPortRead(pVM, pVCpu, uIOPort, &u32Result, cbValue);
    84018403            rc = VBOXSTRICTRC_VAL(rc2);
    84028404            if (IOM_SUCCESS(rc))
     
    84068408            }
    84078409            else if (rc == VINF_IOM_R3_IOPORT_READ)
    8408                 HMR0SavePendingIOPortRead(pVCpu, pMixedCtx->rip, pMixedCtx->rip + cbInstr, uIOPort, uAndVal, cbSize);
     8410                HMR0SavePendingIOPortRead(pVCpu, pMixedCtx->rip, pMixedCtx->rip + cbInstr, uIOPort, uAndVal, cbValue);
    84098411            STAM_COUNTER_INC(&pVCpu->hm.s.StatExitIORead);
    84108412        }
     
    84178419        if (RT_LIKELY(rc == VINF_SUCCESS))
    84188420        {
     8421            /*
     8422             * If any I/O breakpoints are armed, then we should check if a
     8423             * debug trap needs to be generated.
     8424             * Note that the I/O breakpoint type is undefined if CR4.DE is 0.
     8425             */
    84198426            rc = hmR0VmxSaveGuestDebugRegs(pVCpu, pMixedCtx);      /* For DR7. */
    84208427            AssertRCReturn(rc, rc);
    8421 
    8422             /* If any IO breakpoints are armed, then we should check if a debug trap needs to be generated. */
    8423             if (pMixedCtx->dr[7] & X86_DR7_ENABLED_MASK)
     8428            uint32_t const uDr7 = pMixedCtx->dr[7];
     8429            if (   (uDr7 & X86_DR7_ENABLED_MASK)
     8430                && X86_DR7_ANY_RW_IO(uDr7)
     8431                && (pMixedCtx->cr4 & X86_CR4_DE) )
    84248432            {
     8433                /** @todo We're a little late here if we're doing string I/O, as we're supposed
     8434                 *        to break after the each repetition.  Not sooo important, just for a
     8435                 *        rainy day. (Should probably refactor some of this code; after the uDr7
     8436                 *        detection let someone else handle it.) */
     8437                /** @todo The AMD is mumbling something that sounds like cbValue == cbBp.  The
     8438                 *        Intel manual describes it differently, data and I/O breakpoints are to
     8439                 *        be matched in the same way, probably.  Bochs does it that way. We've
     8440                 *        implemented it that way too, but it would be worth having a
     8441                 *        bootsector testcase for asserting the correct behavior (as well as
     8442                 *        correctness of this code). */
    84258443                STAM_COUNTER_INC(&pVCpu->hm.s.StatDRxIoCheck);
    8426                 for (unsigned i = 0; i < 4; i++)
     8444                uint32_t uIOPortLast = uIOPort + cbValue - 1;
     8445                for (unsigned iBp = 0; iBp < 4; iBp++)
    84278446                {
    8428                     uint32_t uBPLen = s_aIOSize[X86_DR7_GET_LEN(pMixedCtx->dr[7], i)];
    8429                     if (   (   uIOPort >= pMixedCtx->dr[i]
    8430                             && uIOPort < pMixedCtx->dr[i] + uBPLen)
    8431                         && (pMixedCtx->dr[7] & (X86_DR7_L(i) | X86_DR7_G(i)))
    8432                         && (pMixedCtx->dr[7] & X86_DR7_RW(i, X86_DR7_RW_IO)) == X86_DR7_RW(i, X86_DR7_RW_IO))
     8447                    if (   (uDr7 & X86_DR7_L_G(iBp))
     8448                        && X86_DR7_GET_RW(uDr7, iBp) == X86_DR7_RW_IO)
    84338449                    {
    8434                         Assert(CPUMIsGuestDebugStateActive(pVCpu));
    8435                         uint64_t uDR6 = ASMGetDR6();
    8436 
    8437                         /* Clear all breakpoint status flags and set the one we just hit. */
    8438                         uDR6 &= ~(X86_DR6_B0 | X86_DR6_B1 | X86_DR6_B2 | X86_DR6_B3);
    8439                         uDR6 |= (uint64_t)RT_BIT(i);
    8440 
    8441                         /*
    8442                          * Note: AMD64 Architecture Programmer's Manual 13.1:
    8443                          * Bits 15:13 of the DR6 register is never cleared by the processor and must
    8444                          * be cleared by software after the contents have been read.
    8445                          */
    8446                         ASMSetDR6(uDR6);
    8447 
    8448                         /* X86_DR7_GD will be cleared if DRx accesses should be trapped inside the guest. */
    8449                         pMixedCtx->dr[7] &= ~X86_DR7_GD;
    8450 
    8451                         /* Paranoia. */
    8452                         pMixedCtx->dr[7] &= 0xffffffff;                                             /* Upper 32 bits MBZ. */
    8453                         pMixedCtx->dr[7] &= ~(RT_BIT(11) | RT_BIT(12) | RT_BIT(14) | RT_BIT(15));   /* MBZ. */
    8454                         pMixedCtx->dr[7] |= 0x400;                                                  /* MB1. */
    8455 
    8456                         /* Resync DR7 */
    8457                         /** @todo probably cheaper to just reload DR7, nothing else needs changing. */
    8458                         pVCpu->hm.s.fContextUseFlags |= HM_CHANGED_GUEST_DEBUG;
    8459 
    8460                         /* Set #DB to be injected into the VM and continue guest execution. */
    8461                         hmR0VmxSetPendingXcptDB(pVCpu, pMixedCtx);
    8462                         break;
     8450                        /* ASSUME the breakpoint and the I/O width qualifier uses the same encoding (1 2 x 4). */
     8451                        static uint8_t const s_abInvAlign[4] = { 0, 1, 7, 3 };
     8452                        uint8_t  cbInvAlign = s_abInvAlign[X86_DR7_GET_LEN(uDr7, iBp)];
     8453                        uint64_t uDrXFirst  = pMixedCtx->dr[iBp] & ~(uint64_t)cbInvAlign;
     8454                        uint64_t uDrXLast   = uDrXFirst + cbInvAlign;
     8455                        if (uDrXFirst <= uIOPortLast && uDrXLast >= uIOPort)
     8456                        {
     8457                            Assert(CPUMIsGuestDebugStateActive(pVCpu));
     8458                            uint64_t uDR6 = ASMGetDR6();
     8459
     8460                            /* Clear all breakpoint status flags and set the one we just hit. */
     8461                            uDR6 &= ~X86_DR6_B_MASK;
     8462                            uDR6 |= X86_DR6_B(iBp);
     8463
     8464                            /*
     8465                             * Note: AMD64 Architecture Programmer's Manual 13.1:
     8466                             * Bits 15:13 of the DR6 register is never cleared by the processor and must
     8467                             * be cleared by software after the contents have been read.
     8468                             */
     8469                            ASMSetDR6(uDR6);
     8470
     8471                            /* X86_DR7_GD will be cleared if DRx accesses should be trapped inside the guest. */
     8472                            pMixedCtx->dr[7] &= ~X86_DR7_GD;
     8473
     8474                            /* Paranoia. */
     8475                            pMixedCtx->dr[7] &= ~(X86_DR7_RAZ_MASK | X86_DR7_MBZ_MASK);
     8476                            pMixedCtx->dr[7] |= X86_DR7_RA1_MASK;
     8477
     8478                            /* Resync DR7 */
     8479                            /** @todo probably cheaper to just reload DR7, nothing else needs changing. */
     8480                            pVCpu->hm.s.fContextUseFlags |= HM_CHANGED_GUEST_DEBUG;
     8481
     8482                            /* Set #DB to be injected into the VM and continue guest execution. */
     8483                            hmR0VmxSetPendingXcptDB(pVCpu, pMixedCtx);
     8484                            break;
     8485                        }
    84638486                    }
    84648487                }
Note: See TracChangeset for help on using the changeset viewer.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette