VirtualBox

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

Last change on this file was 98103, checked in by vboxsync, 16 months ago

Copyright year updates by scm.

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

© 2023 Oracle
ContactPrivacy policyTerms of Use