VirtualBox

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

Last change on this file since 89547 was 87776, checked in by vboxsync, 4 years ago

dbgf.h,VMM/DBGF: A bit of cleanup on the public breakpoint structure and associated helpers, add new flags to indicate whether the breakpoint should hit before or after the instruction was executed, bugref:9837

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 16.2 KB
Line 
1/* $Id: DBGFAllBp.cpp 87776 2021-02-17 12:21:16Z vboxsync $ */
2/** @file
3 * DBGF - Debugger Facility, All Context breakpoint management part.
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#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 VBOXSTRICTRC rcStrict = VINF_SUCCESS;
172
173 if (DBGF_BP_PUB_IS_EXEC_BEFORE(&pBp->Pub))
174 rcStrict = pBpOwnerR0->pfnBpHitR0(pVM, pVCpu->idCpu, pBpR0->pvUserR0, hBp, &pBp->Pub, DBGF_BP_F_HIT_EXEC_BEFORE);
175 if (rcStrict == VINF_SUCCESS)
176 {
177 uint8_t abInstr[DBGF_BP_INSN_MAX];
178 RTGCPTR const GCPtrInstr = pVCpu->cpum.GstCtx.rip + pVCpu->cpum.GstCtx.cs.u64Base;
179 rc = PGMPhysSimpleReadGCPtr(pVCpu, &abInstr[0], GCPtrInstr, sizeof(abInstr));
180 AssertRC(rc);
181 if (RT_SUCCESS(rc))
182 {
183 /* Replace the int3 with the original instruction byte. */
184 abInstr[0] = pBp->Pub.u.Int3.bOrg;
185 rcStrict = IEMExecOneWithPrefetchedByPC(pVCpu, CPUMCTX2CORE(&pVCpu->cpum.GstCtx), GCPtrInstr, &abInstr[0], sizeof(abInstr));
186 if ( rcStrict == VINF_SUCCESS
187 && DBGF_BP_PUB_IS_EXEC_AFTER(&pBp->Pub))
188 {
189 rcStrict = pBpOwnerR0->pfnBpHitR0(pVM, pVCpu->idCpu, pBpR0->pvUserR0, hBp, &pBp->Pub, DBGF_BP_F_HIT_EXEC_AFTER);
190 if (rcStrict == VINF_SUCCESS)
191 rc = VINF_SUCCESS;
192 else if ( rcStrict == VINF_DBGF_BP_HALT
193 || rcStrict == VINF_DBGF_R3_BP_OWNER_DEFER)
194 {
195 pVCpu->dbgf.s.hBpActive = hBp;
196 if (rcStrict == VINF_DBGF_R3_BP_OWNER_DEFER)
197 pVCpu->dbgf.s.fBpInvokeOwnerCallback = true;
198 else
199 pVCpu->dbgf.s.fBpInvokeOwnerCallback = false;
200 }
201 else /* Guru meditation. */
202 rc = VERR_DBGF_BP_OWNER_CALLBACK_WRONG_STATUS;
203 }
204 else
205 rc = VBOXSTRICTRC_VAL(rcStrict);
206 }
207 }
208 else if ( rcStrict == VINF_DBGF_BP_HALT
209 || rcStrict == VINF_DBGF_R3_BP_OWNER_DEFER)
210 {
211 pVCpu->dbgf.s.hBpActive = hBp;
212 if (rcStrict == VINF_DBGF_R3_BP_OWNER_DEFER)
213 pVCpu->dbgf.s.fBpInvokeOwnerCallback = true;
214 else
215 pVCpu->dbgf.s.fBpInvokeOwnerCallback = false;
216 }
217 else /* Guru meditation. */
218 rc = VERR_DBGF_BP_OWNER_CALLBACK_WRONG_STATUS;
219 }
220 else
221 {
222 pVCpu->dbgf.s.fBpInvokeOwnerCallback = true; /* Need to check this for ring-3 only owners. */
223 pVCpu->dbgf.s.hBpActive = hBp;
224 }
225#else
226 RT_NOREF(pVM);
227 pVCpu->dbgf.s.fBpInvokeOwnerCallback = true;
228 pVCpu->dbgf.s.hBpActive = hBp;
229#endif
230
231 return rc;
232}
233
234
235/**
236 * Walks the L2 table starting at the given root index searching for the given key.
237 *
238 * @returns VBox status code.
239 * @param pVM The cross context VM structure.
240 * @param pVCpu The cross context virtual CPU structure.
241 * @param pRegFrame Pointer to the register frame for the trap.
242 * @param idxL2Root L2 table index of the table root.
243 * @param GCPtrKey The key to search for.
244 */
245static int dbgfBpL2Walk(PVMCC pVM, PVMCPUCC pVCpu, PCPUMCTXCORE pRegFrame,
246 uint32_t idxL2Root, RTGCUINTPTR GCPtrKey)
247{
248 /** @todo We don't use the depth right now but abort the walking after a fixed amount of levels. */
249 uint8_t iDepth = 32;
250 PCDBGFBPL2ENTRY pL2Entry = dbgfBpL2GetByIdx(pVM, idxL2Root);
251
252 while (RT_LIKELY( iDepth-- > 0
253 && pL2Entry))
254 {
255 /* Make a copy of the entry before verification. */
256 DBGFBPL2ENTRY L2Entry;
257 L2Entry.u64GCPtrKeyAndBpHnd1 = ASMAtomicReadU64((volatile uint64_t *)&pL2Entry->u64GCPtrKeyAndBpHnd1);
258 L2Entry.u64LeftRightIdxDepthBpHnd2 = ASMAtomicReadU64((volatile uint64_t *)&pL2Entry->u64LeftRightIdxDepthBpHnd2);
259
260 RTGCUINTPTR GCPtrL2Entry = DBGF_BP_L2_ENTRY_GET_GCPTR(L2Entry.u64GCPtrKeyAndBpHnd1);
261 if (GCPtrKey == GCPtrL2Entry)
262 {
263 DBGFBP hBp = DBGF_BP_L2_ENTRY_GET_BP_HND(L2Entry.u64GCPtrKeyAndBpHnd1, L2Entry.u64LeftRightIdxDepthBpHnd2);
264
265 /* Query the internal breakpoint state from the handle. */
266#ifdef IN_RING3
267 PDBGFBPINT pBp = dbgfBpGetByHnd(pVM, hBp);
268#else
269 PDBGFBPINTR0 pBpR0 = NULL;
270 PDBGFBPINT pBp = dbgfBpGetByHnd(pVM, hBp, &pBpR0);
271#endif
272 if ( pBp
273 && DBGF_BP_PUB_GET_TYPE(&pBp->Pub) == DBGFBPTYPE_INT3)
274#ifdef IN_RING3
275 return dbgfBpHit(pVM, pVCpu, pRegFrame, hBp, pBp);
276#else
277 return dbgfBpHit(pVM, pVCpu, pRegFrame, hBp, pBp, pBpR0);
278#endif
279
280 /* The entry got corrupted, just abort. */
281 return VERR_DBGF_BP_L2_LOOKUP_FAILED;
282 }
283
284 /* Not found, get to the next level. */
285 uint32_t idxL2Next = (GCPtrKey < GCPtrL2Entry)
286 ? DBGF_BP_L2_ENTRY_GET_IDX_LEFT(L2Entry.u64LeftRightIdxDepthBpHnd2)
287 : DBGF_BP_L2_ENTRY_GET_IDX_RIGHT(L2Entry.u64LeftRightIdxDepthBpHnd2);
288 /* It is genuine guest trap or we hit some assertion if we are at the end. */
289 if (idxL2Next == DBGF_BP_L2_ENTRY_IDX_END)
290 return VINF_EM_RAW_GUEST_TRAP;
291
292 pL2Entry = dbgfBpL2GetByIdx(pVM, idxL2Next);
293 }
294
295 return VERR_DBGF_BP_L2_LOOKUP_FAILED;
296}
297
298
299/**
300 * \#DB (Debug event) handler.
301 *
302 * @returns VBox status code.
303 * VINF_SUCCESS means we completely handled this trap,
304 * other codes are passed execution to host context.
305 *
306 * @param pVM The cross context VM structure.
307 * @param pVCpu The cross context virtual CPU structure.
308 * @param pRegFrame Pointer to the register frame for the trap.
309 * @param uDr6 The DR6 hypervisor register value.
310 * @param fAltStepping Alternative stepping indicator.
311 */
312VMM_INT_DECL(int) DBGFTrap01Handler(PVM pVM, PVMCPU pVCpu, PCPUMCTXCORE pRegFrame, RTGCUINTREG uDr6, bool fAltStepping)
313{
314 /** @todo Intel docs say that X86_DR6_BS has the highest priority... */
315 RT_NOREF(pRegFrame);
316
317 /*
318 * A breakpoint?
319 */
320 AssertCompile(X86_DR6_B0 == 1 && X86_DR6_B1 == 2 && X86_DR6_B2 == 4 && X86_DR6_B3 == 8);
321 if ( (uDr6 & (X86_DR6_B0 | X86_DR6_B1 | X86_DR6_B2 | X86_DR6_B3))
322 && pVM->dbgf.s.cEnabledHwBreakpoints > 0)
323 {
324 for (unsigned iBp = 0; iBp < RT_ELEMENTS(pVM->dbgf.s.aHwBreakpoints); iBp++)
325 if ( ((uint32_t)uDr6 & RT_BIT_32(iBp))
326 && pVM->dbgf.s.aHwBreakpoints[iBp].hBp != NIL_DBGFBP)
327 {
328 pVCpu->dbgf.s.hBpActive = pVM->dbgf.s.aHwBreakpoints[iBp].hBp;
329 pVCpu->dbgf.s.fSingleSteppingRaw = false;
330 LogFlow(("DBGFRZTrap03Handler: hit hw breakpoint %x at %04x:%RGv\n",
331 pVM->dbgf.s.aHwBreakpoints[iBp].hBp, pRegFrame->cs.Sel, pRegFrame->rip));
332
333 return VINF_EM_DBG_BREAKPOINT;
334 }
335 }
336
337 /*
338 * Single step?
339 * Are we single stepping or is it the guest?
340 */
341 if ( (uDr6 & X86_DR6_BS)
342 && (pVCpu->dbgf.s.fSingleSteppingRaw || fAltStepping))
343 {
344 pVCpu->dbgf.s.fSingleSteppingRaw = false;
345 LogFlow(("DBGFRZTrap01Handler: single step at %04x:%RGv\n", pRegFrame->cs.Sel, pRegFrame->rip));
346 return VINF_EM_DBG_STEPPED;
347 }
348
349 LogFlow(("DBGFRZTrap01Handler: guest debug event %#x at %04x:%RGv!\n", (uint32_t)uDr6, pRegFrame->cs.Sel, pRegFrame->rip));
350 return VINF_EM_RAW_GUEST_TRAP;
351}
352
353
354/**
355 * \#BP (Breakpoint) handler.
356 *
357 * @returns VBox status code.
358 * VINF_SUCCESS means we completely handled this trap,
359 * other codes are passed execution to host context.
360 *
361 * @param pVM The cross context VM structure.
362 * @param pVCpu The cross context virtual CPU structure.
363 * @param pRegFrame Pointer to the register frame for the trap.
364 */
365VMM_INT_DECL(int) DBGFTrap03Handler(PVMCC pVM, PVMCPUCC pVCpu, PCPUMCTXCORE pRegFrame)
366{
367#if defined(IN_RING0)
368 uint32_t volatile *paBpLocL1 = pVM->dbgfr0.s.CTX_SUFF(paBpLocL1);
369#elif defined(IN_RING3)
370 PUVM pUVM = pVM->pUVM;
371 uint32_t volatile *paBpLocL1 = pUVM->dbgf.s.CTX_SUFF(paBpLocL1);
372#else
373# error "Unsupported host context"
374#endif
375 if (paBpLocL1)
376 {
377 RTGCPTR GCPtrBp;
378 int rc = SELMValidateAndConvertCSAddr(pVCpu, pRegFrame->eflags, pRegFrame->ss.Sel, pRegFrame->cs.Sel, &pRegFrame->cs,
379 pRegFrame->rip /* no -1 in R0 */,
380 &GCPtrBp);
381 AssertRCReturn(rc, rc);
382
383 const uint16_t idxL1 = DBGF_BP_INT3_L1_IDX_EXTRACT_FROM_ADDR(GCPtrBp);
384 const uint32_t u32L1Entry = ASMAtomicReadU32(&paBpLocL1[idxL1]);
385
386 LogFlowFunc(("GCPtrBp=%RGv idxL1=%u u32L1Entry=%#x\n", GCPtrBp, idxL1, u32L1Entry));
387 rc = VINF_EM_RAW_GUEST_TRAP;
388 if (u32L1Entry != DBGF_BP_INT3_L1_ENTRY_TYPE_NULL)
389 {
390 uint8_t u8Type = DBGF_BP_INT3_L1_ENTRY_GET_TYPE(u32L1Entry);
391 if (u8Type == DBGF_BP_INT3_L1_ENTRY_TYPE_BP_HND)
392 {
393 DBGFBP hBp = DBGF_BP_INT3_L1_ENTRY_GET_BP_HND(u32L1Entry);
394
395 /* Query the internal breakpoint state from the handle. */
396#ifdef IN_RING3
397 PDBGFBPINT pBp = dbgfBpGetByHnd(pVM, hBp);
398#else
399 PDBGFBPINTR0 pBpR0 = NULL;
400 PDBGFBPINT pBp = dbgfBpGetByHnd(pVM, hBp, &pBpR0);
401#endif
402 if ( pBp
403 && DBGF_BP_PUB_GET_TYPE(&pBp->Pub) == DBGFBPTYPE_INT3)
404 {
405 if (pBp->Pub.u.Int3.GCPtr == (RTGCUINTPTR)GCPtrBp)
406#ifdef IN_RING3
407 rc = dbgfBpHit(pVM, pVCpu, pRegFrame, hBp, pBp);
408#else
409 rc = dbgfBpHit(pVM, pVCpu, pRegFrame, hBp, pBp, pBpR0);
410#endif
411 /* else: Genuine guest trap. */
412 }
413 else /* Invalid breakpoint handle or not an int3 breakpoint. */
414 rc = VERR_DBGF_BP_L1_LOOKUP_FAILED;
415 }
416 else if (u8Type == DBGF_BP_INT3_L1_ENTRY_TYPE_L2_IDX)
417 rc = dbgfBpL2Walk(pVM, pVCpu, pRegFrame, DBGF_BP_INT3_L1_ENTRY_GET_L2_IDX(u32L1Entry),
418 DBGF_BP_INT3_L2_KEY_EXTRACT_FROM_ADDR((RTGCUINTPTR)GCPtrBp));
419 else /* Some invalid type. */
420 rc = VERR_DBGF_BP_L1_LOOKUP_FAILED;
421 }
422 /* else: Genuine guest trap. */
423
424 return rc;
425 }
426
427 return VINF_EM_RAW_GUEST_TRAP;
428}
429
Note: See TracBrowser for help on using the repository browser.

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