VirtualBox

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

Last change on this file was 103408, checked in by vboxsync, 3 months ago

VMM/DBGFR3Bp.cpp: Remove some redundant conditions (checked at the top that phBp is != NULL), bugref:3409

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 106.4 KB
Line 
1/* $Id: DBGFR3Bp.cpp 103408 2024-02-18 17:46:49Z 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 * @param pUVM The user mode VM handle.
785 * @param hBp The breakpoint handle to free.
786 * @param pBp The internal breakpoint state pointer.
787 */
788static void dbgfR3BpFree(PUVM pUVM, DBGFBP hBp, PDBGFBPINT pBp)
789{
790 uint32_t idChunk = DBGF_BP_HND_GET_CHUNK_ID(hBp);
791 uint32_t idxEntry = DBGF_BP_HND_GET_ENTRY(hBp);
792
793 AssertReturnVoid(idChunk < DBGF_BP_CHUNK_COUNT);
794 AssertReturnVoid(idxEntry < DBGF_BP_COUNT_PER_CHUNK);
795
796 PDBGFBPCHUNKR3 pBpChunk = &pUVM->dbgf.s.aBpChunks[idChunk];
797 AssertPtrReturnVoid(pBpChunk->pbmAlloc);
798 AssertReturnVoid(ASMBitTest(pBpChunk->pbmAlloc, idxEntry));
799
800 /** @todo Need a trip to Ring-0 if an owner is assigned with a Ring-0 part to clear the breakpoint. */
801 int rc = dbgfR3BpOwnerRelease(pUVM, pBp->Pub.hOwner); AssertRC(rc); RT_NOREF(rc);
802 memset(pBp, 0, sizeof(*pBp));
803
804 ASMAtomicBitClear(pBpChunk->pbmAlloc, idxEntry);
805 ASMAtomicIncU32(&pBpChunk->cBpsFree);
806}
807
808
809/**
810 * @callback_method_impl{FNVMMEMTRENDEZVOUS}
811 */
812static DECLCALLBACK(VBOXSTRICTRC) dbgfR3BpL2TblChunkAllocEmtWorker(PVM pVM, PVMCPU pVCpu, void *pvUser)
813{
814 uint32_t idChunk = (uint32_t)(uintptr_t)pvUser;
815
816 VMCPU_ASSERT_EMT(pVCpu);
817 VM_ASSERT_VALID_EXT_RETURN(pVM, VERR_INVALID_VM_HANDLE);
818
819 AssertReturn(idChunk < DBGF_BP_L2_TBL_CHUNK_COUNT, VERR_DBGF_BP_IPE_1);
820
821 PUVM pUVM = pVM->pUVM;
822 PDBGFBPL2TBLCHUNKR3 pL2Chunk = &pUVM->dbgf.s.aBpL2TblChunks[idChunk];
823
824 AssertReturn( pL2Chunk->idChunk == DBGF_BP_L2_IDX_CHUNK_ID_INVALID
825 || pL2Chunk->idChunk == idChunk,
826 VERR_DBGF_BP_IPE_2);
827
828 /*
829 * The initialization will be done on EMT(0). It is possible that multiple
830 * allocation attempts are done when multiple racing non EMT threads try to
831 * allocate a breakpoint and a new chunk needs to be allocated.
832 * Ignore the request and succeed if the chunk is allocated meaning that a
833 * previous rendezvous successfully allocated the chunk.
834 */
835 int rc = VINF_SUCCESS;
836 if ( pVCpu->idCpu == 0
837 && pL2Chunk->idChunk == DBGF_BP_L2_IDX_CHUNK_ID_INVALID)
838 {
839 /* Allocate the bitmap first so we can skip calling into VMMR0 if it fails. */
840 AssertCompile(!(DBGF_BP_L2_TBL_ENTRIES_PER_CHUNK % 64));
841 void *pbmAlloc = RTMemAllocZ(DBGF_BP_L2_TBL_ENTRIES_PER_CHUNK / 8);
842 if (RT_LIKELY(pbmAlloc))
843 {
844 if (!SUPR3IsDriverless())
845 {
846 DBGFBPL2TBLCHUNKALLOCREQ Req;
847 Req.Hdr.u32Magic = SUPVMMR0REQHDR_MAGIC;
848 Req.Hdr.cbReq = sizeof(Req);
849 Req.idChunk = idChunk;
850 Req.pChunkBaseR3 = NULL;
851 rc = VMMR3CallR0Emt(pVM, pVCpu, VMMR0_DO_DBGF_BP_L2_TBL_CHUNK_ALLOC, 0 /*u64Arg*/, &Req.Hdr);
852 if (RT_SUCCESS(rc))
853 pL2Chunk->pL2BaseR3 = (PDBGFBPL2ENTRY)Req.pChunkBaseR3;
854 else
855 AssertLogRelMsgRC(rc, ("VMMR0_DO_DBGF_BP_L2_TBL_CHUNK_ALLOC failed: %Rrc\n", rc));
856 }
857 else
858 {
859 /* Driverless: Do dbgfR0BpL2TblChunkAllocWorker here, ring-3 style. */
860 uint32_t const cbTotal = RT_ALIGN_32(DBGF_BP_L2_TBL_ENTRIES_PER_CHUNK * sizeof(DBGFBPL2ENTRY), HOST_PAGE_SIZE);
861 pL2Chunk->pL2BaseR3 = (PDBGFBPL2ENTRY)RTMemPageAllocZ(cbTotal);
862 AssertLogRelMsgStmt(pL2Chunk->pL2BaseR3, ("cbTotal=%#x\n", cbTotal), rc = VERR_NO_PAGE_MEMORY);
863 }
864 if (RT_SUCCESS(rc))
865 {
866 pL2Chunk->pbmAlloc = (void volatile *)pbmAlloc;
867 pL2Chunk->cFree = DBGF_BP_L2_TBL_ENTRIES_PER_CHUNK;
868 pL2Chunk->idChunk = idChunk;
869 return VINF_SUCCESS;
870 }
871
872 RTMemFree(pbmAlloc);
873 }
874 else
875 rc = VERR_NO_MEMORY;
876 }
877
878 return rc;
879}
880
881
882/**
883 * Tries to allocate the given L2 table chunk which requires an EMT rendezvous.
884 *
885 * @returns VBox status code.
886 * @param pUVM The user mode VM handle.
887 * @param idChunk The chunk to allocate.
888 *
889 * @thread Any thread.
890 */
891DECLINLINE(int) dbgfR3BpL2TblChunkAlloc(PUVM pUVM, uint32_t idChunk)
892{
893 return VMMR3EmtRendezvous(pUVM->pVM, VMMEMTRENDEZVOUS_FLAGS_TYPE_ALL_AT_ONCE, dbgfR3BpL2TblChunkAllocEmtWorker, (void *)(uintptr_t)idChunk);
894}
895
896
897/**
898 * Tries to allocate a new breakpoint of the given type.
899 *
900 * @returns VBox status code.
901 * @param pUVM The user mode VM handle.
902 * @param pidxL2Tbl Where to return the L2 table entry index on success.
903 * @param ppL2TblEntry Where to return the pointer to the L2 table entry on success.
904 *
905 * @thread Any thread.
906 */
907static int dbgfR3BpL2TblEntryAlloc(PUVM pUVM, uint32_t *pidxL2Tbl, PDBGFBPL2ENTRY *ppL2TblEntry)
908{
909 /*
910 * Search for a chunk having a free entry, allocating new chunks
911 * if the encountered ones are full.
912 *
913 * This can be called from multiple threads at the same time so special care
914 * has to be taken to not require any locking here.
915 */
916 for (uint32_t i = 0; i < RT_ELEMENTS(pUVM->dbgf.s.aBpL2TblChunks); i++)
917 {
918 PDBGFBPL2TBLCHUNKR3 pL2Chunk = &pUVM->dbgf.s.aBpL2TblChunks[i];
919
920 uint32_t idChunk = ASMAtomicReadU32(&pL2Chunk->idChunk);
921 if (idChunk == DBGF_BP_L2_IDX_CHUNK_ID_INVALID)
922 {
923 int rc = dbgfR3BpL2TblChunkAlloc(pUVM, i);
924 if (RT_FAILURE(rc))
925 {
926 LogRel(("DBGF/Bp: Allocating new breakpoint L2 lookup table chunk failed with %Rrc\n", rc));
927 break;
928 }
929
930 idChunk = ASMAtomicReadU32(&pL2Chunk->idChunk);
931 Assert(idChunk == i);
932 }
933
934 /** @todo Optimize with some hinting if this turns out to be too slow. */
935 for (;;)
936 {
937 uint32_t cFree = ASMAtomicReadU32(&pL2Chunk->cFree);
938 if (cFree)
939 {
940 /*
941 * Scan the associated bitmap for a free entry, if none can be found another thread
942 * raced us and we go to the next chunk.
943 */
944 int32_t iClr = ASMBitFirstClear(pL2Chunk->pbmAlloc, DBGF_BP_L2_TBL_ENTRIES_PER_CHUNK);
945 if (iClr != -1)
946 {
947 /*
948 * Try to allocate, we could get raced here as well. In that case
949 * we try again.
950 */
951 if (!ASMAtomicBitTestAndSet(pL2Chunk->pbmAlloc, iClr))
952 {
953 /* Success, immediately mark as allocated, initialize the breakpoint state and return. */
954 ASMAtomicDecU32(&pL2Chunk->cFree);
955
956 PDBGFBPL2ENTRY pL2Entry = &pL2Chunk->pL2BaseR3[iClr];
957
958 *pidxL2Tbl = DBGF_BP_L2_IDX_CREATE(idChunk, iClr);
959 *ppL2TblEntry = pL2Entry;
960 return VINF_SUCCESS;
961 }
962 /* else Retry with another spot. */
963 }
964 else /* no free entry in bitmap, go to the next chunk */
965 break;
966 }
967 else /* !cFree, go to the next chunk */
968 break;
969 }
970 }
971
972 return VERR_DBGF_NO_MORE_BP_SLOTS;
973}
974
975
976/**
977 * Frees the given breakpoint handle.
978 *
979 * @param pUVM The user mode VM handle.
980 * @param idxL2Tbl The L2 table index to free.
981 * @param pL2TblEntry The L2 table entry pointer to free.
982 */
983static void dbgfR3BpL2TblEntryFree(PUVM pUVM, uint32_t idxL2Tbl, PDBGFBPL2ENTRY pL2TblEntry)
984{
985 uint32_t idChunk = DBGF_BP_L2_IDX_GET_CHUNK_ID(idxL2Tbl);
986 uint32_t idxEntry = DBGF_BP_L2_IDX_GET_ENTRY(idxL2Tbl);
987
988 AssertReturnVoid(idChunk < DBGF_BP_L2_TBL_CHUNK_COUNT);
989 AssertReturnVoid(idxEntry < DBGF_BP_L2_TBL_ENTRIES_PER_CHUNK);
990
991 PDBGFBPL2TBLCHUNKR3 pL2Chunk = &pUVM->dbgf.s.aBpL2TblChunks[idChunk];
992 AssertPtrReturnVoid(pL2Chunk->pbmAlloc);
993 AssertReturnVoid(ASMBitTest(pL2Chunk->pbmAlloc, idxEntry));
994
995 memset(pL2TblEntry, 0, sizeof(*pL2TblEntry));
996
997 ASMAtomicBitClear(pL2Chunk->pbmAlloc, idxEntry);
998 ASMAtomicIncU32(&pL2Chunk->cFree);
999}
1000
1001
1002/**
1003 * Sets the enabled flag of the given breakpoint to the given value.
1004 *
1005 * @param pBp The breakpoint to set the state.
1006 * @param fEnabled Enabled status.
1007 */
1008DECLINLINE(void) dbgfR3BpSetEnabled(PDBGFBPINT pBp, bool fEnabled)
1009{
1010 if (fEnabled)
1011 pBp->Pub.fFlags |= DBGF_BP_F_ENABLED;
1012 else
1013 pBp->Pub.fFlags &= ~DBGF_BP_F_ENABLED;
1014}
1015
1016
1017/**
1018 * Assigns a hardware breakpoint state to the given register breakpoint.
1019 *
1020 * @returns VBox status code.
1021 * @param pVM The cross-context VM structure pointer.
1022 * @param hBp The breakpoint handle to assign.
1023 * @param pBp The internal breakpoint state.
1024 *
1025 * @thread Any thread.
1026 */
1027static int dbgfR3BpRegAssign(PVM pVM, DBGFBP hBp, PDBGFBPINT pBp)
1028{
1029 AssertReturn(pBp->Pub.u.Reg.iReg == UINT8_MAX, VERR_DBGF_BP_IPE_3);
1030
1031 for (uint8_t i = 0; i < RT_ELEMENTS(pVM->dbgf.s.aHwBreakpoints); i++)
1032 {
1033 PDBGFBPHW pHwBp = &pVM->dbgf.s.aHwBreakpoints[i];
1034
1035 AssertCompileSize(DBGFBP, sizeof(uint32_t));
1036 if (ASMAtomicCmpXchgU32(&pHwBp->hBp, hBp, NIL_DBGFBP))
1037 {
1038 pHwBp->GCPtr = pBp->Pub.u.Reg.GCPtr;
1039 pHwBp->fType = pBp->Pub.u.Reg.fType;
1040 pHwBp->cb = pBp->Pub.u.Reg.cb;
1041 pHwBp->fEnabled = DBGF_BP_PUB_IS_ENABLED(&pBp->Pub);
1042
1043 pBp->Pub.u.Reg.iReg = i;
1044 return VINF_SUCCESS;
1045 }
1046 }
1047
1048 return VERR_DBGF_NO_MORE_BP_SLOTS;
1049}
1050
1051
1052/**
1053 * Removes the assigned hardware breakpoint state from the given register breakpoint.
1054 *
1055 * @returns VBox status code.
1056 * @param pVM The cross-context VM structure pointer.
1057 * @param hBp The breakpoint handle to remove.
1058 * @param pBp The internal breakpoint state.
1059 *
1060 * @thread Any thread.
1061 */
1062static int dbgfR3BpRegRemove(PVM pVM, DBGFBP hBp, PDBGFBPINT pBp)
1063{
1064 AssertReturn(pBp->Pub.u.Reg.iReg < RT_ELEMENTS(pVM->dbgf.s.aHwBreakpoints), VERR_DBGF_BP_IPE_3);
1065
1066 PDBGFBPHW pHwBp = &pVM->dbgf.s.aHwBreakpoints[pBp->Pub.u.Reg.iReg];
1067 AssertReturn(pHwBp->hBp == hBp, VERR_DBGF_BP_IPE_4);
1068 AssertReturn(!pHwBp->fEnabled, VERR_DBGF_BP_IPE_5);
1069
1070 pHwBp->GCPtr = 0;
1071 pHwBp->fType = 0;
1072 pHwBp->cb = 0;
1073 ASMCompilerBarrier();
1074
1075 ASMAtomicWriteU32(&pHwBp->hBp, NIL_DBGFBP);
1076 return VINF_SUCCESS;
1077}
1078
1079
1080/**
1081 * Returns the pointer to the L2 table entry from the given index.
1082 *
1083 * @returns Current context pointer to the L2 table entry or NULL if the provided index value is invalid.
1084 * @param pUVM The user mode VM handle.
1085 * @param idxL2 The L2 table index to resolve.
1086 *
1087 * @note The content of the resolved L2 table entry is not validated!.
1088 */
1089DECLINLINE(PDBGFBPL2ENTRY) dbgfR3BpL2GetByIdx(PUVM pUVM, uint32_t idxL2)
1090{
1091 uint32_t idChunk = DBGF_BP_L2_IDX_GET_CHUNK_ID(idxL2);
1092 uint32_t idxEntry = DBGF_BP_L2_IDX_GET_ENTRY(idxL2);
1093
1094 AssertReturn(idChunk < DBGF_BP_L2_TBL_CHUNK_COUNT, NULL);
1095 AssertReturn(idxEntry < DBGF_BP_L2_TBL_ENTRIES_PER_CHUNK, NULL);
1096
1097 PDBGFBPL2TBLCHUNKR3 pL2Chunk = &pUVM->dbgf.s.aBpL2TblChunks[idChunk];
1098 AssertPtrReturn(pL2Chunk->pbmAlloc, NULL);
1099 AssertReturn(ASMBitTest(pL2Chunk->pbmAlloc, idxEntry), NULL);
1100
1101 return &pL2Chunk->CTX_SUFF(pL2Base)[idxEntry];
1102}
1103
1104
1105/**
1106 * Creates a binary search tree with the given root and leaf nodes.
1107 *
1108 * @returns VBox status code.
1109 * @param pUVM The user mode VM handle.
1110 * @param idxL1 The index into the L1 table where the created tree should be linked into.
1111 * @param u32EntryOld The old entry in the L1 table used to compare with in the atomic update.
1112 * @param hBpRoot The root node DBGF handle to assign.
1113 * @param GCPtrRoot The root nodes GC pointer to use as a key.
1114 * @param hBpLeaf The leafs node DBGF handle to assign.
1115 * @param GCPtrLeaf The leafs node GC pointer to use as a key.
1116 */
1117static int dbgfR3BpInt3L2BstCreate(PUVM pUVM, uint32_t idxL1, uint32_t u32EntryOld,
1118 DBGFBP hBpRoot, RTGCUINTPTR GCPtrRoot,
1119 DBGFBP hBpLeaf, RTGCUINTPTR GCPtrLeaf)
1120{
1121 AssertReturn(GCPtrRoot != GCPtrLeaf, VERR_DBGF_BP_IPE_9);
1122 Assert(DBGF_BP_INT3_L1_IDX_EXTRACT_FROM_ADDR(GCPtrRoot) == DBGF_BP_INT3_L1_IDX_EXTRACT_FROM_ADDR(GCPtrLeaf));
1123
1124 /* Allocate two nodes. */
1125 uint32_t idxL2Root = 0;
1126 PDBGFBPL2ENTRY pL2Root = NULL;
1127 int rc = dbgfR3BpL2TblEntryAlloc(pUVM, &idxL2Root, &pL2Root);
1128 if (RT_SUCCESS(rc))
1129 {
1130 uint32_t idxL2Leaf = 0;
1131 PDBGFBPL2ENTRY pL2Leaf = NULL;
1132 rc = dbgfR3BpL2TblEntryAlloc(pUVM, &idxL2Leaf, &pL2Leaf);
1133 if (RT_SUCCESS(rc))
1134 {
1135 dbgfBpL2TblEntryInit(pL2Leaf, hBpLeaf, GCPtrLeaf, DBGF_BP_L2_ENTRY_IDX_END, DBGF_BP_L2_ENTRY_IDX_END, 0 /*iDepth*/);
1136 if (GCPtrLeaf < GCPtrRoot)
1137 dbgfBpL2TblEntryInit(pL2Root, hBpRoot, GCPtrRoot, idxL2Leaf, DBGF_BP_L2_ENTRY_IDX_END, 0 /*iDepth*/);
1138 else
1139 dbgfBpL2TblEntryInit(pL2Root, hBpRoot, GCPtrRoot, DBGF_BP_L2_ENTRY_IDX_END, idxL2Leaf, 0 /*iDepth*/);
1140
1141 uint32_t const u32Entry = DBGF_BP_INT3_L1_ENTRY_CREATE_L2_IDX(idxL2Root);
1142 if (ASMAtomicCmpXchgU32(&pUVM->dbgf.s.paBpLocL1R3[idxL1], u32Entry, u32EntryOld))
1143 return VINF_SUCCESS;
1144
1145 /* The L1 entry has changed due to another thread racing us during insertion, free nodes and try again. */
1146 dbgfR3BpL2TblEntryFree(pUVM, idxL2Leaf, pL2Leaf);
1147 rc = VINF_TRY_AGAIN;
1148 }
1149
1150 dbgfR3BpL2TblEntryFree(pUVM, idxL2Root, pL2Root);
1151 }
1152
1153 return rc;
1154}
1155
1156
1157/**
1158 * Inserts the given breakpoint handle into an existing binary search tree.
1159 *
1160 * @returns VBox status code.
1161 * @param pUVM The user mode VM handle.
1162 * @param idxL2Root The index of the tree root in the L2 table.
1163 * @param hBp The node DBGF handle to insert.
1164 * @param GCPtr The nodes GC pointer to use as a key.
1165 */
1166static int dbgfR3BpInt2L2BstNodeInsert(PUVM pUVM, uint32_t idxL2Root, DBGFBP hBp, RTGCUINTPTR GCPtr)
1167{
1168 GCPtr = DBGF_BP_INT3_L2_KEY_EXTRACT_FROM_ADDR(GCPtr);
1169
1170 /* Allocate a new node first. */
1171 uint32_t idxL2Nd = 0;
1172 PDBGFBPL2ENTRY pL2Nd = NULL;
1173 int rc = dbgfR3BpL2TblEntryAlloc(pUVM, &idxL2Nd, &pL2Nd);
1174 if (RT_SUCCESS(rc))
1175 {
1176 /* Walk the tree and find the correct node to insert to. */
1177 PDBGFBPL2ENTRY pL2Entry = dbgfR3BpL2GetByIdx(pUVM, idxL2Root);
1178 while (RT_LIKELY(pL2Entry))
1179 {
1180 /* Make a copy of the entry. */
1181 DBGFBPL2ENTRY L2Entry;
1182 L2Entry.u64GCPtrKeyAndBpHnd1 = ASMAtomicReadU64(&pL2Entry->u64GCPtrKeyAndBpHnd1);
1183 L2Entry.u64LeftRightIdxDepthBpHnd2 = ASMAtomicReadU64(&pL2Entry->u64LeftRightIdxDepthBpHnd2);
1184
1185 RTGCUINTPTR GCPtrL2Entry = DBGF_BP_L2_ENTRY_GET_GCPTR(L2Entry.u64GCPtrKeyAndBpHnd1);
1186 AssertBreak(GCPtr != GCPtrL2Entry);
1187
1188 /* Not found, get to the next level. */
1189 uint32_t idxL2Next = GCPtr < GCPtrL2Entry
1190 ? DBGF_BP_L2_ENTRY_GET_IDX_LEFT(L2Entry.u64LeftRightIdxDepthBpHnd2)
1191 : DBGF_BP_L2_ENTRY_GET_IDX_RIGHT(L2Entry.u64LeftRightIdxDepthBpHnd2);
1192 if (idxL2Next == DBGF_BP_L2_ENTRY_IDX_END)
1193 {
1194 /* Insert the new node here. */
1195 dbgfBpL2TblEntryInit(pL2Nd, hBp, GCPtr, DBGF_BP_L2_ENTRY_IDX_END, DBGF_BP_L2_ENTRY_IDX_END, 0 /*iDepth*/);
1196 if (GCPtr < GCPtrL2Entry)
1197 dbgfBpL2TblEntryUpdateLeft(pL2Entry, idxL2Next, 0 /*iDepth*/);
1198 else
1199 dbgfBpL2TblEntryUpdateRight(pL2Entry, idxL2Next, 0 /*iDepth*/);
1200 return VINF_SUCCESS;
1201 }
1202
1203 pL2Entry = dbgfR3BpL2GetByIdx(pUVM, idxL2Next);
1204 }
1205
1206 dbgfR3BpL2TblEntryFree(pUVM, idxL2Nd, pL2Nd);
1207 rc = VERR_DBGF_BP_L2_LOOKUP_FAILED;
1208 }
1209
1210 return rc;
1211}
1212
1213
1214/**
1215 * Adds the given breakpoint handle keyed with the GC pointer to the proper L2 binary search tree
1216 * possibly creating a new tree.
1217 *
1218 * @returns VBox status code.
1219 * @param pUVM The user mode VM handle.
1220 * @param idxL1 The index into the L1 table the breakpoint uses.
1221 * @param hBp The breakpoint handle which is to be added.
1222 * @param GCPtr The GC pointer the breakpoint is keyed with.
1223 */
1224static int dbgfR3BpInt3L2BstNodeAdd(PUVM pUVM, uint32_t idxL1, DBGFBP hBp, RTGCUINTPTR GCPtr)
1225{
1226 int rc = RTSemFastMutexRequest(pUVM->dbgf.s.hMtxBpL2Wr); AssertRC(rc);
1227
1228 uint32_t u32Entry = ASMAtomicReadU32(&pUVM->dbgf.s.paBpLocL1R3[idxL1]); /* Re-read, could get raced by a remove operation. */
1229 uint8_t u8Type = DBGF_BP_INT3_L1_ENTRY_GET_TYPE(u32Entry);
1230 if (u8Type == DBGF_BP_INT3_L1_ENTRY_TYPE_BP_HND)
1231 {
1232 /* Create a new search tree, gather the necessary information first. */
1233 DBGFBP hBp2 = DBGF_BP_INT3_L1_ENTRY_GET_BP_HND(u32Entry);
1234 PDBGFBPINT pBp2 = dbgfR3BpGetByHnd(pUVM, hBp2);
1235 AssertStmt(RT_VALID_PTR(pBp2), rc = VERR_DBGF_BP_IPE_7);
1236 if (RT_SUCCESS(rc))
1237 rc = dbgfR3BpInt3L2BstCreate(pUVM, idxL1, u32Entry, hBp, GCPtr, hBp2, pBp2->Pub.u.Int3.GCPtr);
1238 }
1239 else if (u8Type == DBGF_BP_INT3_L1_ENTRY_TYPE_L2_IDX)
1240 rc = dbgfR3BpInt2L2BstNodeInsert(pUVM, DBGF_BP_INT3_L1_ENTRY_GET_L2_IDX(u32Entry), hBp, GCPtr);
1241
1242 int rc2 = RTSemFastMutexRelease(pUVM->dbgf.s.hMtxBpL2Wr); AssertRC(rc2);
1243 return rc;
1244}
1245
1246
1247/**
1248 * Gets the leftmost from the given tree node start index.
1249 *
1250 * @returns VBox status code.
1251 * @param pUVM The user mode VM handle.
1252 * @param idxL2Start The start index to walk from.
1253 * @param pidxL2Leftmost Where to store the L2 table index of the leftmost entry.
1254 * @param ppL2NdLeftmost Where to store the pointer to the leftmost L2 table entry.
1255 * @param pidxL2NdLeftParent Where to store the L2 table index of the leftmost entries parent.
1256 * @param ppL2NdLeftParent Where to store the pointer to the leftmost L2 table entries parent.
1257 */
1258static int dbgfR33BpInt3BstGetLeftmostEntryFromNode(PUVM pUVM, uint32_t idxL2Start,
1259 uint32_t *pidxL2Leftmost, PDBGFBPL2ENTRY *ppL2NdLeftmost,
1260 uint32_t *pidxL2NdLeftParent, PDBGFBPL2ENTRY *ppL2NdLeftParent)
1261{
1262 uint32_t idxL2Parent = DBGF_BP_L2_ENTRY_IDX_END;
1263 PDBGFBPL2ENTRY pL2NdParent = NULL;
1264
1265 for (;;)
1266 {
1267 PDBGFBPL2ENTRY pL2Entry = dbgfR3BpL2GetByIdx(pUVM, idxL2Start);
1268 AssertPtr(pL2Entry);
1269
1270 uint32_t idxL2Left = DBGF_BP_L2_ENTRY_GET_IDX_LEFT(pL2Entry->u64LeftRightIdxDepthBpHnd2);
1271 if (idxL2Start == DBGF_BP_L2_ENTRY_IDX_END)
1272 {
1273 *pidxL2Leftmost = idxL2Start;
1274 *ppL2NdLeftmost = pL2Entry;
1275 *pidxL2NdLeftParent = idxL2Parent;
1276 *ppL2NdLeftParent = pL2NdParent;
1277 break;
1278 }
1279
1280 idxL2Parent = idxL2Start;
1281 idxL2Start = idxL2Left;
1282 pL2NdParent = pL2Entry;
1283 }
1284
1285 return VINF_SUCCESS;
1286}
1287
1288
1289/**
1290 * Removes the given node rearranging the tree.
1291 *
1292 * @returns VBox status code.
1293 * @param pUVM The user mode VM handle.
1294 * @param idxL1 The index into the L1 table pointing to the binary search tree containing the node.
1295 * @param idxL2Root The L2 table index where the tree root is located.
1296 * @param idxL2Nd The node index to remove.
1297 * @param pL2Nd The L2 table entry to remove.
1298 * @param idxL2NdParent The parents index, can be DBGF_BP_L2_ENTRY_IDX_END if the root is about to be removed.
1299 * @param pL2NdParent The parents L2 table entry, can be NULL if the root is about to be removed.
1300 * @param fLeftChild Flag whether the node is the left child of the parent or the right one.
1301 */
1302static int dbgfR3BpInt3BstNodeRemove(PUVM pUVM, uint32_t idxL1, uint32_t idxL2Root,
1303 uint32_t idxL2Nd, PDBGFBPL2ENTRY pL2Nd,
1304 uint32_t idxL2NdParent, PDBGFBPL2ENTRY pL2NdParent,
1305 bool fLeftChild)
1306{
1307 /*
1308 * If there are only two nodes remaining the tree will get destroyed and the
1309 * L1 entry will be converted to the direct handle type.
1310 */
1311 uint32_t idxL2Left = DBGF_BP_L2_ENTRY_GET_IDX_LEFT(pL2Nd->u64LeftRightIdxDepthBpHnd2);
1312 uint32_t idxL2Right = DBGF_BP_L2_ENTRY_GET_IDX_RIGHT(pL2Nd->u64LeftRightIdxDepthBpHnd2);
1313
1314 Assert(idxL2NdParent != DBGF_BP_L2_ENTRY_IDX_END || !pL2NdParent); RT_NOREF(idxL2NdParent);
1315 uint32_t idxL2ParentNew = DBGF_BP_L2_ENTRY_IDX_END;
1316 if (idxL2Right == DBGF_BP_L2_ENTRY_IDX_END)
1317 idxL2ParentNew = idxL2Left;
1318 else
1319 {
1320 /* Find the leftmost entry of the right subtree and move it to the to be removed nodes location in the tree. */
1321 PDBGFBPL2ENTRY pL2NdLeftmostParent = NULL;
1322 PDBGFBPL2ENTRY pL2NdLeftmost = NULL;
1323 uint32_t idxL2NdLeftmostParent = DBGF_BP_L2_ENTRY_IDX_END;
1324 uint32_t idxL2Leftmost = DBGF_BP_L2_ENTRY_IDX_END;
1325 int rc = dbgfR33BpInt3BstGetLeftmostEntryFromNode(pUVM, idxL2Right, &idxL2Leftmost ,&pL2NdLeftmost,
1326 &idxL2NdLeftmostParent, &pL2NdLeftmostParent);
1327 AssertRCReturn(rc, rc);
1328
1329 if (pL2NdLeftmostParent)
1330 {
1331 /* Rearrange the leftmost entries parents pointer. */
1332 dbgfBpL2TblEntryUpdateLeft(pL2NdLeftmostParent, DBGF_BP_L2_ENTRY_GET_IDX_RIGHT(pL2NdLeftmost->u64LeftRightIdxDepthBpHnd2), 0 /*iDepth*/);
1333 dbgfBpL2TblEntryUpdateRight(pL2NdLeftmost, idxL2Right, 0 /*iDepth*/);
1334 }
1335
1336 dbgfBpL2TblEntryUpdateLeft(pL2NdLeftmost, idxL2Left, 0 /*iDepth*/);
1337
1338 /* Update the remove nodes parent to point to the new node. */
1339 idxL2ParentNew = idxL2Leftmost;
1340 }
1341
1342 if (pL2NdParent)
1343 {
1344 /* Asssign the new L2 index to proper parents left or right pointer. */
1345 if (fLeftChild)
1346 dbgfBpL2TblEntryUpdateLeft(pL2NdParent, idxL2ParentNew, 0 /*iDepth*/);
1347 else
1348 dbgfBpL2TblEntryUpdateRight(pL2NdParent, idxL2ParentNew, 0 /*iDepth*/);
1349 }
1350 else
1351 {
1352 /* The root node is removed, set the new root in the L1 table. */
1353 Assert(idxL2ParentNew != DBGF_BP_L2_ENTRY_IDX_END);
1354 idxL2Root = idxL2ParentNew;
1355 ASMAtomicXchgU32(&pUVM->dbgf.s.paBpLocL1R3[idxL1], DBGF_BP_INT3_L1_ENTRY_CREATE_L2_IDX(idxL2Left));
1356 }
1357
1358 /* Free the node. */
1359 dbgfR3BpL2TblEntryFree(pUVM, idxL2Nd, pL2Nd);
1360
1361 /*
1362 * Check whether the old/new root is the only node remaining and convert the L1
1363 * table entry to a direct breakpoint handle one in that case.
1364 */
1365 pL2Nd = dbgfR3BpL2GetByIdx(pUVM, idxL2Root);
1366 AssertPtr(pL2Nd);
1367 if ( DBGF_BP_L2_ENTRY_GET_IDX_LEFT(pL2Nd->u64LeftRightIdxDepthBpHnd2) == DBGF_BP_L2_ENTRY_IDX_END
1368 && DBGF_BP_L2_ENTRY_GET_IDX_RIGHT(pL2Nd->u64LeftRightIdxDepthBpHnd2) == DBGF_BP_L2_ENTRY_IDX_END)
1369 {
1370 DBGFBP hBp = DBGF_BP_L2_ENTRY_GET_BP_HND(pL2Nd->u64GCPtrKeyAndBpHnd1, pL2Nd->u64LeftRightIdxDepthBpHnd2);
1371 dbgfR3BpL2TblEntryFree(pUVM, idxL2Root, pL2Nd);
1372 ASMAtomicXchgU32(&pUVM->dbgf.s.paBpLocL1R3[idxL1], DBGF_BP_INT3_L1_ENTRY_CREATE_BP_HND(hBp));
1373 }
1374
1375 return VINF_SUCCESS;
1376}
1377
1378
1379/**
1380 * Removes the given breakpoint handle keyed with the GC pointer from the L2 binary search tree
1381 * pointed to by the given L2 root index.
1382 *
1383 * @returns VBox status code.
1384 * @param pUVM The user mode VM handle.
1385 * @param idxL1 The index into the L1 table pointing to the binary search tree.
1386 * @param idxL2Root The L2 table index where the tree root is located.
1387 * @param hBp The breakpoint handle which is to be removed.
1388 * @param GCPtr The GC pointer the breakpoint is keyed with.
1389 */
1390static int dbgfR3BpInt3L2BstRemove(PUVM pUVM, uint32_t idxL1, uint32_t idxL2Root, DBGFBP hBp, RTGCUINTPTR GCPtr)
1391{
1392 GCPtr = DBGF_BP_INT3_L2_KEY_EXTRACT_FROM_ADDR(GCPtr);
1393
1394 int rc = RTSemFastMutexRequest(pUVM->dbgf.s.hMtxBpL2Wr); AssertRC(rc);
1395
1396 uint32_t idxL2Cur = idxL2Root;
1397 uint32_t idxL2Parent = DBGF_BP_L2_ENTRY_IDX_END;
1398 bool fLeftChild = false;
1399 PDBGFBPL2ENTRY pL2EntryParent = NULL;
1400 for (;;)
1401 {
1402 PDBGFBPL2ENTRY pL2Entry = dbgfR3BpL2GetByIdx(pUVM, idxL2Cur);
1403 AssertPtr(pL2Entry);
1404
1405 /* Check whether this node is to be removed.. */
1406 RTGCUINTPTR GCPtrL2Entry = DBGF_BP_L2_ENTRY_GET_GCPTR(pL2Entry->u64GCPtrKeyAndBpHnd1);
1407 if (GCPtrL2Entry == GCPtr)
1408 {
1409 Assert(DBGF_BP_L2_ENTRY_GET_BP_HND(pL2Entry->u64GCPtrKeyAndBpHnd1, pL2Entry->u64LeftRightIdxDepthBpHnd2) == hBp); RT_NOREF(hBp);
1410
1411 rc = dbgfR3BpInt3BstNodeRemove(pUVM, idxL1, idxL2Root, idxL2Cur, pL2Entry, idxL2Parent, pL2EntryParent, fLeftChild);
1412 break;
1413 }
1414
1415 pL2EntryParent = pL2Entry;
1416 idxL2Parent = idxL2Cur;
1417
1418 if (GCPtrL2Entry < GCPtr)
1419 {
1420 fLeftChild = true;
1421 idxL2Cur = DBGF_BP_L2_ENTRY_GET_IDX_LEFT(pL2Entry->u64LeftRightIdxDepthBpHnd2);
1422 }
1423 else
1424 {
1425 fLeftChild = false;
1426 idxL2Cur = DBGF_BP_L2_ENTRY_GET_IDX_RIGHT(pL2Entry->u64LeftRightIdxDepthBpHnd2);
1427 }
1428
1429 AssertBreakStmt(idxL2Cur != DBGF_BP_L2_ENTRY_IDX_END, rc = VERR_DBGF_BP_L2_LOOKUP_FAILED);
1430 }
1431
1432 int rc2 = RTSemFastMutexRelease(pUVM->dbgf.s.hMtxBpL2Wr); AssertRC(rc2);
1433
1434 return rc;
1435}
1436
1437
1438/**
1439 * Adds the given int3 breakpoint to the appropriate lookup tables.
1440 *
1441 * @returns VBox status code.
1442 * @param pUVM The user mode VM handle.
1443 * @param hBp The breakpoint handle to add.
1444 * @param pBp The internal breakpoint state.
1445 */
1446static int dbgfR3BpInt3Add(PUVM pUVM, DBGFBP hBp, PDBGFBPINT pBp)
1447{
1448 AssertReturn(DBGF_BP_PUB_GET_TYPE(&pBp->Pub) == DBGFBPTYPE_INT3, VERR_DBGF_BP_IPE_3);
1449
1450 int rc = VINF_SUCCESS;
1451 uint16_t idxL1 = DBGF_BP_INT3_L1_IDX_EXTRACT_FROM_ADDR(pBp->Pub.u.Int3.GCPtr);
1452 uint8_t cTries = 16;
1453
1454 while (cTries--)
1455 {
1456 uint32_t u32Entry = ASMAtomicReadU32(&pUVM->dbgf.s.paBpLocL1R3[idxL1]);
1457 if (u32Entry == DBGF_BP_INT3_L1_ENTRY_TYPE_NULL)
1458 {
1459 /*
1460 * No breakpoint assigned so far for this entry, create an entry containing
1461 * the direct breakpoint handle and try to exchange it atomically.
1462 */
1463 u32Entry = DBGF_BP_INT3_L1_ENTRY_CREATE_BP_HND(hBp);
1464 if (ASMAtomicCmpXchgU32(&pUVM->dbgf.s.paBpLocL1R3[idxL1], u32Entry, DBGF_BP_INT3_L1_ENTRY_TYPE_NULL))
1465 break;
1466 }
1467 else
1468 {
1469 rc = dbgfR3BpInt3L2BstNodeAdd(pUVM, idxL1, hBp, pBp->Pub.u.Int3.GCPtr);
1470 if (rc != VINF_TRY_AGAIN)
1471 break;
1472 }
1473 }
1474
1475 if ( RT_SUCCESS(rc)
1476 && !cTries) /* Too much contention, abort with an error. */
1477 rc = VERR_DBGF_BP_INT3_ADD_TRIES_REACHED;
1478
1479 return rc;
1480}
1481
1482
1483/**
1484 * Adds the given port I/O breakpoint to the appropriate lookup tables.
1485 *
1486 * @returns VBox status code.
1487 * @param pUVM The user mode VM handle.
1488 * @param hBp The breakpoint handle to add.
1489 * @param pBp The internal breakpoint state.
1490 */
1491static int dbgfR3BpPortIoAdd(PUVM pUVM, DBGFBP hBp, PDBGFBPINT pBp)
1492{
1493 AssertReturn(DBGF_BP_PUB_GET_TYPE(&pBp->Pub) == DBGFBPTYPE_PORT_IO, VERR_DBGF_BP_IPE_3);
1494
1495 uint16_t uPortExcl = pBp->Pub.u.PortIo.uPort + pBp->Pub.u.PortIo.cPorts;
1496 uint32_t u32Entry = DBGF_BP_INT3_L1_ENTRY_CREATE_BP_HND(hBp);
1497 for (uint16_t idxPort = pBp->Pub.u.PortIo.uPort; idxPort < uPortExcl; idxPort++)
1498 {
1499 bool fXchg = ASMAtomicCmpXchgU32(&pUVM->dbgf.s.paBpLocPortIoR3[idxPort], u32Entry, DBGF_BP_INT3_L1_ENTRY_TYPE_NULL);
1500 if (!fXchg)
1501 {
1502 /* Something raced us, so roll back the other registrations. */
1503 while (idxPort > pBp->Pub.u.PortIo.uPort)
1504 {
1505 fXchg = ASMAtomicCmpXchgU32(&pUVM->dbgf.s.paBpLocPortIoR3[idxPort], DBGF_BP_INT3_L1_ENTRY_TYPE_NULL, u32Entry);
1506 Assert(fXchg); RT_NOREF(fXchg);
1507 }
1508
1509 return VERR_DBGF_BP_INT3_ADD_TRIES_REACHED; /** @todo New status code */
1510 }
1511 }
1512
1513 return VINF_SUCCESS;
1514}
1515
1516
1517/**
1518 * Get a breakpoint give by address.
1519 *
1520 * @returns The breakpoint handle on success or NIL_DBGFBP if not found.
1521 * @param pUVM The user mode VM handle.
1522 * @param enmType The breakpoint type.
1523 * @param GCPtr The breakpoint address.
1524 * @param ppBp Where to store the pointer to the internal breakpoint state on success, optional.
1525 */
1526static DBGFBP dbgfR3BpGetByAddr(PUVM pUVM, DBGFBPTYPE enmType, RTGCUINTPTR GCPtr, PDBGFBPINT *ppBp)
1527{
1528 DBGFBP hBp = NIL_DBGFBP;
1529
1530 switch (enmType)
1531 {
1532 case DBGFBPTYPE_REG:
1533 {
1534 PVM pVM = pUVM->pVM;
1535 VM_ASSERT_VALID_EXT_RETURN(pVM, NIL_DBGFBP);
1536
1537 for (uint32_t i = 0; i < RT_ELEMENTS(pVM->dbgf.s.aHwBreakpoints); i++)
1538 {
1539 PDBGFBPHW pHwBp = &pVM->dbgf.s.aHwBreakpoints[i];
1540
1541 AssertCompileSize(DBGFBP, sizeof(uint32_t));
1542 DBGFBP hBpTmp = ASMAtomicReadU32(&pHwBp->hBp);
1543 if ( pHwBp->GCPtr == GCPtr
1544 && hBpTmp != NIL_DBGFBP)
1545 {
1546 hBp = hBpTmp;
1547 break;
1548 }
1549 }
1550 break;
1551 }
1552
1553 case DBGFBPTYPE_INT3:
1554 {
1555 const uint16_t idxL1 = DBGF_BP_INT3_L1_IDX_EXTRACT_FROM_ADDR(GCPtr);
1556 const uint32_t u32L1Entry = ASMAtomicReadU32(&pUVM->dbgf.s.CTX_SUFF(paBpLocL1)[idxL1]);
1557
1558 if (u32L1Entry != DBGF_BP_INT3_L1_ENTRY_TYPE_NULL)
1559 {
1560 uint8_t u8Type = DBGF_BP_INT3_L1_ENTRY_GET_TYPE(u32L1Entry);
1561 if (u8Type == DBGF_BP_INT3_L1_ENTRY_TYPE_BP_HND)
1562 hBp = DBGF_BP_INT3_L1_ENTRY_GET_BP_HND(u32L1Entry);
1563 else if (u8Type == DBGF_BP_INT3_L1_ENTRY_TYPE_L2_IDX)
1564 {
1565 RTGCUINTPTR GCPtrKey = DBGF_BP_INT3_L2_KEY_EXTRACT_FROM_ADDR(GCPtr);
1566 PDBGFBPL2ENTRY pL2Nd = dbgfR3BpL2GetByIdx(pUVM, DBGF_BP_INT3_L1_ENTRY_GET_L2_IDX(u32L1Entry));
1567
1568 for (;;)
1569 {
1570 AssertPtr(pL2Nd);
1571
1572 RTGCUINTPTR GCPtrL2Entry = DBGF_BP_L2_ENTRY_GET_GCPTR(pL2Nd->u64GCPtrKeyAndBpHnd1);
1573 if (GCPtrKey == GCPtrL2Entry)
1574 {
1575 hBp = DBGF_BP_L2_ENTRY_GET_BP_HND(pL2Nd->u64GCPtrKeyAndBpHnd1, pL2Nd->u64LeftRightIdxDepthBpHnd2);
1576 break;
1577 }
1578
1579 /* Not found, get to the next level. */
1580 uint32_t idxL2Next = GCPtrKey < GCPtrL2Entry
1581 ? DBGF_BP_L2_ENTRY_GET_IDX_LEFT(pL2Nd->u64LeftRightIdxDepthBpHnd2)
1582 : DBGF_BP_L2_ENTRY_GET_IDX_RIGHT(pL2Nd->u64LeftRightIdxDepthBpHnd2);
1583 /* Address not found if the entry denotes the end. */
1584 if (idxL2Next == DBGF_BP_L2_ENTRY_IDX_END)
1585 break;
1586
1587 pL2Nd = dbgfR3BpL2GetByIdx(pUVM, idxL2Next);
1588 }
1589 }
1590 }
1591 break;
1592 }
1593
1594 default:
1595 AssertMsgFailed(("enmType=%d\n", enmType));
1596 break;
1597 }
1598
1599 if ( hBp != NIL_DBGFBP
1600 && ppBp)
1601 *ppBp = dbgfR3BpGetByHnd(pUVM, hBp);
1602 return hBp;
1603}
1604
1605
1606/**
1607 * Get a port I/O breakpoint given by the range.
1608 *
1609 * @returns The breakpoint handle on success or NIL_DBGF if not found.
1610 * @param pUVM The user mode VM handle.
1611 * @param uPort First port in the range.
1612 * @param cPorts Number of ports in the range.
1613 * @param ppBp Where to store the pointer to the internal breakpoint state on success, optional.
1614 */
1615static DBGFBP dbgfR3BpPortIoGetByRange(PUVM pUVM, RTIOPORT uPort, RTIOPORT cPorts, PDBGFBPINT *ppBp)
1616{
1617 DBGFBP hBp = NIL_DBGFBP;
1618
1619 for (RTIOPORT idxPort = uPort; idxPort < uPort + cPorts; idxPort++)
1620 {
1621 const uint32_t u32Entry = ASMAtomicReadU32(&pUVM->dbgf.s.CTX_SUFF(paBpLocPortIo)[idxPort]);
1622 if (u32Entry != DBGF_BP_INT3_L1_ENTRY_TYPE_NULL)
1623 {
1624 hBp = DBGF_BP_INT3_L1_ENTRY_GET_BP_HND(u32Entry);
1625 break;
1626 }
1627 }
1628
1629 if ( hBp != NIL_DBGFBP
1630 && ppBp)
1631 *ppBp = dbgfR3BpGetByHnd(pUVM, hBp);
1632 return hBp;
1633}
1634
1635
1636/**
1637 * @callback_method_impl{FNVMMEMTRENDEZVOUS}
1638 */
1639static DECLCALLBACK(VBOXSTRICTRC) dbgfR3BpInt3RemoveEmtWorker(PVM pVM, PVMCPU pVCpu, void *pvUser)
1640{
1641 DBGFBP hBp = (DBGFBP)(uintptr_t)pvUser;
1642
1643 VMCPU_ASSERT_EMT(pVCpu);
1644 VM_ASSERT_VALID_EXT_RETURN(pVM, VERR_INVALID_VM_HANDLE);
1645
1646 PUVM pUVM = pVM->pUVM;
1647 PDBGFBPINT pBp = dbgfR3BpGetByHnd(pUVM, hBp);
1648 AssertPtrReturn(pBp, VERR_DBGF_BP_IPE_8);
1649
1650 int rc = VINF_SUCCESS;
1651 if (pVCpu->idCpu == 0)
1652 {
1653 uint16_t idxL1 = DBGF_BP_INT3_L1_IDX_EXTRACT_FROM_ADDR(pBp->Pub.u.Int3.GCPtr);
1654 uint32_t u32Entry = ASMAtomicReadU32(&pUVM->dbgf.s.paBpLocL1R3[idxL1]);
1655 AssertReturn(u32Entry != DBGF_BP_INT3_L1_ENTRY_TYPE_NULL, VERR_DBGF_BP_IPE_6);
1656
1657 uint8_t u8Type = DBGF_BP_INT3_L1_ENTRY_GET_TYPE(u32Entry);
1658 if (u8Type == DBGF_BP_INT3_L1_ENTRY_TYPE_BP_HND)
1659 {
1660 /* Single breakpoint, just exchange atomically with the null value. */
1661 if (!ASMAtomicCmpXchgU32(&pUVM->dbgf.s.paBpLocL1R3[idxL1], DBGF_BP_INT3_L1_ENTRY_TYPE_NULL, u32Entry))
1662 {
1663 /*
1664 * A breakpoint addition must have raced us converting the L1 entry to an L2 index type, re-read
1665 * and remove the node from the created binary search tree.
1666 *
1667 * This works because after the entry was converted to an L2 index it can only be converted back
1668 * to a direct handle by removing one or more nodes which always goes through the fast mutex
1669 * protecting the L2 table. Likewise adding a new breakpoint requires grabbing the mutex as well
1670 * so there is serialization here and the node can be removed safely without having to worry about
1671 * concurrent tree modifications.
1672 */
1673 u32Entry = ASMAtomicReadU32(&pUVM->dbgf.s.paBpLocL1R3[idxL1]);
1674 AssertReturn(DBGF_BP_INT3_L1_ENTRY_GET_TYPE(u32Entry) == DBGF_BP_INT3_L1_ENTRY_TYPE_L2_IDX, VERR_DBGF_BP_IPE_9);
1675
1676 rc = dbgfR3BpInt3L2BstRemove(pUVM, idxL1, DBGF_BP_INT3_L1_ENTRY_GET_L2_IDX(u32Entry),
1677 hBp, pBp->Pub.u.Int3.GCPtr);
1678 }
1679 }
1680 else if (u8Type == DBGF_BP_INT3_L1_ENTRY_TYPE_L2_IDX)
1681 rc = dbgfR3BpInt3L2BstRemove(pUVM, idxL1, DBGF_BP_INT3_L1_ENTRY_GET_L2_IDX(u32Entry),
1682 hBp, pBp->Pub.u.Int3.GCPtr);
1683 }
1684
1685 return rc;
1686}
1687
1688
1689/**
1690 * Removes the given int3 breakpoint from all lookup tables.
1691 *
1692 * @returns VBox status code.
1693 * @param pUVM The user mode VM handle.
1694 * @param hBp The breakpoint handle to remove.
1695 * @param pBp The internal breakpoint state.
1696 */
1697static int dbgfR3BpInt3Remove(PUVM pUVM, DBGFBP hBp, PDBGFBPINT pBp)
1698{
1699 AssertReturn(DBGF_BP_PUB_GET_TYPE(&pBp->Pub) == DBGFBPTYPE_INT3, VERR_DBGF_BP_IPE_3);
1700
1701 /*
1702 * This has to be done by an EMT rendezvous in order to not have an EMT traversing
1703 * any L2 trees while it is being removed.
1704 */
1705 return VMMR3EmtRendezvous(pUVM->pVM, VMMEMTRENDEZVOUS_FLAGS_TYPE_ALL_AT_ONCE, dbgfR3BpInt3RemoveEmtWorker, (void *)(uintptr_t)hBp);
1706}
1707
1708
1709/**
1710 * @callback_method_impl{FNVMMEMTRENDEZVOUS}
1711 */
1712static DECLCALLBACK(VBOXSTRICTRC) dbgfR3BpPortIoRemoveEmtWorker(PVM pVM, PVMCPU pVCpu, void *pvUser)
1713{
1714 DBGFBP hBp = (DBGFBP)(uintptr_t)pvUser;
1715
1716 VMCPU_ASSERT_EMT(pVCpu);
1717 VM_ASSERT_VALID_EXT_RETURN(pVM, VERR_INVALID_VM_HANDLE);
1718
1719 PUVM pUVM = pVM->pUVM;
1720 PDBGFBPINT pBp = dbgfR3BpGetByHnd(pUVM, hBp);
1721 AssertPtrReturn(pBp, VERR_DBGF_BP_IPE_8);
1722
1723 int rc = VINF_SUCCESS;
1724 if (pVCpu->idCpu == 0)
1725 {
1726 /*
1727 * Remove the whole range, there shouldn't be any other breakpoint configured for this range as this is not
1728 * allowed right now.
1729 */
1730 uint16_t uPortExcl = pBp->Pub.u.PortIo.uPort + pBp->Pub.u.PortIo.cPorts;
1731 for (uint16_t idxPort = pBp->Pub.u.PortIo.uPort; idxPort < uPortExcl; idxPort++)
1732 {
1733 uint32_t u32Entry = ASMAtomicReadU32(&pUVM->dbgf.s.paBpLocPortIoR3[idxPort]);
1734 AssertReturn(u32Entry != DBGF_BP_INT3_L1_ENTRY_TYPE_NULL, VERR_DBGF_BP_IPE_6);
1735
1736 uint8_t u8Type = DBGF_BP_INT3_L1_ENTRY_GET_TYPE(u32Entry);
1737 AssertReturn(u8Type == DBGF_BP_INT3_L1_ENTRY_TYPE_BP_HND, VERR_DBGF_BP_IPE_7);
1738
1739 bool fXchg = ASMAtomicCmpXchgU32(&pUVM->dbgf.s.paBpLocPortIoR3[idxPort], DBGF_BP_INT3_L1_ENTRY_TYPE_NULL, u32Entry);
1740 Assert(fXchg); RT_NOREF(fXchg);
1741 }
1742 }
1743
1744 return rc;
1745}
1746
1747
1748/**
1749 * Removes the given port I/O breakpoint from all lookup tables.
1750 *
1751 * @returns VBox status code.
1752 * @param pUVM The user mode VM handle.
1753 * @param hBp The breakpoint handle to remove.
1754 * @param pBp The internal breakpoint state.
1755 */
1756static int dbgfR3BpPortIoRemove(PUVM pUVM, DBGFBP hBp, PDBGFBPINT pBp)
1757{
1758 AssertReturn(DBGF_BP_PUB_GET_TYPE(&pBp->Pub) == DBGFBPTYPE_PORT_IO, VERR_DBGF_BP_IPE_3);
1759
1760 /*
1761 * This has to be done by an EMT rendezvous in order to not have an EMT accessing
1762 * the breakpoint while it is removed.
1763 */
1764 return VMMR3EmtRendezvous(pUVM->pVM, VMMEMTRENDEZVOUS_FLAGS_TYPE_ALL_AT_ONCE, dbgfR3BpPortIoRemoveEmtWorker, (void *)(uintptr_t)hBp);
1765}
1766
1767
1768/**
1769 * @callback_method_impl{FNVMMEMTRENDEZVOUS}
1770 */
1771static DECLCALLBACK(VBOXSTRICTRC) dbgfR3BpRegRecalcOnCpu(PVM pVM, PVMCPU pVCpu, void *pvUser)
1772{
1773 RT_NOREF(pvUser);
1774
1775#if defined(VBOX_VMM_TARGET_ARMV8)
1776 RT_NOREF(pVM, pVCpu);
1777 AssertReleaseFailed();
1778 return VERR_NOT_IMPLEMENTED;
1779#else
1780 /*
1781 * CPU 0 updates the enabled hardware breakpoint counts.
1782 */
1783 if (pVCpu->idCpu == 0)
1784 {
1785 pVM->dbgf.s.cEnabledHwBreakpoints = 0;
1786 pVM->dbgf.s.cEnabledHwIoBreakpoints = 0;
1787
1788 for (uint32_t iBp = 0; iBp < RT_ELEMENTS(pVM->dbgf.s.aHwBreakpoints); iBp++)
1789 {
1790 if (pVM->dbgf.s.aHwBreakpoints[iBp].fEnabled)
1791 {
1792 pVM->dbgf.s.cEnabledHwBreakpoints += 1;
1793 pVM->dbgf.s.cEnabledHwIoBreakpoints += pVM->dbgf.s.aHwBreakpoints[iBp].fType == X86_DR7_RW_IO;
1794 }
1795 }
1796 }
1797
1798 return CPUMRecalcHyperDRx(pVCpu, UINT8_MAX);
1799#endif
1800}
1801
1802
1803/**
1804 * Arms the given breakpoint.
1805 *
1806 * @returns VBox status code.
1807 * @param pUVM The user mode VM handle.
1808 * @param hBp The breakpoint handle to arm.
1809 * @param pBp The internal breakpoint state pointer for the handle.
1810 *
1811 * @thread Any thread.
1812 */
1813static int dbgfR3BpArm(PUVM pUVM, DBGFBP hBp, PDBGFBPINT pBp)
1814{
1815 int rc;
1816 PVM pVM = pUVM->pVM;
1817
1818 Assert(!DBGF_BP_PUB_IS_ENABLED(&pBp->Pub));
1819 switch (DBGF_BP_PUB_GET_TYPE(&pBp->Pub))
1820 {
1821 case DBGFBPTYPE_REG:
1822 {
1823 Assert(pBp->Pub.u.Reg.iReg < RT_ELEMENTS(pVM->dbgf.s.aHwBreakpoints));
1824 PDBGFBPHW pBpHw = &pVM->dbgf.s.aHwBreakpoints[pBp->Pub.u.Reg.iReg];
1825 Assert(pBpHw->hBp == hBp); RT_NOREF(hBp);
1826
1827 dbgfR3BpSetEnabled(pBp, true /*fEnabled*/);
1828 ASMAtomicWriteBool(&pBpHw->fEnabled, true);
1829 rc = VMMR3EmtRendezvous(pVM, VMMEMTRENDEZVOUS_FLAGS_TYPE_ALL_AT_ONCE, dbgfR3BpRegRecalcOnCpu, NULL);
1830 if (RT_FAILURE(rc))
1831 {
1832 ASMAtomicWriteBool(&pBpHw->fEnabled, false);
1833 dbgfR3BpSetEnabled(pBp, false /*fEnabled*/);
1834 }
1835 break;
1836 }
1837 case DBGFBPTYPE_INT3:
1838 {
1839 dbgfR3BpSetEnabled(pBp, true /*fEnabled*/);
1840
1841 /** @todo When we enable the first int3 breakpoint we should do this in an EMT rendezvous
1842 * as the VMX code intercepts #BP only when at least one int3 breakpoint is enabled.
1843 * A racing vCPU might trigger it and forward it to the guest causing panics/crashes/havoc. */
1844 /*
1845 * Save current byte and write the int3 instruction byte.
1846 */
1847 rc = PGMPhysSimpleReadGCPhys(pVM, &pBp->Pub.u.Int3.bOrg, pBp->Pub.u.Int3.PhysAddr, sizeof(pBp->Pub.u.Int3.bOrg));
1848 if (RT_SUCCESS(rc))
1849 {
1850 static const uint8_t s_bInt3 = 0xcc;
1851 rc = PGMPhysSimpleWriteGCPhys(pVM, pBp->Pub.u.Int3.PhysAddr, &s_bInt3, sizeof(s_bInt3));
1852 if (RT_SUCCESS(rc))
1853 {
1854 ASMAtomicIncU32(&pVM->dbgf.s.cEnabledInt3Breakpoints);
1855 Log(("DBGF: Set breakpoint at %RGv (Phys %RGp)\n", pBp->Pub.u.Int3.GCPtr, pBp->Pub.u.Int3.PhysAddr));
1856 }
1857 }
1858
1859 if (RT_FAILURE(rc))
1860 dbgfR3BpSetEnabled(pBp, false /*fEnabled*/);
1861
1862 break;
1863 }
1864 case DBGFBPTYPE_PORT_IO:
1865 {
1866 dbgfR3BpSetEnabled(pBp, true /*fEnabled*/);
1867 ASMAtomicIncU32(&pUVM->dbgf.s.cPortIoBps);
1868 IOMR3NotifyBreakpointCountChange(pVM, true /*fPortIo*/, false /*fMmio*/);
1869 rc = VINF_SUCCESS;
1870 break;
1871 }
1872 case DBGFBPTYPE_MMIO:
1873 rc = VERR_NOT_IMPLEMENTED;
1874 break;
1875 default:
1876 AssertMsgFailedReturn(("Invalid breakpoint type %d\n", DBGF_BP_PUB_GET_TYPE(&pBp->Pub)),
1877 VERR_IPE_NOT_REACHED_DEFAULT_CASE);
1878 }
1879
1880 return rc;
1881}
1882
1883
1884/**
1885 * Disarms the given breakpoint.
1886 *
1887 * @returns VBox status code.
1888 * @param pUVM The user mode VM handle.
1889 * @param hBp The breakpoint handle to disarm.
1890 * @param pBp The internal breakpoint state pointer for the handle.
1891 *
1892 * @thread Any thread.
1893 */
1894static int dbgfR3BpDisarm(PUVM pUVM, DBGFBP hBp, PDBGFBPINT pBp)
1895{
1896 int rc;
1897 PVM pVM = pUVM->pVM;
1898
1899 Assert(DBGF_BP_PUB_IS_ENABLED(&pBp->Pub));
1900 switch (DBGF_BP_PUB_GET_TYPE(&pBp->Pub))
1901 {
1902 case DBGFBPTYPE_REG:
1903 {
1904 Assert(pBp->Pub.u.Reg.iReg < RT_ELEMENTS(pVM->dbgf.s.aHwBreakpoints));
1905 PDBGFBPHW pBpHw = &pVM->dbgf.s.aHwBreakpoints[pBp->Pub.u.Reg.iReg];
1906 Assert(pBpHw->hBp == hBp); RT_NOREF(hBp);
1907
1908 dbgfR3BpSetEnabled(pBp, false /*fEnabled*/);
1909 ASMAtomicWriteBool(&pBpHw->fEnabled, false);
1910 rc = VMMR3EmtRendezvous(pVM, VMMEMTRENDEZVOUS_FLAGS_TYPE_ALL_AT_ONCE, dbgfR3BpRegRecalcOnCpu, NULL);
1911 if (RT_FAILURE(rc))
1912 {
1913 ASMAtomicWriteBool(&pBpHw->fEnabled, true);
1914 dbgfR3BpSetEnabled(pBp, true /*fEnabled*/);
1915 }
1916 break;
1917 }
1918 case DBGFBPTYPE_INT3:
1919 {
1920 /*
1921 * Check that the current byte is the int3 instruction, and restore the original one.
1922 * We currently ignore invalid bytes.
1923 */
1924 uint8_t bCurrent = 0;
1925 rc = PGMPhysSimpleReadGCPhys(pVM, &bCurrent, pBp->Pub.u.Int3.PhysAddr, sizeof(bCurrent));
1926 if ( RT_SUCCESS(rc)
1927 && bCurrent == 0xcc)
1928 {
1929 rc = PGMPhysSimpleWriteGCPhys(pVM, pBp->Pub.u.Int3.PhysAddr, &pBp->Pub.u.Int3.bOrg, sizeof(pBp->Pub.u.Int3.bOrg));
1930 if (RT_SUCCESS(rc))
1931 {
1932 ASMAtomicDecU32(&pVM->dbgf.s.cEnabledInt3Breakpoints);
1933 dbgfR3BpSetEnabled(pBp, false /*fEnabled*/);
1934 Log(("DBGF: Removed breakpoint at %RGv (Phys %RGp)\n", pBp->Pub.u.Int3.GCPtr, pBp->Pub.u.Int3.PhysAddr));
1935 }
1936 }
1937 break;
1938 }
1939 case DBGFBPTYPE_PORT_IO:
1940 {
1941 dbgfR3BpSetEnabled(pBp, false /*fEnabled*/);
1942 uint32_t cPortIoBps = ASMAtomicDecU32(&pUVM->dbgf.s.cPortIoBps);
1943 if (!cPortIoBps) /** @todo Need to gather all EMTs to not have a stray EMT accessing BP data when it might go away. */
1944 IOMR3NotifyBreakpointCountChange(pVM, false /*fPortIo*/, false /*fMmio*/);
1945 rc = VINF_SUCCESS;
1946 break;
1947 }
1948 case DBGFBPTYPE_MMIO:
1949 rc = VERR_NOT_IMPLEMENTED;
1950 break;
1951 default:
1952 AssertMsgFailedReturn(("Invalid breakpoint type %d\n", DBGF_BP_PUB_GET_TYPE(&pBp->Pub)),
1953 VERR_IPE_NOT_REACHED_DEFAULT_CASE);
1954 }
1955
1956 return rc;
1957}
1958
1959
1960/**
1961 * Worker for DBGFR3BpHit() differnetiating on the breakpoint type.
1962 *
1963 * @returns Strict VBox status code.
1964 * @param pVM The cross context VM structure.
1965 * @param pVCpu The vCPU the breakpoint event happened on.
1966 * @param hBp The breakpoint handle.
1967 * @param pBp The breakpoint data.
1968 * @param pBpOwner The breakpoint owner data.
1969 *
1970 * @thread EMT
1971 */
1972static VBOXSTRICTRC dbgfR3BpHit(PVM pVM, PVMCPU pVCpu, DBGFBP hBp, PDBGFBPINT pBp, PCDBGFBPOWNERINT pBpOwner)
1973{
1974 VBOXSTRICTRC rcStrict = VINF_SUCCESS;
1975
1976 switch (DBGF_BP_PUB_GET_TYPE(&pBp->Pub))
1977 {
1978 case DBGFBPTYPE_REG:
1979 case DBGFBPTYPE_INT3:
1980 {
1981 if (DBGF_BP_PUB_IS_EXEC_BEFORE(&pBp->Pub))
1982 rcStrict = pBpOwner->pfnBpHitR3(pVM, pVCpu->idCpu, pBp->pvUserR3, hBp, &pBp->Pub, DBGF_BP_F_HIT_EXEC_BEFORE);
1983 if (rcStrict == VINF_SUCCESS)
1984 {
1985 uint8_t abInstr[DBGF_BP_INSN_MAX];
1986 RTGCPTR const GCPtrInstr = CPUMGetGuestFlatPC(pVCpu);
1987 int rc = PGMPhysSimpleReadGCPtr(pVCpu, &abInstr[0], GCPtrInstr, sizeof(abInstr));
1988 AssertRC(rc);
1989 if (RT_SUCCESS(rc))
1990 {
1991 /* Replace the int3 with the original instruction byte. */
1992 abInstr[0] = pBp->Pub.u.Int3.bOrg;
1993 rcStrict = IEMExecOneWithPrefetchedByPC(pVCpu, GCPtrInstr, &abInstr[0], sizeof(abInstr));
1994 if ( rcStrict == VINF_SUCCESS
1995 && DBGF_BP_PUB_IS_EXEC_AFTER(&pBp->Pub))
1996 {
1997 VBOXSTRICTRC rcStrict2 = pBpOwner->pfnBpHitR3(pVM, pVCpu->idCpu, pBp->pvUserR3, hBp, &pBp->Pub,
1998 DBGF_BP_F_HIT_EXEC_AFTER);
1999 if (rcStrict2 == VINF_SUCCESS)
2000 return VBOXSTRICTRC_VAL(rcStrict);
2001 if (rcStrict2 != VINF_DBGF_BP_HALT)
2002 return VERR_DBGF_BP_OWNER_CALLBACK_WRONG_STATUS;
2003 }
2004 else
2005 return VBOXSTRICTRC_VAL(rcStrict);
2006 }
2007 }
2008 break;
2009 }
2010 case DBGFBPTYPE_PORT_IO:
2011 case DBGFBPTYPE_MMIO:
2012 {
2013 pVCpu->dbgf.s.fBpIoActive = false;
2014 rcStrict = pBpOwner->pfnBpIoHitR3(pVM, pVCpu->idCpu, pBp->pvUserR3, hBp, &pBp->Pub,
2015 pVCpu->dbgf.s.fBpIoBefore
2016 ? DBGF_BP_F_HIT_EXEC_BEFORE
2017 : DBGF_BP_F_HIT_EXEC_AFTER,
2018 pVCpu->dbgf.s.fBpIoAccess, pVCpu->dbgf.s.uBpIoAddress,
2019 pVCpu->dbgf.s.uBpIoValue);
2020
2021 break;
2022 }
2023 default:
2024 AssertMsgFailedReturn(("Invalid breakpoint type %d\n", DBGF_BP_PUB_GET_TYPE(&pBp->Pub)),
2025 VERR_IPE_NOT_REACHED_DEFAULT_CASE);
2026 }
2027
2028 return rcStrict;
2029}
2030
2031
2032/**
2033 * Creates a new breakpoint owner returning a handle which can be used when setting breakpoints.
2034 *
2035 * @returns VBox status code.
2036 * @retval VERR_DBGF_BP_OWNER_NO_MORE_HANDLES if there are no more free owner handles available.
2037 * @param pUVM The user mode VM handle.
2038 * @param pfnBpHit The R3 callback which is called when a breakpoint with the owner handle is hit.
2039 * @param pfnBpIoHit The R3 callback which is called when a I/O breakpoint with the owner handle is hit.
2040 * @param phBpOwner Where to store the owner handle on success.
2041 *
2042 * @thread Any thread but might defer work to EMT on the first call.
2043 */
2044VMMR3DECL(int) DBGFR3BpOwnerCreate(PUVM pUVM, PFNDBGFBPHIT pfnBpHit, PFNDBGFBPIOHIT pfnBpIoHit, PDBGFBPOWNER phBpOwner)
2045{
2046 /*
2047 * Validate the input.
2048 */
2049 UVM_ASSERT_VALID_EXT_RETURN(pUVM, VERR_INVALID_VM_HANDLE);
2050 AssertReturn(pfnBpHit || pfnBpIoHit, VERR_INVALID_PARAMETER);
2051 AssertPtrReturn(phBpOwner, VERR_INVALID_POINTER);
2052
2053 int rc = dbgfR3BpOwnerEnsureInit(pUVM);
2054 AssertRCReturn(rc ,rc);
2055
2056 /* Try to find a free entry in the owner table. */
2057 for (;;)
2058 {
2059 /* Scan the associated bitmap for a free entry. */
2060 int32_t iClr = ASMBitFirstClear(pUVM->dbgf.s.pbmBpOwnersAllocR3, DBGF_BP_OWNER_COUNT_MAX);
2061 if (iClr != -1)
2062 {
2063 /*
2064 * Try to allocate, we could get raced here as well. In that case
2065 * we try again.
2066 */
2067 if (!ASMAtomicBitTestAndSet(pUVM->dbgf.s.pbmBpOwnersAllocR3, iClr))
2068 {
2069 PDBGFBPOWNERINT pBpOwner = &pUVM->dbgf.s.paBpOwnersR3[iClr];
2070 pBpOwner->cRefs = 1;
2071 pBpOwner->pfnBpHitR3 = pfnBpHit;
2072 pBpOwner->pfnBpIoHitR3 = pfnBpIoHit;
2073
2074 *phBpOwner = (DBGFBPOWNER)iClr;
2075 return VINF_SUCCESS;
2076 }
2077 /* else Retry with another spot. */
2078 }
2079 else /* no free entry in bitmap, out of entries. */
2080 {
2081 rc = VERR_DBGF_BP_OWNER_NO_MORE_HANDLES;
2082 break;
2083 }
2084 }
2085
2086 return rc;
2087}
2088
2089
2090/**
2091 * Destroys the owner identified by the given handle.
2092 *
2093 * @returns VBox status code.
2094 * @retval VERR_INVALID_HANDLE if the given owner handle is invalid.
2095 * @retval VERR_DBGF_OWNER_BUSY if there are still breakpoints set with the given owner handle.
2096 * @param pUVM The user mode VM handle.
2097 * @param hBpOwner The breakpoint owner handle to destroy.
2098 */
2099VMMR3DECL(int) DBGFR3BpOwnerDestroy(PUVM pUVM, DBGFBPOWNER hBpOwner)
2100{
2101 /*
2102 * Validate the input.
2103 */
2104 UVM_ASSERT_VALID_EXT_RETURN(pUVM, VERR_INVALID_VM_HANDLE);
2105 AssertReturn(hBpOwner != NIL_DBGFBPOWNER, VERR_INVALID_HANDLE);
2106
2107 int rc = dbgfR3BpOwnerEnsureInit(pUVM);
2108 AssertRCReturn(rc ,rc);
2109
2110 PDBGFBPOWNERINT pBpOwner = dbgfR3BpOwnerGetByHnd(pUVM, hBpOwner);
2111 if (RT_LIKELY(pBpOwner))
2112 {
2113 if (ASMAtomicReadU32(&pBpOwner->cRefs) == 1)
2114 {
2115 pBpOwner->pfnBpHitR3 = NULL;
2116 ASMAtomicDecU32(&pBpOwner->cRefs);
2117 ASMAtomicBitClear(pUVM->dbgf.s.pbmBpOwnersAllocR3, hBpOwner);
2118 }
2119 else
2120 rc = VERR_DBGF_OWNER_BUSY;
2121 }
2122 else
2123 rc = VERR_INVALID_HANDLE;
2124
2125 return rc;
2126}
2127
2128
2129/**
2130 * Sets a breakpoint (int 3 based).
2131 *
2132 * @returns VBox status code.
2133 * @param pUVM The user mode VM handle.
2134 * @param idSrcCpu The ID of the virtual CPU used for the
2135 * breakpoint address resolution.
2136 * @param pAddress The address of the breakpoint.
2137 * @param iHitTrigger The hit count at which the breakpoint start triggering.
2138 * Use 0 (or 1) if it's gonna trigger at once.
2139 * @param iHitDisable The hit count which disables the breakpoint.
2140 * Use ~(uint64_t) if it's never gonna be disabled.
2141 * @param phBp Where to store the breakpoint handle on success.
2142 *
2143 * @thread Any thread.
2144 */
2145VMMR3DECL(int) DBGFR3BpSetInt3(PUVM pUVM, VMCPUID idSrcCpu, PCDBGFADDRESS pAddress,
2146 uint64_t iHitTrigger, uint64_t iHitDisable, PDBGFBP phBp)
2147{
2148 return DBGFR3BpSetInt3Ex(pUVM, NIL_DBGFBPOWNER, NULL /*pvUser*/, idSrcCpu, pAddress,
2149 DBGF_BP_F_DEFAULT, iHitTrigger, iHitDisable, phBp);
2150}
2151
2152
2153/**
2154 * Sets a breakpoint (int 3 based) - extended version.
2155 *
2156 * @returns VBox status code.
2157 * @param pUVM The user mode VM handle.
2158 * @param hOwner The owner handle, use NIL_DBGFBPOWNER if no special owner attached.
2159 * @param pvUser Opaque user data to pass in the owner callback.
2160 * @param idSrcCpu The ID of the virtual CPU used for the
2161 * breakpoint address resolution.
2162 * @param pAddress The address of the breakpoint.
2163 * @param fFlags Combination of DBGF_BP_F_XXX.
2164 * @param iHitTrigger The hit count at which the breakpoint start triggering.
2165 * Use 0 (or 1) if it's gonna trigger at once.
2166 * @param iHitDisable The hit count which disables the breakpoint.
2167 * Use ~(uint64_t) if it's never gonna be disabled.
2168 * @param phBp Where to store the breakpoint handle on success.
2169 *
2170 * @thread Any thread.
2171 */
2172VMMR3DECL(int) DBGFR3BpSetInt3Ex(PUVM pUVM, DBGFBPOWNER hOwner, void *pvUser,
2173 VMCPUID idSrcCpu, PCDBGFADDRESS pAddress, uint16_t fFlags,
2174 uint64_t iHitTrigger, uint64_t iHitDisable, PDBGFBP phBp)
2175{
2176 UVM_ASSERT_VALID_EXT_RETURN(pUVM, VERR_INVALID_VM_HANDLE);
2177 AssertReturn(hOwner != NIL_DBGFBPOWNER || pvUser == NULL, VERR_INVALID_PARAMETER);
2178 AssertReturn(DBGFR3AddrIsValid(pUVM, pAddress), VERR_INVALID_PARAMETER);
2179 AssertReturn(iHitTrigger <= iHitDisable, VERR_INVALID_PARAMETER);
2180 AssertPtrReturn(phBp, VERR_INVALID_POINTER);
2181
2182 int rc = dbgfR3BpEnsureInit(pUVM);
2183 AssertRCReturn(rc, rc);
2184
2185 /*
2186 * Translate & save the breakpoint address into a guest-physical address.
2187 */
2188 RTGCPHYS GCPhysBpAddr = NIL_RTGCPHYS;
2189 rc = DBGFR3AddrToPhys(pUVM, idSrcCpu, pAddress, &GCPhysBpAddr);
2190 if (RT_SUCCESS(rc))
2191 {
2192 /*
2193 * The physical address from DBGFR3AddrToPhys() is the start of the page,
2194 * we need the exact byte offset into the page while writing to it in dbgfR3BpInt3Arm().
2195 */
2196 GCPhysBpAddr |= (pAddress->FlatPtr & X86_PAGE_OFFSET_MASK);
2197
2198 PDBGFBPINT pBp = NULL;
2199 DBGFBP hBp = dbgfR3BpGetByAddr(pUVM, DBGFBPTYPE_INT3, pAddress->FlatPtr, &pBp);
2200 if ( hBp != NIL_DBGFBP
2201 && pBp->Pub.u.Int3.PhysAddr == GCPhysBpAddr)
2202 {
2203 rc = VINF_SUCCESS;
2204 if ( !DBGF_BP_PUB_IS_ENABLED(&pBp->Pub)
2205 && (fFlags & DBGF_BP_F_ENABLED))
2206 rc = dbgfR3BpArm(pUVM, hBp, pBp);
2207 if (RT_SUCCESS(rc))
2208 {
2209 rc = VINF_DBGF_BP_ALREADY_EXIST;
2210 *phBp = hBp;
2211 }
2212 return rc;
2213 }
2214
2215 rc = dbgfR3BpAlloc(pUVM, hOwner, pvUser, DBGFBPTYPE_INT3, fFlags, iHitTrigger, iHitDisable, &hBp, &pBp);
2216 if (RT_SUCCESS(rc))
2217 {
2218 pBp->Pub.u.Int3.PhysAddr = GCPhysBpAddr;
2219 pBp->Pub.u.Int3.GCPtr = pAddress->FlatPtr;
2220
2221 /* Add the breakpoint to the lookup tables. */
2222 rc = dbgfR3BpInt3Add(pUVM, hBp, pBp);
2223 if (RT_SUCCESS(rc))
2224 {
2225 /* Enable the breakpoint if requested. */
2226 if (fFlags & DBGF_BP_F_ENABLED)
2227 rc = dbgfR3BpArm(pUVM, hBp, pBp);
2228 if (RT_SUCCESS(rc))
2229 {
2230 *phBp = hBp;
2231 return VINF_SUCCESS;
2232 }
2233
2234 int rc2 = dbgfR3BpInt3Remove(pUVM, hBp, pBp); AssertRC(rc2);
2235 }
2236
2237 dbgfR3BpFree(pUVM, hBp, pBp);
2238 }
2239 }
2240
2241 return rc;
2242}
2243
2244
2245/**
2246 * Sets a register breakpoint.
2247 *
2248 * @returns VBox status code.
2249 * @param pUVM The user mode VM handle.
2250 * @param pAddress The address of the breakpoint.
2251 * @param iHitTrigger The hit count at which the breakpoint start triggering.
2252 * Use 0 (or 1) if it's gonna trigger at once.
2253 * @param iHitDisable The hit count which disables the breakpoint.
2254 * Use ~(uint64_t) if it's never gonna be disabled.
2255 * @param fType The access type (one of the X86_DR7_RW_* defines).
2256 * @param cb The access size - 1,2,4 or 8 (the latter is AMD64 long mode only.
2257 * Must be 1 if fType is X86_DR7_RW_EO.
2258 * @param phBp Where to store the breakpoint handle.
2259 *
2260 * @thread Any thread.
2261 */
2262VMMR3DECL(int) DBGFR3BpSetReg(PUVM pUVM, PCDBGFADDRESS pAddress, uint64_t iHitTrigger,
2263 uint64_t iHitDisable, uint8_t fType, uint8_t cb, PDBGFBP phBp)
2264{
2265 return DBGFR3BpSetRegEx(pUVM, NIL_DBGFBPOWNER, NULL /*pvUser*/, pAddress,
2266 DBGF_BP_F_DEFAULT, iHitTrigger, iHitDisable, fType, cb, phBp);
2267}
2268
2269
2270/**
2271 * Sets a register breakpoint - extended version.
2272 *
2273 * @returns VBox status code.
2274 * @param pUVM The user mode VM handle.
2275 * @param hOwner The owner handle, use NIL_DBGFBPOWNER if no special owner attached.
2276 * @param pvUser Opaque user data to pass in the owner callback.
2277 * @param pAddress The address of the breakpoint.
2278 * @param fFlags Combination of DBGF_BP_F_XXX.
2279 * @param iHitTrigger The hit count at which the breakpoint start triggering.
2280 * Use 0 (or 1) if it's gonna trigger at once.
2281 * @param iHitDisable The hit count which disables the breakpoint.
2282 * Use ~(uint64_t) if it's never gonna be disabled.
2283 * @param fType The access type (one of the X86_DR7_RW_* defines).
2284 * @param cb The access size - 1,2,4 or 8 (the latter is AMD64 long mode only.
2285 * Must be 1 if fType is X86_DR7_RW_EO.
2286 * @param phBp Where to store the breakpoint handle.
2287 *
2288 * @thread Any thread.
2289 */
2290VMMR3DECL(int) DBGFR3BpSetRegEx(PUVM pUVM, DBGFBPOWNER hOwner, void *pvUser,
2291 PCDBGFADDRESS pAddress, uint16_t fFlags,
2292 uint64_t iHitTrigger, uint64_t iHitDisable,
2293 uint8_t fType, uint8_t cb, PDBGFBP phBp)
2294{
2295 UVM_ASSERT_VALID_EXT_RETURN(pUVM, VERR_INVALID_VM_HANDLE);
2296 AssertReturn(hOwner != NIL_DBGFBPOWNER || pvUser == NULL, VERR_INVALID_PARAMETER);
2297 AssertReturn(DBGFR3AddrIsValid(pUVM, pAddress), VERR_INVALID_PARAMETER);
2298 AssertReturn(iHitTrigger <= iHitDisable, VERR_INVALID_PARAMETER);
2299 AssertReturn(cb > 0 && cb <= 8 && RT_IS_POWER_OF_TWO(cb), VERR_INVALID_PARAMETER);
2300 AssertPtrReturn(phBp, VERR_INVALID_POINTER);
2301 switch (fType)
2302 {
2303 case X86_DR7_RW_EO:
2304 AssertMsgReturn(cb == 1, ("fType=%#x cb=%d != 1\n", fType, cb), VERR_INVALID_PARAMETER);
2305 break;
2306 case X86_DR7_RW_IO:
2307 case X86_DR7_RW_RW:
2308 case X86_DR7_RW_WO:
2309 break;
2310 default:
2311 AssertMsgFailedReturn(("fType=%#x\n", fType), VERR_INVALID_PARAMETER);
2312 }
2313
2314 int rc = dbgfR3BpEnsureInit(pUVM);
2315 AssertRCReturn(rc, rc);
2316
2317 /*
2318 * Check if we've already got a matching breakpoint for that address.
2319 */
2320 PDBGFBPINT pBp = NULL;
2321 DBGFBP hBp = dbgfR3BpGetByAddr(pUVM, DBGFBPTYPE_REG, pAddress->FlatPtr, &pBp);
2322 if ( hBp != NIL_DBGFBP
2323 && pBp->Pub.u.Reg.cb == cb
2324 && pBp->Pub.u.Reg.fType == fType)
2325 {
2326 rc = VINF_SUCCESS;
2327 if (!DBGF_BP_PUB_IS_ENABLED(&pBp->Pub) && (fFlags & DBGF_BP_F_ENABLED))
2328 rc = dbgfR3BpArm(pUVM, hBp, pBp);
2329 /* else: We don't disable it when DBGF_BP_F_ENABLED isn't given. */
2330 if (RT_SUCCESS(rc))
2331 {
2332 rc = VINF_DBGF_BP_ALREADY_EXIST;
2333 *phBp = hBp;
2334 }
2335 return rc;
2336 }
2337
2338 /*
2339 * Allocate new breakpoint.
2340 */
2341 rc = dbgfR3BpAlloc(pUVM, hOwner, pvUser, DBGFBPTYPE_REG, fFlags, iHitTrigger, iHitDisable, &hBp, &pBp);
2342 if (RT_SUCCESS(rc))
2343 {
2344 pBp->Pub.u.Reg.GCPtr = pAddress->FlatPtr;
2345 pBp->Pub.u.Reg.fType = fType;
2346 pBp->Pub.u.Reg.cb = cb;
2347 pBp->Pub.u.Reg.iReg = UINT8_MAX;
2348 ASMCompilerBarrier();
2349
2350 /* Assign the proper hardware breakpoint. */
2351 rc = dbgfR3BpRegAssign(pUVM->pVM, hBp, pBp);
2352 if (RT_SUCCESS(rc))
2353 {
2354 /* Arm the breakpoint. */
2355 if (fFlags & DBGF_BP_F_ENABLED)
2356 rc = dbgfR3BpArm(pUVM, hBp, pBp);
2357 if (RT_SUCCESS(rc))
2358 {
2359 *phBp = hBp;
2360 return VINF_SUCCESS;
2361 }
2362
2363 int rc2 = dbgfR3BpRegRemove(pUVM->pVM, hBp, pBp);
2364 AssertRC(rc2); RT_NOREF(rc2);
2365 }
2366
2367 dbgfR3BpFree(pUVM, hBp, pBp);
2368 }
2369
2370 return rc;
2371}
2372
2373
2374/**
2375 * This is only kept for now to not mess with the debugger implementation at this point,
2376 * recompiler breakpoints are not supported anymore (IEM has some API but it isn't implemented
2377 * and should probably be merged with the DBGF breakpoints).
2378 */
2379VMMR3DECL(int) DBGFR3BpSetREM(PUVM pUVM, PCDBGFADDRESS pAddress, uint64_t iHitTrigger,
2380 uint64_t iHitDisable, PDBGFBP phBp)
2381{
2382 RT_NOREF(pUVM, pAddress, iHitTrigger, iHitDisable, phBp);
2383 return VERR_NOT_SUPPORTED;
2384}
2385
2386
2387/**
2388 * Sets an I/O port breakpoint.
2389 *
2390 * @returns VBox status code.
2391 * @param pUVM The user mode VM handle.
2392 * @param uPort The first I/O port.
2393 * @param cPorts The number of I/O ports, see DBGFBPIOACCESS_XXX.
2394 * @param fAccess The access we want to break on.
2395 * @param iHitTrigger The hit count at which the breakpoint start
2396 * triggering. Use 0 (or 1) if it's gonna trigger at
2397 * once.
2398 * @param iHitDisable The hit count which disables the breakpoint.
2399 * Use ~(uint64_t) if it's never gonna be disabled.
2400 * @param phBp Where to store the breakpoint handle.
2401 *
2402 * @thread Any thread.
2403 */
2404VMMR3DECL(int) DBGFR3BpSetPortIo(PUVM pUVM, RTIOPORT uPort, RTIOPORT cPorts, uint32_t fAccess,
2405 uint64_t iHitTrigger, uint64_t iHitDisable, PDBGFBP phBp)
2406{
2407 return DBGFR3BpSetPortIoEx(pUVM, NIL_DBGFBPOWNER, NULL /*pvUser*/, uPort, cPorts, fAccess,
2408 DBGF_BP_F_DEFAULT, iHitTrigger, iHitDisable, phBp);
2409}
2410
2411
2412/**
2413 * Sets an I/O port breakpoint - extended version.
2414 *
2415 * @returns VBox status code.
2416 * @param pUVM The user mode VM handle.
2417 * @param hOwner The owner handle, use NIL_DBGFBPOWNER if no special owner attached.
2418 * @param pvUser Opaque user data to pass in the owner callback.
2419 * @param uPort The first I/O port.
2420 * @param cPorts The number of I/O ports, see DBGFBPIOACCESS_XXX.
2421 * @param fAccess The access we want to break on.
2422 * @param fFlags Combination of DBGF_BP_F_XXX.
2423 * @param iHitTrigger The hit count at which the breakpoint start
2424 * triggering. Use 0 (or 1) if it's gonna trigger at
2425 * once.
2426 * @param iHitDisable The hit count which disables the breakpoint.
2427 * Use ~(uint64_t) if it's never gonna be disabled.
2428 * @param phBp Where to store the breakpoint handle.
2429 *
2430 * @thread Any thread.
2431 */
2432VMMR3DECL(int) DBGFR3BpSetPortIoEx(PUVM pUVM, DBGFBPOWNER hOwner, void *pvUser,
2433 RTIOPORT uPort, RTIOPORT cPorts, uint32_t fAccess,
2434 uint32_t fFlags, uint64_t iHitTrigger, uint64_t iHitDisable, PDBGFBP phBp)
2435{
2436 UVM_ASSERT_VALID_EXT_RETURN(pUVM, VERR_INVALID_VM_HANDLE);
2437 AssertReturn(hOwner != NIL_DBGFBPOWNER || pvUser == NULL, VERR_INVALID_PARAMETER);
2438 AssertReturn(!(fAccess & ~DBGFBPIOACCESS_VALID_MASK_PORT_IO), VERR_INVALID_FLAGS);
2439 AssertReturn(fAccess, VERR_INVALID_FLAGS);
2440 AssertReturn(!(fFlags & ~DBGF_BP_F_VALID_MASK), VERR_INVALID_FLAGS);
2441 AssertReturn(fFlags, VERR_INVALID_FLAGS);
2442 AssertReturn(iHitTrigger <= iHitDisable, VERR_INVALID_PARAMETER);
2443 AssertPtrReturn(phBp, VERR_INVALID_POINTER);
2444 AssertReturn(cPorts > 0, VERR_OUT_OF_RANGE);
2445 AssertReturn((RTIOPORT)(uPort + (cPorts - 1)) >= uPort, VERR_OUT_OF_RANGE);
2446
2447 int rc = dbgfR3BpPortIoEnsureInit(pUVM);
2448 AssertRCReturn(rc, rc);
2449
2450 PDBGFBPINT pBp = NULL;
2451 DBGFBP hBp = dbgfR3BpPortIoGetByRange(pUVM, uPort, cPorts, &pBp);
2452 if ( hBp != NIL_DBGFBP
2453 && pBp->Pub.u.PortIo.uPort == uPort
2454 && pBp->Pub.u.PortIo.cPorts == cPorts
2455 && pBp->Pub.u.PortIo.fAccess == fAccess)
2456 {
2457 rc = VINF_SUCCESS;
2458 if (!DBGF_BP_PUB_IS_ENABLED(&pBp->Pub))
2459 rc = dbgfR3BpArm(pUVM, hBp, pBp);
2460 if (RT_SUCCESS(rc))
2461 {
2462 rc = VINF_DBGF_BP_ALREADY_EXIST;
2463 *phBp = hBp;
2464 }
2465 return rc;
2466 }
2467
2468 rc = dbgfR3BpAlloc(pUVM, hOwner, pvUser, DBGFBPTYPE_PORT_IO, fFlags, iHitTrigger, iHitDisable, &hBp, &pBp);
2469 if (RT_SUCCESS(rc))
2470 {
2471 pBp->Pub.u.PortIo.uPort = uPort;
2472 pBp->Pub.u.PortIo.cPorts = cPorts;
2473 pBp->Pub.u.PortIo.fAccess = fAccess;
2474
2475 /* Add the breakpoint to the lookup tables. */
2476 rc = dbgfR3BpPortIoAdd(pUVM, hBp, pBp);
2477 if (RT_SUCCESS(rc))
2478 {
2479 /* Enable the breakpoint if requested. */
2480 if (fFlags & DBGF_BP_F_ENABLED)
2481 rc = dbgfR3BpArm(pUVM, hBp, pBp);
2482 if (RT_SUCCESS(rc))
2483 {
2484 *phBp = hBp;
2485 return VINF_SUCCESS;
2486 }
2487
2488 int rc2 = dbgfR3BpPortIoRemove(pUVM, hBp, pBp); AssertRC(rc2);
2489 }
2490
2491 dbgfR3BpFree(pUVM, hBp, pBp);
2492 }
2493
2494 return rc;
2495}
2496
2497
2498/**
2499 * Sets a memory mapped I/O breakpoint.
2500 *
2501 * @returns VBox status code.
2502 * @param pUVM The user mode VM handle.
2503 * @param GCPhys The first MMIO address.
2504 * @param cb The size of the MMIO range to break on.
2505 * @param fAccess The access we want to break on.
2506 * @param iHitTrigger The hit count at which the breakpoint start
2507 * triggering. Use 0 (or 1) if it's gonna trigger at
2508 * once.
2509 * @param iHitDisable The hit count which disables the breakpoint.
2510 * Use ~(uint64_t) if it's never gonna be disabled.
2511 * @param phBp Where to store the breakpoint handle.
2512 *
2513 * @thread Any thread.
2514 */
2515VMMR3DECL(int) DBGFR3BpSetMmio(PUVM pUVM, RTGCPHYS GCPhys, uint32_t cb, uint32_t fAccess,
2516 uint64_t iHitTrigger, uint64_t iHitDisable, PDBGFBP phBp)
2517{
2518 return DBGFR3BpSetMmioEx(pUVM, NIL_DBGFBPOWNER, NULL /*pvUser*/, GCPhys, cb, fAccess,
2519 DBGF_BP_F_DEFAULT, iHitTrigger, iHitDisable, phBp);
2520}
2521
2522
2523/**
2524 * Sets a memory mapped I/O breakpoint - extended version.
2525 *
2526 * @returns VBox status code.
2527 * @param pUVM The user mode VM handle.
2528 * @param hOwner The owner handle, use NIL_DBGFBPOWNER if no special owner attached.
2529 * @param pvUser Opaque user data to pass in the owner callback.
2530 * @param GCPhys The first MMIO address.
2531 * @param cb The size of the MMIO range to break on.
2532 * @param fAccess The access we want to break on.
2533 * @param fFlags Combination of DBGF_BP_F_XXX.
2534 * @param iHitTrigger The hit count at which the breakpoint start
2535 * triggering. Use 0 (or 1) if it's gonna trigger at
2536 * once.
2537 * @param iHitDisable The hit count which disables the breakpoint.
2538 * Use ~(uint64_t) if it's never gonna be disabled.
2539 * @param phBp Where to store the breakpoint handle.
2540 *
2541 * @thread Any thread.
2542 */
2543VMMR3DECL(int) DBGFR3BpSetMmioEx(PUVM pUVM, DBGFBPOWNER hOwner, void *pvUser,
2544 RTGCPHYS GCPhys, uint32_t cb, uint32_t fAccess,
2545 uint32_t fFlags, uint64_t iHitTrigger, uint64_t iHitDisable, PDBGFBP phBp)
2546{
2547 UVM_ASSERT_VALID_EXT_RETURN(pUVM, VERR_INVALID_VM_HANDLE);
2548 AssertReturn(hOwner != NIL_DBGFBPOWNER || pvUser == NULL, VERR_INVALID_PARAMETER);
2549 AssertReturn(!(fAccess & ~DBGFBPIOACCESS_VALID_MASK_MMIO), VERR_INVALID_FLAGS);
2550 AssertReturn(fAccess, VERR_INVALID_FLAGS);
2551 AssertReturn(!(fFlags & ~DBGF_BP_F_VALID_MASK), VERR_INVALID_FLAGS);
2552 AssertReturn(fFlags, VERR_INVALID_FLAGS);
2553 AssertReturn(iHitTrigger <= iHitDisable, VERR_INVALID_PARAMETER);
2554 AssertPtrReturn(phBp, VERR_INVALID_POINTER);
2555 AssertReturn(cb, VERR_OUT_OF_RANGE);
2556 AssertReturn(GCPhys + cb < GCPhys, VERR_OUT_OF_RANGE);
2557
2558 int rc = dbgfR3BpEnsureInit(pUVM);
2559 AssertRCReturn(rc, rc);
2560
2561 return VERR_NOT_IMPLEMENTED;
2562}
2563
2564
2565/**
2566 * Clears a breakpoint.
2567 *
2568 * @returns VBox status code.
2569 * @param pUVM The user mode VM handle.
2570 * @param hBp The handle of the breakpoint which should be removed (cleared).
2571 *
2572 * @thread Any thread.
2573 */
2574VMMR3DECL(int) DBGFR3BpClear(PUVM pUVM, DBGFBP hBp)
2575{
2576 UVM_ASSERT_VALID_EXT_RETURN(pUVM, VERR_INVALID_VM_HANDLE);
2577 AssertReturn(hBp != NIL_DBGFBPOWNER, VERR_INVALID_HANDLE);
2578
2579 PDBGFBPINT pBp = dbgfR3BpGetByHnd(pUVM, hBp);
2580 AssertPtrReturn(pBp, VERR_DBGF_BP_NOT_FOUND);
2581
2582 /* Disarm the breakpoint when it is enabled. */
2583 if (DBGF_BP_PUB_IS_ENABLED(&pBp->Pub))
2584 {
2585 int rc = dbgfR3BpDisarm(pUVM, hBp, pBp);
2586 AssertRC(rc);
2587 }
2588
2589 switch (DBGF_BP_PUB_GET_TYPE(&pBp->Pub))
2590 {
2591 case DBGFBPTYPE_REG:
2592 {
2593 int rc = dbgfR3BpRegRemove(pUVM->pVM, hBp, pBp);
2594 AssertRC(rc);
2595 break;
2596 }
2597 case DBGFBPTYPE_INT3:
2598 {
2599 int rc = dbgfR3BpInt3Remove(pUVM, hBp, pBp);
2600 AssertRC(rc);
2601 break;
2602 }
2603 case DBGFBPTYPE_PORT_IO:
2604 {
2605 int rc = dbgfR3BpPortIoRemove(pUVM, hBp, pBp);
2606 AssertRC(rc);
2607 break;
2608 }
2609 default:
2610 break;
2611 }
2612
2613 dbgfR3BpFree(pUVM, hBp, pBp);
2614 return VINF_SUCCESS;
2615}
2616
2617
2618/**
2619 * Enables a breakpoint.
2620 *
2621 * @returns VBox status code.
2622 * @param pUVM The user mode VM handle.
2623 * @param hBp The handle of the breakpoint which should be enabled.
2624 *
2625 * @thread Any thread.
2626 */
2627VMMR3DECL(int) DBGFR3BpEnable(PUVM pUVM, DBGFBP hBp)
2628{
2629 /*
2630 * Validate the input.
2631 */
2632 UVM_ASSERT_VALID_EXT_RETURN(pUVM, VERR_INVALID_VM_HANDLE);
2633 AssertReturn(hBp != NIL_DBGFBPOWNER, VERR_INVALID_HANDLE);
2634
2635 PDBGFBPINT pBp = dbgfR3BpGetByHnd(pUVM, hBp);
2636 AssertPtrReturn(pBp, VERR_DBGF_BP_NOT_FOUND);
2637
2638 int rc;
2639 if (!DBGF_BP_PUB_IS_ENABLED(&pBp->Pub))
2640 rc = dbgfR3BpArm(pUVM, hBp, pBp);
2641 else
2642 rc = VINF_DBGF_BP_ALREADY_ENABLED;
2643
2644 return rc;
2645}
2646
2647
2648/**
2649 * Disables a breakpoint.
2650 *
2651 * @returns VBox status code.
2652 * @param pUVM The user mode VM handle.
2653 * @param hBp The handle of the breakpoint which should be disabled.
2654 *
2655 * @thread Any thread.
2656 */
2657VMMR3DECL(int) DBGFR3BpDisable(PUVM pUVM, DBGFBP hBp)
2658{
2659 /*
2660 * Validate the input.
2661 */
2662 UVM_ASSERT_VALID_EXT_RETURN(pUVM, VERR_INVALID_VM_HANDLE);
2663 AssertReturn(hBp != NIL_DBGFBPOWNER, VERR_INVALID_HANDLE);
2664
2665 PDBGFBPINT pBp = dbgfR3BpGetByHnd(pUVM, hBp);
2666 AssertPtrReturn(pBp, VERR_DBGF_BP_NOT_FOUND);
2667
2668 int rc;
2669 if (DBGF_BP_PUB_IS_ENABLED(&pBp->Pub))
2670 rc = dbgfR3BpDisarm(pUVM, hBp, pBp);
2671 else
2672 rc = VINF_DBGF_BP_ALREADY_DISABLED;
2673
2674 return rc;
2675}
2676
2677
2678/**
2679 * Enumerate the breakpoints.
2680 *
2681 * @returns VBox status code.
2682 * @param pUVM The user mode VM handle.
2683 * @param pfnCallback The callback function.
2684 * @param pvUser The user argument to pass to the callback.
2685 *
2686 * @thread Any thread.
2687 */
2688VMMR3DECL(int) DBGFR3BpEnum(PUVM pUVM, PFNDBGFBPENUM pfnCallback, void *pvUser)
2689{
2690 UVM_ASSERT_VALID_EXT_RETURN(pUVM, VERR_INVALID_VM_HANDLE);
2691
2692 for (uint32_t idChunk = 0; idChunk < RT_ELEMENTS(pUVM->dbgf.s.aBpChunks); idChunk++)
2693 {
2694 PDBGFBPCHUNKR3 pBpChunk = &pUVM->dbgf.s.aBpChunks[idChunk];
2695
2696 if (pBpChunk->idChunk == DBGF_BP_CHUNK_ID_INVALID)
2697 break; /* Stop here as the first non allocated chunk means there is no one allocated afterwards as well. */
2698
2699 if (pBpChunk->cBpsFree < DBGF_BP_COUNT_PER_CHUNK)
2700 {
2701 /* Scan the bitmap for allocated entries. */
2702 int32_t iAlloc = ASMBitFirstSet(pBpChunk->pbmAlloc, DBGF_BP_COUNT_PER_CHUNK);
2703 if (iAlloc != -1)
2704 {
2705 do
2706 {
2707 DBGFBP hBp = DBGF_BP_HND_CREATE(idChunk, (uint32_t)iAlloc);
2708 PDBGFBPINT pBp = dbgfR3BpGetByHnd(pUVM, hBp);
2709
2710 /* Make a copy of the breakpoints public data to have a consistent view. */
2711 DBGFBPPUB BpPub;
2712 BpPub.cHits = ASMAtomicReadU64((volatile uint64_t *)&pBp->Pub.cHits);
2713 BpPub.iHitTrigger = ASMAtomicReadU64((volatile uint64_t *)&pBp->Pub.iHitTrigger);
2714 BpPub.iHitDisable = ASMAtomicReadU64((volatile uint64_t *)&pBp->Pub.iHitDisable);
2715 BpPub.hOwner = ASMAtomicReadU32((volatile uint32_t *)&pBp->Pub.hOwner);
2716 BpPub.u16Type = ASMAtomicReadU16((volatile uint16_t *)&pBp->Pub.u16Type); /* Actually constant. */
2717 BpPub.fFlags = ASMAtomicReadU16((volatile uint16_t *)&pBp->Pub.fFlags);
2718 memcpy(&BpPub.u, &pBp->Pub.u, sizeof(pBp->Pub.u)); /* Is constant after allocation. */
2719
2720 /* Check if a removal raced us. */
2721 if (ASMBitTest(pBpChunk->pbmAlloc, iAlloc))
2722 {
2723 int rc = pfnCallback(pUVM, pvUser, hBp, &BpPub);
2724 if (RT_FAILURE(rc) || rc == VINF_CALLBACK_RETURN)
2725 return rc;
2726 }
2727
2728 iAlloc = ASMBitNextSet(pBpChunk->pbmAlloc, DBGF_BP_COUNT_PER_CHUNK, iAlloc);
2729 } while (iAlloc != -1);
2730 }
2731 }
2732 }
2733
2734 return VINF_SUCCESS;
2735}
2736
2737
2738/**
2739 * Called whenever a breakpoint event needs to be serviced in ring-3 to decide what to do.
2740 *
2741 * @returns VBox status code.
2742 * @param pVM The cross context VM structure.
2743 * @param pVCpu The vCPU the breakpoint event happened on.
2744 *
2745 * @thread EMT
2746 */
2747VMMR3_INT_DECL(int) DBGFR3BpHit(PVM pVM, PVMCPU pVCpu)
2748{
2749 /* Send it straight into the debugger?. */
2750 if (pVCpu->dbgf.s.fBpInvokeOwnerCallback)
2751 {
2752 DBGFBP hBp = pVCpu->dbgf.s.hBpActive;
2753 pVCpu->dbgf.s.fBpInvokeOwnerCallback = false;
2754
2755 PDBGFBPINT pBp = dbgfR3BpGetByHnd(pVM->pUVM, hBp);
2756 AssertReturn(pBp, VERR_DBGF_BP_IPE_9);
2757
2758 /* Resolve owner (can be NIL_DBGFBPOWNER) and invoke callback if there is one. */
2759 if (pBp->Pub.hOwner != NIL_DBGFBPOWNER)
2760 {
2761 PCDBGFBPOWNERINT pBpOwner = dbgfR3BpOwnerGetByHnd(pVM->pUVM, pBp->Pub.hOwner);
2762 if (pBpOwner)
2763 {
2764 VBOXSTRICTRC rcStrict = dbgfR3BpHit(pVM, pVCpu, hBp, pBp, pBpOwner);
2765 if (VBOXSTRICTRC_VAL(rcStrict) == VINF_SUCCESS)
2766 {
2767 pVCpu->dbgf.s.hBpActive = NIL_DBGFBP;
2768 return VINF_SUCCESS;
2769 }
2770 if (VBOXSTRICTRC_VAL(rcStrict) != VINF_DBGF_BP_HALT) /* Guru meditation. */
2771 return VERR_DBGF_BP_OWNER_CALLBACK_WRONG_STATUS;
2772 /* else: Halt in the debugger. */
2773 }
2774 }
2775 }
2776
2777 return DBGFR3EventBreakpoint(pVM, DBGFEVENT_BREAKPOINT);
2778}
2779
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use