VirtualBox

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

Last change on this file since 87633 was 87594, checked in by vboxsync, 4 years ago

VMM/DBGF,Debugger: Removed the !defined(VBOX_WITH_LOTS_OF_DBGF_BPS) code. bugref:9837

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