VirtualBox

source: vbox/trunk/src/VBox/VMM/VMMR3/DBGFR3Bp.cpp@ 87776

Last change on this file since 87776 was 87776, checked in by vboxsync, 4 years ago

dbgf.h,VMM/DBGF: A bit of cleanup on the public breakpoint structure and associated helpers, add new flags to indicate whether the breakpoint should hit before or after the instruction was executed, bugref:9837

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 91.4 KB
Line 
1/* $Id: DBGFR3Bp.cpp 87776 2021-02-17 12:21:16Z vboxsync $ */
2/** @file
3 * DBGF - Debugger Facility, Breakpoint Management.
4 */
5
6/*
7 * Copyright (C) 2006-2020 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18
19/** @page pg_dbgf_bp DBGF - The Debugger Facility, Breakpoint Management
20 *
21 * The debugger facilities breakpoint managers purpose is to efficiently manage
22 * large amounts of breakpoints for various use cases like dtrace like operations
23 * or execution flow tracing for instance. Especially execution flow tracing can
24 * require thousands of breakpoints which need to be managed efficiently to not slow
25 * down guest operation too much. Before the rewrite starting end of 2020, DBGF could
26 * only handle 32 breakpoints (+ 4 hardware assisted breakpoints). The new
27 * manager is supposed to be able to handle up to one million breakpoints.
28 *
29 * @see grp_dbgf
30 *
31 *
32 * @section sec_dbgf_bp_owner Breakpoint owners
33 *
34 * A single breakpoint owner has a mandatory ring-3 callback and an optional ring-0
35 * callback assigned which is called whenever a breakpoint with the owner assigned is hit.
36 * The common part of the owner is managed by a single table mapped into both ring-0
37 * and ring-3 and the handle being the index into the table. This allows resolving
38 * the handle to the internal structure efficiently. Searching for a free entry is
39 * done using a bitmap indicating free and occupied entries. For the optional
40 * ring-0 owner part there is a separate ring-0 only table for security reasons.
41 *
42 * The callback of the owner can be used to gather and log guest state information
43 * and decide whether to continue guest execution or stop and drop into the debugger.
44 * Breakpoints which don't have an owner assigned will always drop the VM right into
45 * the debugger.
46 *
47 *
48 * @section sec_dbgf_bp_bps Breakpoints
49 *
50 * Breakpoints are referenced by an opaque handle which acts as an index into a global table
51 * mapped into ring-3 and ring-0. Each entry contains the necessary state to manage the breakpoint
52 * like trigger conditions, type, owner, etc. If an owner is given an optional opaque user argument
53 * can be supplied which is passed in the respective owner callback. For owners with ring-0 callbacks
54 * a dedicated ring-0 table is held saving possible ring-0 user arguments.
55 *
56 * To keep memory consumption under control and still support large amounts of
57 * breakpoints the table is split into fixed sized chunks and the chunk index and index
58 * into the chunk can be derived from the handle with only a few logical operations.
59 *
60 *
61 * @section sec_dbgf_bp_resolv Resolving breakpoint addresses
62 *
63 * Whenever a \#BP(0) event is triggered DBGF needs to decide whether the event originated
64 * from within the guest or whether a DBGF breakpoint caused it. This has to happen as fast
65 * as possible. The following scheme is employed to achieve this:
66 *
67 * @verbatim
68 * 7 6 5 4 3 2 1 0
69 * +---+---+---+---+---+---+---+---+
70 * | | | | | | | | | BP address
71 * +---+---+---+---+---+---+---+---+
72 * \_____________________/ \_____/
73 * | |
74 * | +---------------+
75 * | |
76 * BP table | v
77 * +------------+ | +-----------+
78 * | hBp 0 | | X <- | 0 | xxxxx |
79 * | hBp 1 | <----------------+------------------------ | 1 | hBp 1 |
80 * | | | +--- | 2 | idxL2 |
81 * | hBp <m> | <---+ v | |...| ... |
82 * | | | +-----------+ | |...| ... |
83 * | | | | | | |...| ... |
84 * | hBp <n> | <-+ +----- | +> leaf | | | . |
85 * | | | | | | | | . |
86 * | | | | + root + | <------------+ | . |
87 * | | | | | | +-----------+
88 * | | +------- | leaf<+ | L1: 65536
89 * | . | | . |
90 * | . | | . |
91 * | . | | . |
92 * +------------+ +-----------+
93 * L2 idx AVL
94 * @endverbatim
95 *
96 * -# Take the lowest 16 bits of the breakpoint address and use it as an direct index
97 * into the L1 table. The L1 table is contiguous and consists of 4 byte entries
98 * resulting in 256KiB of memory used. The topmost 4 bits indicate how to proceed
99 * and the meaning of the remaining 28bits depends on the topmost 4 bits:
100 * - A 0 type entry means no breakpoint is registered with the matching lowest 16bits,
101 * so forward the event to the guest.
102 * - A 1 in the topmost 4 bits means that the remaining 28bits directly denote a breakpoint
103 * handle which can be resolved by extracting the chunk index and index into the chunk
104 * of the global breakpoint table. If the address matches the breakpoint is processed
105 * according to the configuration. Otherwise the breakpoint is again forwarded to the guest.
106 * - A 2 in the topmost 4 bits means that there are multiple breakpoints registered
107 * matching the lowest 16bits and the search must continue in the L2 table with the
108 * remaining 28bits acting as an index into the L2 table indicating the search root.
109 * -# The L2 table consists of multiple index based AVL trees, there is one for each reference
110 * from the L1 table. The key for the table are the upper 6 bytes of the breakpoint address
111 * used for searching. This tree is traversed until either a matching address is found and
112 * the breakpoint is being processed or again forwarded to the guest if it isn't successful.
113 * Each entry in the L2 table is 16 bytes big and densly packed to avoid excessive memory usage.
114 *
115 *
116 * @section sec_dbgf_bp_note Random thoughts and notes for the implementation
117 *
118 * - The assumption for this approach is that the lowest 16bits of the breakpoint address are
119 * hopefully the ones being the most varying ones across breakpoints so the traversal
120 * can skip the L2 table in most of the cases. Even if the L2 table must be taken the
121 * individual trees should be quite shallow resulting in low overhead when walking it
122 * (though only real world testing can assert this assumption).
123 * - Index based tables and trees are used instead of pointers because the tables
124 * are always mapped into ring-0 and ring-3 with different base addresses.
125 * - Efficent breakpoint allocation is done by having a global bitmap indicating free
126 * and occupied breakpoint entries. Same applies for the L2 AVL table.
127 * - Special care must be taken when modifying the L1 and L2 tables as other EMTs
128 * might still access it (want to try a lockless approach first using
129 * atomic updates, have to resort to locking if that turns out to be too difficult).
130 * - Each BP entry is supposed to be 64 byte big and each chunk should contain 65536
131 * breakpoints which results in 4MiB for each chunk plus the allocation bitmap.
132 * - ring-0 has to take special care when traversing the L2 AVL tree to not run into cycles
133 * and do strict bounds checking before accessing anything. The L1 and L2 table
134 * are written to from ring-3 only. Same goes for the breakpoint table with the
135 * exception being the opaque user argument for ring-0 which is stored in ring-0 only
136 * memory.
137 */
138
139
140/*********************************************************************************************************************************
141* Header Files *
142*********************************************************************************************************************************/
143#define LOG_GROUP LOG_GROUP_DBGF
144#define VMCPU_INCL_CPUM_GST_CTX
145#include <VBox/vmm/dbgf.h>
146#include <VBox/vmm/selm.h>
147#include <VBox/vmm/iem.h>
148#include <VBox/vmm/mm.h>
149#include <VBox/vmm/iom.h>
150#include <VBox/vmm/hm.h>
151#include "DBGFInternal.h"
152#include <VBox/vmm/vm.h>
153#include <VBox/vmm/uvm.h>
154
155#include <VBox/err.h>
156#include <VBox/log.h>
157#include <iprt/assert.h>
158#include <iprt/mem.h>
159
160#include "DBGFInline.h"
161
162
163/*********************************************************************************************************************************
164* Structures and Typedefs *
165*********************************************************************************************************************************/
166
167
168/*********************************************************************************************************************************
169* Internal Functions *
170*********************************************************************************************************************************/
171RT_C_DECLS_BEGIN
172RT_C_DECLS_END
173
174
175/**
176 * Initialize the breakpoint mangement.
177 *
178 * @returns VBox status code.
179 * @param pUVM The user mode VM handle.
180 */
181DECLHIDDEN(int) dbgfR3BpInit(PUVM pUVM)
182{
183 PVM pVM = pUVM->pVM;
184
185 //pUVM->dbgf.s.paBpOwnersR3 = NULL;
186 //pUVM->dbgf.s.pbmBpOwnersAllocR3 = NULL;
187
188 /* Init hardware breakpoint states. */
189 for (uint32_t i = 0; i < RT_ELEMENTS(pVM->dbgf.s.aHwBreakpoints); i++)
190 {
191 PDBGFBPHW pHwBp = &pVM->dbgf.s.aHwBreakpoints[i];
192
193 AssertCompileSize(DBGFBP, sizeof(uint32_t));
194 pHwBp->hBp = NIL_DBGFBP;
195 //pHwBp->fEnabled = false;
196 }
197
198 /* Now the global breakpoint table chunks. */
199 for (uint32_t i = 0; i < RT_ELEMENTS(pUVM->dbgf.s.aBpChunks); i++)
200 {
201 PDBGFBPCHUNKR3 pBpChunk = &pUVM->dbgf.s.aBpChunks[i];
202
203 //pBpChunk->pBpBaseR3 = NULL;
204 //pBpChunk->pbmAlloc = NULL;
205 //pBpChunk->cBpsFree = 0;
206 pBpChunk->idChunk = DBGF_BP_CHUNK_ID_INVALID; /* Not allocated. */
207 }
208
209 for (uint32_t i = 0; i < RT_ELEMENTS(pUVM->dbgf.s.aBpL2TblChunks); i++)
210 {
211 PDBGFBPL2TBLCHUNKR3 pL2Chunk = &pUVM->dbgf.s.aBpL2TblChunks[i];
212
213 //pL2Chunk->pL2BaseR3 = NULL;
214 //pL2Chunk->pbmAlloc = NULL;
215 //pL2Chunk->cFree = 0;
216 pL2Chunk->idChunk = DBGF_BP_CHUNK_ID_INVALID; /* Not allocated. */
217 }
218
219 //pUVM->dbgf.s.paBpLocL1R3 = NULL;
220 pUVM->dbgf.s.hMtxBpL2Wr = NIL_RTSEMFASTMUTEX;
221 return RTSemFastMutexCreate(&pUVM->dbgf.s.hMtxBpL2Wr);
222}
223
224
225/**
226 * Terminates the breakpoint mangement.
227 *
228 * @returns VBox status code.
229 * @param pUVM The user mode VM handle.
230 */
231DECLHIDDEN(int) dbgfR3BpTerm(PUVM pUVM)
232{
233 if (pUVM->dbgf.s.pbmBpOwnersAllocR3)
234 {
235 RTMemFree((void *)pUVM->dbgf.s.pbmBpOwnersAllocR3);
236 pUVM->dbgf.s.pbmBpOwnersAllocR3 = NULL;
237 }
238
239 /* Free all allocated chunk bitmaps (the chunks itself are destroyed during ring-0 VM destruction). */
240 for (uint32_t i = 0; i < RT_ELEMENTS(pUVM->dbgf.s.aBpChunks); i++)
241 {
242 PDBGFBPCHUNKR3 pBpChunk = &pUVM->dbgf.s.aBpChunks[i];
243
244 if (pBpChunk->idChunk != DBGF_BP_CHUNK_ID_INVALID)
245 {
246 AssertPtr(pBpChunk->pbmAlloc);
247 RTMemFree((void *)pBpChunk->pbmAlloc);
248 pBpChunk->pbmAlloc = NULL;
249 pBpChunk->idChunk = DBGF_BP_CHUNK_ID_INVALID;
250 }
251 }
252
253 for (uint32_t i = 0; i < RT_ELEMENTS(pUVM->dbgf.s.aBpL2TblChunks); i++)
254 {
255 PDBGFBPL2TBLCHUNKR3 pL2Chunk = &pUVM->dbgf.s.aBpL2TblChunks[i];
256
257 if (pL2Chunk->idChunk != DBGF_BP_CHUNK_ID_INVALID)
258 {
259 AssertPtr(pL2Chunk->pbmAlloc);
260 RTMemFree((void *)pL2Chunk->pbmAlloc);
261 pL2Chunk->pbmAlloc = NULL;
262 pL2Chunk->idChunk = DBGF_BP_CHUNK_ID_INVALID;
263 }
264 }
265
266 if (pUVM->dbgf.s.hMtxBpL2Wr != NIL_RTSEMFASTMUTEX)
267 {
268 RTSemFastMutexDestroy(pUVM->dbgf.s.hMtxBpL2Wr);
269 pUVM->dbgf.s.hMtxBpL2Wr = NIL_RTSEMFASTMUTEX;
270 }
271
272 return VINF_SUCCESS;
273}
274
275
276/**
277 * @callback_method_impl{FNVMMEMTRENDEZVOUS}
278 */
279static DECLCALLBACK(VBOXSTRICTRC) dbgfR3BpInitEmtWorker(PVM pVM, PVMCPU pVCpu, void *pvUser)
280{
281 RT_NOREF(pvUser);
282
283 VMCPU_ASSERT_EMT(pVCpu);
284 VM_ASSERT_VALID_EXT_RETURN(pVM, VERR_INVALID_VM_HANDLE);
285
286 /*
287 * The initialization will be done on EMT(0). It is possible that multiple
288 * initialization attempts are done because dbgfR3BpEnsureInit() can be called
289 * from racing non EMT threads when trying to set a breakpoint for the first time.
290 * Just fake success if the L1 is already present which means that a previous rendezvous
291 * successfully initialized the breakpoint manager.
292 */
293 PUVM pUVM = pVM->pUVM;
294 if ( pVCpu->idCpu == 0
295 && !pUVM->dbgf.s.paBpLocL1R3)
296 {
297 DBGFBPINITREQ Req;
298 Req.Hdr.u32Magic = SUPVMMR0REQHDR_MAGIC;
299 Req.Hdr.cbReq = sizeof(Req);
300 Req.paBpLocL1R3 = NULL;
301 int rc = VMMR3CallR0Emt(pVM, pVCpu, VMMR0_DO_DBGF_BP_INIT, 0 /*u64Arg*/, &Req.Hdr);
302 AssertLogRelMsgRCReturn(rc, ("VMMR0_DO_DBGF_BP_INIT failed: %Rrc\n", rc), rc);
303 pUVM->dbgf.s.paBpLocL1R3 = Req.paBpLocL1R3;
304 }
305
306 return VINF_SUCCESS;
307}
308
309
310/**
311 * Ensures that the breakpoint manager is fully initialized.
312 *
313 * @returns VBox status code.
314 * @param pUVM The user mode VM handle.
315 *
316 * @thread Any thread.
317 */
318static int dbgfR3BpEnsureInit(PUVM pUVM)
319{
320 /* If the L1 lookup table is allocated initialization succeeded before. */
321 if (RT_LIKELY(pUVM->dbgf.s.paBpLocL1R3))
322 return VINF_SUCCESS;
323
324 /* Gather all EMTs and call into ring-0 to initialize the breakpoint manager. */
325 return VMMR3EmtRendezvous(pUVM->pVM, VMMEMTRENDEZVOUS_FLAGS_TYPE_ALL_AT_ONCE, dbgfR3BpInitEmtWorker, NULL /*pvUser*/);
326}
327
328
329/**
330 * @callback_method_impl{FNVMMEMTRENDEZVOUS}
331 */
332static DECLCALLBACK(VBOXSTRICTRC) dbgfR3BpOwnerInitEmtWorker(PVM pVM, PVMCPU pVCpu, void *pvUser)
333{
334 RT_NOREF(pvUser);
335
336 VMCPU_ASSERT_EMT(pVCpu);
337 VM_ASSERT_VALID_EXT_RETURN(pVM, VERR_INVALID_VM_HANDLE);
338
339 /*
340 * The initialization will be done on EMT(0). It is possible that multiple
341 * initialization attempts are done because dbgfR3BpOwnerEnsureInit() can be called
342 * from racing non EMT threads when trying to create a breakpoint owner for the first time.
343 * Just fake success if the pointers are initialized already, meaning that a previous rendezvous
344 * successfully initialized the breakpoint owner table.
345 */
346 int rc = VINF_SUCCESS;
347 PUVM pUVM = pVM->pUVM;
348 if ( pVCpu->idCpu == 0
349 && !pUVM->dbgf.s.pbmBpOwnersAllocR3)
350 {
351 pUVM->dbgf.s.pbmBpOwnersAllocR3 = (volatile void *)RTMemAllocZ(DBGF_BP_OWNER_COUNT_MAX / 8);
352 if (pUVM->dbgf.s.pbmBpOwnersAllocR3)
353 {
354 DBGFBPOWNERINITREQ Req;
355 Req.Hdr.u32Magic = SUPVMMR0REQHDR_MAGIC;
356 Req.Hdr.cbReq = sizeof(Req);
357 Req.paBpOwnerR3 = NULL;
358 rc = VMMR3CallR0Emt(pVM, pVCpu, VMMR0_DO_DBGF_BP_OWNER_INIT, 0 /*u64Arg*/, &Req.Hdr);
359 AssertLogRelMsgRC(rc, ("VMMR0_DO_DBGF_BP_OWNER_INIT failed: %Rrc\n", rc));
360 if (RT_SUCCESS(rc))
361 {
362 pUVM->dbgf.s.paBpOwnersR3 = (PDBGFBPOWNERINT)Req.paBpOwnerR3;
363 return VINF_SUCCESS;
364 }
365
366 RTMemFree((void *)pUVM->dbgf.s.pbmBpOwnersAllocR3);
367 pUVM->dbgf.s.pbmBpOwnersAllocR3 = NULL;
368 }
369 else
370 rc = VERR_NO_MEMORY;
371 }
372
373 return rc;
374}
375
376
377/**
378 * Ensures that the breakpoint manager is fully initialized.
379 *
380 * @returns VBox status code.
381 * @param pUVM The user mode VM handle.
382 *
383 * @thread Any thread.
384 */
385static int dbgfR3BpOwnerEnsureInit(PUVM pUVM)
386{
387 /* If the allocation bitmap is allocated initialization succeeded before. */
388 if (RT_LIKELY(pUVM->dbgf.s.pbmBpOwnersAllocR3))
389 return VINF_SUCCESS;
390
391 /* Gather all EMTs and call into ring-0 to initialize the breakpoint manager. */
392 return VMMR3EmtRendezvous(pUVM->pVM, VMMEMTRENDEZVOUS_FLAGS_TYPE_ALL_AT_ONCE, dbgfR3BpOwnerInitEmtWorker, NULL /*pvUser*/);
393}
394
395
396/**
397 * Returns the internal breakpoint owner state for the given handle.
398 *
399 * @returns Pointer to the internal breakpoint owner state or NULL if the handle is invalid.
400 * @param pUVM The user mode VM handle.
401 * @param hBpOwner The breakpoint owner handle to resolve.
402 */
403DECLINLINE(PDBGFBPOWNERINT) dbgfR3BpOwnerGetByHnd(PUVM pUVM, DBGFBPOWNER hBpOwner)
404{
405 AssertReturn(hBpOwner < DBGF_BP_OWNER_COUNT_MAX, NULL);
406 AssertPtrReturn(pUVM->dbgf.s.pbmBpOwnersAllocR3, NULL);
407
408 AssertReturn(ASMBitTest(pUVM->dbgf.s.pbmBpOwnersAllocR3, hBpOwner), NULL);
409 return &pUVM->dbgf.s.paBpOwnersR3[hBpOwner];
410}
411
412
413/**
414 * Retains the given breakpoint owner handle for use.
415 *
416 * @returns VBox status code.
417 * @retval VERR_INVALID_HANDLE if the given breakpoint owner handle is invalid.
418 * @param pUVM The user mode VM handle.
419 * @param hBpOwner The breakpoint owner handle to retain, NIL_DBGFOWNER is accepted without doing anything.
420 */
421DECLINLINE(int) dbgfR3BpOwnerRetain(PUVM pUVM, DBGFBPOWNER hBpOwner)
422{
423 if (hBpOwner == NIL_DBGFBPOWNER)
424 return VINF_SUCCESS;
425
426 PDBGFBPOWNERINT pBpOwner = dbgfR3BpOwnerGetByHnd(pUVM, hBpOwner);
427 if (pBpOwner)
428 {
429 ASMAtomicIncU32(&pBpOwner->cRefs);
430 return VINF_SUCCESS;
431 }
432
433 return VERR_INVALID_HANDLE;
434}
435
436
437/**
438 * Releases the given breakpoint owner handle.
439 *
440 * @returns VBox status code.
441 * @retval VERR_INVALID_HANDLE if the given breakpoint owner handle is invalid.
442 * @param pUVM The user mode VM handle.
443 * @param hBpOwner The breakpoint owner handle to retain, NIL_DBGFOWNER is accepted without doing anything.
444 */
445DECLINLINE(int) dbgfR3BpOwnerRelease(PUVM pUVM, DBGFBPOWNER hBpOwner)
446{
447 if (hBpOwner == NIL_DBGFBPOWNER)
448 return VINF_SUCCESS;
449
450 PDBGFBPOWNERINT pBpOwner = dbgfR3BpOwnerGetByHnd(pUVM, hBpOwner);
451 if (pBpOwner)
452 {
453 Assert(pBpOwner->cRefs > 1);
454 ASMAtomicDecU32(&pBpOwner->cRefs);
455 return VINF_SUCCESS;
456 }
457
458 return VERR_INVALID_HANDLE;
459}
460
461
462/**
463 * Returns the internal breakpoint state for the given handle.
464 *
465 * @returns Pointer to the internal breakpoint state or NULL if the handle is invalid.
466 * @param pUVM The user mode VM handle.
467 * @param hBp The breakpoint handle to resolve.
468 */
469DECLINLINE(PDBGFBPINT) dbgfR3BpGetByHnd(PUVM pUVM, DBGFBP hBp)
470{
471 uint32_t idChunk = DBGF_BP_HND_GET_CHUNK_ID(hBp);
472 uint32_t idxEntry = DBGF_BP_HND_GET_ENTRY(hBp);
473
474 AssertReturn(idChunk < DBGF_BP_CHUNK_COUNT, NULL);
475 AssertReturn(idxEntry < DBGF_BP_COUNT_PER_CHUNK, NULL);
476
477 PDBGFBPCHUNKR3 pBpChunk = &pUVM->dbgf.s.aBpChunks[idChunk];
478 AssertReturn(pBpChunk->idChunk == idChunk, NULL);
479 AssertPtrReturn(pBpChunk->pbmAlloc, NULL);
480 AssertReturn(ASMBitTest(pBpChunk->pbmAlloc, idxEntry), NULL);
481
482 return &pBpChunk->pBpBaseR3[idxEntry];
483}
484
485
486/**
487 * @callback_method_impl{FNVMMEMTRENDEZVOUS}
488 */
489static DECLCALLBACK(VBOXSTRICTRC) dbgfR3BpChunkAllocEmtWorker(PVM pVM, PVMCPU pVCpu, void *pvUser)
490{
491 uint32_t idChunk = (uint32_t)(uintptr_t)pvUser;
492
493 VMCPU_ASSERT_EMT(pVCpu);
494 VM_ASSERT_VALID_EXT_RETURN(pVM, VERR_INVALID_VM_HANDLE);
495
496 AssertReturn(idChunk < DBGF_BP_CHUNK_COUNT, VERR_DBGF_BP_IPE_1);
497
498 PUVM pUVM = pVM->pUVM;
499 PDBGFBPCHUNKR3 pBpChunk = &pUVM->dbgf.s.aBpChunks[idChunk];
500
501 AssertReturn( pBpChunk->idChunk == DBGF_BP_CHUNK_ID_INVALID
502 || pBpChunk->idChunk == idChunk,
503 VERR_DBGF_BP_IPE_2);
504
505 /*
506 * The initialization will be done on EMT(0). It is possible that multiple
507 * allocation attempts are done when multiple racing non EMT threads try to
508 * allocate a breakpoint and a new chunk needs to be allocated.
509 * Ignore the request and succeed if the chunk is allocated meaning that a
510 * previous rendezvous successfully allocated the chunk.
511 */
512 int rc = VINF_SUCCESS;
513 if ( pVCpu->idCpu == 0
514 && pBpChunk->idChunk == DBGF_BP_CHUNK_ID_INVALID)
515 {
516 /* Allocate the bitmap first so we can skip calling into VMMR0 if it fails. */
517 AssertCompile(!(DBGF_BP_COUNT_PER_CHUNK % 8));
518 volatile void *pbmAlloc = RTMemAllocZ(DBGF_BP_COUNT_PER_CHUNK / 8);
519 if (RT_LIKELY(pbmAlloc))
520 {
521 DBGFBPCHUNKALLOCREQ Req;
522 Req.Hdr.u32Magic = SUPVMMR0REQHDR_MAGIC;
523 Req.Hdr.cbReq = sizeof(Req);
524 Req.idChunk = idChunk;
525 Req.pChunkBaseR3 = NULL;
526 rc = VMMR3CallR0Emt(pVM, pVCpu, VMMR0_DO_DBGF_BP_CHUNK_ALLOC, 0 /*u64Arg*/, &Req.Hdr);
527 AssertLogRelMsgRC(rc, ("VMMR0_DO_DBGF_BP_CHUNK_ALLOC failed: %Rrc\n", rc));
528 if (RT_SUCCESS(rc))
529 {
530 pBpChunk->pBpBaseR3 = (PDBGFBPINT)Req.pChunkBaseR3;
531 pBpChunk->pbmAlloc = pbmAlloc;
532 pBpChunk->cBpsFree = DBGF_BP_COUNT_PER_CHUNK;
533 pBpChunk->idChunk = idChunk;
534 return VINF_SUCCESS;
535 }
536
537 RTMemFree((void *)pbmAlloc);
538 }
539 else
540 rc = VERR_NO_MEMORY;
541 }
542
543 return rc;
544}
545
546
547/**
548 * Tries to allocate the given chunk which requires an EMT rendezvous.
549 *
550 * @returns VBox status code.
551 * @param pUVM The user mode VM handle.
552 * @param idChunk The chunk to allocate.
553 *
554 * @thread Any thread.
555 */
556DECLINLINE(int) dbgfR3BpChunkAlloc(PUVM pUVM, uint32_t idChunk)
557{
558 return VMMR3EmtRendezvous(pUVM->pVM, VMMEMTRENDEZVOUS_FLAGS_TYPE_ALL_AT_ONCE, dbgfR3BpChunkAllocEmtWorker, (void *)(uintptr_t)idChunk);
559}
560
561
562/**
563 * Tries to allocate a new breakpoint of the given type.
564 *
565 * @returns VBox status code.
566 * @param pUVM The user mode VM handle.
567 * @param hOwner The owner handle, NIL_DBGFBPOWNER if none assigned.
568 * @param pvUser Opaque user data passed in the owner callback.
569 * @param enmType Breakpoint type to allocate.
570 * @param fFlags Flags assoicated with the allocated breakpoint.
571 * @param iHitTrigger The hit count at which the breakpoint start triggering.
572 * Use 0 (or 1) if it's gonna trigger at once.
573 * @param iHitDisable The hit count which disables the breakpoint.
574 * Use ~(uint64_t) if it's never gonna be disabled.
575 * @param phBp Where to return the opaque breakpoint handle on success.
576 * @param ppBp Where to return the pointer to the internal breakpoint state on success.
577 *
578 * @thread Any thread.
579 */
580static int dbgfR3BpAlloc(PUVM pUVM, DBGFBPOWNER hOwner, void *pvUser, DBGFBPTYPE enmType,
581 uint16_t fFlags, uint64_t iHitTrigger, uint64_t iHitDisable, PDBGFBP phBp,
582 PDBGFBPINT *ppBp)
583{
584 int rc = dbgfR3BpOwnerRetain(pUVM, hOwner);
585 if (RT_FAILURE(rc))
586 return rc;
587
588 /*
589 * Search for a chunk having a free entry, allocating new chunks
590 * if the encountered ones are full.
591 *
592 * This can be called from multiple threads at the same time so special care
593 * has to be taken to not require any locking here.
594 */
595 for (uint32_t i = 0; i < RT_ELEMENTS(pUVM->dbgf.s.aBpChunks); i++)
596 {
597 PDBGFBPCHUNKR3 pBpChunk = &pUVM->dbgf.s.aBpChunks[i];
598
599 uint32_t idChunk = ASMAtomicReadU32(&pBpChunk->idChunk);
600 if (idChunk == DBGF_BP_CHUNK_ID_INVALID)
601 {
602 rc = dbgfR3BpChunkAlloc(pUVM, i);
603 if (RT_FAILURE(rc))
604 {
605 LogRel(("DBGF/Bp: Allocating new breakpoint table chunk failed with %Rrc\n", rc));
606 break;
607 }
608
609 idChunk = ASMAtomicReadU32(&pBpChunk->idChunk);
610 Assert(idChunk == i);
611 }
612
613 /** @todo Optimize with some hinting if this turns out to be too slow. */
614 for (;;)
615 {
616 uint32_t cBpsFree = ASMAtomicReadU32(&pBpChunk->cBpsFree);
617 if (cBpsFree)
618 {
619 /*
620 * Scan the associated bitmap for a free entry, if none can be found another thread
621 * raced us and we go to the next chunk.
622 */
623 int32_t iClr = ASMBitFirstClear(pBpChunk->pbmAlloc, DBGF_BP_COUNT_PER_CHUNK);
624 if (iClr != -1)
625 {
626 /*
627 * Try to allocate, we could get raced here as well. In that case
628 * we try again.
629 */
630 if (!ASMAtomicBitTestAndSet(pBpChunk->pbmAlloc, iClr))
631 {
632 /* Success, immediately mark as allocated, initialize the breakpoint state and return. */
633 ASMAtomicDecU32(&pBpChunk->cBpsFree);
634
635 PDBGFBPINT pBp = &pBpChunk->pBpBaseR3[iClr];
636 pBp->Pub.cHits = 0;
637 pBp->Pub.iHitTrigger = iHitTrigger;
638 pBp->Pub.iHitDisable = iHitDisable;
639 pBp->Pub.hOwner = hOwner;
640 pBp->Pub.u16Type = DBGF_BP_PUB_MAKE_TYPE(enmType);
641 pBp->Pub.fFlags = fFlags & ~DBGF_BP_F_ENABLED; /* The enabled flag is handled in the respective APIs. */
642 pBp->pvUserR3 = pvUser;
643
644 /** @todo Owner handling (reference and call ring-0 if it has an ring-0 callback). */
645
646 *phBp = DBGF_BP_HND_CREATE(idChunk, iClr);
647 *ppBp = pBp;
648 return VINF_SUCCESS;
649 }
650 /* else Retry with another spot. */
651 }
652 else /* no free entry in bitmap, go to the next chunk */
653 break;
654 }
655 else /* !cBpsFree, go to the next chunk */
656 break;
657 }
658 }
659
660 rc = dbgfR3BpOwnerRelease(pUVM, hOwner); AssertRC(rc);
661 return VERR_DBGF_NO_MORE_BP_SLOTS;
662}
663
664
665/**
666 * Frees the given breakpoint handle.
667 *
668 * @returns nothing.
669 * @param pUVM The user mode VM handle.
670 * @param hBp The breakpoint handle to free.
671 * @param pBp The internal breakpoint state pointer.
672 */
673static void dbgfR3BpFree(PUVM pUVM, DBGFBP hBp, PDBGFBPINT pBp)
674{
675 uint32_t idChunk = DBGF_BP_HND_GET_CHUNK_ID(hBp);
676 uint32_t idxEntry = DBGF_BP_HND_GET_ENTRY(hBp);
677
678 AssertReturnVoid(idChunk < DBGF_BP_CHUNK_COUNT);
679 AssertReturnVoid(idxEntry < DBGF_BP_COUNT_PER_CHUNK);
680
681 PDBGFBPCHUNKR3 pBpChunk = &pUVM->dbgf.s.aBpChunks[idChunk];
682 AssertPtrReturnVoid(pBpChunk->pbmAlloc);
683 AssertReturnVoid(ASMBitTest(pBpChunk->pbmAlloc, idxEntry));
684
685 /** @todo Need a trip to Ring-0 if an owner is assigned with a Ring-0 part to clear the breakpoint. */
686 int rc = dbgfR3BpOwnerRelease(pUVM, pBp->Pub.hOwner); AssertRC(rc); RT_NOREF(rc);
687 memset(pBp, 0, sizeof(*pBp));
688
689 ASMAtomicBitClear(pBpChunk->pbmAlloc, idxEntry);
690 ASMAtomicIncU32(&pBpChunk->cBpsFree);
691}
692
693
694/**
695 * @callback_method_impl{FNVMMEMTRENDEZVOUS}
696 */
697static DECLCALLBACK(VBOXSTRICTRC) dbgfR3BpL2TblChunkAllocEmtWorker(PVM pVM, PVMCPU pVCpu, void *pvUser)
698{
699 uint32_t idChunk = (uint32_t)(uintptr_t)pvUser;
700
701 VMCPU_ASSERT_EMT(pVCpu);
702 VM_ASSERT_VALID_EXT_RETURN(pVM, VERR_INVALID_VM_HANDLE);
703
704 AssertReturn(idChunk < DBGF_BP_L2_TBL_CHUNK_COUNT, VERR_DBGF_BP_IPE_1);
705
706 PUVM pUVM = pVM->pUVM;
707 PDBGFBPL2TBLCHUNKR3 pL2Chunk = &pUVM->dbgf.s.aBpL2TblChunks[idChunk];
708
709 AssertReturn( pL2Chunk->idChunk == DBGF_BP_L2_IDX_CHUNK_ID_INVALID
710 || pL2Chunk->idChunk == idChunk,
711 VERR_DBGF_BP_IPE_2);
712
713 /*
714 * The initialization will be done on EMT(0). It is possible that multiple
715 * allocation attempts are done when multiple racing non EMT threads try to
716 * allocate a breakpoint and a new chunk needs to be allocated.
717 * Ignore the request and succeed if the chunk is allocated meaning that a
718 * previous rendezvous successfully allocated the chunk.
719 */
720 int rc = VINF_SUCCESS;
721 if ( pVCpu->idCpu == 0
722 && pL2Chunk->idChunk == DBGF_BP_L2_IDX_CHUNK_ID_INVALID)
723 {
724 /* Allocate the bitmap first so we can skip calling into VMMR0 if it fails. */
725 AssertCompile(!(DBGF_BP_L2_TBL_ENTRIES_PER_CHUNK % 8));
726 volatile void *pbmAlloc = RTMemAllocZ(DBGF_BP_L2_TBL_ENTRIES_PER_CHUNK / 8);
727 if (RT_LIKELY(pbmAlloc))
728 {
729 DBGFBPL2TBLCHUNKALLOCREQ Req;
730 Req.Hdr.u32Magic = SUPVMMR0REQHDR_MAGIC;
731 Req.Hdr.cbReq = sizeof(Req);
732 Req.idChunk = idChunk;
733 Req.pChunkBaseR3 = NULL;
734 rc = VMMR3CallR0Emt(pVM, pVCpu, VMMR0_DO_DBGF_BP_L2_TBL_CHUNK_ALLOC, 0 /*u64Arg*/, &Req.Hdr);
735 AssertLogRelMsgRC(rc, ("VMMR0_DO_DBGF_BP_L2_TBL_CHUNK_ALLOC failed: %Rrc\n", rc));
736 if (RT_SUCCESS(rc))
737 {
738 pL2Chunk->pL2BaseR3 = (PDBGFBPL2ENTRY)Req.pChunkBaseR3;
739 pL2Chunk->pbmAlloc = pbmAlloc;
740 pL2Chunk->cFree = DBGF_BP_L2_TBL_ENTRIES_PER_CHUNK;
741 pL2Chunk->idChunk = idChunk;
742 return VINF_SUCCESS;
743 }
744
745 RTMemFree((void *)pbmAlloc);
746 }
747 else
748 rc = VERR_NO_MEMORY;
749 }
750
751 return rc;
752}
753
754
755/**
756 * Tries to allocate the given L2 table chunk which requires an EMT rendezvous.
757 *
758 * @returns VBox status code.
759 * @param pUVM The user mode VM handle.
760 * @param idChunk The chunk to allocate.
761 *
762 * @thread Any thread.
763 */
764DECLINLINE(int) dbgfR3BpL2TblChunkAlloc(PUVM pUVM, uint32_t idChunk)
765{
766 return VMMR3EmtRendezvous(pUVM->pVM, VMMEMTRENDEZVOUS_FLAGS_TYPE_ALL_AT_ONCE, dbgfR3BpL2TblChunkAllocEmtWorker, (void *)(uintptr_t)idChunk);
767}
768
769
770/**
771 * Tries to allocate a new breakpoint of the given type.
772 *
773 * @returns VBox status code.
774 * @param pUVM The user mode VM handle.
775 * @param pidxL2Tbl Where to return the L2 table entry index on success.
776 * @param ppL2TblEntry Where to return the pointer to the L2 table entry on success.
777 *
778 * @thread Any thread.
779 */
780static int dbgfR3BpL2TblEntryAlloc(PUVM pUVM, uint32_t *pidxL2Tbl, PDBGFBPL2ENTRY *ppL2TblEntry)
781{
782 /*
783 * Search for a chunk having a free entry, allocating new chunks
784 * if the encountered ones are full.
785 *
786 * This can be called from multiple threads at the same time so special care
787 * has to be taken to not require any locking here.
788 */
789 for (uint32_t i = 0; i < RT_ELEMENTS(pUVM->dbgf.s.aBpL2TblChunks); i++)
790 {
791 PDBGFBPL2TBLCHUNKR3 pL2Chunk = &pUVM->dbgf.s.aBpL2TblChunks[i];
792
793 uint32_t idChunk = ASMAtomicReadU32(&pL2Chunk->idChunk);
794 if (idChunk == DBGF_BP_L2_IDX_CHUNK_ID_INVALID)
795 {
796 int rc = dbgfR3BpL2TblChunkAlloc(pUVM, i);
797 if (RT_FAILURE(rc))
798 {
799 LogRel(("DBGF/Bp: Allocating new breakpoint L2 lookup table chunk failed with %Rrc\n", rc));
800 break;
801 }
802
803 idChunk = ASMAtomicReadU32(&pL2Chunk->idChunk);
804 Assert(idChunk == i);
805 }
806
807 /** @todo Optimize with some hinting if this turns out to be too slow. */
808 for (;;)
809 {
810 uint32_t cFree = ASMAtomicReadU32(&pL2Chunk->cFree);
811 if (cFree)
812 {
813 /*
814 * Scan the associated bitmap for a free entry, if none can be found another thread
815 * raced us and we go to the next chunk.
816 */
817 int32_t iClr = ASMBitFirstClear(pL2Chunk->pbmAlloc, DBGF_BP_L2_TBL_ENTRIES_PER_CHUNK);
818 if (iClr != -1)
819 {
820 /*
821 * Try to allocate, we could get raced here as well. In that case
822 * we try again.
823 */
824 if (!ASMAtomicBitTestAndSet(pL2Chunk->pbmAlloc, iClr))
825 {
826 /* Success, immediately mark as allocated, initialize the breakpoint state and return. */
827 ASMAtomicDecU32(&pL2Chunk->cFree);
828
829 PDBGFBPL2ENTRY pL2Entry = &pL2Chunk->pL2BaseR3[iClr];
830
831 *pidxL2Tbl = DBGF_BP_L2_IDX_CREATE(idChunk, iClr);
832 *ppL2TblEntry = pL2Entry;
833 return VINF_SUCCESS;
834 }
835 /* else Retry with another spot. */
836 }
837 else /* no free entry in bitmap, go to the next chunk */
838 break;
839 }
840 else /* !cFree, go to the next chunk */
841 break;
842 }
843 }
844
845 return VERR_DBGF_NO_MORE_BP_SLOTS;
846}
847
848
849/**
850 * Frees the given breakpoint handle.
851 *
852 * @returns nothing.
853 * @param pUVM The user mode VM handle.
854 * @param idxL2Tbl The L2 table index to free.
855 * @param pL2TblEntry The L2 table entry pointer to free.
856 */
857static void dbgfR3BpL2TblEntryFree(PUVM pUVM, uint32_t idxL2Tbl, PDBGFBPL2ENTRY pL2TblEntry)
858{
859 uint32_t idChunk = DBGF_BP_L2_IDX_GET_CHUNK_ID(idxL2Tbl);
860 uint32_t idxEntry = DBGF_BP_L2_IDX_GET_ENTRY(idxL2Tbl);
861
862 AssertReturnVoid(idChunk < DBGF_BP_L2_TBL_CHUNK_COUNT);
863 AssertReturnVoid(idxEntry < DBGF_BP_L2_TBL_ENTRIES_PER_CHUNK);
864
865 PDBGFBPL2TBLCHUNKR3 pL2Chunk = &pUVM->dbgf.s.aBpL2TblChunks[idChunk];
866 AssertPtrReturnVoid(pL2Chunk->pbmAlloc);
867 AssertReturnVoid(ASMBitTest(pL2Chunk->pbmAlloc, idxEntry));
868
869 memset(pL2TblEntry, 0, sizeof(*pL2TblEntry));
870
871 ASMAtomicBitClear(pL2Chunk->pbmAlloc, idxEntry);
872 ASMAtomicIncU32(&pL2Chunk->cFree);
873}
874
875
876/**
877 * Sets the enabled flag of the given breakpoint to the given value.
878 *
879 * @returns nothing.
880 * @param pBp The breakpoint to set the state.
881 * @param fEnabled Enabled status.
882 */
883DECLINLINE(void) dbgfR3BpSetEnabled(PDBGFBPINT pBp, bool fEnabled)
884{
885 if (fEnabled)
886 pBp->Pub.fFlags |= DBGF_BP_F_ENABLED;
887 else
888 pBp->Pub.fFlags &= ~DBGF_BP_F_ENABLED;
889}
890
891
892/**
893 * Assigns a hardware breakpoint state to the given register breakpoint.
894 *
895 * @returns VBox status code.
896 * @param pVM The cross-context VM structure pointer.
897 * @param hBp The breakpoint handle to assign.
898 * @param pBp The internal breakpoint state.
899 *
900 * @thread Any thread.
901 */
902static int dbgfR3BpRegAssign(PVM pVM, DBGFBP hBp, PDBGFBPINT pBp)
903{
904 AssertReturn(pBp->Pub.u.Reg.iReg == UINT8_MAX, VERR_DBGF_BP_IPE_3);
905
906 for (uint8_t i = 0; i < RT_ELEMENTS(pVM->dbgf.s.aHwBreakpoints); i++)
907 {
908 PDBGFBPHW pHwBp = &pVM->dbgf.s.aHwBreakpoints[i];
909
910 AssertCompileSize(DBGFBP, sizeof(uint32_t));
911 if (ASMAtomicCmpXchgU32(&pHwBp->hBp, hBp, NIL_DBGFBP))
912 {
913 pHwBp->GCPtr = pBp->Pub.u.Reg.GCPtr;
914 pHwBp->fType = pBp->Pub.u.Reg.fType;
915 pHwBp->cb = pBp->Pub.u.Reg.cb;
916 pHwBp->fEnabled = DBGF_BP_PUB_IS_ENABLED(&pBp->Pub);
917
918 pBp->Pub.u.Reg.iReg = i;
919 return VINF_SUCCESS;
920 }
921 }
922
923 return VERR_DBGF_NO_MORE_BP_SLOTS;
924}
925
926
927/**
928 * Removes the assigned hardware breakpoint state from the given register breakpoint.
929 *
930 * @returns VBox status code.
931 * @param pVM The cross-context VM structure pointer.
932 * @param hBp The breakpoint handle to remove.
933 * @param pBp The internal breakpoint state.
934 *
935 * @thread Any thread.
936 */
937static int dbgfR3BpRegRemove(PVM pVM, DBGFBP hBp, PDBGFBPINT pBp)
938{
939 AssertReturn(pBp->Pub.u.Reg.iReg < RT_ELEMENTS(pVM->dbgf.s.aHwBreakpoints), VERR_DBGF_BP_IPE_3);
940
941 PDBGFBPHW pHwBp = &pVM->dbgf.s.aHwBreakpoints[pBp->Pub.u.Reg.iReg];
942 AssertReturn(pHwBp->hBp == hBp, VERR_DBGF_BP_IPE_4);
943 AssertReturn(!pHwBp->fEnabled, VERR_DBGF_BP_IPE_5);
944
945 pHwBp->GCPtr = 0;
946 pHwBp->fType = 0;
947 pHwBp->cb = 0;
948 ASMCompilerBarrier();
949
950 ASMAtomicWriteU32(&pHwBp->hBp, NIL_DBGFBP);
951 return VINF_SUCCESS;
952}
953
954
955/**
956 * Returns the pointer to the L2 table entry from the given index.
957 *
958 * @returns Current context pointer to the L2 table entry or NULL if the provided index value is invalid.
959 * @param pUVM The user mode VM handle.
960 * @param idxL2 The L2 table index to resolve.
961 *
962 * @note The content of the resolved L2 table entry is not validated!.
963 */
964DECLINLINE(PDBGFBPL2ENTRY) dbgfR3BpL2GetByIdx(PUVM pUVM, uint32_t idxL2)
965{
966 uint32_t idChunk = DBGF_BP_L2_IDX_GET_CHUNK_ID(idxL2);
967 uint32_t idxEntry = DBGF_BP_L2_IDX_GET_ENTRY(idxL2);
968
969 AssertReturn(idChunk < DBGF_BP_L2_TBL_CHUNK_COUNT, NULL);
970 AssertReturn(idxEntry < DBGF_BP_L2_TBL_ENTRIES_PER_CHUNK, NULL);
971
972 PDBGFBPL2TBLCHUNKR3 pL2Chunk = &pUVM->dbgf.s.aBpL2TblChunks[idChunk];
973 AssertPtrReturn(pL2Chunk->pbmAlloc, NULL);
974 AssertReturn(ASMBitTest(pL2Chunk->pbmAlloc, idxEntry), NULL);
975
976 return &pL2Chunk->CTX_SUFF(pL2Base)[idxEntry];
977}
978
979
980/**
981 * Creates a binary search tree with the given root and leaf nodes.
982 *
983 * @returns VBox status code.
984 * @param pUVM The user mode VM handle.
985 * @param idxL1 The index into the L1 table where the created tree should be linked into.
986 * @param u32EntryOld The old entry in the L1 table used to compare with in the atomic update.
987 * @param hBpRoot The root node DBGF handle to assign.
988 * @param GCPtrRoot The root nodes GC pointer to use as a key.
989 * @param hBpLeaf The leafs node DBGF handle to assign.
990 * @param GCPtrLeaf The leafs node GC pointer to use as a key.
991 */
992static int dbgfR3BpInt3L2BstCreate(PUVM pUVM, uint32_t idxL1, uint32_t u32EntryOld,
993 DBGFBP hBpRoot, RTGCUINTPTR GCPtrRoot,
994 DBGFBP hBpLeaf, RTGCUINTPTR GCPtrLeaf)
995{
996 AssertReturn(GCPtrRoot != GCPtrLeaf, VERR_DBGF_BP_IPE_9);
997 Assert(DBGF_BP_INT3_L1_IDX_EXTRACT_FROM_ADDR(GCPtrRoot) == DBGF_BP_INT3_L1_IDX_EXTRACT_FROM_ADDR(GCPtrLeaf));
998
999 /* Allocate two nodes. */
1000 uint32_t idxL2Root = 0;
1001 PDBGFBPL2ENTRY pL2Root = NULL;
1002 int rc = dbgfR3BpL2TblEntryAlloc(pUVM, &idxL2Root, &pL2Root);
1003 if (RT_SUCCESS(rc))
1004 {
1005 uint32_t idxL2Leaf = 0;
1006 PDBGFBPL2ENTRY pL2Leaf = NULL;
1007 rc = dbgfR3BpL2TblEntryAlloc(pUVM, &idxL2Leaf, &pL2Leaf);
1008 if (RT_SUCCESS(rc))
1009 {
1010 dbgfBpL2TblEntryInit(pL2Leaf, hBpLeaf, GCPtrLeaf, DBGF_BP_L2_ENTRY_IDX_END, DBGF_BP_L2_ENTRY_IDX_END, 0 /*iDepth*/);
1011 if (GCPtrLeaf < GCPtrRoot)
1012 dbgfBpL2TblEntryInit(pL2Root, hBpRoot, GCPtrRoot, idxL2Leaf, DBGF_BP_L2_ENTRY_IDX_END, 0 /*iDepth*/);
1013 else
1014 dbgfBpL2TblEntryInit(pL2Root, hBpRoot, GCPtrRoot, DBGF_BP_L2_ENTRY_IDX_END, idxL2Leaf, 0 /*iDepth*/);
1015
1016 uint32_t const u32Entry = DBGF_BP_INT3_L1_ENTRY_CREATE_L2_IDX(idxL2Root);
1017 if (ASMAtomicCmpXchgU32(&pUVM->dbgf.s.paBpLocL1R3[idxL1], u32Entry, u32EntryOld))
1018 return VINF_SUCCESS;
1019
1020 /* The L1 entry has changed due to another thread racing us during insertion, free nodes and try again. */
1021 rc = VINF_TRY_AGAIN;
1022 dbgfR3BpL2TblEntryFree(pUVM, idxL2Leaf, pL2Leaf);
1023 }
1024
1025 dbgfR3BpL2TblEntryFree(pUVM, idxL2Root, pL2Root);
1026 }
1027
1028 return rc;
1029}
1030
1031
1032/**
1033 * Inserts the given breakpoint handle into an existing binary search tree.
1034 *
1035 * @returns VBox status code.
1036 * @param pUVM The user mode VM handle.
1037 * @param idxL2Root The index of the tree root in the L2 table.
1038 * @param hBp The node DBGF handle to insert.
1039 * @param GCPtr The nodes GC pointer to use as a key.
1040 */
1041static int dbgfR3BpInt2L2BstNodeInsert(PUVM pUVM, uint32_t idxL2Root, DBGFBP hBp, RTGCUINTPTR GCPtr)
1042{
1043 GCPtr = DBGF_BP_INT3_L2_KEY_EXTRACT_FROM_ADDR(GCPtr);
1044
1045 /* Allocate a new node first. */
1046 uint32_t idxL2Nd = 0;
1047 PDBGFBPL2ENTRY pL2Nd = NULL;
1048 int rc = dbgfR3BpL2TblEntryAlloc(pUVM, &idxL2Nd, &pL2Nd);
1049 if (RT_SUCCESS(rc))
1050 {
1051 /* Walk the tree and find the correct node to insert to. */
1052 PDBGFBPL2ENTRY pL2Entry = dbgfR3BpL2GetByIdx(pUVM, idxL2Root);
1053 while (RT_LIKELY(pL2Entry))
1054 {
1055 /* Make a copy of the entry. */
1056 DBGFBPL2ENTRY L2Entry;
1057 L2Entry.u64GCPtrKeyAndBpHnd1 = ASMAtomicReadU64((volatile uint64_t *)&pL2Entry->u64GCPtrKeyAndBpHnd1);
1058 L2Entry.u64LeftRightIdxDepthBpHnd2 = ASMAtomicReadU64((volatile uint64_t *)&pL2Entry->u64LeftRightIdxDepthBpHnd2);
1059
1060 RTGCUINTPTR GCPtrL2Entry = DBGF_BP_L2_ENTRY_GET_GCPTR(L2Entry.u64GCPtrKeyAndBpHnd1);
1061 AssertBreak(GCPtr != GCPtrL2Entry);
1062
1063 /* Not found, get to the next level. */
1064 uint32_t idxL2Next = (GCPtr < GCPtrL2Entry)
1065 ? DBGF_BP_L2_ENTRY_GET_IDX_LEFT(L2Entry.u64LeftRightIdxDepthBpHnd2)
1066 : DBGF_BP_L2_ENTRY_GET_IDX_RIGHT(L2Entry.u64LeftRightIdxDepthBpHnd2);
1067 if (idxL2Next == DBGF_BP_L2_ENTRY_IDX_END)
1068 {
1069 /* Insert the new node here. */
1070 dbgfBpL2TblEntryInit(pL2Nd, hBp, GCPtr, DBGF_BP_L2_ENTRY_IDX_END, DBGF_BP_L2_ENTRY_IDX_END, 0 /*iDepth*/);
1071 if (GCPtr < GCPtrL2Entry)
1072 dbgfBpL2TblEntryUpdateLeft(pL2Entry, idxL2Next, 0 /*iDepth*/);
1073 else
1074 dbgfBpL2TblEntryUpdateRight(pL2Entry, idxL2Next, 0 /*iDepth*/);
1075 return VINF_SUCCESS;
1076 }
1077
1078 pL2Entry = dbgfR3BpL2GetByIdx(pUVM, idxL2Next);
1079 }
1080
1081 rc = VERR_DBGF_BP_L2_LOOKUP_FAILED;
1082 dbgfR3BpL2TblEntryFree(pUVM, idxL2Nd, pL2Nd);
1083 }
1084
1085 return rc;
1086}
1087
1088
1089/**
1090 * Adds the given breakpoint handle keyed with the GC pointer to the proper L2 binary search tree
1091 * possibly creating a new tree.
1092 *
1093 * @returns VBox status code.
1094 * @param pUVM The user mode VM handle.
1095 * @param idxL1 The index into the L1 table the breakpoint uses.
1096 * @param hBp The breakpoint handle which is to be added.
1097 * @param GCPtr The GC pointer the breakpoint is keyed with.
1098 */
1099static int dbgfR3BpInt3L2BstNodeAdd(PUVM pUVM, uint32_t idxL1, DBGFBP hBp, RTGCUINTPTR GCPtr)
1100{
1101 int rc = RTSemFastMutexRequest(pUVM->dbgf.s.hMtxBpL2Wr); AssertRC(rc);
1102
1103 uint32_t u32Entry = ASMAtomicReadU32(&pUVM->dbgf.s.paBpLocL1R3[idxL1]); /* Re-read, could get raced by a remove operation. */
1104 uint8_t u8Type = DBGF_BP_INT3_L1_ENTRY_GET_TYPE(u32Entry);
1105 if (u8Type == DBGF_BP_INT3_L1_ENTRY_TYPE_BP_HND)
1106 {
1107 /* Create a new search tree, gather the necessary information first. */
1108 DBGFBP hBp2 = DBGF_BP_INT3_L1_ENTRY_GET_BP_HND(u32Entry);
1109 PDBGFBPINT pBp2 = dbgfR3BpGetByHnd(pUVM, hBp2);
1110 AssertStmt(VALID_PTR(pBp2), rc = VERR_DBGF_BP_IPE_7);
1111 if (RT_SUCCESS(rc))
1112 rc = dbgfR3BpInt3L2BstCreate(pUVM, idxL1, u32Entry, hBp, GCPtr, hBp2, pBp2->Pub.u.Int3.GCPtr);
1113 }
1114 else if (u8Type == DBGF_BP_INT3_L1_ENTRY_TYPE_L2_IDX)
1115 rc = dbgfR3BpInt2L2BstNodeInsert(pUVM, DBGF_BP_INT3_L1_ENTRY_GET_L2_IDX(u32Entry), hBp, GCPtr);
1116
1117 int rc2 = RTSemFastMutexRelease(pUVM->dbgf.s.hMtxBpL2Wr); AssertRC(rc2);
1118 return rc;
1119}
1120
1121
1122/**
1123 * Gets the leftmost from the given tree node start index.
1124 *
1125 * @returns VBox status code.
1126 * @param pUVM The user mode VM handle.
1127 * @param idxL2Start The start index to walk from.
1128 * @param pidxL2Leftmost Where to store the L2 table index of the leftmost entry.
1129 * @param ppL2NdLeftmost Where to store the pointer to the leftmost L2 table entry.
1130 * @param pidxL2NdLeftParent Where to store the L2 table index of the leftmost entries parent.
1131 * @param ppL2NdLeftParent Where to store the pointer to the leftmost L2 table entries parent.
1132 */
1133static int dbgfR33BpInt3BstGetLeftmostEntryFromNode(PUVM pUVM, uint32_t idxL2Start,
1134 uint32_t *pidxL2Leftmost, PDBGFBPL2ENTRY *ppL2NdLeftmost,
1135 uint32_t *pidxL2NdLeftParent, PDBGFBPL2ENTRY *ppL2NdLeftParent)
1136{
1137 uint32_t idxL2Parent = DBGF_BP_L2_ENTRY_IDX_END;
1138 PDBGFBPL2ENTRY pL2NdParent = NULL;
1139
1140 for (;;)
1141 {
1142 PDBGFBPL2ENTRY pL2Entry = dbgfR3BpL2GetByIdx(pUVM, idxL2Start);
1143 AssertPtr(pL2Entry);
1144
1145 uint32_t idxL2Left = DBGF_BP_L2_ENTRY_GET_IDX_LEFT(pL2Entry->u64LeftRightIdxDepthBpHnd2);
1146 if (idxL2Start == DBGF_BP_L2_ENTRY_IDX_END)
1147 {
1148 *pidxL2Leftmost = idxL2Start;
1149 *ppL2NdLeftmost = pL2Entry;
1150 *pidxL2NdLeftParent = idxL2Parent;
1151 *ppL2NdLeftParent = pL2NdParent;
1152 break;
1153 }
1154
1155 idxL2Parent = idxL2Start;
1156 idxL2Start = idxL2Left;
1157 pL2NdParent = pL2Entry;
1158 }
1159
1160 return VINF_SUCCESS;
1161}
1162
1163
1164/**
1165 * Removes the given node rearranging the tree.
1166 *
1167 * @returns VBox status code.
1168 * @param pUVM The user mode VM handle.
1169 * @param idxL1 The index into the L1 table pointing to the binary search tree containing the node.
1170 * @param idxL2Root The L2 table index where the tree root is located.
1171 * @param idxL2Nd The node index to remove.
1172 * @param pL2Nd The L2 table entry to remove.
1173 * @param idxL2NdParent The parents index, can be DBGF_BP_L2_ENTRY_IDX_END if the root is about to be removed.
1174 * @param pL2NdParent The parents L2 table entry, can be NULL if the root is about to be removed.
1175 * @param fLeftChild Flag whether the node is the left child of the parent or the right one.
1176 */
1177static int dbgfR3BpInt3BstNodeRemove(PUVM pUVM, uint32_t idxL1, uint32_t idxL2Root,
1178 uint32_t idxL2Nd, PDBGFBPL2ENTRY pL2Nd,
1179 uint32_t idxL2NdParent, PDBGFBPL2ENTRY pL2NdParent,
1180 bool fLeftChild)
1181{
1182 /*
1183 * If there are only two nodes remaining the tree will get destroyed and the
1184 * L1 entry will be converted to the direct handle type.
1185 */
1186 uint32_t idxL2Left = DBGF_BP_L2_ENTRY_GET_IDX_LEFT(pL2Nd->u64LeftRightIdxDepthBpHnd2);
1187 uint32_t idxL2Right = DBGF_BP_L2_ENTRY_GET_IDX_RIGHT(pL2Nd->u64LeftRightIdxDepthBpHnd2);
1188
1189 Assert(idxL2NdParent != DBGF_BP_L2_ENTRY_IDX_END || !pL2NdParent); RT_NOREF(idxL2NdParent);
1190 uint32_t idxL2ParentNew = DBGF_BP_L2_ENTRY_IDX_END;
1191 if (idxL2Right == DBGF_BP_L2_ENTRY_IDX_END)
1192 idxL2ParentNew = idxL2Left;
1193 else
1194 {
1195 /* Find the leftmost entry of the right subtree and move it to the to be removed nodes location in the tree. */
1196 PDBGFBPL2ENTRY pL2NdLeftmostParent = NULL;
1197 PDBGFBPL2ENTRY pL2NdLeftmost = NULL;
1198 uint32_t idxL2NdLeftmostParent = DBGF_BP_L2_ENTRY_IDX_END;
1199 uint32_t idxL2Leftmost = DBGF_BP_L2_ENTRY_IDX_END;
1200 int rc = dbgfR33BpInt3BstGetLeftmostEntryFromNode(pUVM, idxL2Right, &idxL2Leftmost ,&pL2NdLeftmost,
1201 &idxL2NdLeftmostParent, &pL2NdLeftmostParent);
1202 AssertRCReturn(rc, rc);
1203
1204 if (pL2NdLeftmostParent)
1205 {
1206 /* Rearrange the leftmost entries parents pointer. */
1207 dbgfBpL2TblEntryUpdateLeft(pL2NdLeftmostParent, DBGF_BP_L2_ENTRY_GET_IDX_RIGHT(pL2NdLeftmost->u64LeftRightIdxDepthBpHnd2), 0 /*iDepth*/);
1208 dbgfBpL2TblEntryUpdateRight(pL2NdLeftmost, idxL2Right, 0 /*iDepth*/);
1209 }
1210
1211 dbgfBpL2TblEntryUpdateLeft(pL2NdLeftmost, idxL2Left, 0 /*iDepth*/);
1212
1213 /* Update the remove nodes parent to point to the new node. */
1214 idxL2ParentNew = idxL2Leftmost;
1215 }
1216
1217 if (pL2NdParent)
1218 {
1219 /* Asssign the new L2 index to proper parents left or right pointer. */
1220 if (fLeftChild)
1221 dbgfBpL2TblEntryUpdateLeft(pL2NdParent, idxL2ParentNew, 0 /*iDepth*/);
1222 else
1223 dbgfBpL2TblEntryUpdateRight(pL2NdParent, idxL2ParentNew, 0 /*iDepth*/);
1224 }
1225 else
1226 {
1227 /* The root node is removed, set the new root in the L1 table. */
1228 Assert(idxL2ParentNew != DBGF_BP_L2_ENTRY_IDX_END);
1229 idxL2Root = idxL2ParentNew;
1230 ASMAtomicXchgU32(&pUVM->dbgf.s.paBpLocL1R3[idxL1], DBGF_BP_INT3_L1_ENTRY_CREATE_L2_IDX(idxL2Left));
1231 }
1232
1233 /* Free the node. */
1234 dbgfR3BpL2TblEntryFree(pUVM, idxL2Nd, pL2Nd);
1235
1236 /*
1237 * Check whether the old/new root is the only node remaining and convert the L1
1238 * table entry to a direct breakpoint handle one in that case.
1239 */
1240 pL2Nd = dbgfR3BpL2GetByIdx(pUVM, idxL2Root);
1241 AssertPtr(pL2Nd);
1242 if ( DBGF_BP_L2_ENTRY_GET_IDX_LEFT(pL2Nd->u64LeftRightIdxDepthBpHnd2) == DBGF_BP_L2_ENTRY_IDX_END
1243 && DBGF_BP_L2_ENTRY_GET_IDX_RIGHT(pL2Nd->u64LeftRightIdxDepthBpHnd2) == DBGF_BP_L2_ENTRY_IDX_END)
1244 {
1245 DBGFBP hBp = DBGF_BP_L2_ENTRY_GET_BP_HND(pL2Nd->u64GCPtrKeyAndBpHnd1, pL2Nd->u64LeftRightIdxDepthBpHnd2);
1246 dbgfR3BpL2TblEntryFree(pUVM, idxL2Root, pL2Nd);
1247 ASMAtomicXchgU32(&pUVM->dbgf.s.paBpLocL1R3[idxL1], DBGF_BP_INT3_L1_ENTRY_CREATE_BP_HND(hBp));
1248 }
1249
1250 return VINF_SUCCESS;
1251}
1252
1253
1254/**
1255 * Removes the given breakpoint handle keyed with the GC pointer from the L2 binary search tree
1256 * pointed to by the given L2 root index.
1257 *
1258 * @returns VBox status code.
1259 * @param pUVM The user mode VM handle.
1260 * @param idxL1 The index into the L1 table pointing to the binary search tree.
1261 * @param idxL2Root The L2 table index where the tree root is located.
1262 * @param hBp The breakpoint handle which is to be removed.
1263 * @param GCPtr The GC pointer the breakpoint is keyed with.
1264 */
1265static int dbgfR3BpInt3L2BstRemove(PUVM pUVM, uint32_t idxL1, uint32_t idxL2Root, DBGFBP hBp, RTGCUINTPTR GCPtr)
1266{
1267 GCPtr = DBGF_BP_INT3_L2_KEY_EXTRACT_FROM_ADDR(GCPtr);
1268
1269 int rc = RTSemFastMutexRequest(pUVM->dbgf.s.hMtxBpL2Wr); AssertRC(rc);
1270
1271 uint32_t idxL2Cur = idxL2Root;
1272 uint32_t idxL2Parent = DBGF_BP_L2_ENTRY_IDX_END;
1273 bool fLeftChild = false;
1274 PDBGFBPL2ENTRY pL2EntryParent = NULL;
1275 for (;;)
1276 {
1277 PDBGFBPL2ENTRY pL2Entry = dbgfR3BpL2GetByIdx(pUVM, idxL2Cur);
1278 AssertPtr(pL2Entry);
1279
1280 /* Check whether this node is to be removed.. */
1281 RTGCUINTPTR GCPtrL2Entry = DBGF_BP_L2_ENTRY_GET_GCPTR(pL2Entry->u64GCPtrKeyAndBpHnd1);
1282 if (GCPtrL2Entry == GCPtr)
1283 {
1284 Assert(DBGF_BP_L2_ENTRY_GET_BP_HND(pL2Entry->u64GCPtrKeyAndBpHnd1, pL2Entry->u64LeftRightIdxDepthBpHnd2) == hBp); RT_NOREF(hBp);
1285
1286 rc = dbgfR3BpInt3BstNodeRemove(pUVM, idxL1, idxL2Root, idxL2Cur, pL2Entry,
1287 idxL2Parent, pL2EntryParent, fLeftChild);
1288 break;
1289 }
1290
1291 pL2EntryParent = pL2Entry;
1292 idxL2Parent = idxL2Cur;
1293
1294 if (GCPtrL2Entry < GCPtr)
1295 {
1296 fLeftChild = true;
1297 idxL2Cur = DBGF_BP_L2_ENTRY_GET_IDX_LEFT(pL2Entry->u64LeftRightIdxDepthBpHnd2);
1298 }
1299 else
1300 {
1301 fLeftChild = false;
1302 idxL2Cur = DBGF_BP_L2_ENTRY_GET_IDX_RIGHT(pL2Entry->u64LeftRightIdxDepthBpHnd2);
1303 }
1304
1305 AssertBreakStmt(idxL2Cur != DBGF_BP_L2_ENTRY_IDX_END, rc = VERR_DBGF_BP_L2_LOOKUP_FAILED);
1306 }
1307
1308 int rc2 = RTSemFastMutexRelease(pUVM->dbgf.s.hMtxBpL2Wr); AssertRC(rc2);
1309
1310 return rc;
1311}
1312
1313
1314/**
1315 * Adds the given int3 breakpoint to the appropriate lookup tables.
1316 *
1317 * @returns VBox status code.
1318 * @param pUVM The user mode VM handle.
1319 * @param hBp The breakpoint handle to add.
1320 * @param pBp The internal breakpoint state.
1321 */
1322static int dbgfR3BpInt3Add(PUVM pUVM, DBGFBP hBp, PDBGFBPINT pBp)
1323{
1324 AssertReturn(DBGF_BP_PUB_GET_TYPE(&pBp->Pub) == DBGFBPTYPE_INT3, VERR_DBGF_BP_IPE_3);
1325
1326 int rc = VINF_SUCCESS;
1327 uint16_t idxL1 = DBGF_BP_INT3_L1_IDX_EXTRACT_FROM_ADDR(pBp->Pub.u.Int3.GCPtr);
1328 uint8_t cTries = 16;
1329
1330 while (cTries--)
1331 {
1332 uint32_t u32Entry = ASMAtomicReadU32(&pUVM->dbgf.s.paBpLocL1R3[idxL1]);
1333 if (u32Entry == DBGF_BP_INT3_L1_ENTRY_TYPE_NULL)
1334 {
1335 /*
1336 * No breakpoint assigned so far for this entry, create an entry containing
1337 * the direct breakpoint handle and try to exchange it atomically.
1338 */
1339 u32Entry = DBGF_BP_INT3_L1_ENTRY_CREATE_BP_HND(hBp);
1340 if (ASMAtomicCmpXchgU32(&pUVM->dbgf.s.paBpLocL1R3[idxL1], u32Entry, DBGF_BP_INT3_L1_ENTRY_TYPE_NULL))
1341 break;
1342 }
1343 else
1344 {
1345 rc = dbgfR3BpInt3L2BstNodeAdd(pUVM, idxL1, hBp, pBp->Pub.u.Int3.GCPtr);
1346 if (rc != VINF_TRY_AGAIN)
1347 break;
1348 }
1349 }
1350
1351 if ( RT_SUCCESS(rc)
1352 && !cTries) /* Too much contention, abort with an error. */
1353 rc = VERR_DBGF_BP_INT3_ADD_TRIES_REACHED;
1354
1355 return rc;
1356}
1357
1358
1359/**
1360 * Get a breakpoint give by address.
1361 *
1362 * @returns The breakpoint handle on success or NIL_DBGF if not found.
1363 * @param pUVM The user mode VM handle.
1364 * @param enmType The breakpoint type.
1365 * @param GCPtr The breakpoint address.
1366 * @param ppBp Where to store the pointer to the internal breakpoint state on success, optional.
1367 */
1368static DBGFBP dbgfR3BpGetByAddr(PUVM pUVM, DBGFBPTYPE enmType, RTGCUINTPTR GCPtr, PDBGFBPINT *ppBp)
1369{
1370 DBGFBP hBp = NIL_DBGFBP;
1371
1372 switch (enmType)
1373 {
1374 case DBGFBPTYPE_REG:
1375 {
1376 PVM pVM = pUVM->pVM;
1377 VM_ASSERT_VALID_EXT_RETURN(pVM, NIL_DBGFBP);
1378
1379 for (uint32_t i = 0; i < RT_ELEMENTS(pVM->dbgf.s.aHwBreakpoints); i++)
1380 {
1381 PDBGFBPHW pHwBp = &pVM->dbgf.s.aHwBreakpoints[i];
1382
1383 AssertCompileSize(DBGFBP, sizeof(uint32_t));
1384 DBGFBP hBpTmp = ASMAtomicReadU32(&pHwBp->hBp);
1385 if ( pHwBp->GCPtr == GCPtr
1386 && hBpTmp != NIL_DBGFBP)
1387 {
1388 hBp = hBpTmp;
1389 break;
1390 }
1391 }
1392
1393 break;
1394 }
1395
1396 case DBGFBPTYPE_INT3:
1397 {
1398 const uint16_t idxL1 = DBGF_BP_INT3_L1_IDX_EXTRACT_FROM_ADDR(GCPtr);
1399 const uint32_t u32L1Entry = ASMAtomicReadU32(&pUVM->dbgf.s.CTX_SUFF(paBpLocL1)[idxL1]);
1400
1401 if (u32L1Entry != DBGF_BP_INT3_L1_ENTRY_TYPE_NULL)
1402 {
1403 uint8_t u8Type = DBGF_BP_INT3_L1_ENTRY_GET_TYPE(u32L1Entry);
1404 if (u8Type == DBGF_BP_INT3_L1_ENTRY_TYPE_BP_HND)
1405 hBp = DBGF_BP_INT3_L1_ENTRY_GET_BP_HND(u32L1Entry);
1406 else if (u8Type == DBGF_BP_INT3_L1_ENTRY_TYPE_L2_IDX)
1407 {
1408 RTGCUINTPTR GCPtrKey = DBGF_BP_INT3_L2_KEY_EXTRACT_FROM_ADDR(GCPtr);
1409 PDBGFBPL2ENTRY pL2Nd = dbgfR3BpL2GetByIdx(pUVM, DBGF_BP_INT3_L1_ENTRY_GET_L2_IDX(u32L1Entry));
1410
1411 for (;;)
1412 {
1413 AssertPtr(pL2Nd);
1414
1415 RTGCUINTPTR GCPtrL2Entry = DBGF_BP_L2_ENTRY_GET_GCPTR(pL2Nd->u64GCPtrKeyAndBpHnd1);
1416 if (GCPtrKey == GCPtrL2Entry)
1417 {
1418 hBp = DBGF_BP_L2_ENTRY_GET_BP_HND(pL2Nd->u64GCPtrKeyAndBpHnd1, pL2Nd->u64LeftRightIdxDepthBpHnd2);
1419 break;
1420 }
1421
1422 /* Not found, get to the next level. */
1423 uint32_t idxL2Next = (GCPtrKey < GCPtrL2Entry)
1424 ? DBGF_BP_L2_ENTRY_GET_IDX_LEFT(pL2Nd->u64LeftRightIdxDepthBpHnd2)
1425 : DBGF_BP_L2_ENTRY_GET_IDX_RIGHT(pL2Nd->u64LeftRightIdxDepthBpHnd2);
1426 /* Address not found if the entry denotes the end. */
1427 if (idxL2Next == DBGF_BP_L2_ENTRY_IDX_END)
1428 break;
1429
1430 pL2Nd = dbgfR3BpL2GetByIdx(pUVM, idxL2Next);
1431 }
1432 }
1433 }
1434 break;
1435 }
1436
1437 default:
1438 AssertMsgFailed(("enmType=%d\n", enmType));
1439 break;
1440 }
1441
1442 if ( hBp != NIL_DBGFBP
1443 && ppBp)
1444 *ppBp = dbgfR3BpGetByHnd(pUVM, hBp);
1445 return hBp;
1446}
1447
1448
1449/**
1450 * @callback_method_impl{FNVMMEMTRENDEZVOUS}
1451 */
1452static DECLCALLBACK(VBOXSTRICTRC) dbgfR3BpInt3RemoveEmtWorker(PVM pVM, PVMCPU pVCpu, void *pvUser)
1453{
1454 DBGFBP hBp = (DBGFBP)(uintptr_t)pvUser;
1455
1456 VMCPU_ASSERT_EMT(pVCpu);
1457 VM_ASSERT_VALID_EXT_RETURN(pVM, VERR_INVALID_VM_HANDLE);
1458
1459 PUVM pUVM = pVM->pUVM;
1460 PDBGFBPINT pBp = dbgfR3BpGetByHnd(pUVM, hBp);
1461 AssertPtrReturn(pBp, VERR_DBGF_BP_IPE_8);
1462
1463 int rc = VINF_SUCCESS;
1464 if (pVCpu->idCpu == 0)
1465 {
1466 uint16_t idxL1 = DBGF_BP_INT3_L1_IDX_EXTRACT_FROM_ADDR(pBp->Pub.u.Int3.GCPtr);
1467 uint32_t u32Entry = ASMAtomicReadU32(&pUVM->dbgf.s.paBpLocL1R3[idxL1]);
1468 AssertReturn(u32Entry != DBGF_BP_INT3_L1_ENTRY_TYPE_NULL, VERR_DBGF_BP_IPE_6);
1469
1470 uint8_t u8Type = DBGF_BP_INT3_L1_ENTRY_GET_TYPE(u32Entry);
1471 if (u8Type == DBGF_BP_INT3_L1_ENTRY_TYPE_BP_HND)
1472 {
1473 /* Single breakpoint, just exchange atomically with the null value. */
1474 if (!ASMAtomicCmpXchgU32(&pUVM->dbgf.s.paBpLocL1R3[idxL1], DBGF_BP_INT3_L1_ENTRY_TYPE_NULL, u32Entry))
1475 {
1476 /*
1477 * A breakpoint addition must have raced us converting the L1 entry to an L2 index type, re-read
1478 * and remove the node from the created binary search tree.
1479 *
1480 * This works because after the entry was converted to an L2 index it can only be converted back
1481 * to a direct handle by removing one or more nodes which always goes through the fast mutex
1482 * protecting the L2 table. Likewise adding a new breakpoint requires grabbing the mutex as well
1483 * so there is serialization here and the node can be removed safely without having to worry about
1484 * concurrent tree modifications.
1485 */
1486 u32Entry = ASMAtomicReadU32(&pUVM->dbgf.s.paBpLocL1R3[idxL1]);
1487 AssertReturn(DBGF_BP_INT3_L1_ENTRY_GET_TYPE(u32Entry) == DBGF_BP_INT3_L1_ENTRY_TYPE_L2_IDX, VERR_DBGF_BP_IPE_9);
1488
1489 rc = dbgfR3BpInt3L2BstRemove(pUVM, idxL1, DBGF_BP_INT3_L1_ENTRY_GET_L2_IDX(u32Entry),
1490 hBp, pBp->Pub.u.Int3.GCPtr);
1491 }
1492 }
1493 else if (u8Type == DBGF_BP_INT3_L1_ENTRY_TYPE_L2_IDX)
1494 rc = dbgfR3BpInt3L2BstRemove(pUVM, idxL1, DBGF_BP_INT3_L1_ENTRY_GET_L2_IDX(u32Entry),
1495 hBp, pBp->Pub.u.Int3.GCPtr);
1496 }
1497
1498 return rc;
1499}
1500
1501
1502/**
1503 * Removes the given int3 breakpoint from all lookup tables.
1504 *
1505 * @returns VBox status code.
1506 * @param pUVM The user mode VM handle.
1507 * @param hBp The breakpoint handle to remove.
1508 * @param pBp The internal breakpoint state.
1509 */
1510static int dbgfR3BpInt3Remove(PUVM pUVM, DBGFBP hBp, PDBGFBPINT pBp)
1511{
1512 AssertReturn(DBGF_BP_PUB_GET_TYPE(&pBp->Pub) == DBGFBPTYPE_INT3, VERR_DBGF_BP_IPE_3);
1513
1514 /*
1515 * This has to be done by an EMT rendezvous in order to not have an EMT traversing
1516 * any L2 trees while it is being removed.
1517 */
1518 return VMMR3EmtRendezvous(pUVM->pVM, VMMEMTRENDEZVOUS_FLAGS_TYPE_ALL_AT_ONCE, dbgfR3BpInt3RemoveEmtWorker, (void *)(uintptr_t)hBp);
1519}
1520
1521
1522/**
1523 * @callback_method_impl{FNVMMEMTRENDEZVOUS}
1524 */
1525static DECLCALLBACK(VBOXSTRICTRC) dbgfR3BpRegRecalcOnCpu(PVM pVM, PVMCPU pVCpu, void *pvUser)
1526{
1527 RT_NOREF(pvUser);
1528
1529 /*
1530 * CPU 0 updates the enabled hardware breakpoint counts.
1531 */
1532 if (pVCpu->idCpu == 0)
1533 {
1534 pVM->dbgf.s.cEnabledHwBreakpoints = 0;
1535 pVM->dbgf.s.cEnabledHwIoBreakpoints = 0;
1536
1537 for (uint32_t iBp = 0; iBp < RT_ELEMENTS(pVM->dbgf.s.aHwBreakpoints); iBp++)
1538 {
1539 if (pVM->dbgf.s.aHwBreakpoints[iBp].fEnabled)
1540 {
1541 pVM->dbgf.s.cEnabledHwBreakpoints += 1;
1542 pVM->dbgf.s.cEnabledHwIoBreakpoints += pVM->dbgf.s.aHwBreakpoints[iBp].fType == X86_DR7_RW_IO;
1543 }
1544 }
1545 }
1546
1547 return CPUMRecalcHyperDRx(pVCpu, UINT8_MAX);
1548}
1549
1550
1551/**
1552 * Arms the given breakpoint.
1553 *
1554 * @returns VBox status code.
1555 * @param pUVM The user mode VM handle.
1556 * @param hBp The breakpoint handle to arm.
1557 * @param pBp The internal breakpoint state pointer for the handle.
1558 *
1559 * @thread Any thread.
1560 */
1561static int dbgfR3BpArm(PUVM pUVM, DBGFBP hBp, PDBGFBPINT pBp)
1562{
1563 int rc;
1564 PVM pVM = pUVM->pVM;
1565
1566 Assert(!DBGF_BP_PUB_IS_ENABLED(&pBp->Pub));
1567 switch (DBGF_BP_PUB_GET_TYPE(&pBp->Pub))
1568 {
1569 case DBGFBPTYPE_REG:
1570 {
1571 Assert(pBp->Pub.u.Reg.iReg < RT_ELEMENTS(pVM->dbgf.s.aHwBreakpoints));
1572 PDBGFBPHW pBpHw = &pVM->dbgf.s.aHwBreakpoints[pBp->Pub.u.Reg.iReg];
1573 Assert(pBpHw->hBp == hBp); RT_NOREF(hBp);
1574
1575 dbgfR3BpSetEnabled(pBp, true /*fEnabled*/);
1576 ASMAtomicWriteBool(&pBpHw->fEnabled, true);
1577 rc = VMMR3EmtRendezvous(pVM, VMMEMTRENDEZVOUS_FLAGS_TYPE_ALL_AT_ONCE, dbgfR3BpRegRecalcOnCpu, NULL);
1578 if (RT_FAILURE(rc))
1579 {
1580 ASMAtomicWriteBool(&pBpHw->fEnabled, false);
1581 dbgfR3BpSetEnabled(pBp, false /*fEnabled*/);
1582 }
1583 break;
1584 }
1585 case DBGFBPTYPE_INT3:
1586 {
1587 dbgfR3BpSetEnabled(pBp, true /*fEnabled*/);
1588
1589 /** @todo When we enable the first int3 breakpoint we should do this in an EMT rendezvous
1590 * as the VMX code intercepts #BP only when at least one int3 breakpoint is enabled.
1591 * A racing vCPU might trigger it and forward it to the guest causing panics/crashes/havoc. */
1592 /*
1593 * Save current byte and write the int3 instruction byte.
1594 */
1595 rc = PGMPhysSimpleReadGCPhys(pVM, &pBp->Pub.u.Int3.bOrg, pBp->Pub.u.Int3.PhysAddr, sizeof(pBp->Pub.u.Int3.bOrg));
1596 if (RT_SUCCESS(rc))
1597 {
1598 static const uint8_t s_bInt3 = 0xcc;
1599 rc = PGMPhysSimpleWriteGCPhys(pVM, pBp->Pub.u.Int3.PhysAddr, &s_bInt3, sizeof(s_bInt3));
1600 if (RT_SUCCESS(rc))
1601 {
1602 ASMAtomicIncU32(&pVM->dbgf.s.cEnabledInt3Breakpoints);
1603 Log(("DBGF: Set breakpoint at %RGv (Phys %RGp)\n", pBp->Pub.u.Int3.GCPtr, pBp->Pub.u.Int3.PhysAddr));
1604 }
1605 }
1606
1607 if (RT_FAILURE(rc))
1608 dbgfR3BpSetEnabled(pBp, false /*fEnabled*/);
1609
1610 break;
1611 }
1612 case DBGFBPTYPE_PORT_IO:
1613 case DBGFBPTYPE_MMIO:
1614 rc = VERR_NOT_IMPLEMENTED;
1615 break;
1616 default:
1617 AssertMsgFailedReturn(("Invalid breakpoint type %d\n", DBGF_BP_PUB_GET_TYPE(&pBp->Pub)),
1618 VERR_IPE_NOT_REACHED_DEFAULT_CASE);
1619 }
1620
1621 return rc;
1622}
1623
1624
1625/**
1626 * Disarms the given breakpoint.
1627 *
1628 * @returns VBox status code.
1629 * @param pUVM The user mode VM handle.
1630 * @param hBp The breakpoint handle to disarm.
1631 * @param pBp The internal breakpoint state pointer for the handle.
1632 *
1633 * @thread Any thread.
1634 */
1635static int dbgfR3BpDisarm(PUVM pUVM, DBGFBP hBp, PDBGFBPINT pBp)
1636{
1637 int rc;
1638 PVM pVM = pUVM->pVM;
1639
1640 Assert(DBGF_BP_PUB_IS_ENABLED(&pBp->Pub));
1641 switch (DBGF_BP_PUB_GET_TYPE(&pBp->Pub))
1642 {
1643 case DBGFBPTYPE_REG:
1644 {
1645 Assert(pBp->Pub.u.Reg.iReg < RT_ELEMENTS(pVM->dbgf.s.aHwBreakpoints));
1646 PDBGFBPHW pBpHw = &pVM->dbgf.s.aHwBreakpoints[pBp->Pub.u.Reg.iReg];
1647 Assert(pBpHw->hBp == hBp); RT_NOREF(hBp);
1648
1649 dbgfR3BpSetEnabled(pBp, false /*fEnabled*/);
1650 ASMAtomicWriteBool(&pBpHw->fEnabled, false);
1651 rc = VMMR3EmtRendezvous(pVM, VMMEMTRENDEZVOUS_FLAGS_TYPE_ALL_AT_ONCE, dbgfR3BpRegRecalcOnCpu, NULL);
1652 if (RT_FAILURE(rc))
1653 {
1654 ASMAtomicWriteBool(&pBpHw->fEnabled, true);
1655 dbgfR3BpSetEnabled(pBp, true /*fEnabled*/);
1656 }
1657 break;
1658 }
1659 case DBGFBPTYPE_INT3:
1660 {
1661 /*
1662 * Check that the current byte is the int3 instruction, and restore the original one.
1663 * We currently ignore invalid bytes.
1664 */
1665 uint8_t bCurrent = 0;
1666 rc = PGMPhysSimpleReadGCPhys(pVM, &bCurrent, pBp->Pub.u.Int3.PhysAddr, sizeof(bCurrent));
1667 if ( RT_SUCCESS(rc)
1668 && bCurrent == 0xcc)
1669 {
1670 rc = PGMPhysSimpleWriteGCPhys(pVM, pBp->Pub.u.Int3.PhysAddr, &pBp->Pub.u.Int3.bOrg, sizeof(pBp->Pub.u.Int3.bOrg));
1671 if (RT_SUCCESS(rc))
1672 {
1673 ASMAtomicDecU32(&pVM->dbgf.s.cEnabledInt3Breakpoints);
1674 dbgfR3BpSetEnabled(pBp, false /*fEnabled*/);
1675 Log(("DBGF: Removed breakpoint at %RGv (Phys %RGp)\n", pBp->Pub.u.Int3.GCPtr, pBp->Pub.u.Int3.PhysAddr));
1676 }
1677 }
1678 break;
1679 }
1680 case DBGFBPTYPE_PORT_IO:
1681 case DBGFBPTYPE_MMIO:
1682 rc = VERR_NOT_IMPLEMENTED;
1683 break;
1684 default:
1685 AssertMsgFailedReturn(("Invalid breakpoint type %d\n", DBGF_BP_PUB_GET_TYPE(&pBp->Pub)),
1686 VERR_IPE_NOT_REACHED_DEFAULT_CASE);
1687 }
1688
1689 return rc;
1690}
1691
1692
1693/**
1694 * Creates a new breakpoint owner returning a handle which can be used when setting breakpoints.
1695 *
1696 * @returns VBox status code.
1697 * @retval VERR_DBGF_BP_OWNER_NO_MORE_HANDLES if there are no more free owner handles available.
1698 * @param pUVM The user mode VM handle.
1699 * @param pfnBpHit The R3 callback which is called when a breakpoint with the owner handle is hit.
1700 * @param phBpOwner Where to store the owner handle on success.
1701 *
1702 * @thread Any thread but might defer work to EMT on the first call.
1703 */
1704VMMR3DECL(int) DBGFR3BpOwnerCreate(PUVM pUVM, PFNDBGFBPHIT pfnBpHit, PDBGFBPOWNER phBpOwner)
1705{
1706 /*
1707 * Validate the input.
1708 */
1709 UVM_ASSERT_VALID_EXT_RETURN(pUVM, VERR_INVALID_VM_HANDLE);
1710 AssertPtrReturn(pfnBpHit, VERR_INVALID_PARAMETER);
1711 AssertPtrReturn(phBpOwner, VERR_INVALID_POINTER);
1712
1713 int rc = dbgfR3BpOwnerEnsureInit(pUVM);
1714 AssertRCReturn(rc ,rc);
1715
1716 /* Try to find a free entry in the owner table. */
1717 for (;;)
1718 {
1719 /* Scan the associated bitmap for a free entry. */
1720 int32_t iClr = ASMBitFirstClear(pUVM->dbgf.s.pbmBpOwnersAllocR3, DBGF_BP_OWNER_COUNT_MAX);
1721 if (iClr != -1)
1722 {
1723 /*
1724 * Try to allocate, we could get raced here as well. In that case
1725 * we try again.
1726 */
1727 if (!ASMAtomicBitTestAndSet(pUVM->dbgf.s.pbmBpOwnersAllocR3, iClr))
1728 {
1729 PDBGFBPOWNERINT pBpOwner = &pUVM->dbgf.s.paBpOwnersR3[iClr];
1730 pBpOwner->cRefs = 1;
1731 pBpOwner->pfnBpHitR3 = pfnBpHit;
1732
1733 *phBpOwner = (DBGFBPOWNER)iClr;
1734 return VINF_SUCCESS;
1735 }
1736 /* else Retry with another spot. */
1737 }
1738 else /* no free entry in bitmap, out of entries. */
1739 {
1740 rc = VERR_DBGF_BP_OWNER_NO_MORE_HANDLES;
1741 break;
1742 }
1743 }
1744
1745 return rc;
1746}
1747
1748
1749/**
1750 * Destroys the owner identified by the given handle.
1751 *
1752 * @returns VBox status code.
1753 * @retval VERR_INVALID_HANDLE if the given owner handle is invalid.
1754 * @retval VERR_DBGF_OWNER_BUSY if there are still breakpoints set with the given owner handle.
1755 * @param pUVM The user mode VM handle.
1756 * @param hBpOwner The breakpoint owner handle to destroy.
1757 */
1758VMMR3DECL(int) DBGFR3BpOwnerDestroy(PUVM pUVM, DBGFBPOWNER hBpOwner)
1759{
1760 /*
1761 * Validate the input.
1762 */
1763 UVM_ASSERT_VALID_EXT_RETURN(pUVM, VERR_INVALID_VM_HANDLE);
1764 AssertReturn(hBpOwner != NIL_DBGFBPOWNER, VERR_INVALID_HANDLE);
1765
1766 int rc = dbgfR3BpOwnerEnsureInit(pUVM);
1767 AssertRCReturn(rc ,rc);
1768
1769 PDBGFBPOWNERINT pBpOwner = dbgfR3BpOwnerGetByHnd(pUVM, hBpOwner);
1770 if (RT_LIKELY(pBpOwner))
1771 {
1772 if (ASMAtomicReadU32(&pBpOwner->cRefs) == 1)
1773 {
1774 pBpOwner->pfnBpHitR3 = NULL;
1775 ASMAtomicDecU32(&pBpOwner->cRefs);
1776 ASMAtomicBitClear(pUVM->dbgf.s.pbmBpOwnersAllocR3, hBpOwner);
1777 }
1778 else
1779 rc = VERR_DBGF_OWNER_BUSY;
1780 }
1781 else
1782 rc = VERR_INVALID_HANDLE;
1783
1784 return rc;
1785}
1786
1787
1788/**
1789 * Sets a breakpoint (int 3 based).
1790 *
1791 * @returns VBox status code.
1792 * @param pUVM The user mode VM handle.
1793 * @param idSrcCpu The ID of the virtual CPU used for the
1794 * breakpoint address resolution.
1795 * @param pAddress The address of the breakpoint.
1796 * @param iHitTrigger The hit count at which the breakpoint start triggering.
1797 * Use 0 (or 1) if it's gonna trigger at once.
1798 * @param iHitDisable The hit count which disables the breakpoint.
1799 * Use ~(uint64_t) if it's never gonna be disabled.
1800 * @param phBp Where to store the breakpoint handle on success.
1801 *
1802 * @thread Any thread.
1803 */
1804VMMR3DECL(int) DBGFR3BpSetInt3(PUVM pUVM, VMCPUID idSrcCpu, PCDBGFADDRESS pAddress,
1805 uint64_t iHitTrigger, uint64_t iHitDisable, PDBGFBP phBp)
1806{
1807 return DBGFR3BpSetInt3Ex(pUVM, NIL_DBGFBPOWNER, NULL /*pvUser*/, idSrcCpu, pAddress,
1808 DBGF_BP_F_DEFAULT, iHitTrigger, iHitDisable, phBp);
1809}
1810
1811
1812/**
1813 * Sets a breakpoint (int 3 based) - extended version.
1814 *
1815 * @returns VBox status code.
1816 * @param pUVM The user mode VM handle.
1817 * @param hOwner The owner handle, use NIL_DBGFBPOWNER if no special owner attached.
1818 * @param pvUser Opaque user data to pass in the owner callback.
1819 * @param idSrcCpu The ID of the virtual CPU used for the
1820 * breakpoint address resolution.
1821 * @param pAddress The address of the breakpoint.
1822 * @param fFlags Combination of DBGF_BP_F_XXX.
1823 * @param iHitTrigger The hit count at which the breakpoint start triggering.
1824 * Use 0 (or 1) if it's gonna trigger at once.
1825 * @param iHitDisable The hit count which disables the breakpoint.
1826 * Use ~(uint64_t) if it's never gonna be disabled.
1827 * @param phBp Where to store the breakpoint handle on success.
1828 *
1829 * @thread Any thread.
1830 */
1831VMMR3DECL(int) DBGFR3BpSetInt3Ex(PUVM pUVM, DBGFBPOWNER hOwner, void *pvUser,
1832 VMCPUID idSrcCpu, PCDBGFADDRESS pAddress, uint16_t fFlags,
1833 uint64_t iHitTrigger, uint64_t iHitDisable, PDBGFBP phBp)
1834{
1835 UVM_ASSERT_VALID_EXT_RETURN(pUVM, VERR_INVALID_VM_HANDLE);
1836 AssertReturn(hOwner != NIL_DBGFBPOWNER || pvUser == NULL, VERR_INVALID_PARAMETER);
1837 AssertReturn(DBGFR3AddrIsValid(pUVM, pAddress), VERR_INVALID_PARAMETER);
1838 AssertReturn(iHitTrigger <= iHitDisable, VERR_INVALID_PARAMETER);
1839 AssertPtrReturn(phBp, VERR_INVALID_POINTER);
1840
1841 int rc = dbgfR3BpEnsureInit(pUVM);
1842 AssertRCReturn(rc, rc);
1843
1844 /*
1845 * Translate & save the breakpoint address into a guest-physical address.
1846 */
1847 RTGCPHYS GCPhysBpAddr = NIL_RTGCPHYS;
1848 rc = DBGFR3AddrToPhys(pUVM, idSrcCpu, pAddress, &GCPhysBpAddr);
1849 if (RT_SUCCESS(rc))
1850 {
1851 /*
1852 * The physical address from DBGFR3AddrToPhys() is the start of the page,
1853 * we need the exact byte offset into the page while writing to it in dbgfR3BpInt3Arm().
1854 */
1855 GCPhysBpAddr |= (pAddress->FlatPtr & X86_PAGE_OFFSET_MASK);
1856
1857 PDBGFBPINT pBp = NULL;
1858 DBGFBP hBp = dbgfR3BpGetByAddr(pUVM, DBGFBPTYPE_INT3, pAddress->FlatPtr, &pBp);
1859 if ( hBp != NIL_DBGFBP
1860 && pBp->Pub.u.Int3.PhysAddr == GCPhysBpAddr)
1861 {
1862 rc = VINF_SUCCESS;
1863 if (!DBGF_BP_PUB_IS_ENABLED(&pBp->Pub))
1864 rc = dbgfR3BpArm(pUVM, hBp, pBp);
1865 if (RT_SUCCESS(rc))
1866 {
1867 rc = VINF_DBGF_BP_ALREADY_EXIST;
1868 if (phBp)
1869 *phBp = hBp;
1870 }
1871 return rc;
1872 }
1873
1874 rc = dbgfR3BpAlloc(pUVM, hOwner, pvUser, DBGFBPTYPE_INT3, fFlags, iHitTrigger, iHitDisable, &hBp, &pBp);
1875 if (RT_SUCCESS(rc))
1876 {
1877 pBp->Pub.u.Int3.PhysAddr = GCPhysBpAddr;
1878 pBp->Pub.u.Int3.GCPtr = pAddress->FlatPtr;
1879
1880 /* Add the breakpoint to the lookup tables. */
1881 rc = dbgfR3BpInt3Add(pUVM, hBp, pBp);
1882 if (RT_SUCCESS(rc))
1883 {
1884 /* Enable the breakpoint if requested. */
1885 if (fFlags & DBGF_BP_F_ENABLED)
1886 rc = dbgfR3BpArm(pUVM, hBp, pBp);
1887 if (RT_SUCCESS(rc))
1888 {
1889 *phBp = hBp;
1890 return VINF_SUCCESS;
1891 }
1892
1893 int rc2 = dbgfR3BpInt3Remove(pUVM, hBp, pBp); AssertRC(rc2);
1894 }
1895
1896 dbgfR3BpFree(pUVM, hBp, pBp);
1897 }
1898 }
1899
1900 return rc;
1901}
1902
1903
1904/**
1905 * Sets a register breakpoint.
1906 *
1907 * @returns VBox status code.
1908 * @param pUVM The user mode VM handle.
1909 * @param pAddress The address of the breakpoint.
1910 * @param iHitTrigger The hit count at which the breakpoint start triggering.
1911 * Use 0 (or 1) if it's gonna trigger at once.
1912 * @param iHitDisable The hit count which disables the breakpoint.
1913 * Use ~(uint64_t) if it's never gonna be disabled.
1914 * @param fType The access type (one of the X86_DR7_RW_* defines).
1915 * @param cb The access size - 1,2,4 or 8 (the latter is AMD64 long mode only.
1916 * Must be 1 if fType is X86_DR7_RW_EO.
1917 * @param phBp Where to store the breakpoint handle.
1918 *
1919 * @thread Any thread.
1920 */
1921VMMR3DECL(int) DBGFR3BpSetReg(PUVM pUVM, PCDBGFADDRESS pAddress, uint64_t iHitTrigger,
1922 uint64_t iHitDisable, uint8_t fType, uint8_t cb, PDBGFBP phBp)
1923{
1924 return DBGFR3BpSetRegEx(pUVM, NIL_DBGFBPOWNER, NULL /*pvUser*/, pAddress,
1925 DBGF_BP_F_DEFAULT, iHitTrigger, iHitDisable, fType, cb, phBp);
1926}
1927
1928
1929/**
1930 * Sets a register breakpoint - extended version.
1931 *
1932 * @returns VBox status code.
1933 * @param pUVM The user mode VM handle.
1934 * @param hOwner The owner handle, use NIL_DBGFBPOWNER if no special owner attached.
1935 * @param pvUser Opaque user data to pass in the owner callback.
1936 * @param pAddress The address of the breakpoint.
1937 * @param fFlags Combination of DBGF_BP_F_XXX.
1938 * @param iHitTrigger The hit count at which the breakpoint start triggering.
1939 * Use 0 (or 1) if it's gonna trigger at once.
1940 * @param iHitDisable The hit count which disables the breakpoint.
1941 * Use ~(uint64_t) if it's never gonna be disabled.
1942 * @param fType The access type (one of the X86_DR7_RW_* defines).
1943 * @param cb The access size - 1,2,4 or 8 (the latter is AMD64 long mode only.
1944 * Must be 1 if fType is X86_DR7_RW_EO.
1945 * @param phBp Where to store the breakpoint handle.
1946 *
1947 * @thread Any thread.
1948 */
1949VMMR3DECL(int) DBGFR3BpSetRegEx(PUVM pUVM, DBGFBPOWNER hOwner, void *pvUser,
1950 PCDBGFADDRESS pAddress, uint16_t fFlags,
1951 uint64_t iHitTrigger, uint64_t iHitDisable,
1952 uint8_t fType, uint8_t cb, PDBGFBP phBp)
1953{
1954 UVM_ASSERT_VALID_EXT_RETURN(pUVM, VERR_INVALID_VM_HANDLE);
1955 AssertReturn(hOwner != NIL_DBGFBPOWNER || pvUser == NULL, VERR_INVALID_PARAMETER);
1956 AssertReturn(DBGFR3AddrIsValid(pUVM, pAddress), VERR_INVALID_PARAMETER);
1957 AssertReturn(iHitTrigger <= iHitDisable, VERR_INVALID_PARAMETER);
1958 AssertReturn(cb > 0 && cb <= 8 && RT_IS_POWER_OF_TWO(cb), VERR_INVALID_PARAMETER);
1959 AssertPtrReturn(phBp, VERR_INVALID_POINTER);
1960 switch (fType)
1961 {
1962 case X86_DR7_RW_EO:
1963 if (cb == 1)
1964 break;
1965 AssertMsgFailedReturn(("fType=%#x cb=%d != 1\n", fType, cb), VERR_INVALID_PARAMETER);
1966 case X86_DR7_RW_IO:
1967 case X86_DR7_RW_RW:
1968 case X86_DR7_RW_WO:
1969 break;
1970 default:
1971 AssertMsgFailedReturn(("fType=%#x\n", fType), VERR_INVALID_PARAMETER);
1972 }
1973
1974 int rc = dbgfR3BpEnsureInit(pUVM);
1975 AssertRCReturn(rc, rc);
1976
1977 PDBGFBPINT pBp = NULL;
1978 DBGFBP hBp = dbgfR3BpGetByAddr(pUVM, DBGFBPTYPE_REG, pAddress->FlatPtr, &pBp);
1979 if ( hBp != NIL_DBGFBP
1980 && pBp->Pub.u.Reg.cb == cb
1981 && pBp->Pub.u.Reg.fType == fType)
1982 {
1983 rc = VINF_SUCCESS;
1984 if (!DBGF_BP_PUB_IS_ENABLED(&pBp->Pub))
1985 rc = dbgfR3BpArm(pUVM, hBp, pBp);
1986 if (RT_SUCCESS(rc))
1987 {
1988 rc = VINF_DBGF_BP_ALREADY_EXIST;
1989 if (phBp)
1990 *phBp = hBp;
1991 }
1992 return rc;
1993 }
1994
1995 /* Allocate new breakpoint. */
1996 rc = dbgfR3BpAlloc(pUVM, hOwner, pvUser, DBGFBPTYPE_REG, fFlags,
1997 iHitTrigger, iHitDisable, &hBp, &pBp);
1998 if (RT_SUCCESS(rc))
1999 {
2000 pBp->Pub.u.Reg.GCPtr = pAddress->FlatPtr;
2001 pBp->Pub.u.Reg.fType = fType;
2002 pBp->Pub.u.Reg.cb = cb;
2003 pBp->Pub.u.Reg.iReg = UINT8_MAX;
2004 ASMCompilerBarrier();
2005
2006 /* Assign the proper hardware breakpoint. */
2007 rc = dbgfR3BpRegAssign(pUVM->pVM, hBp, pBp);
2008 if (RT_SUCCESS(rc))
2009 {
2010 /* Arm the breakpoint. */
2011 if (fFlags & DBGF_BP_F_ENABLED)
2012 rc = dbgfR3BpArm(pUVM, hBp, pBp);
2013 if (RT_SUCCESS(rc))
2014 {
2015 if (phBp)
2016 *phBp = hBp;
2017 return VINF_SUCCESS;
2018 }
2019
2020 int rc2 = dbgfR3BpRegRemove(pUVM->pVM, hBp, pBp);
2021 AssertRC(rc2); RT_NOREF(rc2);
2022 }
2023
2024 dbgfR3BpFree(pUVM, hBp, pBp);
2025 }
2026
2027 return rc;
2028}
2029
2030
2031/**
2032 * This is only kept for now to not mess with the debugger implementation at this point,
2033 * recompiler breakpoints are not supported anymore (IEM has some API but it isn't implemented
2034 * and should probably be merged with the DBGF breakpoints).
2035 */
2036VMMR3DECL(int) DBGFR3BpSetREM(PUVM pUVM, PCDBGFADDRESS pAddress, uint64_t iHitTrigger,
2037 uint64_t iHitDisable, PDBGFBP phBp)
2038{
2039 RT_NOREF(pUVM, pAddress, iHitTrigger, iHitDisable, phBp);
2040 return VERR_NOT_SUPPORTED;
2041}
2042
2043
2044/**
2045 * Sets an I/O port breakpoint.
2046 *
2047 * @returns VBox status code.
2048 * @param pUVM The user mode VM handle.
2049 * @param uPort The first I/O port.
2050 * @param cPorts The number of I/O ports, see DBGFBPIOACCESS_XXX.
2051 * @param fAccess The access we want to break on.
2052 * @param iHitTrigger The hit count at which the breakpoint start
2053 * triggering. Use 0 (or 1) if it's gonna trigger at
2054 * once.
2055 * @param iHitDisable The hit count which disables the breakpoint.
2056 * Use ~(uint64_t) if it's never gonna be disabled.
2057 * @param phBp Where to store the breakpoint handle.
2058 *
2059 * @thread Any thread.
2060 */
2061VMMR3DECL(int) DBGFR3BpSetPortIo(PUVM pUVM, RTIOPORT uPort, RTIOPORT cPorts, uint32_t fAccess,
2062 uint64_t iHitTrigger, uint64_t iHitDisable, PDBGFBP phBp)
2063{
2064 return DBGFR3BpSetPortIoEx(pUVM, NIL_DBGFBPOWNER, NULL /*pvUser*/, uPort, cPorts,
2065 fAccess, iHitTrigger, iHitDisable, phBp);
2066}
2067
2068
2069/**
2070 * Sets an I/O port breakpoint - extended version.
2071 *
2072 * @returns VBox status code.
2073 * @param pUVM The user mode VM handle.
2074 * @param hOwner The owner handle, use NIL_DBGFBPOWNER if no special owner attached.
2075 * @param pvUser Opaque user data to pass in the owner callback.
2076 * @param uPort The first I/O port.
2077 * @param cPorts The number of I/O ports, see DBGFBPIOACCESS_XXX.
2078 * @param fAccess The access we want to break on.
2079 * @param iHitTrigger The hit count at which the breakpoint start
2080 * triggering. Use 0 (or 1) if it's gonna trigger at
2081 * once.
2082 * @param iHitDisable The hit count which disables the breakpoint.
2083 * Use ~(uint64_t) if it's never gonna be disabled.
2084 * @param phBp Where to store the breakpoint handle.
2085 *
2086 * @thread Any thread.
2087 */
2088VMMR3DECL(int) DBGFR3BpSetPortIoEx(PUVM pUVM, DBGFBPOWNER hOwner, void *pvUser,
2089 RTIOPORT uPort, RTIOPORT cPorts, uint32_t fAccess,
2090 uint64_t iHitTrigger, uint64_t iHitDisable, PDBGFBP phBp)
2091{
2092 UVM_ASSERT_VALID_EXT_RETURN(pUVM, VERR_INVALID_VM_HANDLE);
2093 AssertReturn(hOwner != NIL_DBGFBPOWNER || pvUser == NULL, VERR_INVALID_PARAMETER);
2094 AssertReturn(!(fAccess & ~DBGFBPIOACCESS_VALID_MASK_PORT_IO), VERR_INVALID_FLAGS);
2095 AssertReturn(fAccess, VERR_INVALID_FLAGS);
2096 AssertReturn(iHitTrigger <= iHitDisable, VERR_INVALID_PARAMETER);
2097 AssertPtrReturn(phBp, VERR_INVALID_POINTER);
2098 AssertReturn(cPorts > 0, VERR_OUT_OF_RANGE);
2099 AssertReturn((RTIOPORT)(uPort + cPorts) < uPort, VERR_OUT_OF_RANGE);
2100
2101 int rc = dbgfR3BpEnsureInit(pUVM);
2102 AssertRCReturn(rc, rc);
2103
2104 return VERR_NOT_IMPLEMENTED;
2105}
2106
2107
2108/**
2109 * Sets a memory mapped I/O breakpoint.
2110 *
2111 * @returns VBox status code.
2112 * @param pUVM The user mode VM handle.
2113 * @param GCPhys The first MMIO address.
2114 * @param cb The size of the MMIO range to break on.
2115 * @param fAccess The access we want to break on.
2116 * @param iHitTrigger The hit count at which the breakpoint start
2117 * triggering. Use 0 (or 1) if it's gonna trigger at
2118 * once.
2119 * @param iHitDisable The hit count which disables the breakpoint.
2120 * Use ~(uint64_t) if it's never gonna be disabled.
2121 * @param phBp Where to store the breakpoint handle.
2122 *
2123 * @thread Any thread.
2124 */
2125VMMR3DECL(int) DBGFR3BpSetMmio(PUVM pUVM, RTGCPHYS GCPhys, uint32_t cb, uint32_t fAccess,
2126 uint64_t iHitTrigger, uint64_t iHitDisable, PDBGFBP phBp)
2127{
2128 return DBGFR3BpSetMmioEx(pUVM, NIL_DBGFBPOWNER, NULL /*pvUser*/, GCPhys, cb, fAccess,
2129 iHitTrigger, iHitDisable, phBp);
2130}
2131
2132
2133/**
2134 * Sets a memory mapped I/O breakpoint - extended version.
2135 *
2136 * @returns VBox status code.
2137 * @param pUVM The user mode VM handle.
2138 * @param hOwner The owner handle, use NIL_DBGFBPOWNER if no special owner attached.
2139 * @param pvUser Opaque user data to pass in the owner callback.
2140 * @param GCPhys The first MMIO address.
2141 * @param cb The size of the MMIO range to break on.
2142 * @param fAccess The access we want to break on.
2143 * @param iHitTrigger The hit count at which the breakpoint start
2144 * triggering. Use 0 (or 1) if it's gonna trigger at
2145 * once.
2146 * @param iHitDisable The hit count which disables the breakpoint.
2147 * Use ~(uint64_t) if it's never gonna be disabled.
2148 * @param phBp Where to store the breakpoint handle.
2149 *
2150 * @thread Any thread.
2151 */
2152VMMR3DECL(int) DBGFR3BpSetMmioEx(PUVM pUVM, DBGFBPOWNER hOwner, void *pvUser,
2153 RTGCPHYS GCPhys, uint32_t cb, uint32_t fAccess,
2154 uint64_t iHitTrigger, uint64_t iHitDisable, PDBGFBP phBp)
2155{
2156 UVM_ASSERT_VALID_EXT_RETURN(pUVM, VERR_INVALID_VM_HANDLE);
2157 AssertReturn(hOwner != NIL_DBGFBPOWNER || pvUser == NULL, VERR_INVALID_PARAMETER);
2158 AssertReturn(!(fAccess & ~DBGFBPIOACCESS_VALID_MASK_MMIO), VERR_INVALID_FLAGS);
2159 AssertReturn(fAccess, VERR_INVALID_FLAGS);
2160 AssertReturn(iHitTrigger <= iHitDisable, VERR_INVALID_PARAMETER);
2161 AssertPtrReturn(phBp, VERR_INVALID_POINTER);
2162 AssertReturn(cb, VERR_OUT_OF_RANGE);
2163 AssertReturn(GCPhys + cb < GCPhys, VERR_OUT_OF_RANGE);
2164
2165 int rc = dbgfR3BpEnsureInit(pUVM);
2166 AssertRCReturn(rc, rc);
2167
2168 return VERR_NOT_IMPLEMENTED;
2169}
2170
2171
2172/**
2173 * Clears a breakpoint.
2174 *
2175 * @returns VBox status code.
2176 * @param pUVM The user mode VM handle.
2177 * @param hBp The handle of the breakpoint which should be removed (cleared).
2178 *
2179 * @thread Any thread.
2180 */
2181VMMR3DECL(int) DBGFR3BpClear(PUVM pUVM, DBGFBP hBp)
2182{
2183 UVM_ASSERT_VALID_EXT_RETURN(pUVM, VERR_INVALID_VM_HANDLE);
2184 AssertReturn(hBp != NIL_DBGFBPOWNER, VERR_INVALID_HANDLE);
2185
2186 PDBGFBPINT pBp = dbgfR3BpGetByHnd(pUVM, hBp);
2187 AssertPtrReturn(pBp, VERR_DBGF_BP_NOT_FOUND);
2188
2189 /* Disarm the breakpoint when it is enabled. */
2190 if (DBGF_BP_PUB_IS_ENABLED(&pBp->Pub))
2191 {
2192 int rc = dbgfR3BpDisarm(pUVM, hBp, pBp);
2193 AssertRC(rc);
2194 }
2195
2196 switch (DBGF_BP_PUB_GET_TYPE(&pBp->Pub))
2197 {
2198 case DBGFBPTYPE_REG:
2199 {
2200 int rc = dbgfR3BpRegRemove(pUVM->pVM, hBp, pBp);
2201 AssertRC(rc);
2202 break;
2203 }
2204 default:
2205 break;
2206 }
2207
2208 dbgfR3BpFree(pUVM, hBp, pBp);
2209 return VINF_SUCCESS;
2210}
2211
2212
2213/**
2214 * Enables a breakpoint.
2215 *
2216 * @returns VBox status code.
2217 * @param pUVM The user mode VM handle.
2218 * @param hBp The handle of the breakpoint which should be enabled.
2219 *
2220 * @thread Any thread.
2221 */
2222VMMR3DECL(int) DBGFR3BpEnable(PUVM pUVM, DBGFBP hBp)
2223{
2224 /*
2225 * Validate the input.
2226 */
2227 UVM_ASSERT_VALID_EXT_RETURN(pUVM, VERR_INVALID_VM_HANDLE);
2228 AssertReturn(hBp != NIL_DBGFBPOWNER, VERR_INVALID_HANDLE);
2229
2230 PDBGFBPINT pBp = dbgfR3BpGetByHnd(pUVM, hBp);
2231 AssertPtrReturn(pBp, VERR_DBGF_BP_NOT_FOUND);
2232
2233 int rc;
2234 if (!DBGF_BP_PUB_IS_ENABLED(&pBp->Pub))
2235 rc = dbgfR3BpArm(pUVM, hBp, pBp);
2236 else
2237 rc = VINF_DBGF_BP_ALREADY_ENABLED;
2238
2239 return rc;
2240}
2241
2242
2243/**
2244 * Disables a breakpoint.
2245 *
2246 * @returns VBox status code.
2247 * @param pUVM The user mode VM handle.
2248 * @param hBp The handle of the breakpoint which should be disabled.
2249 *
2250 * @thread Any thread.
2251 */
2252VMMR3DECL(int) DBGFR3BpDisable(PUVM pUVM, DBGFBP hBp)
2253{
2254 /*
2255 * Validate the input.
2256 */
2257 UVM_ASSERT_VALID_EXT_RETURN(pUVM, VERR_INVALID_VM_HANDLE);
2258 AssertReturn(hBp != NIL_DBGFBPOWNER, VERR_INVALID_HANDLE);
2259
2260 PDBGFBPINT pBp = dbgfR3BpGetByHnd(pUVM, hBp);
2261 AssertPtrReturn(pBp, VERR_DBGF_BP_NOT_FOUND);
2262
2263 int rc;
2264 if (DBGF_BP_PUB_IS_ENABLED(&pBp->Pub))
2265 rc = dbgfR3BpDisarm(pUVM, hBp, pBp);
2266 else
2267 rc = VINF_DBGF_BP_ALREADY_DISABLED;
2268
2269 return rc;
2270}
2271
2272
2273/**
2274 * Enumerate the breakpoints.
2275 *
2276 * @returns VBox status code.
2277 * @param pUVM The user mode VM handle.
2278 * @param pfnCallback The callback function.
2279 * @param pvUser The user argument to pass to the callback.
2280 *
2281 * @thread Any thread.
2282 */
2283VMMR3DECL(int) DBGFR3BpEnum(PUVM pUVM, PFNDBGFBPENUM pfnCallback, void *pvUser)
2284{
2285 UVM_ASSERT_VALID_EXT_RETURN(pUVM, VERR_INVALID_VM_HANDLE);
2286
2287 for (uint32_t idChunk = 0; idChunk < RT_ELEMENTS(pUVM->dbgf.s.aBpChunks); idChunk++)
2288 {
2289 PDBGFBPCHUNKR3 pBpChunk = &pUVM->dbgf.s.aBpChunks[idChunk];
2290
2291 if (pBpChunk->idChunk == DBGF_BP_CHUNK_ID_INVALID)
2292 break; /* Stop here as the first non allocated chunk means there is no one allocated afterwards as well. */
2293
2294 if (pBpChunk->cBpsFree < DBGF_BP_COUNT_PER_CHUNK)
2295 {
2296 /* Scan the bitmap for allocated entries. */
2297 int32_t iAlloc = ASMBitFirstSet(pBpChunk->pbmAlloc, DBGF_BP_COUNT_PER_CHUNK);
2298 if (iAlloc != -1)
2299 {
2300 do
2301 {
2302 DBGFBP hBp = DBGF_BP_HND_CREATE(idChunk, (uint32_t)iAlloc);
2303 PDBGFBPINT pBp = dbgfR3BpGetByHnd(pUVM, hBp);
2304
2305 /* Make a copy of the breakpoints public data to have a consistent view. */
2306 DBGFBPPUB BpPub;
2307 BpPub.cHits = ASMAtomicReadU64((volatile uint64_t *)&pBp->Pub.cHits);
2308 BpPub.iHitTrigger = ASMAtomicReadU64((volatile uint64_t *)&pBp->Pub.iHitTrigger);
2309 BpPub.iHitDisable = ASMAtomicReadU64((volatile uint64_t *)&pBp->Pub.iHitDisable);
2310 BpPub.hOwner = ASMAtomicReadU32((volatile uint32_t *)&pBp->Pub.hOwner);
2311 BpPub.u16Type = ASMAtomicReadU16((volatile uint16_t *)&pBp->Pub.u16Type); /* Actually constant. */
2312 BpPub.fFlags = ASMAtomicReadU16((volatile uint16_t *)&pBp->Pub.fFlags);
2313 memcpy(&BpPub.u, &pBp->Pub.u, sizeof(pBp->Pub.u)); /* Is constant after allocation. */
2314
2315 /* Check if a removal raced us. */
2316 if (ASMBitTest(pBpChunk->pbmAlloc, iAlloc))
2317 {
2318 int rc = pfnCallback(pUVM, pvUser, hBp, &BpPub);
2319 if (RT_FAILURE(rc) || rc == VINF_CALLBACK_RETURN)
2320 return rc;
2321 }
2322
2323 iAlloc = ASMBitNextSet(pBpChunk->pbmAlloc, DBGF_BP_COUNT_PER_CHUNK, iAlloc);
2324 } while (iAlloc != -1);
2325 }
2326 }
2327 }
2328
2329 return VINF_SUCCESS;
2330}
2331
2332
2333/**
2334 * Called whenever a breakpoint event needs to be serviced in ring-3 to decide what to do.
2335 *
2336 * @returns VBox status code.
2337 * @param pVM The cross context VM structure.
2338 * @param pVCpu The vCPU the breakpoint event happened on.
2339 *
2340 * @thread EMT
2341 */
2342VMMR3_INT_DECL(int) DBGFR3BpHit(PVM pVM, PVMCPU pVCpu)
2343{
2344 /* Send it straight into the debugger?. */
2345 if (pVCpu->dbgf.s.fBpInvokeOwnerCallback)
2346 {
2347 DBGFBP hBp = pVCpu->dbgf.s.hBpActive;
2348 PDBGFBPINT pBp = dbgfR3BpGetByHnd(pVM->pUVM, pVCpu->dbgf.s.hBpActive);
2349 AssertReturn(pBp, VERR_DBGF_BP_IPE_9);
2350
2351 /* Resolve owner (can be NIL_DBGFBPOWNER) and invoke callback if there is one. */
2352 PCDBGFBPOWNERINT pBpOwner = dbgfR3BpOwnerGetByHnd(pVM->pUVM, pBp->Pub.hOwner);
2353 if (pBpOwner)
2354 {
2355 VBOXSTRICTRC rcStrict = VINF_SUCCESS;
2356
2357 if (DBGF_BP_PUB_IS_EXEC_BEFORE(&pBp->Pub))
2358 rcStrict = pBpOwner->pfnBpHitR3(pVM, pVCpu->idCpu, pBp->pvUserR3, hBp, &pBp->Pub, DBGF_BP_F_HIT_EXEC_BEFORE);
2359 if (rcStrict == VINF_SUCCESS)
2360 {
2361 uint8_t abInstr[DBGF_BP_INSN_MAX];
2362 RTGCPTR const GCPtrInstr = pVCpu->cpum.GstCtx.rip + pVCpu->cpum.GstCtx.cs.u64Base;
2363 int rc = PGMPhysSimpleReadGCPtr(pVCpu, &abInstr[0], GCPtrInstr, sizeof(abInstr));
2364 AssertRC(rc);
2365 if (RT_SUCCESS(rc))
2366 {
2367 /* Replace the int3 with the original instruction byte. */
2368 abInstr[0] = pBp->Pub.u.Int3.bOrg;
2369 rcStrict = IEMExecOneWithPrefetchedByPC(pVCpu, CPUMCTX2CORE(&pVCpu->cpum.GstCtx), GCPtrInstr, &abInstr[0], sizeof(abInstr));
2370 if ( rcStrict == VINF_SUCCESS
2371 && DBGF_BP_PUB_IS_EXEC_AFTER(&pBp->Pub))
2372 {
2373 VBOXSTRICTRC rcStrict2 = pBpOwner->pfnBpHitR3(pVM, pVCpu->idCpu, pBp->pvUserR3, hBp, &pBp->Pub, DBGF_BP_F_HIT_EXEC_AFTER);
2374 if (rcStrict2 == VINF_SUCCESS)
2375 return VBOXSTRICTRC_VAL(rcStrict);
2376 else if (rcStrict2 != VINF_DBGF_BP_HALT)
2377 return VERR_DBGF_BP_OWNER_CALLBACK_WRONG_STATUS;
2378 }
2379 else
2380 return VBOXSTRICTRC_VAL(rcStrict);
2381 }
2382 }
2383 else if (rcStrict != VINF_DBGF_BP_HALT) /* Guru meditation. */
2384 return VERR_DBGF_BP_OWNER_CALLBACK_WRONG_STATUS;
2385 /* else: Halt in the debugger. */
2386 }
2387 }
2388
2389 return DBGFR3EventBreakpoint(pVM, DBGFEVENT_BREAKPOINT);
2390}
2391
Note: See TracBrowser for help on using the repository browser.

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