VirtualBox

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

Last change on this file since 87346 was 87346, checked in by vboxsync, 3 years ago

VMM/CPUM: Dropped the fForceHyper parameter of CPUMRecalcHyperDRx. It seems to stem from some confusion in the HM implementation about how to handle DRx registers. The DBGF breakpoints shall always take precedence over the guest ones.

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

© 2023 Oracle
ContactPrivacy policyTerms of Use