VirtualBox

Changeset 86699 in vbox


Ignore:
Timestamp:
Oct 25, 2020 10:44:39 AM (5 years ago)
Author:
vboxsync
svn:sync-xref-src-repo-rev:
141083
Message:

VMM/DBGF: Updates to the new breakpoint manager, implement global breakpoint table chunk allocation and register breakpoints which should work again, bugref:9837

Location:
trunk
Files:
1 added
11 edited

Legend:

Unmodified
Added
Removed
  • trunk/include/VBox/err.h

    r86666 r86699  
    331331/** The breakpoint owner handle is still used by one or more breakpoints. */
    332332#define VERR_DBGF_OWNER_BUSY                (-1225)
     333/** Internal processing error \#1 in the DBGF breakpoint manager code. */
     334#define VERR_DBGF_BP_IPE_1                  (-1226)
     335/** Internal processing error \#2 in the DBGF breakpoint manager code. */
     336#define VERR_DBGF_BP_IPE_2                  (-1227)
     337/** Internal processing error \#3 in the DBGF breakpoint manager code. */
     338#define VERR_DBGF_BP_IPE_3                  (-1228)
     339/** Internal processing error \#4 in the DBGF breakpoint manager code. */
     340#define VERR_DBGF_BP_IPE_4                  (-1229)
     341/** Internal processing error \#5 in the DBGF breakpoint manager code. */
     342#define VERR_DBGF_BP_IPE_5                  (-1230)
    333343/** @} */
    334344
  • trunk/include/VBox/vmm/dbgf.h

    r86683 r86699  
    8686
    8787VMMR0_INT_DECL(int) DBGFR0TracerCreateReqHandler(PGVM pGVM, PDBGFTRACERCREATEREQ pReq);
     88
     89#ifdef VBOX_WITH_LOTS_OF_DBGF_BPS
     90/**
     91 * Request buffer for DBGFR0BpInitReqHandler / VMMR0_DO_DBGF_BP_INIT.
     92 * @see DBGFR0BpInitReqHandler.
     93 */
     94typedef struct DBGFBPINITREQ
     95{
     96    /** The header. */
     97    SUPVMMR0REQHDR                  Hdr;
     98    /** Out: Ring-3 pointer of the L1 lookup table on success. */
     99    R3PTRTYPE(volatile uint32_t *)  paBpLocL1R3;
     100} DBGFBPINITREQ;
     101/** Pointer to a DBGFR0BpInitReqHandler / VMMR0_DO_DBGF_BP_INIT request buffer. */
     102typedef DBGFBPINITREQ *PDBGFBPINITREQ;
     103
     104VMMR0_INT_DECL(int) DBGFR0BpInitReqHandler(PGVM pGVM, PDBGFBPINITREQ pReq);
     105
     106/**
     107 * Request buffer for DBGFR0BpChunkAllocReqHandler / VMMR0_DO_DBGF_CHUNK_ALLOC.
     108 * @see DBGFR0BpChunkAllocReqHandler.
     109 */
     110typedef struct DBGFBPCHUNKALLOCREQ
     111{
     112    /** The header. */
     113    SUPVMMR0REQHDR          Hdr;
     114    /** Out: Ring-3 pointer of the chunk base on success. */
     115    R3PTRTYPE(void *)       pChunkBaseR3;
     116
     117    /** The chunk ID to allocate. */
     118    uint32_t                idChunk;
     119} DBGFBPCHUNKALLOCREQ;
     120/** Pointer to a DBGFR0BpChunkAllocReqHandler / VMMR0_DO_DBGF_CHUNK_ALLOC request buffer. */
     121typedef DBGFBPCHUNKALLOCREQ *PDBGFBPCHUNKALLOCREQ;
     122
     123VMMR0_INT_DECL(int) DBGFR0BpChunkAllocReqHandler(PGVM pGVM, PDBGFBPCHUNKALLOCREQ pReq);
     124#endif
    88125/** @} */
    89126
     
    829866     * debugger). */
    830867    DBGFBPOWNER     hOwner;
    831     /** The breakpoint type. */
    832     DBGFBPTYPE      enmType;
    833     /** The breakpoint handle this state belongs to. */
    834     DBGFBP          hBp;
    835     /** Breakpoint flags, see DBGF_BP_F_XXX. */
    836     uint32_t        fFlags;
     868    /** Breakpoint type and flags, see DBGFBPTYPE for type and DBGF_BP_F_XXX for flags.
     869     * Needs to be smashed together to be able to stay in the size limits. */
     870    uint32_t        fFlagsAndType;
    837871
    838872    /** Union of type specific data. */
     
    889923
    890924        /** Padding to the anticipated size. */
    891         uint64_t    u64Padding[3];
     925        uint64_t    u64Padding[2];
    892926    } u;
    893927} DBGFBPPUB;
    894 AssertCompileSize(DBGFBPPUB, 64);
     928AssertCompileSize(DBGFBPPUB, 64 - 8);
    895929AssertCompileMembersAtSameOffset(DBGFBPPUB, u.GCPtr, DBGFBPPUB, u.Reg.GCPtr);
    896930AssertCompileMembersAtSameOffset(DBGFBPPUB, u.GCPtr, DBGFBPPUB, u.Int3.GCPtr);
     
    901935typedef const DBGFBPPUB *PCDBGFBPPUB;
    902936
    903 /** @name Possible DBGFBPPUB::fFlags flags.
     937/** Sets the DBGFPUB::fFlagsAndType member. */
     938#define DBGF_BP_PUB_SET_FLAGS_AND_TYPE(a_enmType, a_fFlags) ((uint32_t)(a_enmType) | (a_fFlags))
     939/** Returns the type of the DBGFPUB::fFlagsAndType member. */
     940#define DBGF_BP_PUB_GET_TYPE(a_fFlagsAndType) ((DBGFBPTYPE)((a_fFlagsAndType) & (UINT32_C(0x7fffffff))))
     941/** Returns the enabled status of DBGFPUB::fFlagsAndType member. */
     942#define DBGF_BP_PUB_IS_ENABLED(a_fFlagsAndType) RT_BOOL((DBGFBPTYPE)((a_fFlagsAndType) & DBGF_BP_F_ENABLED))
     943
     944/** @name Possible DBGFBPPUB::fFlagsAndType flags.
    904945 * @{ */
     946/** Default flags. */
     947#define DBGF_BP_F_DEFAULT                   0
    905948/** Flag whether the breakpoint is enabled currently. */
    906 #define DBGF_BP_F_ENABLED                   RT_BIT_32(0)
     949#define DBGF_BP_F_ENABLED                   RT_BIT_32(31)
    907950/** @} */
     951
    908952
    909953/**
     
    916960 * @param   pVM         The cross-context VM structure pointer.
    917961 * @param   idCpu       ID of the vCPU triggering the breakpoint.
    918  * @param   pvUser      User argument of the set breakpoint.
     962 * @param   pvUserBp    User argument of the set breakpoint.
     963 * @param   hBp         The breakpoint handle.
    919964 * @param   pBpPub      Pointer to the readonly public state of the breakpoint.
    920965 *
     
    923968 *          guru meditation.
    924969 */
    925 typedef DECLCALLBACKTYPE(VBOXSTRICTRC, FNDBGFBPHIT,(PVM pVM, VMCPUID idCpu, void *pvUserBp, PCDBGFBPPUB pBpPub));
     970typedef DECLCALLBACKTYPE(VBOXSTRICTRC, FNDBGFBPHIT,(PVM pVM, VMCPUID idCpu, void *pvUserBp, DBGFBP hBp, PCDBGFBPPUB pBpPub));
    926971/** Pointer to a FNDBGFBPHIT(). */
    927972typedef FNDBGFBPHIT *PFNDBGFBPHIT;
     
    9671012 * @param   pUVM        The user mode VM handle.
    9681013 * @param   pvUser      The user argument.
    969  * @param   pBp         Pointer to the breakpoint information. (readonly)
    970  */
    971 typedef DECLCALLBACKTYPE(int, FNDBGFBPENUM,(PUVM pUVM, PCDBGFBPPUB pBpPub));
     1014 * @param   hBp         The breakpoint handle.
     1015 * @param   pBp         Pointer to the public breakpoint information. (readonly)
     1016 */
     1017typedef DECLCALLBACKTYPE(int, FNDBGFBPENUM,(PUVM pUVM, void *pvUser, DBGFBP hBp, PCDBGFBPPUB pBpPub));
    9721018/** Pointer to a breakpoint enumeration callback function. */
    9731019typedef FNDBGFBPENUM *PFNDBGFBPENUM;
  • trunk/include/VBox/vmm/gvm.h

    r84458 r86699  
    234234        struct DBGFR0PERVM   s;
    235235#endif
    236         uint8_t             padding[64];
     236        uint8_t             padding[1024];
    237237    } dbgfr0;
    238238
    239239    /** Padding so aCpus starts on a page boundrary.  */
    240240#ifdef VBOX_WITH_NEM_R0
    241     uint8_t         abPadding2[4096*2 - 64 - 256 - 1024 - 256 - 64 - 2176 - 640 - 512 - 64 - 64 - sizeof(PGVMCPU) * VMM_MAX_CPU_COUNT];
    242 #else
    243     uint8_t         abPadding2[4096*2 - 64 - 256 - 1024       - 64 - 2176 - 640 - 512 - 64 - 64 - sizeof(PGVMCPU) * VMM_MAX_CPU_COUNT];
     241    uint8_t         abPadding2[4096*2 - 64 - 256 - 1024 - 256 - 64 - 2176 - 640 - 512 - 64 - 1024 - sizeof(PGVMCPU) * VMM_MAX_CPU_COUNT];
     242#else
     243    uint8_t         abPadding2[4096*2 - 64 - 256 - 1024       - 64 - 2176 - 640 - 512 - 64 - 1024 - sizeof(PGVMCPU) * VMM_MAX_CPU_COUNT];
    244244#endif
    245245
  • trunk/include/VBox/vmm/uvm.h

    r82968 r86699  
    148148        struct DBGFUSERPERVM    s;
    149149#endif
    150         uint8_t                 padding[384];
     150        uint8_t                 padding[1024];
    151151    } dbgf;
    152152
  • trunk/include/VBox/vmm/vmm.h

    r86452 r86699  
    431431    /** Call DBGFR0TraceCallReqHandler. */
    432432    VMMR0_DO_DBGF_TRACER_CALL_REQ_HANDLER,
     433    /** Call DBGFR0BpInitReqHandler(). */
     434    VMMR0_DO_DBGF_BP_INIT,
     435    /** Call DBGFR0BpChunkAllocReqHandler(). */
     436    VMMR0_DO_DBGF_BP_CHUNK_ALLOC,
    433437
    434438    /** The usual 32-bit type blow up. */
  • trunk/src/VBox/VMM/Makefile.kmk

    r86683 r86699  
    457457VMMR0_DEFS     += VBOX_WITH_DBGF_TRACING
    458458 endif
     459 ifdef VBOX_WITH_LOTS_OF_DBGF_BPS
     460VMMR0_DEFS     += VBOX_WITH_LOTS_OF_DBGF_BPS
     461 endif
    459462 ifdef VBOX_WITH_VMM_R0_SWITCH_STACK
    460463VMMR0_DEFS     += VMM_R0_SWITCH_STACK
     
    479482        VMMR0/CPUMR0A.asm \
    480483        VMMR0/DBGFR0.cpp \
     484        $(if-expr defined(VBOX_WITH_LOTS_OF_DBGF_BPS), VMMR0/DBGFR0Bp.cpp,) \
    481485        $(if-expr defined(VBOX_WITH_DBGF_TRACING), VMMR0/DBGFR0Tracer.cpp,) \
    482486        VMMR0/GIMR0.cpp \
  • trunk/src/VBox/VMM/VMMR0/DBGFR0.cpp

    r86683 r86699  
    5555{
    5656    pGVM->dbgfr0.s.pTracerR0 = NULL;
     57
     58#ifdef VBOX_WITH_LOTS_OF_DBGF_BPS
     59    dbgfR0BpInit(pGVM);
     60#endif
    5761}
    5862
     
    6872    pGVM->dbgfr0.s.pTracerR0 = NULL;
    6973#endif
     74
     75#ifdef VBOX_WITH_LOTS_OF_DBGF_BPS
     76    dbgfR0BpDestroy(pGVM);
     77#endif
    7078}
    7179
  • trunk/src/VBox/VMM/VMMR0/VMMR0.cpp

    r86452 r86699  
    22422242        }
    22432243
     2244        /*
     2245         * DBGF requests.
     2246         */
    22442247#ifdef VBOX_WITH_DBGF_TRACING
    22452248        case VMMR0_DO_DBGF_TRACER_CREATE:
     
    22612264            rc = VERR_NOT_IMPLEMENTED;
    22622265#endif
     2266            VMM_CHECK_SMAP_CHECK2(pGVM, RT_NOTHING);
     2267            break;
     2268        }
     2269#endif
     2270
     2271#ifdef VBOX_WITH_LOTS_OF_DBGF_BPS
     2272        case VMMR0_DO_DBGF_BP_INIT:
     2273        {
     2274            if (!pReqHdr || u64Arg || idCpu != 0)
     2275                return VERR_INVALID_PARAMETER;
     2276            rc = DBGFR0BpInitReqHandler(pGVM, (PDBGFBPINITREQ)pReqHdr);
     2277            VMM_CHECK_SMAP_CHECK2(pGVM, RT_NOTHING);
     2278            break;
     2279        }
     2280
     2281        case VMMR0_DO_DBGF_BP_CHUNK_ALLOC:
     2282        {
     2283            if (!pReqHdr || u64Arg || idCpu != 0)
     2284                return VERR_INVALID_PARAMETER;
     2285            rc = DBGFR0BpChunkAllocReqHandler(pGVM, (PDBGFBPCHUNKALLOCREQ)pReqHdr);
    22632286            VMM_CHECK_SMAP_CHECK2(pGVM, RT_NOTHING);
    22642287            break;
     
    23682391            case VMMR0_DO_IOM_GROW_IO_PORTS:
    23692392            case VMMR0_DO_IOM_GROW_IO_PORT_STATS:
     2393
     2394#ifdef VBOX_WITH_LOTS_OF_DBGF_BPS
     2395            case VMMR0_DO_DBGF_BP_INIT:
     2396            case VMMR0_DO_DBGF_BP_CHUNK_ALLOC:
     2397#endif
    23702398            {
    23712399                PGVMCPU        pGVCpu        = &pGVM->aCpus[idCpu];
  • trunk/src/VBox/VMM/VMMR3/DBGF.cpp

    r86666 r86699  
    149149                if (RT_SUCCESS(rc))
    150150                {
     151#ifndef VBOX_WITH_LOTS_OF_DBGF_BPS
    151152                    rc = dbgfR3BpInit(pVM);
     153#else
     154                    rc = dbgfR3BpInit(pUVM);
     155#endif
    152156                    if (RT_SUCCESS(rc))
    153157                    {
     
    175179                        }
    176180#ifdef VBOX_WITH_LOTS_OF_DBGF_BPS
    177                         dbgfR3BpTerm(pVM);
     181                        dbgfR3BpTerm(pUVM);
    178182#endif
    179183                    }
     
    207211    dbgfR3OSTermPart2(pUVM);
    208212#ifdef VBOX_WITH_LOTS_OF_DBGF_BPS
    209     dbgfR3BpTerm(pVM);
     213    dbgfR3BpTerm(pUVM);
    210214#endif
    211215    dbgfR3AsTerm(pUVM);
  • trunk/src/VBox/VMM/VMMR3/DBGFR3Bp.cpp

    r86666 r86699  
    110110 *        from the L1 table. The key for the table are the upper 6 bytes of the breakpoint address
    111111 *        used for searching. This tree is traversed until either a matching address is found and
    112  *        the breakpoint is being processed or again forwardedto the guest if it isn't successful.
     112 *        the breakpoint is being processed or again forwarded to the guest if it isn't successful.
    113113 *        Each entry in the L2 table is 16 bytes big and densly packed to avoid excessive memory usage.
    114114 *
     
    155155#include <VBox/log.h>
    156156#include <iprt/assert.h>
    157 #include <iprt/string.h>
     157#include <iprt/mem.h>
    158158
    159159
     
    170170
    171171
    172 
    173172/**
    174173 * Initialize the breakpoint mangement.
    175174 *
    176175 * @returns VBox status code.
    177  * @param   pVM                 The cross context VM structure.
    178  */
    179 DECLHIDDEN(int) dbgfR3BpInit(PVM pVM)
    180 {
    181     RT_NOREF(pVM);
     176 * @param   pUVM                The user mode VM handle.
     177 */
     178DECLHIDDEN(int) dbgfR3BpInit(PUVM pUVM)
     179{
     180    PVM pVM = pUVM->pVM;
     181
     182    /* Init hardware breakpoint states. */
     183    for (uint32_t i = 0; i < RT_ELEMENTS(pVM->dbgf.s.aHwBreakpoints); i++)
     184    {
     185        PDBGFBPHW pHwBp = &pVM->dbgf.s.aHwBreakpoints[i];
     186
     187        AssertCompileSize(DBGFBP, sizeof(uint32_t));
     188        pHwBp->hBp      = NIL_DBGFBP;
     189        //pHwBp->fEnabled = false;
     190    }
     191
     192    /* Now the global breakpoint table chunks. */
     193    for (uint32_t i = 0; i < RT_ELEMENTS(pUVM->dbgf.s.aBpChunks); i++)
     194    {
     195        PDBGFBPCHUNKR3 pBpChunk = &pUVM->dbgf.s.aBpChunks[i];
     196
     197        //pBpChunk->pBpBaseR3 = NULL;
     198        //pBpChunk->pbmAlloc  = NULL;
     199        //pBpChunk->cBpsFree  = 0;
     200        pBpChunk->idChunk = DBGF_BP_CHUNK_ID_INVALID; /* Not allocated. */
     201    }
     202
     203    //pUVM->dbgf.s.paBpLocL1R3 = NULL;
    182204    return VINF_SUCCESS;
    183205}
     
    188210 *
    189211 * @returns VBox status code.
    190  * @param   pVM                 The cross context VM structure.
    191  */
    192 DECLHIDDEN(int) dbgfR3BpTerm(PVM pVM)
    193 {
    194     RT_NOREF(pVM);
     212 * @param   pUVM                The user mode VM handle.
     213 */
     214DECLHIDDEN(int) dbgfR3BpTerm(PUVM pUVM)
     215{
     216    /* Free all allocated chunk bitmaps (the chunks itself are destroyed during ring-0 VM destruction). */
     217    for (uint32_t i = 0; i < RT_ELEMENTS(pUVM->dbgf.s.aBpChunks); i++)
     218    {
     219        PDBGFBPCHUNKR3 pBpChunk = &pUVM->dbgf.s.aBpChunks[i];
     220
     221        if (pBpChunk->idChunk != DBGF_BP_CHUNK_ID_INVALID)
     222        {
     223            AssertPtr(pBpChunk->pbmAlloc);
     224            RTMemFree((void *)pBpChunk->pbmAlloc);
     225            pBpChunk->pbmAlloc = NULL;
     226            pBpChunk->idChunk = DBGF_BP_CHUNK_ID_INVALID;
     227        }
     228    }
     229
    195230    return VINF_SUCCESS;
     231}
     232
     233
     234/**
     235 * @callback_method_impl{FNVMMEMTRENDEZVOUS}
     236 */
     237static DECLCALLBACK(VBOXSTRICTRC) dbgfR3BpInitEmtWorker(PVM pVM, PVMCPU pVCpu, void *pvUser)
     238{
     239    RT_NOREF(pvUser);
     240
     241    VMCPU_ASSERT_EMT(pVCpu);
     242    VM_ASSERT_VALID_EXT_RETURN(pVM, VERR_INVALID_VM_HANDLE);
     243
     244    /*
     245     * The initialization will be done on EMT(0). It is possible that multiple
     246     * initialization attempts are done because dbgfR3BpEnsureInit() can be called
     247     * from racing non EMT threads when trying to set a breakpoint for the first time.
     248     * Just fake success if the L1 is already present which means that a previous rendezvous
     249     * successfully initialized the breakpoint manager.
     250     */
     251    PUVM pUVM = pVM->pUVM;
     252    if (   pVCpu->idCpu == 0
     253        && !pUVM->dbgf.s.paBpLocL1R3)
     254    {
     255        DBGFBPINITREQ Req;
     256        Req.Hdr.u32Magic = SUPVMMR0REQHDR_MAGIC;
     257        Req.Hdr.cbReq    = sizeof(Req);
     258        Req.paBpLocL1R3  = NULL;
     259        int rc = VMMR3CallR0Emt(pVM, pVCpu, VMMR0_DO_DBGF_BP_INIT, 0 /*u64Arg*/, &Req.Hdr);
     260        AssertLogRelMsgRCReturn(rc, ("VMMR0_DO_DBGF_BP_INIT failed: %Rrc\n", rc), rc);
     261        pUVM->dbgf.s.paBpLocL1R3 = Req.paBpLocL1R3;
     262    }
     263
     264    return VINF_SUCCESS;
     265}
     266
     267
     268/**
     269 * Ensures that the breakpoint manager is fully initialized.
     270 *
     271 * @returns VBox status code.
     272 * @param   pUVM                The user mode VM handle.
     273 *
     274 * @thread Any thread.
     275 */
     276static int dbgfR3BpEnsureInit(PUVM pUVM)
     277{
     278    /* If the L1 lookup table is allocated initialization succeeded before. */
     279    if (RT_LIKELY(pUVM->dbgf.s.paBpLocL1R3))
     280        return VINF_SUCCESS;
     281
     282    /* Gather all EMTs and call into ring-0 to initialize the breakpoint manager. */
     283    return VMMR3EmtRendezvous(pUVM->pVM, VMMEMTRENDEZVOUS_FLAGS_TYPE_ALL_AT_ONCE, dbgfR3BpInitEmtWorker, NULL /*pvUser*/);
     284}
     285
     286
     287/**
     288 * Returns the internal breakpoint state for the given handle.
     289 *
     290 * @returns Pointer to the internal breakpoint state or NULL if the handle is invalid.
     291 * @param   pUVM                The user mode VM handle.
     292 * @param   hBp                 The breakpoint handle to resolve.
     293 */
     294DECLINLINE(PDBGFBPINT) dbgfR3BpGetByHnd(PUVM pUVM, DBGFBP hBp)
     295{
     296    uint32_t idChunk  = DBGF_BP_HND_GET_CHUNK_ID(hBp);
     297    uint32_t idxEntry = DBGF_BP_HND_GET_ENTRY(hBp);
     298
     299    AssertReturn(idChunk < DBGF_BP_CHUNK_COUNT, NULL);
     300    AssertReturn(idxEntry < DBGF_BP_COUNT_PER_CHUNK, NULL);
     301
     302    PDBGFBPCHUNKR3 pBpChunk = &pUVM->dbgf.s.aBpChunks[idChunk];
     303    AssertReturn(pBpChunk->idChunk == idChunk, NULL);
     304    AssertPtrReturn(pBpChunk->pbmAlloc, NULL);
     305    AssertReturn(ASMBitTest(pBpChunk->pbmAlloc, idxEntry), NULL);
     306
     307    return &pBpChunk->pBpBaseR3[idxEntry];
     308}
     309
     310
     311/**
     312 * Get a breakpoint give by address.
     313 *
     314 * @returns The breakpoint handle on success or NIL_DBGF if not found.
     315 * @param   pUVM                The user mode VM handle.
     316 * @param   enmType             The breakpoint type.
     317 * @param   GCPtr               The breakpoint address.
     318 * @param   ppBp                Where to store the pointer to the internal breakpoint state on success, optional.
     319 */
     320static DBGFBP dbgfR3BpGetByAddr(PUVM pUVM, DBGFBPTYPE enmType, RTGCUINTPTR GCPtr, PDBGFBPINT *ppBp)
     321{
     322    DBGFBP hBp = NIL_DBGFBP;
     323
     324    switch (enmType)
     325    {
     326        case DBGFBPTYPE_REG:
     327        {
     328            PVM pVM = pUVM->pVM;
     329            VM_ASSERT_VALID_EXT_RETURN(pVM, NIL_DBGFBP);
     330
     331            for (uint32_t i = 0; i < RT_ELEMENTS(pVM->dbgf.s.aHwBreakpoints); i++)
     332            {
     333                PDBGFBPHW pHwBp = &pVM->dbgf.s.aHwBreakpoints[i];
     334
     335                AssertCompileSize(DBGFBP, sizeof(uint32_t));
     336                DBGFBP hBpTmp = ASMAtomicReadU32(&pHwBp->hBp);
     337                if (   pHwBp->GCPtr == GCPtr
     338                    && hBpTmp != NIL_DBGFBP)
     339                {
     340                    hBp = hBpTmp;
     341                    break;
     342                }
     343            }
     344
     345            break;
     346        }
     347
     348        case DBGFBPTYPE_INT3:
     349            break;
     350
     351        default:
     352            AssertMsgFailed(("enmType=%d\n", enmType));
     353            break;
     354    }
     355
     356    if (   hBp != NIL_DBGFBP
     357        && ppBp)
     358        *ppBp =  dbgfR3BpGetByHnd(pUVM, hBp);
     359    return hBp;
     360}
     361
     362
     363/**
     364 * @callback_method_impl{FNVMMEMTRENDEZVOUS}
     365 */
     366static DECLCALLBACK(VBOXSTRICTRC) dbgfR3BpChunkAllocEmtWorker(PVM pVM, PVMCPU pVCpu, void *pvUser)
     367{
     368    uint32_t idChunk = (uint32_t)(uintptr_t)pvUser;
     369
     370    VMCPU_ASSERT_EMT(pVCpu);
     371    VM_ASSERT_VALID_EXT_RETURN(pVM, VERR_INVALID_VM_HANDLE);
     372
     373    AssertReturn(idChunk < DBGF_BP_CHUNK_COUNT, VERR_DBGF_BP_IPE_1);
     374
     375    PUVM pUVM = pVM->pUVM;
     376    PDBGFBPCHUNKR3 pBpChunk = &pUVM->dbgf.s.aBpChunks[idChunk];
     377
     378    AssertReturn(   pBpChunk->idChunk == DBGF_BP_CHUNK_ID_INVALID
     379                 || pBpChunk->idChunk == idChunk,
     380                 VERR_DBGF_BP_IPE_2);
     381
     382    /*
     383     * The initialization will be done on EMT(0). It is possible that multiple
     384     * allocation attempts are done when multiple racing non EMT threads try to
     385     * allocate a breakpoint and a new chunk needs to be allocated.
     386     * Ignore the request and succeed if the chunk is allocated meaning that a
     387     * previous rendezvous successfully allocated the chunk.
     388     */
     389    int rc = VINF_SUCCESS;
     390    if (   pVCpu->idCpu == 0
     391        && pBpChunk->idChunk == DBGF_BP_CHUNK_ID_INVALID)
     392    {
     393        /* Allocate the bitmap first so we can skip calling into VMMR0 if it fails. */
     394        AssertCompile(!(DBGF_BP_COUNT_PER_CHUNK % 8));
     395        volatile void *pbmAlloc = RTMemAllocZ(DBGF_BP_COUNT_PER_CHUNK / 8);
     396        if (RT_LIKELY(pbmAlloc))
     397        {
     398            DBGFBPCHUNKALLOCREQ Req;
     399            Req.Hdr.u32Magic = SUPVMMR0REQHDR_MAGIC;
     400            Req.Hdr.cbReq    = sizeof(Req);
     401            Req.idChunk      = idChunk;
     402            Req.pChunkBaseR3 = NULL;
     403            rc = VMMR3CallR0Emt(pVM, pVCpu, VMMR0_DO_DBGF_BP_CHUNK_ALLOC, 0 /*u64Arg*/, &Req.Hdr);
     404            AssertLogRelMsgRC(rc, ("VMMR0_DO_DBGF_BP_CHUNK_ALLOC failed: %Rrc\n", rc));
     405            if (RT_SUCCESS(rc))
     406            {
     407                pBpChunk->pBpBaseR3 = (PDBGFBPINT)Req.pChunkBaseR3;
     408                pBpChunk->pbmAlloc   = pbmAlloc;
     409                pBpChunk->cBpsFree  = DBGF_BP_COUNT_PER_CHUNK;
     410                pBpChunk->idChunk   = idChunk;
     411                return VINF_SUCCESS;
     412            }
     413
     414            RTMemFree((void *)pbmAlloc);
     415        }
     416        else
     417            rc = VERR_NO_MEMORY;
     418    }
     419
     420    return rc;
     421}
     422
     423
     424/**
     425 * Tries to allocate the given chunk which requires an EMT rendezvous.
     426 *
     427 * @returns VBox status code.
     428 * @param   pUVM                The user mode VM handle.
     429 * @param   idChunk             The chunk to allocate.
     430 *
     431 * @thread Any thread.
     432 */
     433DECLINLINE(int) dbgfR3BpChunkAlloc(PUVM pUVM, uint32_t idChunk)
     434{
     435    return VMMR3EmtRendezvous(pUVM->pVM, VMMEMTRENDEZVOUS_FLAGS_TYPE_ALL_AT_ONCE, dbgfR3BpChunkAllocEmtWorker, (void *)(uintptr_t)idChunk);
     436}
     437
     438
     439/**
     440 * Tries to allocate a new breakpoint of the given type.
     441 *
     442 * @returns VBox status code.
     443 * @param   pUVM                The user mode VM handle.
     444 * @param   hOwner              The owner handle, NIL_DBGFBPOWNER if none assigned.
     445 * @param   pvUser              Opaque user data passed in the owner callback.
     446 * @param   enmType             Breakpoint type to allocate.
     447 * @param   iHitTrigger         The hit count at which the breakpoint start triggering.
     448 *                              Use 0 (or 1) if it's gonna trigger at once.
     449 * @param   iHitDisable         The hit count which disables the breakpoint.
     450 *                              Use ~(uint64_t) if it's never gonna be disabled.
     451 * @param   phBp                Where to return the opaque breakpoint handle on success.
     452 * @param   ppBp                Where to return the pointer to the internal breakpoint state on success.
     453 *
     454 * @thread Any thread.
     455 */
     456static int dbgfR3BpAlloc(PUVM pUVM, DBGFBPOWNER hOwner, void *pvUser, DBGFBPTYPE enmType,
     457                         uint64_t iHitTrigger, uint64_t iHitDisable, PDBGFBP phBp,
     458                         PDBGFBPINT *ppBp)
     459{
     460    /*
     461     * Search for a chunk having a free entry, allocating new chunks
     462     * if the encountered ones are full.
     463     *
     464     * This can be called from multiple threads at the same time so special care
     465     * has to be taken to not require any locking here.
     466     */
     467    for (uint32_t i = 0; i < RT_ELEMENTS(pUVM->dbgf.s.aBpChunks); i++)
     468    {
     469        PDBGFBPCHUNKR3 pBpChunk = &pUVM->dbgf.s.aBpChunks[i];
     470
     471        uint32_t idChunk  = ASMAtomicReadU32(&pBpChunk->idChunk);
     472        if (idChunk == DBGF_BP_CHUNK_ID_INVALID)
     473        {
     474            int rc = dbgfR3BpChunkAlloc(pUVM, i);
     475            if (RT_FAILURE(rc))
     476            {
     477                LogRel(("DBGF/Bp: Allocating new breakpoint table chunk failed with %Rrc\n", rc));
     478                break;
     479            }
     480
     481            idChunk = ASMAtomicReadU32(&pBpChunk->idChunk);
     482            Assert(idChunk == i);
     483        }
     484
     485        /** @todo Optimize with some hinting if this turns out to be too slow. */
     486        for (;;)
     487        {
     488            uint32_t cBpsFree = ASMAtomicReadU32(&pBpChunk->cBpsFree);
     489            if (cBpsFree)
     490            {
     491                /*
     492                 * Scan the associated bitmap for a free entry, if none can be found another thread
     493                 * raced us and we go to the next chunk.
     494                 */
     495                int32_t iClr = ASMBitFirstClear(pBpChunk->pbmAlloc, DBGF_BP_COUNT_PER_CHUNK);
     496                if (iClr != -1)
     497                {
     498                    /*
     499                     * Try to allocate, we could get raced here as well. In that case
     500                     * we try again.
     501                     */
     502                    if (!ASMAtomicBitTestAndSet(pBpChunk->pbmAlloc, iClr))
     503                    {
     504                        /* Success, immediately mark as allocated, initialize the breakpoint state and return. */
     505                        ASMAtomicDecU32(&pBpChunk->cBpsFree);
     506
     507                        PDBGFBPINT pBp = &pBpChunk->pBpBaseR3[iClr];
     508                        pBp->Pub.cHits = 0;
     509                        pBp->Pub.iHitTrigger   = iHitTrigger;
     510                        pBp->Pub.iHitDisable   = iHitDisable;
     511                        pBp->Pub.hOwner        = hOwner;
     512                        pBp->Pub.fFlagsAndType = DBGF_BP_PUB_SET_FLAGS_AND_TYPE(enmType, DBGF_BP_F_DEFAULT);
     513                        pBp->pvUserR3          = pvUser;
     514
     515                        /** @todo Owner handling (reference and call ring-0 if it has an ring-0 callback). */
     516
     517                        *phBp = DBGF_BP_HND_CREATE(idChunk, iClr);
     518                        *ppBp = pBp;
     519                        return VINF_SUCCESS;
     520                    }
     521                    /* else Retry with another spot. */
     522                }
     523                else /* no free entry in bitmap, go to the next chunk */
     524                    break;
     525            }
     526            else /* !cBpsFree, go to the next chunk */
     527                break;
     528        }
     529    }
     530
     531    return VERR_DBGF_NO_MORE_BP_SLOTS;
     532}
     533
     534
     535/**
     536 * Frees the given breakpoint handle.
     537 *
     538 * @returns nothing.
     539 * @param   pUVM                The user mode VM handle.
     540 * @param   hBp                 The breakpoint handle to free.
     541 * @param   pBp                 The internal breakpoint state pointer.
     542 */
     543static void dbgfR3BpFree(PUVM pUVM, DBGFBP hBp, PDBGFBPINT pBp)
     544{
     545    uint32_t idChunk  = DBGF_BP_HND_GET_CHUNK_ID(hBp);
     546    uint32_t idxEntry = DBGF_BP_HND_GET_ENTRY(hBp);
     547
     548    AssertReturnVoid(idChunk < DBGF_BP_CHUNK_COUNT);
     549    AssertReturnVoid(idxEntry < DBGF_BP_COUNT_PER_CHUNK);
     550
     551    PDBGFBPCHUNKR3 pBpChunk = &pUVM->dbgf.s.aBpChunks[idChunk];
     552    AssertPtrReturnVoid(pBpChunk->pbmAlloc);
     553    AssertReturnVoid(ASMBitTest(pBpChunk->pbmAlloc, idxEntry));
     554
     555    /** @todo Need a trip to Ring-0 if an owner is assigned with a Ring-0 part to clear the breakpoint. */
     556    /** @todo Release owner. */
     557    memset(pBp, 0, sizeof(*pBp));
     558
     559    ASMAtomicBitClear(pBpChunk->pbmAlloc, idxEntry);
     560    ASMAtomicIncU32(&pBpChunk->cBpsFree);
     561}
     562
     563
     564/**
     565 * Sets the enabled flag of the given breakpoint to the given value.
     566 *
     567 * @returns nothing.
     568 * @param   pBp                 The breakpoint to set the state.
     569 * @param   fEnabled            Enabled status.
     570 */
     571DECLINLINE(void) dbgfR3BpSetEnabled(PDBGFBPINT pBp, bool fEnabled)
     572{
     573    DBGFBPTYPE enmType = DBGF_BP_PUB_GET_TYPE(pBp->Pub.fFlagsAndType);
     574    if (fEnabled)
     575        pBp->Pub.fFlagsAndType = DBGF_BP_PUB_SET_FLAGS_AND_TYPE(enmType, DBGF_BP_F_ENABLED);
     576    else
     577        pBp->Pub.fFlagsAndType = DBGF_BP_PUB_SET_FLAGS_AND_TYPE(enmType, 0 /*fFlags*/);
     578}
     579
     580
     581/**
     582 * Assigns a hardware breakpoint state to the given register breakpoint.
     583 *
     584 * @returns VBox status code.
     585 * @param   pVM                 The cross-context VM structure pointer.
     586 * @param   hBp                 The breakpoint handle to assign.
     587 * @param   pBp                 The internal breakpoint state.
     588 *
     589 * @thread Any thread.
     590 */
     591static int dbgfR3BpRegAssign(PVM pVM, DBGFBP hBp, PDBGFBPINT pBp)
     592{
     593    AssertReturn(pBp->Pub.u.Reg.iReg == UINT8_MAX, VERR_DBGF_BP_IPE_3);
     594
     595    for (uint8_t i = 0; i < RT_ELEMENTS(pVM->dbgf.s.aHwBreakpoints); i++)
     596    {
     597        PDBGFBPHW pHwBp = &pVM->dbgf.s.aHwBreakpoints[i];
     598
     599        AssertCompileSize(DBGFBP, sizeof(uint32_t));
     600        if (ASMAtomicCmpXchgU32(&pHwBp->hBp, hBp, NIL_DBGFBP))
     601        {
     602            pHwBp->GCPtr    = pBp->Pub.u.Reg.GCPtr;
     603            pHwBp->fType    = pBp->Pub.u.Reg.fType;
     604            pHwBp->cb       = pBp->Pub.u.Reg.cb;
     605            pHwBp->fEnabled = DBGF_BP_PUB_IS_ENABLED(pBp->Pub.fFlagsAndType);
     606
     607            pBp->Pub.u.Reg.iReg = i;
     608            return VINF_SUCCESS;
     609        }
     610    }
     611
     612    return VERR_DBGF_NO_MORE_BP_SLOTS;
     613}
     614
     615
     616/**
     617 * Removes the assigned hardware breakpoint state from the given register breakpoint.
     618 *
     619 * @returns VBox status code.
     620 * @param   pVM                 The cross-context VM structure pointer.
     621 * @param   hBp                 The breakpoint handle to remove.
     622 * @param   pBp                 The internal breakpoint state.
     623 *
     624 * @thread Any thread.
     625 */
     626static int dbgfR3BpRegRemove(PVM pVM, DBGFBP hBp, PDBGFBPINT pBp)
     627{
     628    AssertReturn(pBp->Pub.u.Reg.iReg < RT_ELEMENTS(pVM->dbgf.s.aHwBreakpoints), VERR_DBGF_BP_IPE_3);
     629
     630    PDBGFBPHW pHwBp = &pVM->dbgf.s.aHwBreakpoints[pBp->Pub.u.Reg.iReg];
     631    AssertReturn(pHwBp->hBp == hBp, VERR_DBGF_BP_IPE_4);
     632    AssertReturn(!pHwBp->fEnabled, VERR_DBGF_BP_IPE_5);
     633
     634    pHwBp->GCPtr = 0;
     635    pHwBp->fType = 0;
     636    pHwBp->cb    = 0;
     637    ASMCompilerBarrier();
     638
     639    ASMAtomicWriteU32(&pHwBp->hBp, NIL_DBGFBP);
     640    return VINF_SUCCESS;
     641}
     642
     643
     644/**
     645 * @callback_method_impl{FNVMMEMTRENDEZVOUS}
     646 */
     647static DECLCALLBACK(VBOXSTRICTRC) dbgfR3BpRegRecalcOnCpu(PVM pVM, PVMCPU pVCpu, void *pvUser)
     648{
     649    RT_NOREF(pvUser);
     650
     651    /*
     652     * CPU 0 updates the enabled hardware breakpoint counts.
     653     */
     654    if (pVCpu->idCpu == 0)
     655    {
     656        pVM->dbgf.s.cEnabledHwBreakpoints   = 0;
     657        pVM->dbgf.s.cEnabledHwIoBreakpoints = 0;
     658
     659        for (uint32_t iBp = 0; iBp < RT_ELEMENTS(pVM->dbgf.s.aHwBreakpoints); iBp++)
     660        {
     661            if (pVM->dbgf.s.aHwBreakpoints[iBp].fEnabled)
     662            {
     663                pVM->dbgf.s.cEnabledHwBreakpoints   += 1;
     664                pVM->dbgf.s.cEnabledHwIoBreakpoints += pVM->dbgf.s.aHwBreakpoints[iBp].fType == X86_DR7_RW_IO;
     665            }
     666        }
     667    }
     668
     669    return CPUMRecalcHyperDRx(pVCpu, UINT8_MAX, false);
     670}
     671
     672
     673/**
     674 * Arms the given breakpoint.
     675 *
     676 * @returns VBox status code.
     677 * @param   pUVM                The user mode VM handle.
     678 * @param   hBp                 The breakpoint handle to arm.
     679 * @param   pBp                 The internal breakpoint state pointer for the handle.
     680 */
     681static int dbgfR3BpArm(PUVM pUVM, DBGFBP hBp, PDBGFBPINT pBp)
     682{
     683    int rc = VINF_SUCCESS;
     684    PVM pVM = pUVM->pVM;
     685
     686    Assert(!DBGF_BP_PUB_IS_ENABLED(pBp->Pub.fFlagsAndType));
     687    switch (DBGF_BP_PUB_GET_TYPE(pBp->Pub.fFlagsAndType))
     688    {
     689        case DBGFBPTYPE_REG:
     690        {
     691            Assert(pBp->Pub.u.Reg.iReg < RT_ELEMENTS(pVM->dbgf.s.aHwBreakpoints));
     692            PDBGFBPHW pBpHw = &pVM->dbgf.s.aHwBreakpoints[pBp->Pub.u.Reg.iReg];
     693            Assert(pBpHw->hBp == hBp); RT_NOREF(hBp);
     694
     695            dbgfR3BpSetEnabled(pBp, true /*fEnabled*/);
     696            ASMAtomicWriteBool(&pBpHw->fEnabled, true);
     697            rc = VMMR3EmtRendezvous(pVM, VMMEMTRENDEZVOUS_FLAGS_TYPE_ALL_AT_ONCE, dbgfR3BpRegRecalcOnCpu, NULL);
     698            if (RT_FAILURE(rc))
     699            {
     700                ASMAtomicWriteBool(&pBpHw->fEnabled, false);
     701                dbgfR3BpSetEnabled(pBp, false /*fEnabled*/);
     702            }
     703            break;
     704        }
     705        case DBGFBPTYPE_INT3:
     706        case DBGFBPTYPE_PORT_IO:
     707        case DBGFBPTYPE_MMIO:
     708            rc = VERR_NOT_IMPLEMENTED;
     709            break;
     710        default:
     711            AssertMsgFailedReturn(("Invalid breakpoint type %d\n", DBGF_BP_PUB_GET_TYPE(pBp->Pub.fFlagsAndType)),
     712                                  VERR_IPE_NOT_REACHED_DEFAULT_CASE);
     713    }
     714
     715    return rc;
     716}
     717
     718
     719/**
     720 * Disarms the given breakpoint.
     721 *
     722 * @returns VBox status code.
     723 * @param   pUVM                The user mode VM handle.
     724 * @param   hBp                 The breakpoint handle to disarm.
     725 * @param   pBp                 The internal breakpoint state pointer for the handle.
     726 */
     727static int dbgfR3BpDisarm(PUVM pUVM, DBGFBP hBp, PDBGFBPINT pBp)
     728{
     729    int rc = VINF_SUCCESS;
     730    PVM pVM = pUVM->pVM;
     731
     732    Assert(DBGF_BP_PUB_IS_ENABLED(pBp->Pub.fFlagsAndType));
     733    switch (DBGF_BP_PUB_GET_TYPE(pBp->Pub.fFlagsAndType))
     734    {
     735        case DBGFBPTYPE_REG:
     736        {
     737            Assert(pBp->Pub.u.Reg.iReg < RT_ELEMENTS(pVM->dbgf.s.aHwBreakpoints));
     738            PDBGFBPHW pBpHw = &pVM->dbgf.s.aHwBreakpoints[pBp->Pub.u.Reg.iReg];
     739            Assert(pBpHw->hBp == hBp); RT_NOREF(hBp);
     740
     741            dbgfR3BpSetEnabled(pBp, false /*fEnabled*/);
     742            ASMAtomicWriteBool(&pBpHw->fEnabled, false);
     743            rc = VMMR3EmtRendezvous(pVM, VMMEMTRENDEZVOUS_FLAGS_TYPE_ALL_AT_ONCE, dbgfR3BpRegRecalcOnCpu, NULL);
     744            if (RT_FAILURE(rc))
     745            {
     746                ASMAtomicWriteBool(&pBpHw->fEnabled, true);
     747                dbgfR3BpSetEnabled(pBp, true /*fEnabled*/);
     748            }
     749            break;
     750        }
     751        case DBGFBPTYPE_INT3:
     752        case DBGFBPTYPE_PORT_IO:
     753        case DBGFBPTYPE_MMIO:
     754            rc = VERR_NOT_IMPLEMENTED;
     755            break;
     756        default:
     757            AssertMsgFailedReturn(("Invalid breakpoint type %d\n", DBGF_BP_PUB_GET_TYPE(pBp->Pub.fFlagsAndType)),
     758                                  VERR_IPE_NOT_REACHED_DEFAULT_CASE);
     759    }
     760
     761    return rc;
    196762}
    197763
     
    285851{
    286852    UVM_ASSERT_VALID_EXT_RETURN(pUVM, VERR_INVALID_VM_HANDLE);
    287     AssertReturn(hOwner != NIL_DBGFBPOWNER || pvUser != NULL, VERR_INVALID_PARAMETER);
     853    AssertReturn(hOwner != NIL_DBGFBPOWNER || pvUser == NULL, VERR_INVALID_PARAMETER);
    288854    AssertReturn(DBGFR3AddrIsValid(pUVM, pAddress), VERR_INVALID_PARAMETER);
    289855    AssertReturn(iHitTrigger <= iHitDisable, VERR_INVALID_PARAMETER);
    290856    AssertPtrReturn(phBp, VERR_INVALID_POINTER);
    291857
    292     RT_NOREF(idSrcCpu);
    293 
    294     return VERR_NOT_IMPLEMENTED;
     858    int rc = dbgfR3BpEnsureInit(pUVM);
     859    AssertRCReturn(rc, rc);
     860
     861    DBGFBP hBp = NIL_DBGFBP;
     862    PDBGFBPINT pBp = NULL;
     863    rc = dbgfR3BpAlloc(pUVM, hOwner, pvUser, DBGFBPTYPE_INT3, iHitTrigger, iHitDisable, &hBp, &pBp);
     864    if (RT_SUCCESS(rc))
     865    {
     866        /*
     867         * Translate & save the breakpoint address into a guest-physical address.
     868         */
     869        rc = DBGFR3AddrToPhys(pUVM, idSrcCpu, pAddress, &pBp->Pub.u.Int3.PhysAddr);
     870        if (RT_SUCCESS(rc))
     871        {
     872            /*
     873             * The physical address from DBGFR3AddrToPhys() is the start of the page,
     874             * we need the exact byte offset into the page while writing to it in dbgfR3BpInt3Arm().
     875             */
     876            pBp->Pub.u.Int3.PhysAddr |= (pAddress->FlatPtr & X86_PAGE_OFFSET_MASK);
     877            pBp->Pub.u.Int3.GCPtr     = pAddress->FlatPtr;
     878
     879            /* Enable the breakpoint. */
     880            rc = dbgfR3BpArm(pUVM, hBp, pBp);
     881            if (RT_SUCCESS(rc))
     882            {
     883                *phBp = hBp;
     884                return VINF_SUCCESS;
     885            }
     886        }
     887
     888        dbgfR3BpFree(pUVM, hBp, pBp);
     889    }
     890
     891    return rc;
    295892}
    296893
     
    345942{
    346943    UVM_ASSERT_VALID_EXT_RETURN(pUVM, VERR_INVALID_VM_HANDLE);
    347     AssertReturn(hOwner != NIL_DBGFBPOWNER || pvUser != NULL, VERR_INVALID_PARAMETER);
     944    AssertReturn(hOwner != NIL_DBGFBPOWNER || pvUser == NULL, VERR_INVALID_PARAMETER);
    348945    AssertReturn(DBGFR3AddrIsValid(pUVM, pAddress), VERR_INVALID_PARAMETER);
    349946    AssertReturn(iHitTrigger <= iHitDisable, VERR_INVALID_PARAMETER);
     
    364961    }
    365962
    366     return VERR_NOT_IMPLEMENTED;
     963    int rc = dbgfR3BpEnsureInit(pUVM);
     964    AssertRCReturn(rc, rc);
     965
     966    PDBGFBPINT pBp = NULL;
     967    DBGFBP hBp = dbgfR3BpGetByAddr(pUVM, DBGFBPTYPE_REG, pAddress->FlatPtr, &pBp);
     968    if (    hBp != NIL_DBGFBP
     969        &&  pBp->Pub.u.Reg.cb == cb
     970        &&  pBp->Pub.u.Reg.fType == fType)
     971    {
     972        rc = VINF_SUCCESS;
     973        if (!DBGF_BP_PUB_IS_ENABLED(pBp->Pub.fFlagsAndType))
     974            rc = dbgfR3BpArm(pUVM, hBp, pBp);
     975        if (RT_SUCCESS(rc))
     976        {
     977            rc = VINF_DBGF_BP_ALREADY_EXIST;
     978            if (phBp)
     979                *phBp = hBp;
     980        }
     981        return rc;
     982    }
     983
     984    /* Allocate new breakpoint. */
     985    rc = dbgfR3BpAlloc(pUVM, hOwner, pvUser, DBGFBPTYPE_REG, iHitTrigger, iHitDisable, &hBp, &pBp);
     986    if (RT_SUCCESS(rc))
     987    {
     988        pBp->Pub.u.Reg.GCPtr = pAddress->FlatPtr;
     989        pBp->Pub.u.Reg.fType = fType;
     990        pBp->Pub.u.Reg.cb    = cb;
     991        pBp->Pub.u.Reg.iReg  = UINT8_MAX;
     992        ASMCompilerBarrier();
     993
     994        /* Assign the proper hardware breakpoint. */
     995        rc = dbgfR3BpRegAssign(pUVM->pVM, hBp, pBp);
     996        if (RT_SUCCESS(rc))
     997        {
     998            /* Arm the breakpoint. */
     999            rc = dbgfR3BpArm(pUVM, hBp, pBp);
     1000            if (RT_SUCCESS(rc))
     1001            {
     1002                if (phBp)
     1003                    *phBp = hBp;
     1004                return VINF_SUCCESS;
     1005            }
     1006            else
     1007            {
     1008                int rc2 = dbgfR3BpRegRemove(pUVM->pVM, hBp, pBp);
     1009                AssertRC(rc2); RT_NOREF(rc2);
     1010            }
     1011        }
     1012
     1013        dbgfR3BpFree(pUVM, hBp, pBp);
     1014    }
     1015
     1016    return rc;
    3671017}
    3681018
     
    4301080{
    4311081    UVM_ASSERT_VALID_EXT_RETURN(pUVM, VERR_INVALID_VM_HANDLE);
    432     AssertReturn(hOwner != NIL_DBGFBPOWNER || pvUser != NULL, VERR_INVALID_PARAMETER);
     1082    AssertReturn(hOwner != NIL_DBGFBPOWNER || pvUser == NULL, VERR_INVALID_PARAMETER);
    4331083    AssertReturn(!(fAccess & ~DBGFBPIOACCESS_VALID_MASK_PORT_IO), VERR_INVALID_FLAGS);
    4341084    AssertReturn(fAccess, VERR_INVALID_FLAGS);
     
    4371087    AssertReturn(cPorts > 0, VERR_OUT_OF_RANGE);
    4381088    AssertReturn((RTIOPORT)(uPort + cPorts) < uPort, VERR_OUT_OF_RANGE);
     1089
     1090    int rc = dbgfR3BpEnsureInit(pUVM);
     1091    AssertRCReturn(rc, rc);
    4391092
    4401093    return VERR_NOT_IMPLEMENTED;
     
    4911144{
    4921145    UVM_ASSERT_VALID_EXT_RETURN(pUVM, VERR_INVALID_VM_HANDLE);
    493     AssertReturn(hOwner != NIL_DBGFBPOWNER || pvUser != NULL, VERR_INVALID_PARAMETER);
     1146    AssertReturn(hOwner != NIL_DBGFBPOWNER || pvUser == NULL, VERR_INVALID_PARAMETER);
    4941147    AssertReturn(!(fAccess & ~DBGFBPIOACCESS_VALID_MASK_MMIO), VERR_INVALID_FLAGS);
    4951148    AssertReturn(fAccess, VERR_INVALID_FLAGS);
     
    4991152    AssertReturn(GCPhys + cb < GCPhys, VERR_OUT_OF_RANGE);
    5001153
     1154    int rc = dbgfR3BpEnsureInit(pUVM);
     1155    AssertRCReturn(rc, rc);
     1156
    5011157    return VERR_NOT_IMPLEMENTED;
    5021158}
     
    5171173    AssertReturn(hBp != NIL_DBGFBPOWNER, VERR_INVALID_HANDLE);
    5181174
    519     return VERR_NOT_IMPLEMENTED;
     1175    PDBGFBPINT pBp = dbgfR3BpGetByHnd(pUVM, hBp);
     1176    AssertPtrReturn(pBp, VERR_DBGF_BP_NOT_FOUND);
     1177
     1178    /* Disarm the breakpoint when it is enabled. */
     1179    if (DBGF_BP_PUB_IS_ENABLED(pBp->Pub.fFlagsAndType))
     1180    {
     1181        int rc = dbgfR3BpDisarm(pUVM, hBp, pBp);
     1182        AssertRC(rc);
     1183    }
     1184
     1185    switch (DBGF_BP_PUB_GET_TYPE(pBp->Pub.fFlagsAndType))
     1186    {
     1187        case DBGFBPTYPE_REG:
     1188        {
     1189            int rc = dbgfR3BpRegRemove(pUVM->pVM, hBp, pBp);
     1190            AssertRC(rc);
     1191            break;
     1192        }
     1193        default:
     1194            break;
     1195    }
     1196
     1197    dbgfR3BpFree(pUVM, hBp, pBp);
     1198    return VINF_SUCCESS;
    5201199}
    5211200
     
    5311210 */
    5321211VMMR3DECL(int) DBGFR3BpEnable(PUVM pUVM, DBGFBP hBp)
    533 {
    534     UVM_ASSERT_VALID_EXT_RETURN(pUVM, VERR_INVALID_VM_HANDLE);
    535     AssertReturn(hBp != NIL_DBGFBPOWNER, VERR_INVALID_HANDLE);
    536 
    537     return VERR_NOT_IMPLEMENTED;
    538 }
    539 
    540 
    541 /**
    542  * Disables a breakpoint.
    543  *
    544  * @returns VBox status code.
    545  * @param   pUVM        The user mode VM handle.
    546  * @param   hBp         The handle of the breakpoint which should be disabled.
    547  *
    548  * @thread  Any thread.
    549  */
    550 VMMR3DECL(int) DBGFR3BpDisable(PUVM pUVM, DBGFBP hBp)
    5511212{
    5521213    /*
     
    5561217    AssertReturn(hBp != NIL_DBGFBPOWNER, VERR_INVALID_HANDLE);
    5571218
    558     return VERR_NOT_IMPLEMENTED;
     1219    PDBGFBPINT pBp = dbgfR3BpGetByHnd(pUVM, hBp);
     1220    AssertPtrReturn(pBp, VERR_DBGF_BP_NOT_FOUND);
     1221
     1222    int rc = VINF_SUCCESS;
     1223    if (!DBGF_BP_PUB_IS_ENABLED(pBp->Pub.fFlagsAndType))
     1224        rc = dbgfR3BpArm(pUVM, hBp, pBp);
     1225    else
     1226        rc = VINF_DBGF_BP_ALREADY_ENABLED;
     1227
     1228    return rc;
     1229}
     1230
     1231
     1232/**
     1233 * Disables a breakpoint.
     1234 *
     1235 * @returns VBox status code.
     1236 * @param   pUVM        The user mode VM handle.
     1237 * @param   hBp         The handle of the breakpoint which should be disabled.
     1238 *
     1239 * @thread  Any thread.
     1240 */
     1241VMMR3DECL(int) DBGFR3BpDisable(PUVM pUVM, DBGFBP hBp)
     1242{
     1243    /*
     1244     * Validate the input.
     1245     */
     1246    UVM_ASSERT_VALID_EXT_RETURN(pUVM, VERR_INVALID_VM_HANDLE);
     1247    AssertReturn(hBp != NIL_DBGFBPOWNER, VERR_INVALID_HANDLE);
     1248
     1249    PDBGFBPINT pBp = dbgfR3BpGetByHnd(pUVM, hBp);
     1250    AssertPtrReturn(pBp, VERR_DBGF_BP_NOT_FOUND);
     1251
     1252    int rc = VINF_SUCCESS;
     1253    if (DBGF_BP_PUB_IS_ENABLED(pBp->Pub.fFlagsAndType))
     1254        rc = dbgfR3BpDisarm(pUVM, hBp, pBp);
     1255    else
     1256        rc = VINF_DBGF_BP_ALREADY_DISABLED;
     1257
     1258    return rc;
    5591259}
    5601260
  • trunk/src/VBox/VMM/include/DBGFInternal.h

    r86683 r86699  
    5454#define DBGF_TRACER_EVT_SZ               (DBGF_TRACER_EVT_HDR_SZ + DBGF_TRACER_EVT_PAYLOAD_SZ)
    5555
     56
     57#ifdef VBOX_WITH_LOTS_OF_DBGF_BPS
     58/** @name Breakpoint handling defines.
     59 * @{ */
     60/** Maximum number of breakpoints supported (power of two). */
     61#define DBGF_BP_COUNT_MAX                   _1M
     62/** Size of a single breakpoint structure in bytes. */
     63#define DBGF_BP_ENTRY_SZ                    64
     64/** Number of breakpoints handled in one chunk (power of two). */
     65#define DBGF_BP_COUNT_PER_CHUNK             _64K
     66/** Number of chunks required to support all breakpoints. */
     67#define DBGF_BP_CHUNK_COUNT                 (DBGF_BP_COUNT_MAX / DBGF_BP_COUNT_PER_CHUNK)
     68/** @} */
     69#endif
    5670
    5771
     
    763777typedef DBGFBPSEARCHOPT *PDBGFBPSEARCHOPT;
    764778#else
     779
     780/** An invalid breakpoint chunk ID. */
     781#define DBGF_BP_CHUNK_ID_INVALID                    UINT32_MAX
     782/** Generates a unique breakpoint handle from the given chunk ID and entry inside the chunk. */
     783#define DBGF_BP_HND_CREATE(a_idChunk, a_idEntry)    RT_MAKE_U32(a_idEntry, a_idChunk);
     784/** Returns the chunk ID from the given breakpoint handle. */
     785#define DBGF_BP_HND_GET_CHUNK_ID(a_hBp)             ((uint32_t)RT_HI_U16(a_hBp))
     786/** Returns the entry index inside a chunk from the given breakpoint handle. */
     787#define DBGF_BP_HND_GET_ENTRY(a_hBp)                ((uint32_t)RT_LO_U16(a_hBp))
     788
     789
     790/**
     791 * The internal breakpoint state, shared part.
     792 */
     793typedef struct DBGFBPINT
     794{
     795    /** The publicly visible part. */
     796    DBGFBPPUB                   Pub;
     797    /** The opaque user argument for the owner callback, Ring-3 Ptr. */
     798    R3PTRTYPE(void *)           pvUserR3;
     799} DBGFBPINT;
     800AssertCompileSize(DBGFBPINT, DBGF_BP_ENTRY_SZ);
     801/** Pointer to an internal breakpoint state. */
     802typedef DBGFBPINT *PDBGFBPINT;
     803/** Pointer to an const internal breakpoint state. */
     804typedef const DBGFBPINT *PCDBGFBPINT;
     805
     806
     807/**
     808 * The internal breakpoint state, R0 part.
     809 */
     810typedef struct DBGFBPINTR0
     811{
     812    /** The owner handle. */
     813    DBGFBPOWNER                 hOwner;
     814    /** Flag whether the breakpoint is in use. */
     815    bool                        fInUse;
     816    /** Padding to 8 byte alignment. */
     817    bool                        afPad[3];
     818    /** Opaque user data for the owner callback, Ring-0 Ptr. */
     819    R0PTRTYPE(void *)           pvUserR0;
     820} DBGFBPINTR0;
     821AssertCompileMemberAlignment(DBGFBPINTR0, pvUserR0, 8);
     822AssertCompileSize(DBGFBPINTR0, 16);
     823/** Pointer to an internal breakpoint state - Ring-0 Ptr. */
     824typedef R0PTRTYPE(DBGFBPINTR0 *) PDBGFBPINTR0;
     825
     826
    765827/**
    766828 * Hardware breakpoint state.
     
    769831{
    770832    /** The flat GC address of the breakpoint. */
    771     RTGCUINTPTR     GCPtr;
    772     /** The breakpoint handle if active, NIL_DBGFBP if disabled. */
    773     DBGFBP          hBp;
     833    RTGCUINTPTR                 GCPtr;
     834    /** The breakpoint handle if active, NIL_DBGFBP if not in use. */
     835    volatile DBGFBP             hBp;
    774836    /** The access type (one of the X86_DR7_RW_* value). */
    775     uint8_t         fType;
     837    uint8_t                     fType;
    776838    /** The access size. */
    777     uint8_t         cb;
     839    uint8_t                     cb;
    778840    /** Flag whether the breakpoint is currently enabled. */
    779     bool            fEnabled;
     841    volatile bool               fEnabled;
    780842    /** Padding. */
    781     uint8_t         bPad;
     843    uint8_t                     bPad;
    782844} DBGFBPHW;
    783845AssertCompileSize(DBGFBPHW, 16);
     
    786848/** Pointer to a const hardware breakpoint state. */
    787849typedef const DBGFBPHW *PCDBGFBPHW;
     850
     851
     852/**
     853 * A breakpoint table chunk, ring-3 state.
     854 */
     855typedef struct DBGFBPCHUNKR3
     856{
     857    /** Pointer to the R3 base of the chunk. */
     858    R3PTRTYPE(PDBGFBPINT)       pBpBaseR3;
     859    /** Bitmap of free/occupied breakpoint entries. */
     860    R3PTRTYPE(volatile void *)  pbmAlloc;
     861    /** Number of free breakpoints in the chunk. */
     862    volatile uint32_t           cBpsFree;
     863    /** The chunk index this tracking structure refers to. */
     864    uint32_t                    idChunk;
     865} DBGFBPCHUNKR3;
     866/** Pointer to a breakpoint table chunk - Ring-3 Ptr. */
     867typedef DBGFBPCHUNKR3 *PDBGFBPCHUNKR3;
     868/** Pointer to a const breakpoint table chunk - Ring-3 Ptr. */
     869typedef const DBGFBPCHUNKR3 *PCDBGFBPCHUNKR3;
     870
     871
     872/**
     873 * Breakpoint table chunk, ring-0 state.
     874 */
     875typedef struct DBGFBPCHUNKR0
     876{
     877    /** The chunks memory. */
     878    RTR0MEMOBJ                  hMemObj;
     879    /** The ring-3 mapping object. */
     880    RTR0MEMOBJ                  hMapObj;
     881    /** Pointer to the breakpoint entries base. */
     882    R0PTRTYPE(PDBGFBPINT)       paBpBaseSharedR0;
     883    /** Pointer to the Ring-0 only part of the breakpoints. */
     884    PDBGFBPINTR0                paBpBaseR0Only;
     885} DBGFBPCHUNKR0;
     886/** Pointer to a breakpoint table chunk - Ring-0 Ptr. */
     887typedef R0PTRTYPE(DBGFBPCHUNKR0 *) PDBGFBPCHUNKR0;
    788888#endif
    789889
     
    875975    DBGFBPSEARCHOPT             Int3;
    876976#else
     977    /** @name Breakpoint handling related state.
     978     * @{ */
    877979    /** Array of hardware breakpoints (0..3).
    878980     * This is shared among all the CPUs because life is much simpler that way. */
    879     DBGFBPHW                    aHwBreakpoints[4];
     981    DBGFBPHW                        aHwBreakpoints[4];
     982    /** @} */
    880983#endif
    881984
     
    10171120    /** Pointer to the tracer instance if enabled. */
    10181121    R0PTRTYPE(struct DBGFTRACERINSR0 *) pTracerR0;
     1122
     1123#ifdef VBOX_WITH_LOTS_OF_DBGF_BPS
     1124    /** @name Breakpoint handling related state, Ring-0 only part.
     1125     * @{ */
     1126    /** Global breakpoint table chunk array. */
     1127    DBGFBPCHUNKR0                       aBpChunks[DBGF_BP_CHUNK_COUNT];
     1128    /** The L1 lookup tables memory object. */
     1129    RTR0MEMOBJ                          hMemObjBpLocL1;
     1130    /** The L1 lookup tables mapping object. */
     1131    RTR0MEMOBJ                          hMapObjBpLocL1;
     1132    /** Base pointer to the L1 locator table. */
     1133    R0PTRTYPE(volatile uint32_t *)      paBpLocL1R0;
     1134    /** Flag whether the breakpoint manager was initialized (on demand). */
     1135    bool                                fInit;
     1136    /** @} */
     1137#endif
    10191138} DBGFR0PERVM;
    10201139
     
    10911210    /** @} */
    10921211
     1212#ifdef VBOX_WITH_LOTS_OF_DBGF_BPS
     1213    /** @name Breakpoint handling related state.
     1214     * @{ */
     1215    /** Global breakpoint table chunk array. */
     1216    DBGFBPCHUNKR3                   aBpChunks[DBGF_BP_CHUNK_COUNT];
     1217    /** Base pointer to the L1 locator table. */
     1218    R3PTRTYPE(volatile uint32_t *)  paBpLocL1R3;
     1219    /** @} */
     1220#endif
     1221
    10931222    /** The type database lock. */
    10941223    RTSEMRW                     hTypeDbLock;
     
    11381267void dbgfR3AsRelocate(PUVM pUVM, RTGCUINTPTR offDelta);
    11391268#ifdef VBOX_WITH_LOTS_OF_DBGF_BPS
    1140 DECLHIDDEN(int) dbgfR3BpInit(PVM pVM);
    1141 DECLHIDDEN(int) dbgfR3BpTerm(PVM pVM);
     1269DECLHIDDEN(int) dbgfR3BpInit(PUVM pUVM);
     1270DECLHIDDEN(int) dbgfR3BpTerm(PUVM pUVM);
    11421271#else
    11431272int  dbgfR3BpInit(PVM pVM);
     
    11881317#ifdef IN_RING0
    11891318DECLHIDDEN(void) dbgfR0TracerDestroy(PGVM pGVM, PDBGFTRACERINSR0 pTracer);
     1319DECLHIDDEN(void) dbgfR0BpInit(PGVM pGVM);
     1320DECLHIDDEN(void) dbgfR0BpDestroy(PGVM pGVM);
    11901321#endif /* !IN_RING0 */
    11911322
Note: See TracChangeset for help on using the changeset viewer.

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