VirtualBox

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

Last change on this file since 99377 was 98970, checked in by vboxsync, 21 months ago

VMM: More ARMv8 x86/amd64 separation work, bugref:10385

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