VirtualBox

source: vbox/trunk/src/VBox/VMM/VMMAll/DBGFAllBp.cpp@ 93115

Last change on this file since 93115 was 93115, checked in by vboxsync, 2 years ago

scm --update-copyright-year

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 23.6 KB
Line 
1/* $Id: DBGFAllBp.cpp 93115 2022-01-01 11:31:46Z vboxsync $ */
2/** @file
3 * DBGF - Debugger Facility, All Context breakpoint management part.
4 */
5
6/*
7 * Copyright (C) 2006-2022 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#define VMCPU_INCL_CPUM_GST_CTX
24#include <VBox/vmm/dbgf.h>
25#include <VBox/vmm/iem.h>
26#include <VBox/vmm/pgm.h>
27#include <VBox/vmm/selm.h>
28#include <VBox/log.h>
29#include "DBGFInternal.h"
30#include <VBox/vmm/vmcc.h>
31#include <VBox/err.h>
32#include <iprt/assert.h>
33
34#include "DBGFInline.h"
35
36
37#ifdef IN_RC
38# error "You lucky person have the pleasure to implement the raw mode part for this!"
39#endif
40
41
42/**
43 * Returns the internal breakpoint state for the given handle.
44 *
45 * @returns Pointer to the internal breakpoint state or NULL if the handle is invalid.
46 * @param pVM The ring-0 VM structure pointer.
47 * @param hBp The breakpoint handle to resolve.
48 * @param ppBpR0 Where to store the pointer to the ring-0 only part of the breakpoint
49 * on success, optional.
50 */
51#ifdef IN_RING0
52DECLINLINE(PDBGFBPINT) dbgfBpGetByHnd(PVMCC pVM, DBGFBP hBp, PDBGFBPINTR0 *ppBpR0)
53#else
54DECLINLINE(PDBGFBPINT) dbgfBpGetByHnd(PVMCC pVM, DBGFBP hBp)
55#endif
56{
57 uint32_t idChunk = DBGF_BP_HND_GET_CHUNK_ID(hBp);
58 uint32_t idxEntry = DBGF_BP_HND_GET_ENTRY(hBp);
59
60 AssertReturn(idChunk < DBGF_BP_CHUNK_COUNT, NULL);
61 AssertReturn(idxEntry < DBGF_BP_COUNT_PER_CHUNK, NULL);
62
63#ifdef IN_RING0
64 PDBGFBPCHUNKR0 pBpChunk = &pVM->dbgfr0.s.aBpChunks[idChunk];
65 AssertPtrReturn(pBpChunk->CTX_SUFF(paBpBaseShared), NULL);
66
67 if (ppBpR0)
68 *ppBpR0 = &pBpChunk->paBpBaseR0Only[idxEntry];
69 return &pBpChunk->CTX_SUFF(paBpBaseShared)[idxEntry];
70
71#elif defined(IN_RING3)
72 PUVM pUVM = pVM->pUVM;
73 PDBGFBPCHUNKR3 pBpChunk = &pUVM->dbgf.s.aBpChunks[idChunk];
74 AssertPtrReturn(pBpChunk->CTX_SUFF(pBpBase), NULL);
75
76 return &pBpChunk->CTX_SUFF(pBpBase)[idxEntry];
77
78#else
79# error "Unsupported context"
80#endif
81}
82
83
84/**
85 * Returns the pointer to the L2 table entry from the given index.
86 *
87 * @returns Current context pointer to the L2 table entry or NULL if the provided index value is invalid.
88 * @param pVM The cross context VM structure.
89 * @param idxL2 The L2 table index to resolve.
90 *
91 * @note The content of the resolved L2 table entry is not validated!.
92 */
93DECLINLINE(PCDBGFBPL2ENTRY) dbgfBpL2GetByIdx(PVMCC pVM, uint32_t idxL2)
94{
95 uint32_t idChunk = DBGF_BP_L2_IDX_GET_CHUNK_ID(idxL2);
96 uint32_t idxEntry = DBGF_BP_L2_IDX_GET_ENTRY(idxL2);
97
98 AssertReturn(idChunk < DBGF_BP_L2_TBL_CHUNK_COUNT, NULL);
99 AssertReturn(idxEntry < DBGF_BP_L2_TBL_ENTRIES_PER_CHUNK, NULL);
100
101#ifdef IN_RING0
102 PDBGFBPL2TBLCHUNKR0 pL2Chunk = &pVM->dbgfr0.s.aBpL2TblChunks[idChunk];
103 AssertPtrReturn(pL2Chunk->CTX_SUFF(paBpL2TblBaseShared), NULL);
104
105 return &pL2Chunk->CTX_SUFF(paBpL2TblBaseShared)[idxEntry];
106#elif defined(IN_RING3)
107 PUVM pUVM = pVM->pUVM;
108 PDBGFBPL2TBLCHUNKR3 pL2Chunk = &pUVM->dbgf.s.aBpL2TblChunks[idChunk];
109 AssertPtrReturn(pL2Chunk->pbmAlloc, NULL);
110 AssertReturn(ASMBitTest(pL2Chunk->pbmAlloc, idxEntry), NULL);
111
112 return &pL2Chunk->CTX_SUFF(pL2Base)[idxEntry];
113#endif
114}
115
116
117#ifdef IN_RING0
118/**
119 * Returns the internal breakpoint owner state for the given handle.
120 *
121 * @returns Pointer to the internal ring-0 breakpoint owner state or NULL if the handle is invalid.
122 * @param pVM The cross context VM structure.
123 * @param hBpOwner The breakpoint owner handle to resolve.
124 */
125DECLINLINE(PCDBGFBPOWNERINTR0) dbgfR0BpOwnerGetByHnd(PVMCC pVM, DBGFBPOWNER hBpOwner)
126{
127 if (hBpOwner == NIL_DBGFBPOWNER)
128 return NULL;
129
130 AssertReturn(hBpOwner < DBGF_BP_OWNER_COUNT_MAX, NULL);
131
132 PCDBGFBPOWNERINTR0 pBpOwnerR0 = &pVM->dbgfr0.s.paBpOwnersR0[hBpOwner];
133 AssertReturn(pBpOwnerR0->cRefs > 1, NULL);
134
135 return pBpOwnerR0;
136}
137#endif
138
139
140/**
141 * Executes the actions associated with the given breakpoint.
142 *
143 * @returns VBox status code.
144 * @param pVM The cross context VM structure.
145 * @param pVCpu The cross context virtual CPU structure.
146 * @param pRegFrame Pointer to the register frame for the trap.
147 * @param hBp The breakpoint handle which hit.
148 * @param pBp The shared breakpoint state.
149 * @param pBpR0 The ring-0 only breakpoint state.
150 */
151#ifdef IN_RING0
152DECLINLINE(int) dbgfBpHit(PVMCC pVM, PVMCPUCC pVCpu, PCPUMCTXCORE pRegFrame, DBGFBP hBp, PDBGFBPINT pBp, PDBGFBPINTR0 pBpR0)
153#else
154DECLINLINE(int) dbgfBpHit(PVMCC pVM, PVMCPUCC pVCpu, PCPUMCTXCORE pRegFrame, DBGFBP hBp, PDBGFBPINT pBp)
155#endif
156{
157 uint64_t cHits = ASMAtomicIncU64(&pBp->Pub.cHits); RT_NOREF(cHits);
158
159 RT_NOREF(pRegFrame);
160 LogFlow(("dbgfBpHit: hit breakpoint %u at %04x:%RGv cHits=0x%RX64\n",
161 hBp, pRegFrame->cs.Sel, pRegFrame->rip, cHits));
162
163 int rc = VINF_EM_DBG_BREAKPOINT;
164#ifdef IN_RING0
165 PCDBGFBPOWNERINTR0 pBpOwnerR0 = dbgfR0BpOwnerGetByHnd(pVM,
166 pBpR0->fInUse
167 ? pBpR0->hOwner
168 : NIL_DBGFBPOWNER);
169 if (pBpOwnerR0)
170 {
171 AssertReturn(pBpOwnerR0->pfnBpIoHitR0, VERR_DBGF_BP_IPE_1);
172
173 VBOXSTRICTRC rcStrict = VINF_SUCCESS;
174
175 if (DBGF_BP_PUB_IS_EXEC_BEFORE(&pBp->Pub))
176 rcStrict = pBpOwnerR0->pfnBpHitR0(pVM, pVCpu->idCpu, pBpR0->pvUserR0, hBp, &pBp->Pub, DBGF_BP_F_HIT_EXEC_BEFORE);
177 if (rcStrict == VINF_SUCCESS)
178 {
179 uint8_t abInstr[DBGF_BP_INSN_MAX];
180 RTGCPTR const GCPtrInstr = pVCpu->cpum.GstCtx.rip + pVCpu->cpum.GstCtx.cs.u64Base;
181 rc = PGMPhysSimpleReadGCPtr(pVCpu, &abInstr[0], GCPtrInstr, sizeof(abInstr));
182 AssertRC(rc);
183 if (RT_SUCCESS(rc))
184 {
185 /* Replace the int3 with the original instruction byte. */
186 abInstr[0] = pBp->Pub.u.Int3.bOrg;
187 rcStrict = IEMExecOneWithPrefetchedByPC(pVCpu, CPUMCTX2CORE(&pVCpu->cpum.GstCtx), GCPtrInstr, &abInstr[0], sizeof(abInstr));
188 if ( rcStrict == VINF_SUCCESS
189 && DBGF_BP_PUB_IS_EXEC_AFTER(&pBp->Pub))
190 {
191 rcStrict = pBpOwnerR0->pfnBpHitR0(pVM, pVCpu->idCpu, pBpR0->pvUserR0, hBp, &pBp->Pub, DBGF_BP_F_HIT_EXEC_AFTER);
192 if (rcStrict == VINF_SUCCESS)
193 rc = VINF_SUCCESS;
194 else if ( rcStrict == VINF_DBGF_BP_HALT
195 || rcStrict == VINF_DBGF_R3_BP_OWNER_DEFER)
196 {
197 pVCpu->dbgf.s.hBpActive = hBp;
198 if (rcStrict == VINF_DBGF_R3_BP_OWNER_DEFER)
199 pVCpu->dbgf.s.fBpInvokeOwnerCallback = true;
200 else
201 pVCpu->dbgf.s.fBpInvokeOwnerCallback = false;
202 }
203 else /* Guru meditation. */
204 rc = VERR_DBGF_BP_OWNER_CALLBACK_WRONG_STATUS;
205 }
206 else
207 rc = VBOXSTRICTRC_VAL(rcStrict);
208 }
209 }
210 else if ( rcStrict == VINF_DBGF_BP_HALT
211 || rcStrict == VINF_DBGF_R3_BP_OWNER_DEFER)
212 {
213 pVCpu->dbgf.s.hBpActive = hBp;
214 if (rcStrict == VINF_DBGF_R3_BP_OWNER_DEFER)
215 pVCpu->dbgf.s.fBpInvokeOwnerCallback = true;
216 else
217 pVCpu->dbgf.s.fBpInvokeOwnerCallback = false;
218 }
219 else /* Guru meditation. */
220 rc = VERR_DBGF_BP_OWNER_CALLBACK_WRONG_STATUS;
221 }
222 else
223 {
224 pVCpu->dbgf.s.fBpInvokeOwnerCallback = true; /* Need to check this for ring-3 only owners. */
225 pVCpu->dbgf.s.hBpActive = hBp;
226 }
227#else
228 RT_NOREF(pVM);
229 pVCpu->dbgf.s.fBpInvokeOwnerCallback = true;
230 pVCpu->dbgf.s.hBpActive = hBp;
231#endif
232
233 return rc;
234}
235
236
237/**
238 * Executes the actions associated with the given port I/O breakpoint.
239 *
240 * @returns VBox status code.
241 * @param pVM The cross context VM structure.
242 * @param pVCpu The cross context virtual CPU structure.
243 * @param fBefore Flag whether the check is done before the access is carried out,
244 * false if it is done after the access.
245 * @param fAccess Access flags, see DBGFBPIOACCESS_XXX.
246 * @param uAddr The address of the access, for port I/O this will hold the port number.
247 * @param uValue The value read or written (the value for reads is only valid when DBGF_BP_F_HIT_EXEC_AFTER is set).
248 * @param hBp The breakpoint handle which hit.
249 * @param pBp The shared breakpoint state.
250 * @param pBpR0 The ring-0 only breakpoint state.
251 */
252#ifdef IN_RING0
253DECLINLINE(VBOXSTRICTRC) dbgfBpPortIoHit(PVMCC pVM, PVMCPU pVCpu, bool fBefore, uint32_t fAccess, uint64_t uAddr, uint64_t uValue,
254 DBGFBP hBp, PDBGFBPINT pBp, PDBGFBPINTR0 pBpR0)
255#else
256DECLINLINE(VBOXSTRICTRC) dbgfBpPortIoHit(PVMCC pVM, PVMCPU pVCpu, bool fBefore, uint32_t fAccess, uint64_t uAddr, uint64_t uValue,
257 DBGFBP hBp, PDBGFBPINT pBp)
258#endif
259{
260 ASMAtomicIncU64(&pBp->Pub.cHits);
261
262 VBOXSTRICTRC rcStrict = VINF_EM_DBG_BREAKPOINT;
263#ifdef IN_RING0
264 PCDBGFBPOWNERINTR0 pBpOwnerR0 = dbgfR0BpOwnerGetByHnd(pVM,
265 pBpR0->fInUse
266 ? pBpR0->hOwner
267 : NIL_DBGFBPOWNER);
268 if (pBpOwnerR0)
269 {
270 AssertReturn(pBpOwnerR0->pfnBpIoHitR0, VERR_DBGF_BP_IPE_1);
271 rcStrict = pBpOwnerR0->pfnBpIoHitR0(pVM, pVCpu->idCpuUnsafe, pBpR0->pvUserR0, hBp, &pBp->Pub,
272 fBefore
273 ? DBGF_BP_F_HIT_EXEC_BEFORE
274 : DBGF_BP_F_HIT_EXEC_AFTER,
275 fAccess, uAddr, uValue);
276 }
277 else
278 {
279 pVCpu->dbgf.s.fBpInvokeOwnerCallback = true; /* Need to check this for ring-3 only owners. */
280 pVCpu->dbgf.s.hBpActive = hBp;
281 pVCpu->dbgf.s.fBpIoActive = true;
282 pVCpu->dbgf.s.fBpIoBefore = fBefore;
283 pVCpu->dbgf.s.uBpIoAddress = uAddr;
284 pVCpu->dbgf.s.fBpIoAccess = fAccess;
285 pVCpu->dbgf.s.uBpIoValue = uValue;
286 }
287#else
288 /* Resolve owner (can be NIL_DBGFBPOWNER) and invoke callback if there is one. */
289 if (pBp->Pub.hOwner != NIL_DBGFBPOWNER)
290 {
291 PCDBGFBPOWNERINT pBpOwner = dbgfR3BpOwnerGetByHnd(pVM->pUVM, pBp->Pub.hOwner);
292 if (pBpOwner)
293 {
294 AssertReturn(pBpOwner->pfnBpIoHitR3, VERR_DBGF_BP_IPE_1);
295 rcStrict = pBpOwner->pfnBpIoHitR3(pVM, pVCpu->idCpu, pBp->pvUserR3, hBp, &pBp->Pub,
296 fBefore
297 ? DBGF_BP_F_HIT_EXEC_BEFORE
298 : DBGF_BP_F_HIT_EXEC_AFTER,
299 fAccess, uAddr, uValue);
300 }
301 }
302#endif
303 if ( rcStrict == VINF_DBGF_BP_HALT
304 || rcStrict == VINF_DBGF_R3_BP_OWNER_DEFER)
305 {
306 pVCpu->dbgf.s.hBpActive = hBp;
307 if (rcStrict == VINF_DBGF_R3_BP_OWNER_DEFER)
308 pVCpu->dbgf.s.fBpInvokeOwnerCallback = true;
309 else
310 pVCpu->dbgf.s.fBpInvokeOwnerCallback = false;
311 rcStrict = VINF_EM_DBG_BREAKPOINT;
312 }
313 else if ( rcStrict != VINF_SUCCESS
314 && rcStrict != VINF_EM_DBG_BREAKPOINT)
315 rcStrict = VERR_DBGF_BP_OWNER_CALLBACK_WRONG_STATUS; /* Guru meditation. */
316
317 return rcStrict;
318}
319
320
321/**
322 * Walks the L2 table starting at the given root index searching for the given key.
323 *
324 * @returns VBox status code.
325 * @param pVM The cross context VM structure.
326 * @param pVCpu The cross context virtual CPU structure.
327 * @param pRegFrame Pointer to the register frame for the trap.
328 * @param idxL2Root L2 table index of the table root.
329 * @param GCPtrKey The key to search for.
330 */
331static int dbgfBpL2Walk(PVMCC pVM, PVMCPUCC pVCpu, PCPUMCTXCORE pRegFrame,
332 uint32_t idxL2Root, RTGCUINTPTR GCPtrKey)
333{
334 /** @todo We don't use the depth right now but abort the walking after a fixed amount of levels. */
335 uint8_t iDepth = 32;
336 PCDBGFBPL2ENTRY pL2Entry = dbgfBpL2GetByIdx(pVM, idxL2Root);
337
338 while (RT_LIKELY( iDepth-- > 0
339 && pL2Entry))
340 {
341 /* Make a copy of the entry before verification. */
342 DBGFBPL2ENTRY L2Entry;
343 L2Entry.u64GCPtrKeyAndBpHnd1 = ASMAtomicReadU64((volatile uint64_t *)&pL2Entry->u64GCPtrKeyAndBpHnd1);
344 L2Entry.u64LeftRightIdxDepthBpHnd2 = ASMAtomicReadU64((volatile uint64_t *)&pL2Entry->u64LeftRightIdxDepthBpHnd2);
345
346 RTGCUINTPTR GCPtrL2Entry = DBGF_BP_L2_ENTRY_GET_GCPTR(L2Entry.u64GCPtrKeyAndBpHnd1);
347 if (GCPtrKey == GCPtrL2Entry)
348 {
349 DBGFBP hBp = DBGF_BP_L2_ENTRY_GET_BP_HND(L2Entry.u64GCPtrKeyAndBpHnd1, L2Entry.u64LeftRightIdxDepthBpHnd2);
350
351 /* Query the internal breakpoint state from the handle. */
352#ifdef IN_RING3
353 PDBGFBPINT pBp = dbgfBpGetByHnd(pVM, hBp);
354#else
355 PDBGFBPINTR0 pBpR0 = NULL;
356 PDBGFBPINT pBp = dbgfBpGetByHnd(pVM, hBp, &pBpR0);
357#endif
358 if ( pBp
359 && DBGF_BP_PUB_GET_TYPE(&pBp->Pub) == DBGFBPTYPE_INT3)
360#ifdef IN_RING3
361 return dbgfBpHit(pVM, pVCpu, pRegFrame, hBp, pBp);
362#else
363 return dbgfBpHit(pVM, pVCpu, pRegFrame, hBp, pBp, pBpR0);
364#endif
365
366 /* The entry got corrupted, just abort. */
367 return VERR_DBGF_BP_L2_LOOKUP_FAILED;
368 }
369
370 /* Not found, get to the next level. */
371 uint32_t idxL2Next = (GCPtrKey < GCPtrL2Entry)
372 ? DBGF_BP_L2_ENTRY_GET_IDX_LEFT(L2Entry.u64LeftRightIdxDepthBpHnd2)
373 : DBGF_BP_L2_ENTRY_GET_IDX_RIGHT(L2Entry.u64LeftRightIdxDepthBpHnd2);
374 /* It is genuine guest trap or we hit some assertion if we are at the end. */
375 if (idxL2Next == DBGF_BP_L2_ENTRY_IDX_END)
376 return VINF_EM_RAW_GUEST_TRAP;
377
378 pL2Entry = dbgfBpL2GetByIdx(pVM, idxL2Next);
379 }
380
381 return VERR_DBGF_BP_L2_LOOKUP_FAILED;
382}
383
384
385/**
386 * Checks whether there is a port I/O breakpoint for the given range and access size.
387 *
388 * @returns VBox status code.
389 * @retval VINF_EM_DBG_BREAKPOINT means there is a breakpoint pending.
390 * @retval VINF_SUCCESS means everything is fine to continue.
391 * @retval anything else means a fatal error causing a guru meditation.
392 *
393 * @param pVM The current context VM structure.
394 * @param pVCpu The cross context virtual CPU structure.
395 * @param uIoPort The I/O port being accessed.
396 * @param fAccess Appropriate DBGFBPIOACCESS_XXX.
397 * @param uValue The value being written to or read from the device
398 * (The value is only valid for a read when the
399 * call is made after the access, writes are always valid).
400 * @param fBefore Flag whether the check is done before the access is carried out,
401 * false if it is done after the access.
402 */
403VMM_INT_DECL(VBOXSTRICTRC) DBGFBpCheckPortIo(PVMCC pVM, PVMCPU pVCpu, RTIOPORT uIoPort,
404 uint32_t fAccess, uint32_t uValue, bool fBefore)
405{
406 RT_NOREF(uValue); /** @todo Trigger only on specific values. */
407
408 /* Don't trigger in single stepping mode. */
409 if (pVCpu->dbgf.s.fSingleSteppingRaw)
410 return VINF_SUCCESS;
411
412#if defined(IN_RING0)
413 uint32_t volatile *paBpLocPortIo = pVM->dbgfr0.s.CTX_SUFF(paBpLocPortIo);
414#elif defined(IN_RING3)
415 PUVM pUVM = pVM->pUVM;
416 uint32_t volatile *paBpLocPortIo = pUVM->dbgf.s.CTX_SUFF(paBpLocPortIo);
417#else
418# error "Unsupported host context"
419#endif
420 if (paBpLocPortIo)
421 {
422 const uint32_t u32Entry = ASMAtomicReadU32(&paBpLocPortIo[uIoPort]);
423 if (u32Entry != DBGF_BP_INT3_L1_ENTRY_TYPE_NULL)
424 {
425 uint8_t u8Type = DBGF_BP_INT3_L1_ENTRY_GET_TYPE(u32Entry);
426 if (RT_LIKELY(u8Type == DBGF_BP_INT3_L1_ENTRY_TYPE_BP_HND))
427 {
428 DBGFBP hBp = DBGF_BP_INT3_L1_ENTRY_GET_BP_HND(u32Entry);
429
430 /* Query the internal breakpoint state from the handle. */
431#ifdef IN_RING3
432 PDBGFBPINT pBp = dbgfBpGetByHnd(pVM, hBp);
433#else
434 PDBGFBPINTR0 pBpR0 = NULL;
435 PDBGFBPINT pBp = dbgfBpGetByHnd(pVM, hBp, &pBpR0);
436#endif
437 if ( pBp
438 && DBGF_BP_PUB_GET_TYPE(&pBp->Pub) == DBGFBPTYPE_PORT_IO)
439 {
440 if ( uIoPort >= pBp->Pub.u.PortIo.uPort
441 && uIoPort < pBp->Pub.u.PortIo.uPort + pBp->Pub.u.PortIo.cPorts
442 && pBp->Pub.u.PortIo.fAccess & fAccess
443 && ( ( fBefore
444 && DBGF_BP_PUB_IS_EXEC_BEFORE(&pBp->Pub))
445 || ( !fBefore
446 && DBGF_BP_PUB_IS_EXEC_AFTER(&pBp->Pub))))
447#ifdef IN_RING3
448 return dbgfBpPortIoHit(pVM, pVCpu, fBefore, fAccess, uIoPort, uValue, hBp, pBp);
449#else
450 return dbgfBpPortIoHit(pVM, pVCpu, fBefore, fAccess, uIoPort, uValue, hBp, pBp, pBpR0);
451#endif
452 /* else: No matching port I/O breakpoint. */
453 }
454 else /* Invalid breakpoint handle or not an port I/O breakpoint. */
455 return VERR_DBGF_BP_L1_LOOKUP_FAILED;
456 }
457 else /* Some invalid type. */
458 return VERR_DBGF_BP_L1_LOOKUP_FAILED;
459 }
460 }
461
462 return VINF_SUCCESS;
463}
464
465
466/**
467 * \#DB (Debug event) handler.
468 *
469 * @returns VBox status code.
470 * VINF_SUCCESS means we completely handled this trap,
471 * other codes are passed execution to host context.
472 *
473 * @param pVM The cross context VM structure.
474 * @param pVCpu The cross context virtual CPU structure.
475 * @param pRegFrame Pointer to the register frame for the trap.
476 * @param uDr6 The DR6 hypervisor register value.
477 * @param fAltStepping Alternative stepping indicator.
478 */
479VMM_INT_DECL(int) DBGFTrap01Handler(PVM pVM, PVMCPU pVCpu, PCPUMCTXCORE pRegFrame, RTGCUINTREG uDr6, bool fAltStepping)
480{
481 /** @todo Intel docs say that X86_DR6_BS has the highest priority... */
482 RT_NOREF(pRegFrame);
483
484 /*
485 * A breakpoint?
486 */
487 AssertCompile(X86_DR6_B0 == 1 && X86_DR6_B1 == 2 && X86_DR6_B2 == 4 && X86_DR6_B3 == 8);
488 if ( (uDr6 & (X86_DR6_B0 | X86_DR6_B1 | X86_DR6_B2 | X86_DR6_B3))
489 && pVM->dbgf.s.cEnabledHwBreakpoints > 0)
490 {
491 for (unsigned iBp = 0; iBp < RT_ELEMENTS(pVM->dbgf.s.aHwBreakpoints); iBp++)
492 if ( ((uint32_t)uDr6 & RT_BIT_32(iBp))
493 && pVM->dbgf.s.aHwBreakpoints[iBp].hBp != NIL_DBGFBP)
494 {
495 pVCpu->dbgf.s.hBpActive = pVM->dbgf.s.aHwBreakpoints[iBp].hBp;
496 pVCpu->dbgf.s.fSingleSteppingRaw = false;
497 LogFlow(("DBGFRZTrap03Handler: hit hw breakpoint %x at %04x:%RGv\n",
498 pVM->dbgf.s.aHwBreakpoints[iBp].hBp, pRegFrame->cs.Sel, pRegFrame->rip));
499
500 return VINF_EM_DBG_BREAKPOINT;
501 }
502 }
503
504 /*
505 * Single step?
506 * Are we single stepping or is it the guest?
507 */
508 if ( (uDr6 & X86_DR6_BS)
509 && (pVCpu->dbgf.s.fSingleSteppingRaw || fAltStepping))
510 {
511 pVCpu->dbgf.s.fSingleSteppingRaw = false;
512 LogFlow(("DBGFRZTrap01Handler: single step at %04x:%RGv\n", pRegFrame->cs.Sel, pRegFrame->rip));
513 return VINF_EM_DBG_STEPPED;
514 }
515
516 LogFlow(("DBGFRZTrap01Handler: guest debug event %#x at %04x:%RGv!\n", (uint32_t)uDr6, pRegFrame->cs.Sel, pRegFrame->rip));
517 return VINF_EM_RAW_GUEST_TRAP;
518}
519
520
521/**
522 * \#BP (Breakpoint) handler.
523 *
524 * @returns VBox status code.
525 * VINF_SUCCESS means we completely handled this trap,
526 * other codes are passed execution to host context.
527 *
528 * @param pVM The cross context VM structure.
529 * @param pVCpu The cross context virtual CPU structure.
530 * @param pRegFrame Pointer to the register frame for the trap.
531 */
532VMM_INT_DECL(VBOXSTRICTRC) DBGFTrap03Handler(PVMCC pVM, PVMCPUCC pVCpu, PCPUMCTXCORE pRegFrame)
533{
534#if defined(IN_RING0)
535 uint32_t volatile *paBpLocL1 = pVM->dbgfr0.s.CTX_SUFF(paBpLocL1);
536#elif defined(IN_RING3)
537 PUVM pUVM = pVM->pUVM;
538 uint32_t volatile *paBpLocL1 = pUVM->dbgf.s.CTX_SUFF(paBpLocL1);
539#else
540# error "Unsupported host context"
541#endif
542 if (paBpLocL1)
543 {
544 RTGCPTR GCPtrBp;
545 int rc = SELMValidateAndConvertCSAddr(pVCpu, pRegFrame->eflags, pRegFrame->ss.Sel, pRegFrame->cs.Sel, &pRegFrame->cs,
546 pRegFrame->rip /* no -1 in R0 */,
547 &GCPtrBp);
548 AssertRCReturn(rc, rc);
549
550 const uint16_t idxL1 = DBGF_BP_INT3_L1_IDX_EXTRACT_FROM_ADDR(GCPtrBp);
551 const uint32_t u32L1Entry = ASMAtomicReadU32(&paBpLocL1[idxL1]);
552
553 LogFlowFunc(("GCPtrBp=%RGv idxL1=%u u32L1Entry=%#x\n", GCPtrBp, idxL1, u32L1Entry));
554 if (u32L1Entry != DBGF_BP_INT3_L1_ENTRY_TYPE_NULL)
555 {
556 uint8_t u8Type = DBGF_BP_INT3_L1_ENTRY_GET_TYPE(u32L1Entry);
557 if (u8Type == DBGF_BP_INT3_L1_ENTRY_TYPE_BP_HND)
558 {
559 DBGFBP hBp = DBGF_BP_INT3_L1_ENTRY_GET_BP_HND(u32L1Entry);
560
561 /* Query the internal breakpoint state from the handle. */
562#ifdef IN_RING3
563 PDBGFBPINT pBp = dbgfBpGetByHnd(pVM, hBp);
564#else
565 PDBGFBPINTR0 pBpR0 = NULL;
566 PDBGFBPINT pBp = dbgfBpGetByHnd(pVM, hBp, &pBpR0);
567#endif
568 if ( pBp
569 && DBGF_BP_PUB_GET_TYPE(&pBp->Pub) == DBGFBPTYPE_INT3)
570 {
571 if (pBp->Pub.u.Int3.GCPtr == (RTGCUINTPTR)GCPtrBp)
572#ifdef IN_RING3
573 rc = dbgfBpHit(pVM, pVCpu, pRegFrame, hBp, pBp);
574#else
575 rc = dbgfBpHit(pVM, pVCpu, pRegFrame, hBp, pBp, pBpR0);
576#endif
577 else
578 rc = VINF_EM_RAW_GUEST_TRAP; /* Genuine guest trap. */
579 }
580 else /* Invalid breakpoint handle or not an int3 breakpoint. */
581 rc = VERR_DBGF_BP_L1_LOOKUP_FAILED;
582 }
583 else if (u8Type == DBGF_BP_INT3_L1_ENTRY_TYPE_L2_IDX)
584 rc = dbgfBpL2Walk(pVM, pVCpu, pRegFrame, DBGF_BP_INT3_L1_ENTRY_GET_L2_IDX(u32L1Entry),
585 DBGF_BP_INT3_L2_KEY_EXTRACT_FROM_ADDR((RTGCUINTPTR)GCPtrBp));
586 else /* Some invalid type. */
587 rc = VERR_DBGF_BP_L1_LOOKUP_FAILED;
588 }
589 else
590 rc = VINF_EM_RAW_GUEST_TRAP; /* Genuine guest trap. */
591
592 return rc;
593 }
594
595 return VINF_EM_RAW_GUEST_TRAP;
596}
597
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use