VirtualBox

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

Last change on this file since 96860 was 96407, checked in by vboxsync, 21 months ago

scm copyright and license note update

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

© 2023 Oracle
ContactPrivacy policyTerms of Use