VirtualBox

source: vbox/trunk/src/VBox/VMM/VMMR3/DBGFBp.cpp@ 58903

Last change on this file since 58903 was 58903, checked in by vboxsync, 9 years ago

DBGF: I/O and MMIO breakpoint API changes.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 36.6 KB
Line 
1/* $Id: DBGFBp.cpp 58903 2015-11-27 15:07:07Z vboxsync $ */
2/** @file
3 * DBGF - Debugger Facility, Breakpoint Management.
4 */
5
6/*
7 * Copyright (C) 2006-2015 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.215389.xyz. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18
19/*********************************************************************************************************************************
20* Header Files *
21*********************************************************************************************************************************/
22#define LOG_GROUP LOG_GROUP_DBGF
23#include <VBox/vmm/dbgf.h>
24#include <VBox/vmm/selm.h>
25#ifdef VBOX_WITH_REM
26# include <VBox/vmm/rem.h>
27#else
28# include <VBox/vmm/iem.h>
29#endif
30#include <VBox/vmm/mm.h>
31#include <VBox/vmm/iom.h>
32#include <VBox/vmm/hm.h>
33#include "DBGFInternal.h"
34#include <VBox/vmm/vm.h>
35#include <VBox/vmm/uvm.h>
36
37#include <VBox/err.h>
38#include <VBox/log.h>
39#include <iprt/assert.h>
40#include <iprt/string.h>
41
42
43/*********************************************************************************************************************************
44* Internal Functions *
45*********************************************************************************************************************************/
46RT_C_DECLS_BEGIN
47static int dbgfR3BpRegArm(PVM pVM, PDBGFBP pBp);
48static int dbgfR3BpInt3Arm(PUVM pUVM, PDBGFBP pBp);
49RT_C_DECLS_END
50
51
52
53/**
54 * Initialize the breakpoint stuff.
55 *
56 * @returns VINF_SUCCESS
57 * @param pVM The cross context VM structure.
58 */
59int dbgfR3BpInit(PVM pVM)
60{
61 /*
62 * Init structures.
63 */
64 unsigned i;
65 for (i = 0; i < RT_ELEMENTS(pVM->dbgf.s.aHwBreakpoints); i++)
66 {
67 pVM->dbgf.s.aHwBreakpoints[i].iBp = i;
68 pVM->dbgf.s.aHwBreakpoints[i].enmType = DBGFBPTYPE_FREE;
69 pVM->dbgf.s.aHwBreakpoints[i].u.Reg.iReg = i;
70 }
71
72 for (i = 0; i < RT_ELEMENTS(pVM->dbgf.s.aBreakpoints); i++)
73 {
74 pVM->dbgf.s.aBreakpoints[i].iBp = i + RT_ELEMENTS(pVM->dbgf.s.aHwBreakpoints);
75 pVM->dbgf.s.aBreakpoints[i].enmType = DBGFBPTYPE_FREE;
76 }
77
78 for (VMCPUID idCpu = 0; idCpu < pVM->cCpus; idCpu++)
79 {
80 PVMCPU pVCpu = &pVM->aCpus[idCpu];
81 pVCpu->dbgf.s.iActiveBp = ~0U;
82 }
83
84 /*
85 * Register saved state.
86 */
87 /** @todo */
88
89 return VINF_SUCCESS;
90}
91
92
93
94/**
95 * Allocate a breakpoint.
96 *
97 * @returns Pointer to the allocated breakpoint.
98 * @returns NULL if we're out of breakpoints.
99 * @param pVM The cross context VM structure.
100 * @param enmType The type to allocate.
101 */
102static PDBGFBP dbgfR3BpAlloc(PVM pVM, DBGFBPTYPE enmType)
103{
104 /*
105 * Determine which array to search.
106 */
107 unsigned cBps;
108 uint32_t *pcBpsCur;
109 PDBGFBP paBps;
110 switch (enmType)
111 {
112 case DBGFBPTYPE_REG:
113 cBps = RT_ELEMENTS(pVM->dbgf.s.aHwBreakpoints);
114 paBps = &pVM->dbgf.s.aHwBreakpoints[0];
115 pcBpsCur = &pVM->dbgf.s.cHwBreakpoints;
116 break;
117
118 case DBGFBPTYPE_INT3:
119 case DBGFBPTYPE_REM:
120 cBps = RT_ELEMENTS(pVM->dbgf.s.aBreakpoints);
121 paBps = &pVM->dbgf.s.aBreakpoints[0];
122 pcBpsCur = &pVM->dbgf.s.cBreakpoints;
123 break;
124
125 default:
126 AssertMsgFailed(("enmType=%d\n", enmType));
127 return NULL;
128 }
129
130 /*
131 * Search.
132 */
133 for (unsigned iBp = 0; iBp < cBps; iBp++)
134 if (paBps[iBp].enmType == DBGFBPTYPE_FREE)
135 {
136 ++*pcBpsCur;
137 paBps[iBp].cHits = 0;
138 paBps[iBp].enmType = enmType;
139 return &paBps[iBp];
140 }
141
142 LogFlow(("dbgfR3BpAlloc: returns NULL - we're out of breakpoint slots! %u/%u\n", *pcBpsCur, cBps));
143 return NULL;
144}
145
146
147/**
148 * Get a breakpoint give by breakpoint id.
149 *
150 * @returns Pointer to the allocated breakpoint.
151 * @returns NULL if the breakpoint is invalid.
152 * @param pVM The cross context VM structure.
153 * @param iBp The breakpoint id.
154 */
155static PDBGFBP dbgfR3BpGet(PVM pVM, uint32_t iBp)
156{
157 /* Find it. */
158 PDBGFBP pBp;
159 if (iBp < RT_ELEMENTS(pVM->dbgf.s.aHwBreakpoints))
160 pBp = &pVM->dbgf.s.aHwBreakpoints[iBp];
161 else
162 {
163 iBp -= RT_ELEMENTS(pVM->dbgf.s.aHwBreakpoints);
164 if (iBp >= RT_ELEMENTS(pVM->dbgf.s.aBreakpoints))
165 return NULL;
166 pBp = &pVM->dbgf.s.aBreakpoints[iBp];
167 }
168
169 /* check if it's valid. */
170 switch (pBp->enmType)
171 {
172 case DBGFBPTYPE_FREE:
173 return NULL;
174
175 case DBGFBPTYPE_REG:
176 case DBGFBPTYPE_INT3:
177 case DBGFBPTYPE_REM:
178 break;
179
180 default:
181 AssertMsgFailed(("Invalid enmType=%d!\n", pBp->enmType));
182 return NULL;
183 }
184
185 return pBp;
186}
187
188
189/**
190 * Get a breakpoint give by address.
191 *
192 * @returns Pointer to the allocated breakpoint.
193 * @returns NULL if the breakpoint is invalid.
194 * @param pVM The cross context VM structure.
195 * @param enmType The breakpoint type.
196 * @param GCPtr The breakpoint address.
197 */
198static PDBGFBP dbgfR3BpGetByAddr(PVM pVM, DBGFBPTYPE enmType, RTGCUINTPTR GCPtr)
199{
200 /*
201 * Determine which array to search.
202 */
203 unsigned cBps;
204 PDBGFBP paBps;
205 switch (enmType)
206 {
207 case DBGFBPTYPE_REG:
208 cBps = RT_ELEMENTS(pVM->dbgf.s.aHwBreakpoints);
209 paBps = &pVM->dbgf.s.aHwBreakpoints[0];
210 break;
211
212 case DBGFBPTYPE_INT3:
213 case DBGFBPTYPE_REM:
214 cBps = RT_ELEMENTS(pVM->dbgf.s.aBreakpoints);
215 paBps = &pVM->dbgf.s.aBreakpoints[0];
216 break;
217
218 default:
219 AssertMsgFailed(("enmType=%d\n", enmType));
220 return NULL;
221 }
222
223 /*
224 * Search.
225 */
226 for (unsigned iBp = 0; iBp < cBps; iBp++)
227 if ( paBps[iBp].enmType == enmType
228 && paBps[iBp].u.GCPtr == GCPtr)
229 return &paBps[iBp];
230
231 return NULL;
232}
233
234
235/**
236 * Frees a breakpoint.
237 *
238 * @param pVM The cross context VM structure.
239 * @param pBp The breakpoint to free.
240 */
241static void dbgfR3BpFree(PVM pVM, PDBGFBP pBp)
242{
243 switch (pBp->enmType)
244 {
245 case DBGFBPTYPE_FREE:
246 AssertMsgFailed(("Already freed!\n"));
247 return;
248
249 case DBGFBPTYPE_REG:
250 Assert(pVM->dbgf.s.cHwBreakpoints > 0);
251 pVM->dbgf.s.cHwBreakpoints--;
252 break;
253
254 case DBGFBPTYPE_INT3:
255 case DBGFBPTYPE_REM:
256 Assert(pVM->dbgf.s.cBreakpoints > 0);
257 pVM->dbgf.s.cBreakpoints--;
258 break;
259
260 default:
261 AssertMsgFailed(("Invalid enmType=%d!\n", pBp->enmType));
262 return;
263
264 }
265 pBp->enmType = DBGFBPTYPE_FREE;
266}
267
268
269/**
270 * Sets a breakpoint (int 3 based).
271 *
272 * @returns VBox status code.
273 * @param pUVM The user mode VM handle.
274 * @param pAddress The address of the breakpoint.
275 * @param piHitTrigger The hit count at which the breakpoint start triggering.
276 * Use 0 (or 1) if it's gonna trigger at once.
277 * @param piHitDisable The hit count which disables the breakpoint.
278 * Use ~(uint64_t) if it's never gonna be disabled.
279 * @param piBp Where to store the breakpoint id. (optional)
280 * @thread Any thread.
281 */
282static DECLCALLBACK(int) dbgfR3BpSetInt3(PUVM pUVM, PCDBGFADDRESS pAddress, uint64_t *piHitTrigger,
283 uint64_t *piHitDisable, uint32_t *piBp)
284{
285 /*
286 * Validate input.
287 */
288 PVM pVM = pUVM->pVM;
289 VM_ASSERT_VALID_EXT_RETURN(pVM, VERR_INVALID_VM_HANDLE);
290 if (!DBGFR3AddrIsValid(pUVM, pAddress))
291 return VERR_INVALID_PARAMETER;
292 if (*piHitTrigger > *piHitDisable)
293 return VERR_INVALID_PARAMETER;
294 AssertMsgReturn(!piBp || VALID_PTR(piBp), ("piBp=%p\n", piBp), VERR_INVALID_POINTER);
295 if (piBp)
296 *piBp = ~0;
297
298 /*
299 * Check if the breakpoint already exists.
300 */
301 PDBGFBP pBp = dbgfR3BpGetByAddr(pVM, DBGFBPTYPE_INT3, pAddress->FlatPtr);
302 if (pBp)
303 {
304 int rc = VINF_SUCCESS;
305 if (!pBp->fEnabled)
306 rc = dbgfR3BpInt3Arm(pUVM, pBp);
307 if (RT_SUCCESS(rc))
308 {
309 rc = VINF_DBGF_BP_ALREADY_EXIST;
310 if (piBp)
311 *piBp = pBp->iBp;
312 }
313 return rc;
314 }
315
316 /*
317 * Allocate and initialize the bp.
318 */
319 pBp = dbgfR3BpAlloc(pVM, DBGFBPTYPE_INT3);
320 if (!pBp)
321 return VERR_DBGF_NO_MORE_BP_SLOTS;
322 pBp->u.Int3.GCPtr = pAddress->FlatPtr;
323 pBp->iHitTrigger = *piHitTrigger;
324 pBp->iHitDisable = *piHitDisable;
325 ASMCompilerBarrier();
326 pBp->fEnabled = true;
327
328 /*
329 * Now ask REM to set the breakpoint.
330 */
331 int rc = dbgfR3BpInt3Arm(pUVM, pBp);
332 if (RT_SUCCESS(rc))
333 {
334 if (piBp)
335 *piBp = pBp->iBp;
336 }
337 else
338 dbgfR3BpFree(pVM, pBp);
339
340 return rc;
341}
342
343
344/**
345 * Sets a breakpoint (int 3 based).
346 *
347 * @returns VBox status code.
348 * @param pUVM The user mode VM handle.
349 * @param pAddress The address of the breakpoint.
350 * @param iHitTrigger The hit count at which the breakpoint start triggering.
351 * Use 0 (or 1) if it's gonna trigger at once.
352 * @param iHitDisable The hit count which disables the breakpoint.
353 * Use ~(uint64_t) if it's never gonna be disabled.
354 * @param piBp Where to store the breakpoint id. (optional)
355 * @thread Any thread.
356 */
357VMMR3DECL(int) DBGFR3BpSet(PUVM pUVM, PCDBGFADDRESS pAddress, uint64_t iHitTrigger, uint64_t iHitDisable, uint32_t *piBp)
358{
359 /*
360 * This must be done on EMT.
361 */
362 /** @todo SMP? */
363 int rc = VMR3ReqCallWaitU(pUVM, VMCPUID_ANY, (PFNRT)dbgfR3BpSetInt3, 5,
364 pUVM, pAddress, &iHitTrigger, &iHitDisable, piBp);
365 LogFlow(("DBGFR3BpSet: returns %Rrc\n", rc));
366 return rc;
367}
368
369
370/**
371 * Arms an int 3 breakpoint.
372 * This is used to implement both DBGFR3BpSetReg() and DBGFR3BpEnable().
373 *
374 * @returns VBox status code.
375 * @param pUVM The user mode VM handle.
376 * @param pBp The breakpoint.
377 */
378static int dbgfR3BpInt3Arm(PUVM pUVM, PDBGFBP pBp)
379{
380 /** @todo should actually use physical address here! */
381
382 /** @todo SMP support! */
383 VMCPUID idCpu = 0;
384
385 /*
386 * Save current byte and write int3 instruction.
387 */
388 DBGFADDRESS Addr;
389 DBGFR3AddrFromFlat(pUVM, &Addr, pBp->u.Int3.GCPtr);
390 int rc = DBGFR3MemRead(pUVM, idCpu, &Addr, &pBp->u.Int3.bOrg, 1);
391 if (RT_SUCCESS(rc))
392 {
393 static const uint8_t s_bInt3 = 0xcc;
394 rc = DBGFR3MemWrite(pUVM, idCpu, &Addr, &s_bInt3, 1);
395 }
396 return rc;
397}
398
399
400/**
401 * Disarms an int 3 breakpoint.
402 * This is used to implement both DBGFR3BpClear() and DBGFR3BpDisable().
403 *
404 * @returns VBox status code.
405 * @param pUVM The user mode VM handle.
406 * @param pBp The breakpoint.
407 */
408static int dbgfR3BpInt3Disarm(PUVM pUVM, PDBGFBP pBp)
409{
410 /* @todo SMP support! */
411 VMCPUID idCpu = 0;
412
413 /*
414 * Check that the current byte is the int3 instruction, and restore the original one.
415 * We currently ignore invalid bytes.
416 */
417 DBGFADDRESS Addr;
418 DBGFR3AddrFromFlat(pUVM, &Addr, pBp->u.Int3.GCPtr);
419 uint8_t bCurrent;
420 int rc = DBGFR3MemRead(pUVM, idCpu, &Addr, &bCurrent, 1);
421 if (bCurrent == 0xcc)
422 rc = DBGFR3MemWrite(pUVM, idCpu, &Addr, &pBp->u.Int3.bOrg, 1);
423 return rc;
424}
425
426
427/**
428 * Sets a register breakpoint.
429 *
430 * @returns VBox status code.
431 * @param pUVM The user mode VM handle.
432 * @param pAddress The address of the breakpoint.
433 * @param piHitTrigger The hit count at which the breakpoint start triggering.
434 * Use 0 (or 1) if it's gonna trigger at once.
435 * @param piHitDisable The hit count which disables the breakpoint.
436 * Use ~(uint64_t) if it's never gonna be disabled.
437 * @param fType The access type (one of the X86_DR7_RW_* defines).
438 * @param cb The access size - 1,2,4 or 8 (the latter is AMD64 long mode only.
439 * Must be 1 if fType is X86_DR7_RW_EO.
440 * @param piBp Where to store the breakpoint id. (optional)
441 * @thread EMT
442 * @internal
443 */
444static DECLCALLBACK(int) dbgfR3BpSetReg(PUVM pUVM, PCDBGFADDRESS pAddress, uint64_t *piHitTrigger, uint64_t *piHitDisable,
445 uint8_t fType, uint8_t cb, uint32_t *piBp)
446{
447 /*
448 * Validate input.
449 */
450 PVM pVM = pUVM->pVM;
451 VM_ASSERT_VALID_EXT_RETURN(pVM, VERR_INVALID_VM_HANDLE);
452 if (!DBGFR3AddrIsValid(pUVM, pAddress))
453 return VERR_INVALID_PARAMETER;
454 if (*piHitTrigger > *piHitDisable)
455 return VERR_INVALID_PARAMETER;
456 AssertMsgReturn(!piBp || VALID_PTR(piBp), ("piBp=%p\n", piBp), VERR_INVALID_POINTER);
457 if (piBp)
458 *piBp = ~0;
459 switch (fType)
460 {
461 case X86_DR7_RW_EO:
462 if (cb == 1)
463 break;
464 AssertMsgFailed(("fType=%#x cb=%d != 1\n", fType, cb));
465 return VERR_INVALID_PARAMETER;
466 case X86_DR7_RW_IO:
467 case X86_DR7_RW_RW:
468 case X86_DR7_RW_WO:
469 break;
470 default:
471 AssertMsgFailed(("fType=%#x\n", fType));
472 return VERR_INVALID_PARAMETER;
473 }
474 switch (cb)
475 {
476 case 1:
477 case 2:
478 case 4:
479 break;
480 default:
481 AssertMsgFailed(("cb=%#x\n", cb));
482 return VERR_INVALID_PARAMETER;
483 }
484
485 /*
486 * Check if the breakpoint already exists.
487 */
488 PDBGFBP pBp = dbgfR3BpGetByAddr(pVM, DBGFBPTYPE_REG, pAddress->FlatPtr);
489 if ( pBp
490 && pBp->u.Reg.cb == cb
491 && pBp->u.Reg.fType == fType)
492 {
493 int rc = VINF_SUCCESS;
494 if (!pBp->fEnabled)
495 rc = dbgfR3BpRegArm(pVM, pBp);
496 if (RT_SUCCESS(rc))
497 {
498 rc = VINF_DBGF_BP_ALREADY_EXIST;
499 if (piBp)
500 *piBp = pBp->iBp;
501 }
502 return rc;
503 }
504
505 /*
506 * Allocate and initialize the bp.
507 */
508 pBp = dbgfR3BpAlloc(pVM, DBGFBPTYPE_REG);
509 if (!pBp)
510 return VERR_DBGF_NO_MORE_BP_SLOTS;
511 pBp->iHitTrigger = *piHitTrigger;
512 pBp->iHitDisable = *piHitDisable;
513 Assert(pBp->iBp == pBp->u.Reg.iReg);
514 pBp->u.Reg.GCPtr = pAddress->FlatPtr;
515 pBp->u.Reg.fType = fType;
516 pBp->u.Reg.cb = cb;
517 ASMCompilerBarrier();
518 pBp->fEnabled = true;
519
520 /*
521 * Arm the breakpoint.
522 */
523 int rc = dbgfR3BpRegArm(pVM, pBp);
524 if (RT_SUCCESS(rc))
525 {
526 if (piBp)
527 *piBp = pBp->iBp;
528 }
529 else
530 dbgfR3BpFree(pVM, pBp);
531
532 return rc;
533}
534
535
536/**
537 * Sets a register breakpoint.
538 *
539 * @returns VBox status code.
540 * @param pUVM The user mode VM handle.
541 * @param pAddress The address of the breakpoint.
542 * @param iHitTrigger The hit count at which the breakpoint start triggering.
543 * Use 0 (or 1) if it's gonna trigger at once.
544 * @param iHitDisable The hit count which disables the breakpoint.
545 * Use ~(uint64_t) if it's never gonna be disabled.
546 * @param fType The access type (one of the X86_DR7_RW_* defines).
547 * @param cb The access size - 1,2,4 or 8 (the latter is AMD64 long mode only.
548 * Must be 1 if fType is X86_DR7_RW_EO.
549 * @param piBp Where to store the breakpoint id. (optional)
550 * @thread Any thread.
551 */
552VMMR3DECL(int) DBGFR3BpSetReg(PUVM pUVM, PCDBGFADDRESS pAddress, uint64_t iHitTrigger, uint64_t iHitDisable,
553 uint8_t fType, uint8_t cb, uint32_t *piBp)
554{
555 /*
556 * This must be done on EMT.
557 */
558 int rc = VMR3ReqCallWaitU(pUVM, VMCPUID_ANY, (PFNRT)dbgfR3BpSetReg, 7,
559 pUVM, pAddress, &iHitTrigger, &iHitDisable, fType, cb, piBp);
560 LogFlow(("DBGFR3BpSetReg: returns %Rrc\n", rc));
561 return rc;
562
563}
564
565
566/**
567 * @callback_method_impl{FNVMMEMTRENDEZVOUS}
568 */
569DECLCALLBACK(VBOXSTRICTRC) dbgfR3BpRegRecalcOnCpu(PVM pVM, PVMCPU pVCpu, void *pvUser)
570{
571 NOREF(pVM); NOREF(pvUser);
572 return CPUMRecalcHyperDRx(pVCpu, UINT8_MAX, false);
573}
574
575
576/**
577 * Arms a debug register breakpoint.
578 * This is used to implement both DBGFR3BpSetReg() and DBGFR3BpEnable().
579 *
580 * @returns VBox status code.
581 * @param pVM The cross context VM structure.
582 * @param pBp The breakpoint.
583 */
584static int dbgfR3BpRegArm(PVM pVM, PDBGFBP pBp)
585{
586 Assert(pBp->fEnabled);
587 return VMMR3EmtRendezvous(pVM, VMMEMTRENDEZVOUS_FLAGS_TYPE_ALL_AT_ONCE, dbgfR3BpRegRecalcOnCpu, NULL);
588}
589
590
591/**
592 * Disarms a debug register breakpoint.
593 * This is used to implement both DBGFR3BpClear() and DBGFR3BpDisable().
594 *
595 * @returns VBox status code.
596 * @param pVM The cross context VM structure.
597 * @param pBp The breakpoint.
598 */
599static int dbgfR3BpRegDisarm(PVM pVM, PDBGFBP pBp)
600{
601 Assert(!pBp->fEnabled);
602 return VMMR3EmtRendezvous(pVM, VMMEMTRENDEZVOUS_FLAGS_TYPE_ALL_AT_ONCE, dbgfR3BpRegRecalcOnCpu, NULL);
603}
604
605
606/**
607 * EMT worker for DBGFR3BpSetREM().
608 *
609 * @returns VBox status code.
610 * @param pUVM The user mode VM handle.
611 * @param pAddress The address of the breakpoint.
612 * @param piHitTrigger The hit count at which the breakpoint start triggering.
613 * Use 0 (or 1) if it's gonna trigger at once.
614 * @param piHitDisable The hit count which disables the breakpoint.
615 * Use ~(uint64_t) if it's never gonna be disabled.
616 * @param piBp Where to store the breakpoint id. (optional)
617 * @thread EMT
618 * @internal
619 */
620static DECLCALLBACK(int) dbgfR3BpSetREM(PUVM pUVM, PCDBGFADDRESS pAddress, uint64_t *piHitTrigger,
621 uint64_t *piHitDisable, uint32_t *piBp)
622{
623 /*
624 * Validate input.
625 */
626 PVM pVM = pUVM->pVM;
627 VM_ASSERT_VALID_EXT_RETURN(pVM, VERR_INVALID_VM_HANDLE);
628 if (!DBGFR3AddrIsValid(pUVM, pAddress))
629 return VERR_INVALID_PARAMETER;
630 if (*piHitTrigger > *piHitDisable)
631 return VERR_INVALID_PARAMETER;
632 AssertMsgReturn(!piBp || VALID_PTR(piBp), ("piBp=%p\n", piBp), VERR_INVALID_POINTER);
633 if (piBp)
634 *piBp = ~0;
635
636
637 /*
638 * Check if the breakpoint already exists.
639 */
640 PDBGFBP pBp = dbgfR3BpGetByAddr(pVM, DBGFBPTYPE_REM, pAddress->FlatPtr);
641 if (pBp)
642 {
643 int rc = VINF_SUCCESS;
644 if (!pBp->fEnabled)
645#ifdef VBOX_WITH_REM
646 rc = REMR3BreakpointSet(pVM, pBp->u.Rem.GCPtr);
647#else
648 rc = IEMBreakpointSet(pVM, pBp->u.Rem.GCPtr);
649#endif
650 if (RT_SUCCESS(rc))
651 {
652 rc = VINF_DBGF_BP_ALREADY_EXIST;
653 if (piBp)
654 *piBp = pBp->iBp;
655 }
656 return rc;
657 }
658
659 /*
660 * Allocate and initialize the bp.
661 */
662 pBp = dbgfR3BpAlloc(pVM, DBGFBPTYPE_REM);
663 if (!pBp)
664 return VERR_DBGF_NO_MORE_BP_SLOTS;
665 pBp->u.Rem.GCPtr = pAddress->FlatPtr;
666 pBp->iHitTrigger = *piHitTrigger;
667 pBp->iHitDisable = *piHitDisable;
668 ASMCompilerBarrier();
669 pBp->fEnabled = true;
670
671 /*
672 * Now ask REM to set the breakpoint.
673 */
674#ifdef VBOX_WITH_REM
675 int rc = REMR3BreakpointSet(pVM, pAddress->FlatPtr);
676#else
677 int rc = IEMBreakpointSet(pVM, pAddress->FlatPtr);
678#endif
679 if (RT_SUCCESS(rc))
680 {
681 if (piBp)
682 *piBp = pBp->iBp;
683 }
684 else
685 dbgfR3BpFree(pVM, pBp);
686
687 return rc;
688}
689
690
691/**
692 * Sets a recompiler breakpoint.
693 *
694 * @returns VBox status code.
695 * @param pUVM The user mode VM handle.
696 * @param pAddress The address of the breakpoint.
697 * @param iHitTrigger The hit count at which the breakpoint start triggering.
698 * Use 0 (or 1) if it's gonna trigger at once.
699 * @param iHitDisable The hit count which disables the breakpoint.
700 * Use ~(uint64_t) if it's never gonna be disabled.
701 * @param piBp Where to store the breakpoint id. (optional)
702 * @thread Any thread.
703 */
704VMMR3DECL(int) DBGFR3BpSetREM(PUVM pUVM, PCDBGFADDRESS pAddress, uint64_t iHitTrigger, uint64_t iHitDisable, uint32_t *piBp)
705{
706 /*
707 * This must be done on EMT.
708 */
709 int rc = VMR3ReqCallWaitU(pUVM, VMCPUID_ANY, (PFNRT)dbgfR3BpSetREM, 5,
710 pUVM, pAddress, &iHitTrigger, &iHitDisable, piBp);
711 LogFlow(("DBGFR3BpSetREM: returns %Rrc\n", rc));
712 return rc;
713}
714
715
716
717/**
718 * Updates IOM on whether we've got any armed I/O port or MMIO breakpoints.
719 *
720 * @returns VINF_SUCCESS
721 * @param pVM The cross context VM structure.
722 */
723static int dbgfR3BpUpdateIom(PVM pVM)
724{
725 unsigned cPortIo = 0;
726 unsigned cMmio = 0;
727 for (uint32_t iBp = 0; iBp < RT_ELEMENTS(pVM->dbgf.s.aBreakpoints); iBp++)
728 if (pVM->dbgf.s.aBreakpoints[iBp].fEnabled)
729 {
730 if (pVM->dbgf.s.aBreakpoints[iBp].enmType == DBGFBPTYPE_PORT_IO)
731 cPortIo++;
732 else if (pVM->dbgf.s.aBreakpoints[iBp].enmType == DBGFBPTYPE_MMIO)
733 cMmio++;
734 }
735
736 pVM->dbgf.s.fHasPortIoBps = cPortIo != 0;
737 pVM->dbgf.s.fHasMmioBps = cMmio != 0;
738
739 IOMR3NotifyBreakpointCountChange(pVM, cPortIo, cMmio);
740 return VINF_SUCCESS;
741}
742
743
744/**
745 * EMT worker for DBGFR3BpSetPortIo.
746 *
747 * @returns VBox status code.
748 * @param pUVM The user mode VM handle.
749 * @param uPort The first I/O port.
750 * @param cPorts The number of I/O ports.
751 * @param fAccess The access we want to break on.
752 * @param piHitTrigger The hit count at which the breakpoint start triggering.
753 * Use 0 (or 1) if it's gonna trigger at once.
754 * @param piHitDisable The hit count which disables the breakpoint.
755 * Use ~(uint64_t) if it's never gonna be disabled.
756 * @param piBp Where to store the breakpoint ID.
757 */
758static DECLCALLBACK(int) dbgfR3BpSetPortIo(PUVM pUVM, RTIOPORT uPort, RTIOPORT cPorts, uint32_t fAccess,
759 uint64_t const *piHitTrigger, uint64_t const *piHitDisable, uint32_t *piBp)
760{
761 /*
762 * Validate input.
763 */
764 PVM pVM = pUVM->pVM;
765 VM_ASSERT_VALID_EXT_RETURN(pVM, VERR_INVALID_VM_HANDLE);
766 *piBp = ~0;
767
768 /*
769 * Check if the breakpoint already exists.
770 */
771 for (uint32_t i = 0; i < RT_ELEMENTS(pVM->dbgf.s.aBreakpoints); i++)
772 if ( pVM->dbgf.s.aBreakpoints[i].enmType == DBGFBPTYPE_PORT_IO
773 && pVM->dbgf.s.aBreakpoints[i].u.PortIo.uPort == uPort
774 && pVM->dbgf.s.aBreakpoints[i].u.PortIo.cPorts == cPorts
775 && pVM->dbgf.s.aBreakpoints[i].u.PortIo.fAccess == fAccess)
776 {
777 if (!pVM->dbgf.s.aBreakpoints[i].fEnabled)
778 {
779 pVM->dbgf.s.aBreakpoints[i].fEnabled = true;
780 dbgfR3BpUpdateIom(pVM);
781 }
782 *piBp = pVM->dbgf.s.aBreakpoints[i].iBp;
783 return VINF_DBGF_BP_ALREADY_EXIST;
784 }
785
786 /*
787 * Allocate and initialize the breakpoint.
788 */
789 PDBGFBP pBp = dbgfR3BpAlloc(pVM, DBGFBPTYPE_PORT_IO);
790 if (!pBp)
791 return VERR_DBGF_NO_MORE_BP_SLOTS;
792 pBp->iHitTrigger = *piHitTrigger;
793 pBp->iHitDisable = *piHitDisable;
794 pBp->u.PortIo.uPort = uPort;
795 pBp->u.PortIo.cPorts = cPorts;
796 pBp->u.PortIo.fAccess = fAccess;
797 ASMCompilerBarrier();
798 pBp->fEnabled = true;
799
800 /*
801 * Tell IOM.
802 */
803 dbgfR3BpUpdateIom(pVM);
804 *piBp = pBp->iBp;
805 return VINF_SUCCESS;
806}
807
808
809/**
810 * Sets an I/O port breakpoint.
811 *
812 * @returns VBox status code.
813 * @param pUvm The user mode VM handle.
814 * @param uPort The first I/O port.
815 * @param cPorts The number of I/O ports, see DBGFBPIOACCESS_XXX.
816 * @param fAccess The access we want to break on.
817 * @param iHitTrigger The hit count at which the breakpoint start
818 * triggering. Use 0 (or 1) if it's gonna trigger at
819 * once.
820 * @param iHitDisable The hit count which disables the breakpoint.
821 * Use ~(uint64_t) if it's never gonna be disabled.
822 * @param piBp Where to store the breakpoint ID. Optional.
823 */
824VMMR3DECL(int) DBGFR3BpSetPortIo(PUVM pUVM, RTIOPORT uPort, RTIOPORT cPorts, uint32_t fAccess,
825 uint64_t iHitTrigger, uint64_t iHitDisable, uint32_t *piBp)
826{
827 AssertReturn(!(fAccess & ~DBGFBPIOACCESS_VALID_MASK_PORT_IO), VERR_INVALID_FLAGS);
828 AssertReturn(fAccess, VERR_INVALID_FLAGS);
829 if (iHitTrigger > iHitDisable)
830 return VERR_INVALID_PARAMETER;
831 AssertPtrNullReturn(piBp, VERR_INVALID_POINTER);
832 AssertReturn(cPorts > 0, VERR_OUT_OF_RANGE);
833 AssertReturn((RTIOPORT)(uPort + cPorts) < uPort, VERR_OUT_OF_RANGE);
834
835 /*
836 * This must be done on EMT.
837 */
838 uint32_t iBp = -1;
839 int rc = VMR3ReqCallWaitU(pUVM, VMCPUID_ANY, (PFNRT)dbgfR3BpSetPortIo, 7,
840 pUVM, uPort, cPorts, fAccess, &iHitTrigger, &iHitDisable, piBp);
841 if (piBp)
842 *piBp = iBp;
843 LogFlow(("DBGFR3BpSetPortIo: returns %Rrc *piBp=%d\n", rc, iBp));
844 return rc;
845}
846
847
848/**
849 * EMT worker for DBGFR3BpSetMmio.
850 *
851 * @returns VBox status code.
852 * @param pUVM The user mode VM handle.
853 * @param pGCPhys The start of the MMIO range to break on.
854 * @param cb The the size of the MMIO range.
855 * @param fAccess The access we want to break on.
856 * @param piHitTrigger The hit count at which the breakpoint start triggering.
857 * Use 0 (or 1) if it's gonna trigger at once.
858 * @param piHitDisable The hit count which disables the breakpoint.
859 * Use ~(uint64_t) if it's never gonna be disabled.
860 * @param piBp Where to store the breakpoint ID.
861 */
862static DECLCALLBACK(int) dbgfR3BpSetMmio(PUVM pUVM, PCRTGCPHYS pGCPhys, uint32_t cb, uint32_t fAccess,
863 uint64_t const *piHitTrigger, uint64_t const *piHitDisable, uint32_t *piBp)
864{
865 RTGCPHYS const GCPhys = *pGCPhys;
866
867 /*
868 * Validate input.
869 */
870 PVM pVM = pUVM->pVM;
871 VM_ASSERT_VALID_EXT_RETURN(pVM, VERR_INVALID_VM_HANDLE);
872 *piBp = ~0;
873
874 /*
875 * Check if the breakpoint already exists.
876 */
877 for (uint32_t i = 0; i < RT_ELEMENTS(pVM->dbgf.s.aBreakpoints); i++)
878 if ( pVM->dbgf.s.aBreakpoints[i].enmType == DBGFBPTYPE_MMIO
879 && pVM->dbgf.s.aBreakpoints[i].u.Mmio.PhysAddr == GCPhys
880 && pVM->dbgf.s.aBreakpoints[i].u.Mmio.cb == cb
881 && pVM->dbgf.s.aBreakpoints[i].u.Mmio.fAccess == fAccess)
882 {
883 if (!pVM->dbgf.s.aBreakpoints[i].fEnabled)
884 {
885 pVM->dbgf.s.aBreakpoints[i].fEnabled = true;
886 dbgfR3BpUpdateIom(pVM);
887 }
888 *piBp = pVM->dbgf.s.aBreakpoints[i].iBp;
889 return VINF_DBGF_BP_ALREADY_EXIST;
890 }
891
892 /*
893 * Allocate and initialize the breakpoint.
894 */
895 PDBGFBP pBp = dbgfR3BpAlloc(pVM, DBGFBPTYPE_PORT_IO);
896 if (!pBp)
897 return VERR_DBGF_NO_MORE_BP_SLOTS;
898 pBp->iHitTrigger = *piHitTrigger;
899 pBp->iHitDisable = *piHitDisable;
900 pBp->u.Mmio.PhysAddr = GCPhys;
901 pBp->u.Mmio.cb = cb;
902 pBp->u.Mmio.fAccess = fAccess;
903 ASMCompilerBarrier();
904 pBp->fEnabled = true;
905
906 /*
907 * Tell IOM.
908 */
909 dbgfR3BpUpdateIom(pVM);
910 *piBp = pBp->iBp;
911 return VINF_SUCCESS;
912}
913
914
915/**
916 * Sets a memory mapped I/O breakpoint.
917 *
918 * @returns VBox status code.
919 * @param pUvm The user mode VM handle.
920 * @param GCPhys The first MMIO address.
921 * @param cb The size of the MMIO range to break on.
922 * @param fAccess The access we want to break on.
923 * @param iHitTrigger The hit count at which the breakpoint start
924 * triggering. Use 0 (or 1) if it's gonna trigger at
925 * once.
926 * @param iHitDisable The hit count which disables the breakpoint.
927 * Use ~(uint64_t) if it's never gonna be disabled.
928 * @param piBp Where to store the breakpoint ID. Optional.
929 */
930VMMR3DECL(int) DBGFR3BpSetMmio(PUVM pUVM, RTGCPHYS GCPhys, uint32_t cb, uint32_t fAccess,
931 uint64_t iHitTrigger, uint64_t iHitDisable, uint32_t *piBp)
932{
933 AssertReturn(!(fAccess & ~DBGFBPIOACCESS_VALID_MASK_MMIO), VERR_INVALID_FLAGS);
934 AssertReturn(fAccess, VERR_INVALID_FLAGS);
935 if (iHitTrigger > iHitDisable)
936 return VERR_INVALID_PARAMETER;
937 AssertPtrNullReturn(piBp, VERR_INVALID_POINTER);
938 AssertReturn(cb, VERR_OUT_OF_RANGE);
939 AssertReturn(GCPhys + cb < GCPhys, VERR_OUT_OF_RANGE);
940
941 /*
942 * This must be done on EMT.
943 */
944 uint32_t iBp = -1;
945 int rc = VMR3ReqCallWaitU(pUVM, VMCPUID_ANY, (PFNRT)dbgfR3BpSetMmio, 7,
946 pUVM, &GCPhys, cb, fAccess, &iHitTrigger, &iHitDisable, piBp);
947 if (piBp)
948 *piBp = iBp;
949 LogFlow(("DBGFR3BpSetMmio: returns %Rrc *piBp=%d\n", rc, iBp));
950 return rc;
951}
952
953
954/**
955 * EMT worker for DBGFR3BpClear().
956 *
957 * @returns VBox status code.
958 * @param pUVM The user mode VM handle.
959 * @param iBp The id of the breakpoint which should be removed (cleared).
960 * @thread EMT
961 * @internal
962 */
963static DECLCALLBACK(int) dbgfR3BpClear(PUVM pUVM, uint32_t iBp)
964{
965 /*
966 * Validate input.
967 */
968 PVM pVM = pUVM->pVM;
969 VM_ASSERT_VALID_EXT_RETURN(pVM, VERR_INVALID_VM_HANDLE);
970 PDBGFBP pBp = dbgfR3BpGet(pVM, iBp);
971 if (!pBp)
972 return VERR_DBGF_BP_NOT_FOUND;
973
974 /*
975 * Disarm the breakpoint if it's enabled.
976 */
977 if (pBp->fEnabled)
978 {
979 pBp->fEnabled = false;
980 int rc;
981 switch (pBp->enmType)
982 {
983 case DBGFBPTYPE_REG:
984 rc = dbgfR3BpRegDisarm(pVM, pBp);
985 break;
986
987 case DBGFBPTYPE_INT3:
988 rc = dbgfR3BpInt3Disarm(pUVM, pBp);
989 break;
990
991 case DBGFBPTYPE_REM:
992#ifdef VBOX_WITH_REM
993 rc = REMR3BreakpointClear(pVM, pBp->u.Rem.GCPtr);
994#else
995 rc = IEMBreakpointClear(pVM, pBp->u.Rem.GCPtr);
996#endif
997 break;
998
999 case DBGFBPTYPE_PORT_IO:
1000 case DBGFBPTYPE_MMIO:
1001 rc = dbgfR3BpUpdateIom(pVM);
1002 break;
1003
1004 default:
1005 AssertMsgFailedReturn(("Invalid enmType=%d!\n", pBp->enmType), VERR_IPE_NOT_REACHED_DEFAULT_CASE);
1006 }
1007 AssertRCReturn(rc, rc);
1008 }
1009
1010 /*
1011 * Free the breakpoint.
1012 */
1013 dbgfR3BpFree(pVM, pBp);
1014 return VINF_SUCCESS;
1015}
1016
1017
1018/**
1019 * Clears a breakpoint.
1020 *
1021 * @returns VBox status code.
1022 * @param pUVM The user mode VM handle.
1023 * @param iBp The id of the breakpoint which should be removed (cleared).
1024 * @thread Any thread.
1025 */
1026VMMR3DECL(int) DBGFR3BpClear(PUVM pUVM, uint32_t iBp)
1027{
1028 /*
1029 * This must be done on EMT.
1030 */
1031 int rc = VMR3ReqCallWaitU(pUVM, VMCPUID_ANY, (PFNRT)dbgfR3BpClear, 2, pUVM, iBp);
1032 LogFlow(("DBGFR3BpClear: returns %Rrc\n", rc));
1033 return rc;
1034}
1035
1036
1037/**
1038 * EMT worker for DBGFR3BpEnable().
1039 *
1040 * @returns VBox status code.
1041 * @param pUVM The user mode VM handle.
1042 * @param iBp The id of the breakpoint which should be enabled.
1043 * @thread EMT
1044 * @internal
1045 */
1046static DECLCALLBACK(int) dbgfR3BpEnable(PUVM pUVM, uint32_t iBp)
1047{
1048 /*
1049 * Validate input.
1050 */
1051 PVM pVM = pUVM->pVM;
1052 VM_ASSERT_VALID_EXT_RETURN(pVM, VERR_INVALID_VM_HANDLE);
1053 PDBGFBP pBp = dbgfR3BpGet(pVM, iBp);
1054 if (!pBp)
1055 return VERR_DBGF_BP_NOT_FOUND;
1056
1057 /*
1058 * Already enabled?
1059 */
1060 if (pBp->fEnabled)
1061 return VINF_DBGF_BP_ALREADY_ENABLED;
1062
1063 /*
1064 * Remove the breakpoint.
1065 */
1066 int rc;
1067 pBp->fEnabled = true;
1068 switch (pBp->enmType)
1069 {
1070 case DBGFBPTYPE_REG:
1071 rc = dbgfR3BpRegArm(pVM, pBp);
1072 break;
1073
1074 case DBGFBPTYPE_INT3:
1075 rc = dbgfR3BpInt3Arm(pUVM, pBp);
1076 break;
1077
1078 case DBGFBPTYPE_REM:
1079#ifdef VBOX_WITH_REM
1080 rc = REMR3BreakpointSet(pVM, pBp->u.Rem.GCPtr);
1081#else
1082 rc = IEMBreakpointSet(pVM, pBp->u.Rem.GCPtr);
1083#endif
1084 break;
1085
1086 case DBGFBPTYPE_PORT_IO:
1087 case DBGFBPTYPE_MMIO:
1088 rc = dbgfR3BpUpdateIom(pVM);
1089 break;
1090
1091 default:
1092 AssertMsgFailedReturn(("Invalid enmType=%d!\n", pBp->enmType), VERR_IPE_NOT_REACHED_DEFAULT_CASE);
1093 }
1094 if (RT_FAILURE(rc))
1095 pBp->fEnabled = false;
1096
1097 return rc;
1098}
1099
1100
1101/**
1102 * Enables a breakpoint.
1103 *
1104 * @returns VBox status code.
1105 * @param pUVM The user mode VM handle.
1106 * @param iBp The id of the breakpoint which should be enabled.
1107 * @thread Any thread.
1108 */
1109VMMR3DECL(int) DBGFR3BpEnable(PUVM pUVM, uint32_t iBp)
1110{
1111 /*
1112 * This must be done on EMT.
1113 */
1114 int rc = VMR3ReqCallWaitU(pUVM, VMCPUID_ANY, (PFNRT)dbgfR3BpEnable, 2, pUVM, iBp);
1115 LogFlow(("DBGFR3BpEnable: returns %Rrc\n", rc));
1116 return rc;
1117}
1118
1119
1120/**
1121 * EMT worker for DBGFR3BpDisable().
1122 *
1123 * @returns VBox status code.
1124 * @param pUVM The user mode VM handle.
1125 * @param iBp The id of the breakpoint which should be disabled.
1126 * @thread EMT
1127 * @internal
1128 */
1129static DECLCALLBACK(int) dbgfR3BpDisable(PUVM pUVM, uint32_t iBp)
1130{
1131 /*
1132 * Validate input.
1133 */
1134 PVM pVM = pUVM->pVM;
1135 VM_ASSERT_VALID_EXT_RETURN(pVM, VERR_INVALID_VM_HANDLE);
1136 PDBGFBP pBp = dbgfR3BpGet(pVM, iBp);
1137 if (!pBp)
1138 return VERR_DBGF_BP_NOT_FOUND;
1139
1140 /*
1141 * Already enabled?
1142 */
1143 if (!pBp->fEnabled)
1144 return VINF_DBGF_BP_ALREADY_DISABLED;
1145
1146 /*
1147 * Remove the breakpoint.
1148 */
1149 pBp->fEnabled = false;
1150 int rc;
1151 switch (pBp->enmType)
1152 {
1153 case DBGFBPTYPE_REG:
1154 rc = dbgfR3BpRegDisarm(pVM, pBp);
1155 break;
1156
1157 case DBGFBPTYPE_INT3:
1158 rc = dbgfR3BpInt3Disarm(pUVM, pBp);
1159 break;
1160
1161 case DBGFBPTYPE_REM:
1162#ifdef VBOX_WITH_REM
1163 rc = REMR3BreakpointClear(pVM, pBp->u.Rem.GCPtr);
1164#else
1165 rc = IEMBreakpointClear(pVM, pBp->u.Rem.GCPtr);
1166#endif
1167 break;
1168
1169 case DBGFBPTYPE_PORT_IO:
1170 case DBGFBPTYPE_MMIO:
1171 rc = dbgfR3BpUpdateIom(pVM);
1172 break;
1173
1174 default:
1175 AssertMsgFailedReturn(("Invalid enmType=%d!\n", pBp->enmType), VERR_IPE_NOT_REACHED_DEFAULT_CASE);
1176 }
1177
1178 return rc;
1179}
1180
1181
1182/**
1183 * Disables a breakpoint.
1184 *
1185 * @returns VBox status code.
1186 * @param pUVM The user mode VM handle.
1187 * @param iBp The id of the breakpoint which should be disabled.
1188 * @thread Any thread.
1189 */
1190VMMR3DECL(int) DBGFR3BpDisable(PUVM pUVM, uint32_t iBp)
1191{
1192 /*
1193 * This must be done on EMT.
1194 */
1195 int rc = VMR3ReqCallWaitU(pUVM, VMCPUID_ANY, (PFNRT)dbgfR3BpDisable, 2, pUVM, iBp);
1196 LogFlow(("DBGFR3BpDisable: returns %Rrc\n", rc));
1197 return rc;
1198}
1199
1200
1201/**
1202 * EMT worker for DBGFR3BpEnum().
1203 *
1204 * @returns VBox status code.
1205 * @param pUVM The user mode VM handle.
1206 * @param pfnCallback The callback function.
1207 * @param pvUser The user argument to pass to the callback.
1208 * @thread EMT
1209 * @internal
1210 */
1211static DECLCALLBACK(int) dbgfR3BpEnum(PUVM pUVM, PFNDBGFBPENUM pfnCallback, void *pvUser)
1212{
1213 /*
1214 * Validate input.
1215 */
1216 PVM pVM = pUVM->pVM;
1217 VM_ASSERT_VALID_EXT_RETURN(pVM, VERR_INVALID_VM_HANDLE);
1218 AssertPtrReturn(pfnCallback, VERR_INVALID_POINTER);
1219
1220 /*
1221 * Enumerate the hardware breakpoints.
1222 */
1223 unsigned i;
1224 for (i = 0; i < RT_ELEMENTS(pVM->dbgf.s.aHwBreakpoints); i++)
1225 if (pVM->dbgf.s.aHwBreakpoints[i].enmType != DBGFBPTYPE_FREE)
1226 {
1227 int rc = pfnCallback(pUVM, pvUser, &pVM->dbgf.s.aHwBreakpoints[i]);
1228 if (RT_FAILURE(rc))
1229 return rc;
1230 }
1231
1232 /*
1233 * Enumerate the other breakpoints.
1234 */
1235 for (i = 0; i < RT_ELEMENTS(pVM->dbgf.s.aBreakpoints); i++)
1236 if (pVM->dbgf.s.aBreakpoints[i].enmType != DBGFBPTYPE_FREE)
1237 {
1238 int rc = pfnCallback(pUVM, pvUser, &pVM->dbgf.s.aBreakpoints[i]);
1239 if (RT_FAILURE(rc))
1240 return rc;
1241 }
1242
1243 return VINF_SUCCESS;
1244}
1245
1246
1247/**
1248 * Enumerate the breakpoints.
1249 *
1250 * @returns VBox status code.
1251 * @param pUVM The user mode VM handle.
1252 * @param pfnCallback The callback function.
1253 * @param pvUser The user argument to pass to the callback.
1254 * @thread Any thread but the callback will be called from EMT.
1255 */
1256VMMR3DECL(int) DBGFR3BpEnum(PUVM pUVM, PFNDBGFBPENUM pfnCallback, void *pvUser)
1257{
1258 /*
1259 * This must be done on EMT.
1260 */
1261 int rc = VMR3ReqPriorityCallWaitU(pUVM, VMCPUID_ANY, (PFNRT)dbgfR3BpEnum, 3, pUVM, pfnCallback, pvUser);
1262 LogFlow(("DBGFR3BpClear: returns %Rrc\n", rc));
1263 return rc;
1264}
1265
Note: See TracBrowser for help on using the repository browser.

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