[23] | 1 | /* $Id: DBGFAll.cpp 99051 2023-03-19 16:40:06Z vboxsync $ */
|
---|
[1] | 2 | /** @file
|
---|
[23] | 3 | * DBGF - Debugger Facility, All Context Code.
|
---|
[1] | 4 | */
|
---|
| 5 |
|
---|
| 6 | /*
|
---|
[98103] | 7 | * Copyright (C) 2006-2023 Oracle and/or its affiliates.
|
---|
[1] | 8 | *
|
---|
[96407] | 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
|
---|
[1] | 26 | */
|
---|
| 27 |
|
---|
| 28 |
|
---|
[57358] | 29 | /*********************************************************************************************************************************
|
---|
| 30 | * Header Files *
|
---|
| 31 | *********************************************************************************************************************************/
|
---|
[1] | 32 | #define LOG_GROUP LOG_GROUP_DBGF
|
---|
[97693] | 33 | #define VMCPU_INCL_CPUM_GST_CTX
|
---|
[35346] | 34 | #include <VBox/vmm/dbgf.h>
|
---|
[1] | 35 | #include "DBGFInternal.h"
|
---|
[97693] | 36 | #include <VBox/vmm/cpum.h>
|
---|
[80268] | 37 | #include <VBox/vmm/vmcc.h>
|
---|
[47681] | 38 | #include <VBox/err.h>
|
---|
[1] | 39 | #include <iprt/assert.h>
|
---|
[59073] | 40 | #include <iprt/asm.h>
|
---|
[73348] | 41 | #include <iprt/stdarg.h>
|
---|
[1] | 42 |
|
---|
| 43 |
|
---|
[58909] | 44 | /*
|
---|
| 45 | * Check the read-only VM members.
|
---|
| 46 | */
|
---|
| 47 | AssertCompileMembersSameSizeAndOffset(VM, dbgf.s.bmSoftIntBreakpoints, VM, dbgf.ro.bmSoftIntBreakpoints);
|
---|
| 48 | AssertCompileMembersSameSizeAndOffset(VM, dbgf.s.bmHardIntBreakpoints, VM, dbgf.ro.bmHardIntBreakpoints);
|
---|
| 49 | AssertCompileMembersSameSizeAndOffset(VM, dbgf.s.bmSelectedEvents, VM, dbgf.ro.bmSelectedEvents);
|
---|
| 50 | AssertCompileMembersSameSizeAndOffset(VM, dbgf.s.cHardIntBreakpoints, VM, dbgf.ro.cHardIntBreakpoints);
|
---|
| 51 | AssertCompileMembersSameSizeAndOffset(VM, dbgf.s.cSoftIntBreakpoints, VM, dbgf.ro.cSoftIntBreakpoints);
|
---|
| 52 | AssertCompileMembersSameSizeAndOffset(VM, dbgf.s.cSelectedEvents, VM, dbgf.ro.cSelectedEvents);
|
---|
| 53 |
|
---|
| 54 |
|
---|
[99051] | 55 | #if !defined(VBOX_VMM_TARGET_ARMV8)
|
---|
[1] | 56 | /**
|
---|
| 57 | * Gets the hardware breakpoint configuration as DR7.
|
---|
| 58 | *
|
---|
| 59 | * @returns DR7 from the DBGF point of view.
|
---|
[58122] | 60 | * @param pVM The cross context VM structure.
|
---|
[1] | 61 | */
|
---|
[44399] | 62 | VMM_INT_DECL(RTGCUINTREG) DBGFBpGetDR7(PVM pVM)
|
---|
[1] | 63 | {
|
---|
[47328] | 64 | RTGCUINTREG uDr7 = X86_DR7_GD | X86_DR7_GE | X86_DR7_LE | X86_DR7_RA1_MASK;
|
---|
[86666] | 65 | for (uint32_t i = 0; i < RT_ELEMENTS(pVM->dbgf.s.aHwBreakpoints); i++)
|
---|
| 66 | {
|
---|
[87594] | 67 | if ( pVM->dbgf.s.aHwBreakpoints[i].fEnabled
|
---|
| 68 | && pVM->dbgf.s.aHwBreakpoints[i].hBp != NIL_DBGFBP)
|
---|
[86666] | 69 | {
|
---|
| 70 | static const uint8_t s_au8Sizes[8] =
|
---|
| 71 | {
|
---|
| 72 | X86_DR7_LEN_BYTE, X86_DR7_LEN_BYTE, X86_DR7_LEN_WORD, X86_DR7_LEN_BYTE,
|
---|
| 73 | X86_DR7_LEN_DWORD,X86_DR7_LEN_BYTE, X86_DR7_LEN_BYTE, X86_DR7_LEN_QWORD
|
---|
| 74 | };
|
---|
| 75 | uDr7 |= X86_DR7_G(i)
|
---|
[87594] | 76 | | X86_DR7_RW(i, pVM->dbgf.s.aHwBreakpoints[i].fType)
|
---|
| 77 | | X86_DR7_LEN(i, s_au8Sizes[pVM->dbgf.s.aHwBreakpoints[i].cb]);
|
---|
[86666] | 78 | }
|
---|
| 79 | }
|
---|
[1] | 80 | return uDr7;
|
---|
| 81 | }
|
---|
| 82 |
|
---|
| 83 |
|
---|
| 84 | /**
|
---|
| 85 | * Gets the address of the hardware breakpoint number 0.
|
---|
| 86 | *
|
---|
| 87 | * @returns DR0 from the DBGF point of view.
|
---|
[58122] | 88 | * @param pVM The cross context VM structure.
|
---|
[1] | 89 | */
|
---|
[44399] | 90 | VMM_INT_DECL(RTGCUINTREG) DBGFBpGetDR0(PVM pVM)
|
---|
[1] | 91 | {
|
---|
[87594] | 92 | return pVM->dbgf.s.aHwBreakpoints[0].GCPtr;
|
---|
[1] | 93 | }
|
---|
| 94 |
|
---|
| 95 |
|
---|
| 96 | /**
|
---|
| 97 | * Gets the address of the hardware breakpoint number 1.
|
---|
| 98 | *
|
---|
| 99 | * @returns DR1 from the DBGF point of view.
|
---|
[58122] | 100 | * @param pVM The cross context VM structure.
|
---|
[1] | 101 | */
|
---|
[44399] | 102 | VMM_INT_DECL(RTGCUINTREG) DBGFBpGetDR1(PVM pVM)
|
---|
[1] | 103 | {
|
---|
[87594] | 104 | return pVM->dbgf.s.aHwBreakpoints[1].GCPtr;
|
---|
[1] | 105 | }
|
---|
| 106 |
|
---|
| 107 |
|
---|
| 108 | /**
|
---|
| 109 | * Gets the address of the hardware breakpoint number 2.
|
---|
| 110 | *
|
---|
| 111 | * @returns DR2 from the DBGF point of view.
|
---|
[58122] | 112 | * @param pVM The cross context VM structure.
|
---|
[1] | 113 | */
|
---|
[44399] | 114 | VMM_INT_DECL(RTGCUINTREG) DBGFBpGetDR2(PVM pVM)
|
---|
[1] | 115 | {
|
---|
[87594] | 116 | return pVM->dbgf.s.aHwBreakpoints[2].GCPtr;
|
---|
[1] | 117 | }
|
---|
| 118 |
|
---|
| 119 |
|
---|
| 120 | /**
|
---|
| 121 | * Gets the address of the hardware breakpoint number 3.
|
---|
| 122 | *
|
---|
| 123 | * @returns DR3 from the DBGF point of view.
|
---|
[58122] | 124 | * @param pVM The cross context VM structure.
|
---|
[1] | 125 | */
|
---|
[44399] | 126 | VMM_INT_DECL(RTGCUINTREG) DBGFBpGetDR3(PVM pVM)
|
---|
[1] | 127 | {
|
---|
[87594] | 128 | return pVM->dbgf.s.aHwBreakpoints[3].GCPtr;
|
---|
[1] | 129 | }
|
---|
| 130 |
|
---|
[2168] | 131 |
|
---|
| 132 | /**
|
---|
[47671] | 133 | * Checks if any of the hardware breakpoints are armed.
|
---|
| 134 | *
|
---|
| 135 | * @returns true if armed, false if not.
|
---|
[58122] | 136 | * @param pVM The cross context VM structure.
|
---|
[58909] | 137 | * @remarks Don't call this from CPUMRecalcHyperDRx!
|
---|
[47671] | 138 | */
|
---|
| 139 | VMM_INT_DECL(bool) DBGFBpIsHwArmed(PVM pVM)
|
---|
| 140 | {
|
---|
[58909] | 141 | return pVM->dbgf.s.cEnabledHwBreakpoints > 0;
|
---|
[47671] | 142 | }
|
---|
| 143 |
|
---|
| 144 |
|
---|
| 145 | /**
|
---|
[47681] | 146 | * Checks if any of the hardware I/O breakpoints are armed.
|
---|
| 147 | *
|
---|
| 148 | * @returns true if armed, false if not.
|
---|
[58122] | 149 | * @param pVM The cross context VM structure.
|
---|
[58909] | 150 | * @remarks Don't call this from CPUMRecalcHyperDRx!
|
---|
[47681] | 151 | */
|
---|
| 152 | VMM_INT_DECL(bool) DBGFBpIsHwIoArmed(PVM pVM)
|
---|
| 153 | {
|
---|
[58909] | 154 | return pVM->dbgf.s.cEnabledHwIoBreakpoints > 0;
|
---|
[47681] | 155 | }
|
---|
| 156 |
|
---|
| 157 |
|
---|
| 158 | /**
|
---|
[64770] | 159 | * Checks if any INT3 breakpoints are armed.
|
---|
| 160 | *
|
---|
| 161 | * @returns true if armed, false if not.
|
---|
| 162 | * @param pVM The cross context VM structure.
|
---|
| 163 | * @remarks Don't call this from CPUMRecalcHyperDRx!
|
---|
| 164 | */
|
---|
| 165 | VMM_INT_DECL(bool) DBGFBpIsInt3Armed(PVM pVM)
|
---|
| 166 | {
|
---|
[87594] | 167 | /** @todo There was a todo here and returning false when I (bird) removed
|
---|
| 168 | * VBOX_WITH_LOTS_OF_DBGF_BPS, so this might not be correct. */
|
---|
[64770] | 169 | return pVM->dbgf.s.cEnabledInt3Breakpoints > 0;
|
---|
| 170 | }
|
---|
| 171 |
|
---|
| 172 |
|
---|
| 173 | /**
|
---|
[97693] | 174 | * Checks instruction boundrary for guest or hypervisor hardware breakpoints.
|
---|
[47681] | 175 | *
|
---|
[97693] | 176 | * @returns Strict VBox status code. May return DRx register import errors in
|
---|
| 177 | * addition to the ones detailed.
|
---|
| 178 | * @retval VINF_SUCCESS no breakpoint.
|
---|
| 179 | * @retval VINF_EM_DBG_BREAKPOINT hypervisor breakpoint triggered.
|
---|
| 180 | * @retval VINF_EM_RAW_GUEST_TRAP caller must trigger \#DB trap, DR6 and DR7
|
---|
| 181 | * have been updated appropriately.
|
---|
| 182 | *
|
---|
| 183 | * @param pVM The cross context VM structure.
|
---|
| 184 | * @param pVCpu The cross context virtual CPU structure of the calling EMT.
|
---|
| 185 | * @param GCPtrPC The unsegmented PC address.
|
---|
| 186 | */
|
---|
| 187 | VMM_INT_DECL(VBOXSTRICTRC) DBGFBpCheckInstruction(PVMCC pVM, PVMCPUCC pVCpu, RTGCPTR GCPtrPC)
|
---|
| 188 | {
|
---|
| 189 | CPUM_ASSERT_NOT_EXTRN(pVCpu, CPUMCTX_EXTRN_DR7);
|
---|
| 190 |
|
---|
| 191 | /*
|
---|
| 192 | * Check hyper breakpoints first as the VMM debugger has priority over
|
---|
| 193 | * the guest.
|
---|
| 194 | */
|
---|
[97820] | 195 | /** @todo we need some kind of resume flag for these. */
|
---|
[97699] | 196 | if (pVM->dbgf.s.cEnabledHwBreakpoints > 0)
|
---|
[97693] | 197 | for (unsigned iBp = 0; iBp < RT_ELEMENTS(pVM->dbgf.s.aHwBreakpoints); iBp++)
|
---|
| 198 | {
|
---|
| 199 | if ( pVM->dbgf.s.aHwBreakpoints[iBp].GCPtr != GCPtrPC
|
---|
| 200 | || pVM->dbgf.s.aHwBreakpoints[iBp].fType != X86_DR7_RW_EO
|
---|
| 201 | || pVM->dbgf.s.aHwBreakpoints[iBp].cb != 1
|
---|
| 202 | || !pVM->dbgf.s.aHwBreakpoints[iBp].fEnabled
|
---|
| 203 | || pVM->dbgf.s.aHwBreakpoints[iBp].hBp == NIL_DBGFBP)
|
---|
| 204 | { /*likely*/ }
|
---|
| 205 | else
|
---|
| 206 | {
|
---|
| 207 | /* (See also DBGFRZTrap01Handler.) */
|
---|
| 208 | pVCpu->dbgf.s.hBpActive = pVM->dbgf.s.aHwBreakpoints[iBp].hBp;
|
---|
| 209 | pVCpu->dbgf.s.fSingleSteppingRaw = false;
|
---|
| 210 |
|
---|
| 211 | LogFlow(("DBGFBpCheckInstruction: hit hw breakpoint %u at %04x:%RGv (%RGv)\n",
|
---|
| 212 | iBp, pVCpu->cpum.GstCtx.cs.Sel, pVCpu->cpum.GstCtx.rip, GCPtrPC));
|
---|
| 213 | return VINF_EM_DBG_BREAKPOINT;
|
---|
| 214 | }
|
---|
| 215 | }
|
---|
| 216 |
|
---|
| 217 | /*
|
---|
| 218 | * Check the guest.
|
---|
| 219 | */
|
---|
| 220 | uint32_t const fDr7 = (uint32_t)pVCpu->cpum.GstCtx.dr[7];
|
---|
[97820] | 221 | if (X86_DR7_ANY_EO_ENABLED(fDr7) && !pVCpu->cpum.GstCtx.eflags.Bits.u1RF)
|
---|
[97693] | 222 | {
|
---|
| 223 | /*
|
---|
| 224 | * The CPU (10980XE & 6700K at least) will set the DR6.BPx bits for any
|
---|
| 225 | * DRx that matches the current PC and is configured as an execution
|
---|
| 226 | * breakpoint (RWx=EO, LENx=1byte). They don't have to be enabled,
|
---|
| 227 | * however one that is enabled must match for the #DB to be raised and
|
---|
| 228 | * DR6 to be modified, of course.
|
---|
| 229 | */
|
---|
| 230 | CPUM_IMPORT_EXTRN_RET(pVCpu, CPUMCTX_EXTRN_DR0_DR3);
|
---|
| 231 | uint32_t fMatched = 0;
|
---|
| 232 | uint32_t fEnabled = 0;
|
---|
[97705] | 233 | for (unsigned iBp = 0, uBpMask = 1; iBp < 4; iBp++, uBpMask <<= 1)
|
---|
[97693] | 234 | if (X86_DR7_IS_EO_CFG(fDr7, iBp))
|
---|
| 235 | {
|
---|
| 236 | if (fDr7 & X86_DR7_L_G(iBp))
|
---|
[97705] | 237 | fEnabled |= uBpMask;
|
---|
[97693] | 238 | if (pVCpu->cpum.GstCtx.dr[iBp] == GCPtrPC)
|
---|
[97705] | 239 | fMatched |= uBpMask;
|
---|
[97693] | 240 | }
|
---|
| 241 | if (!(fEnabled & fMatched))
|
---|
| 242 | { /*likely*/ }
|
---|
| 243 | else if (fEnabled & fMatched)
|
---|
| 244 | {
|
---|
| 245 | /*
|
---|
| 246 | * Update DR6 and DR7.
|
---|
| 247 | *
|
---|
| 248 | * See "AMD64 Architecture Programmer's Manual Volume 2", chapter
|
---|
| 249 | * 13.1.1.3 for details on DR6 bits. The basics is that the B0..B3
|
---|
| 250 | * bits are always cleared while the others must be cleared by software.
|
---|
| 251 | *
|
---|
| 252 | * The following sub chapters says the GD bit is always cleared when
|
---|
| 253 | * generating a #DB so the handler can safely access the debug registers.
|
---|
| 254 | */
|
---|
| 255 | CPUM_IMPORT_EXTRN_RET(pVCpu, CPUMCTX_EXTRN_DR6);
|
---|
| 256 | pVCpu->cpum.GstCtx.dr[6] &= ~X86_DR6_B_MASK;
|
---|
[98029] | 257 | if (pVM->cpum.ro.GuestFeatures.enmCpuVendor != CPUMCPUVENDOR_INTEL)
|
---|
| 258 | pVCpu->cpum.GstCtx.dr[6] |= fMatched & fEnabled;
|
---|
| 259 | else
|
---|
| 260 | pVCpu->cpum.GstCtx.dr[6] |= fMatched; /* Intel: All matched, regardless of whether they're enabled or not */
|
---|
[97693] | 261 | pVCpu->cpum.GstCtx.dr[7] &= ~X86_DR7_GD;
|
---|
| 262 | LogFlow(("DBGFBpCheckInstruction: hit hw breakpoints %#x at %04x:%RGv (%RGv)\n",
|
---|
| 263 | fMatched, pVCpu->cpum.GstCtx.cs.Sel, pVCpu->cpum.GstCtx.rip, GCPtrPC));
|
---|
| 264 | return VINF_EM_RAW_GUEST_TRAP;
|
---|
| 265 | }
|
---|
| 266 | }
|
---|
| 267 | return VINF_SUCCESS;
|
---|
| 268 | }
|
---|
| 269 |
|
---|
| 270 |
|
---|
| 271 | /**
|
---|
| 272 | * Checks I/O access for guest or hypervisor hardware breakpoints.
|
---|
| 273 | *
|
---|
[47681] | 274 | * @returns Strict VBox status code
|
---|
| 275 | * @retval VINF_SUCCESS no breakpoint.
|
---|
| 276 | * @retval VINF_EM_DBG_BREAKPOINT hypervisor breakpoint triggered.
|
---|
| 277 | * @retval VINF_EM_RAW_GUEST_TRAP guest breakpoint triggered, DR6 and DR7 have
|
---|
| 278 | * been updated appropriately.
|
---|
| 279 | *
|
---|
[87594] | 280 | * @param pVM The cross context VM structure.
|
---|
[58123] | 281 | * @param pVCpu The cross context virtual CPU structure of the calling EMT.
|
---|
[47681] | 282 | * @param pCtx The CPU context for the calling EMT.
|
---|
| 283 | * @param uIoPort The I/O port being accessed.
|
---|
| 284 | * @param cbValue The size/width of the access, in bytes.
|
---|
| 285 | */
|
---|
| 286 | VMM_INT_DECL(VBOXSTRICTRC) DBGFBpCheckIo(PVM pVM, PVMCPU pVCpu, PCPUMCTX pCtx, RTIOPORT uIoPort, uint8_t cbValue)
|
---|
| 287 | {
|
---|
| 288 | uint32_t const uIoPortFirst = uIoPort;
|
---|
| 289 | uint32_t const uIoPortLast = uIoPortFirst + cbValue - 1;
|
---|
[86667] | 290 |
|
---|
[47681] | 291 | /*
|
---|
| 292 | * Check hyper breakpoints first as the VMM debugger has priority over
|
---|
| 293 | * the guest.
|
---|
| 294 | */
|
---|
[58909] | 295 | if (pVM->dbgf.s.cEnabledHwIoBreakpoints > 0)
|
---|
[47681] | 296 | {
|
---|
[58909] | 297 | for (unsigned iBp = 0; iBp < RT_ELEMENTS(pVM->dbgf.s.aHwBreakpoints); iBp++)
|
---|
[47681] | 298 | {
|
---|
[87594] | 299 | if ( pVM->dbgf.s.aHwBreakpoints[iBp].fType == X86_DR7_RW_IO
|
---|
[58909] | 300 | && pVM->dbgf.s.aHwBreakpoints[iBp].fEnabled
|
---|
[89911] | 301 | && pVM->dbgf.s.aHwBreakpoints[iBp].hBp != NIL_DBGFBP)
|
---|
[47681] | 302 | {
|
---|
[87594] | 303 | uint8_t cbReg = pVM->dbgf.s.aHwBreakpoints[iBp].cb; Assert(RT_IS_POWER_OF_TWO(cbReg));
|
---|
| 304 | uint64_t uDrXFirst = pVM->dbgf.s.aHwBreakpoints[iBp].GCPtr & ~(uint64_t)(cbReg - 1);
|
---|
[58909] | 305 | uint64_t uDrXLast = uDrXFirst + cbReg - 1;
|
---|
| 306 | if (uDrXFirst <= uIoPortLast && uDrXLast >= uIoPortFirst)
|
---|
| 307 | {
|
---|
| 308 | /* (See also DBGFRZTrap01Handler.) */
|
---|
[87594] | 309 | pVCpu->dbgf.s.hBpActive = pVM->dbgf.s.aHwBreakpoints[iBp].hBp;
|
---|
[58909] | 310 | pVCpu->dbgf.s.fSingleSteppingRaw = false;
|
---|
[47681] | 311 |
|
---|
[58909] | 312 | LogFlow(("DBGFBpCheckIo: hit hw breakpoint %d at %04x:%RGv (iop %#x)\n",
|
---|
[86666] | 313 | iBp, pCtx->cs.Sel, pCtx->rip, uIoPort));
|
---|
| 314 | return VINF_EM_DBG_BREAKPOINT;
|
---|
| 315 | }
|
---|
| 316 | }
|
---|
[47681] | 317 | }
|
---|
| 318 | }
|
---|
| 319 |
|
---|
| 320 | /*
|
---|
| 321 | * Check the guest.
|
---|
| 322 | */
|
---|
| 323 | uint32_t const uDr7 = pCtx->dr[7];
|
---|
| 324 | if ( (uDr7 & X86_DR7_ENABLED_MASK)
|
---|
| 325 | && X86_DR7_ANY_RW_IO(uDr7)
|
---|
| 326 | && (pCtx->cr4 & X86_CR4_DE) )
|
---|
| 327 | {
|
---|
| 328 | for (unsigned iBp = 0; iBp < 4; iBp++)
|
---|
| 329 | {
|
---|
| 330 | if ( (uDr7 & X86_DR7_L_G(iBp))
|
---|
| 331 | && X86_DR7_GET_RW(uDr7, iBp) == X86_DR7_RW_IO)
|
---|
| 332 | {
|
---|
| 333 | /* ASSUME the breakpoint and the I/O width qualifier uses the same encoding (1 2 x 4). */
|
---|
| 334 | static uint8_t const s_abInvAlign[4] = { 0, 1, 7, 3 };
|
---|
| 335 | uint8_t cbInvAlign = s_abInvAlign[X86_DR7_GET_LEN(uDr7, iBp)];
|
---|
| 336 | uint64_t uDrXFirst = pCtx->dr[iBp] & ~(uint64_t)cbInvAlign;
|
---|
| 337 | uint64_t uDrXLast = uDrXFirst + cbInvAlign;
|
---|
| 338 |
|
---|
| 339 | if (uDrXFirst <= uIoPortLast && uDrXLast >= uIoPortFirst)
|
---|
| 340 | {
|
---|
| 341 | /*
|
---|
| 342 | * Update DR6 and DR7.
|
---|
| 343 | *
|
---|
| 344 | * See "AMD64 Architecture Programmer's Manual Volume 2",
|
---|
| 345 | * chapter 13.1.1.3 for details on DR6 bits. The basics is
|
---|
| 346 | * that the B0..B3 bits are always cleared while the others
|
---|
| 347 | * must be cleared by software.
|
---|
| 348 | *
|
---|
[47683] | 349 | * The following sub chapters says the GD bit is always
|
---|
| 350 | * cleared when generating a #DB so the handler can safely
|
---|
| 351 | * access the debug registers.
|
---|
[47681] | 352 | */
|
---|
| 353 | pCtx->dr[6] &= ~X86_DR6_B_MASK;
|
---|
| 354 | pCtx->dr[6] |= X86_DR6_B(iBp);
|
---|
| 355 | pCtx->dr[7] &= ~X86_DR7_GD;
|
---|
| 356 | LogFlow(("DBGFBpCheckIo: hit hw breakpoint %d at %04x:%RGv (iop %#x)\n",
|
---|
[86666] | 357 | iBp, pCtx->cs.Sel, pCtx->rip, uIoPort));
|
---|
[47681] | 358 | return VINF_EM_RAW_GUEST_TRAP;
|
---|
| 359 | }
|
---|
| 360 | }
|
---|
| 361 | }
|
---|
| 362 | }
|
---|
| 363 | return VINF_SUCCESS;
|
---|
| 364 | }
|
---|
| 365 |
|
---|
| 366 |
|
---|
| 367 | /**
|
---|
[97705] | 368 | * Checks I/O access for guest or hypervisor hardware breakpoints.
|
---|
| 369 | *
|
---|
| 370 | * Caller must make sure DR0-3 and DR7 are present in the CPU context before
|
---|
| 371 | * calling this function.
|
---|
| 372 | *
|
---|
| 373 | * @returns CPUMCTX_DBG_DBGF_BP, CPUMCTX_DBG_HIT_DRX_MASK, or 0 (no match).
|
---|
| 374 | *
|
---|
| 375 | * @param pVM The cross context VM structure.
|
---|
| 376 | * @param pVCpu The cross context virtual CPU structure of the calling EMT.
|
---|
| 377 | * @param uIoPort The I/O port being accessed.
|
---|
| 378 | * @param cbValue The size/width of the access, in bytes.
|
---|
| 379 | */
|
---|
| 380 | VMM_INT_DECL(uint32_t) DBGFBpCheckIo2(PVMCC pVM, PVMCPUCC pVCpu, RTIOPORT uIoPort, uint8_t cbValue)
|
---|
| 381 | {
|
---|
| 382 | uint32_t const uIoPortFirst = uIoPort;
|
---|
| 383 | uint32_t const uIoPortLast = uIoPortFirst + cbValue - 1;
|
---|
| 384 |
|
---|
| 385 | /*
|
---|
| 386 | * Check hyper breakpoints first as the VMM debugger has priority over
|
---|
| 387 | * the guest.
|
---|
| 388 | */
|
---|
| 389 | if (pVM->dbgf.s.cEnabledHwIoBreakpoints > 0)
|
---|
| 390 | for (unsigned iBp = 0; iBp < RT_ELEMENTS(pVM->dbgf.s.aHwBreakpoints); iBp++)
|
---|
| 391 | {
|
---|
| 392 | if ( pVM->dbgf.s.aHwBreakpoints[iBp].fType == X86_DR7_RW_IO
|
---|
| 393 | && pVM->dbgf.s.aHwBreakpoints[iBp].fEnabled
|
---|
| 394 | && pVM->dbgf.s.aHwBreakpoints[iBp].hBp != NIL_DBGFBP)
|
---|
| 395 | {
|
---|
| 396 | uint8_t cbReg = pVM->dbgf.s.aHwBreakpoints[iBp].cb; Assert(RT_IS_POWER_OF_TWO(cbReg));
|
---|
| 397 | uint64_t uDrXFirst = pVM->dbgf.s.aHwBreakpoints[iBp].GCPtr & ~(uint64_t)(cbReg - 1);
|
---|
| 398 | uint64_t uDrXLast = uDrXFirst + cbReg - 1;
|
---|
| 399 | if (uDrXFirst <= uIoPortLast && uDrXLast >= uIoPortFirst)
|
---|
| 400 | {
|
---|
| 401 | /* (See also DBGFRZTrap01Handler.) */
|
---|
| 402 | pVCpu->dbgf.s.hBpActive = pVM->dbgf.s.aHwBreakpoints[iBp].hBp;
|
---|
| 403 | pVCpu->dbgf.s.fSingleSteppingRaw = false;
|
---|
| 404 |
|
---|
| 405 | LogFlow(("DBGFBpCheckIo2: hit hw breakpoint %d at %04x:%RGv (iop %#x L %u)\n",
|
---|
| 406 | iBp, pVCpu->cpum.GstCtx.cs.Sel, pVCpu->cpum.GstCtx.rip, uIoPort, cbValue));
|
---|
| 407 | return CPUMCTX_DBG_DBGF_BP;
|
---|
| 408 | }
|
---|
| 409 | }
|
---|
| 410 | }
|
---|
| 411 |
|
---|
| 412 | /*
|
---|
| 413 | * Check the guest.
|
---|
| 414 | */
|
---|
| 415 | uint32_t const fDr7 = pVCpu->cpum.GstCtx.dr[7];
|
---|
| 416 | if ( (fDr7 & X86_DR7_ENABLED_MASK)
|
---|
| 417 | && X86_DR7_ANY_RW_IO(fDr7)
|
---|
| 418 | && (pVCpu->cpum.GstCtx.cr4 & X86_CR4_DE) )
|
---|
| 419 | {
|
---|
| 420 | uint32_t fEnabled = 0;
|
---|
| 421 | uint32_t fMatched = 0;
|
---|
| 422 | for (unsigned iBp = 0, uBpMask = 1; iBp < 4; iBp++, uBpMask <<= 1)
|
---|
| 423 | {
|
---|
| 424 | if (fDr7 & X86_DR7_L_G(iBp))
|
---|
| 425 | fEnabled |= uBpMask;
|
---|
| 426 | if (X86_DR7_GET_RW(fDr7, iBp) == X86_DR7_RW_IO)
|
---|
| 427 | {
|
---|
| 428 | /* ASSUME the breakpoint and the I/O width qualifier uses the same encoding (1 2 x 4). */
|
---|
| 429 | static uint8_t const s_abInvAlign[4] = { 0, 1, 7, 3 };
|
---|
| 430 | uint8_t const cbInvAlign = s_abInvAlign[X86_DR7_GET_LEN(fDr7, iBp)];
|
---|
| 431 | uint64_t const uDrXFirst = pVCpu->cpum.GstCtx.dr[iBp] & ~(uint64_t)cbInvAlign;
|
---|
| 432 | uint64_t const uDrXLast = uDrXFirst + cbInvAlign;
|
---|
| 433 | if (uDrXFirst <= uIoPortLast && uDrXLast >= uIoPortFirst)
|
---|
| 434 | fMatched |= uBpMask;
|
---|
| 435 | }
|
---|
| 436 | }
|
---|
| 437 | if (fEnabled & fMatched)
|
---|
| 438 | {
|
---|
| 439 | LogFlow(("DBGFBpCheckIo2: hit hw breakpoint %#x at %04x:%RGv (iop %#x L %u)\n",
|
---|
| 440 | fMatched, pVCpu->cpum.GstCtx.cs.Sel, pVCpu->cpum.GstCtx.rip, uIoPort, cbValue));
|
---|
| 441 | return fMatched << CPUMCTX_DBG_HIT_DRX_SHIFT;
|
---|
| 442 | }
|
---|
| 443 | }
|
---|
| 444 |
|
---|
| 445 | return 0;
|
---|
| 446 | }
|
---|
[99051] | 447 | #endif /* VBOX_VMM_TARGET_ARMV8 */
|
---|
[97705] | 448 |
|
---|
| 449 |
|
---|
| 450 | /**
|
---|
[19286] | 451 | * Returns the single stepping state for a virtual CPU.
|
---|
[2168] | 452 | *
|
---|
[19286] | 453 | * @returns stepping (true) or not (false).
|
---|
| 454 | *
|
---|
[58123] | 455 | * @param pVCpu The cross context virtual CPU structure.
|
---|
[2168] | 456 | */
|
---|
[44399] | 457 | VMM_INT_DECL(bool) DBGFIsStepping(PVMCPU pVCpu)
|
---|
[2168] | 458 | {
|
---|
[19286] | 459 | return pVCpu->dbgf.s.fSingleSteppingRaw;
|
---|
[2199] | 460 | }
|
---|
[12688] | 461 |
|
---|
[58998] | 462 |
|
---|
[59073] | 463 | /**
|
---|
| 464 | * Checks if the specified generic event is enabled or not.
|
---|
| 465 | *
|
---|
| 466 | * @returns true / false.
|
---|
| 467 | * @param pVM The cross context VM structure.
|
---|
| 468 | * @param enmEvent The generic event being raised.
|
---|
| 469 | * @param uEventArg The argument of that event.
|
---|
| 470 | */
|
---|
| 471 | DECLINLINE(bool) dbgfEventIsGenericWithArgEnabled(PVM pVM, DBGFEVENTTYPE enmEvent, uint64_t uEventArg)
|
---|
[58998] | 472 | {
|
---|
[59073] | 473 | if (DBGF_IS_EVENT_ENABLED(pVM, enmEvent))
|
---|
| 474 | {
|
---|
| 475 | switch (enmEvent)
|
---|
| 476 | {
|
---|
| 477 | case DBGFEVENT_INTERRUPT_HARDWARE:
|
---|
| 478 | AssertReturn(uEventArg < 256, false);
|
---|
| 479 | return ASMBitTest(pVM->dbgf.s.bmHardIntBreakpoints, (uint32_t)uEventArg);
|
---|
| 480 |
|
---|
| 481 | case DBGFEVENT_INTERRUPT_SOFTWARE:
|
---|
| 482 | AssertReturn(uEventArg < 256, false);
|
---|
| 483 | return ASMBitTest(pVM->dbgf.s.bmSoftIntBreakpoints, (uint32_t)uEventArg);
|
---|
| 484 |
|
---|
| 485 | default:
|
---|
| 486 | return true;
|
---|
| 487 |
|
---|
| 488 | }
|
---|
| 489 | }
|
---|
| 490 | return false;
|
---|
| 491 | }
|
---|
| 492 |
|
---|
| 493 |
|
---|
| 494 | /**
|
---|
| 495 | * Raises a generic debug event if enabled and not being ignored.
|
---|
| 496 | *
|
---|
| 497 | * @returns Strict VBox status code.
|
---|
| 498 | * @retval VINF_EM_DBG_EVENT if the event was raised and the caller should
|
---|
[61628] | 499 | * return ASAP to the debugger (via EM). We set VMCPU_FF_DBGF so, it
|
---|
[73348] | 500 | * is okay not to pass this along in some situations.
|
---|
[59073] | 501 | * @retval VINF_SUCCESS if the event was disabled or ignored.
|
---|
| 502 | *
|
---|
| 503 | * @param pVM The cross context VM structure.
|
---|
| 504 | * @param pVCpu The cross context virtual CPU structure.
|
---|
| 505 | * @param enmEvent The generic event being raised.
|
---|
[73348] | 506 | * @param enmCtx The context in which this event is being raised.
|
---|
| 507 | * @param cArgs Number of arguments (0 - 6).
|
---|
[73354] | 508 | * @param ... Event arguments.
|
---|
[59073] | 509 | *
|
---|
| 510 | * @thread EMT(pVCpu)
|
---|
| 511 | */
|
---|
[73348] | 512 | VMM_INT_DECL(VBOXSTRICTRC) DBGFEventGenericWithArgs(PVM pVM, PVMCPU pVCpu, DBGFEVENTTYPE enmEvent, DBGFEVENTCTX enmCtx,
|
---|
| 513 | unsigned cArgs, ...)
|
---|
[59073] | 514 | {
|
---|
[73348] | 515 | Assert(cArgs < RT_ELEMENTS(pVCpu->dbgf.s.aEvents[0].Event.u.Generic.auArgs));
|
---|
| 516 |
|
---|
[59073] | 517 | /*
|
---|
| 518 | * Is it enabled.
|
---|
| 519 | */
|
---|
[73348] | 520 | va_list va;
|
---|
| 521 | va_start(va, cArgs);
|
---|
| 522 | uint64_t uEventArg0 = cArgs ? va_arg(va, uint64_t) : 0;
|
---|
| 523 | if (dbgfEventIsGenericWithArgEnabled(pVM, enmEvent, uEventArg0))
|
---|
[59073] | 524 | {
|
---|
| 525 | /*
|
---|
| 526 | * Any events on the stack. Should the incoming event be ignored?
|
---|
| 527 | */
|
---|
[99051] | 528 | #if defined(VBOX_VMM_TARGET_ARMV8)
|
---|
| 529 | uint64_t const rip = CPUMGetGuestFlatPC(pVCpu); /* rip is a misnomer but saves us #ifdef's later on. */
|
---|
| 530 | #else
|
---|
[59073] | 531 | uint64_t const rip = CPUMGetGuestRIP(pVCpu);
|
---|
[99051] | 532 | #endif
|
---|
[59073] | 533 | uint32_t i = pVCpu->dbgf.s.cEvents;
|
---|
| 534 | if (i > 0)
|
---|
| 535 | {
|
---|
| 536 | while (i-- > 0)
|
---|
| 537 | {
|
---|
| 538 | if ( pVCpu->dbgf.s.aEvents[i].Event.enmType == enmEvent
|
---|
| 539 | && pVCpu->dbgf.s.aEvents[i].enmState == DBGFEVENTSTATE_IGNORE
|
---|
| 540 | && pVCpu->dbgf.s.aEvents[i].rip == rip)
|
---|
| 541 | {
|
---|
| 542 | pVCpu->dbgf.s.aEvents[i].enmState = DBGFEVENTSTATE_RESTORABLE;
|
---|
[73348] | 543 | va_end(va);
|
---|
[59073] | 544 | return VINF_SUCCESS;
|
---|
| 545 | }
|
---|
| 546 | Assert(pVCpu->dbgf.s.aEvents[i].enmState != DBGFEVENTSTATE_CURRENT);
|
---|
| 547 | }
|
---|
| 548 |
|
---|
| 549 | /*
|
---|
| 550 | * Trim the event stack.
|
---|
| 551 | */
|
---|
| 552 | i = pVCpu->dbgf.s.cEvents;
|
---|
| 553 | while (i-- > 0)
|
---|
| 554 | {
|
---|
| 555 | if ( pVCpu->dbgf.s.aEvents[i].rip == rip
|
---|
| 556 | && ( pVCpu->dbgf.s.aEvents[i].enmState == DBGFEVENTSTATE_RESTORABLE
|
---|
| 557 | || pVCpu->dbgf.s.aEvents[i].enmState == DBGFEVENTSTATE_IGNORE) )
|
---|
| 558 | pVCpu->dbgf.s.aEvents[i].enmState = DBGFEVENTSTATE_IGNORE;
|
---|
| 559 | else
|
---|
| 560 | {
|
---|
| 561 | if (i + 1 != pVCpu->dbgf.s.cEvents)
|
---|
| 562 | memmove(&pVCpu->dbgf.s.aEvents[i], &pVCpu->dbgf.s.aEvents[i + 1],
|
---|
| 563 | (pVCpu->dbgf.s.cEvents - i) * sizeof(pVCpu->dbgf.s.aEvents));
|
---|
| 564 | pVCpu->dbgf.s.cEvents--;
|
---|
| 565 | }
|
---|
| 566 | }
|
---|
| 567 |
|
---|
| 568 | i = pVCpu->dbgf.s.cEvents;
|
---|
| 569 | AssertStmt(i < RT_ELEMENTS(pVCpu->dbgf.s.aEvents), i = RT_ELEMENTS(pVCpu->dbgf.s.aEvents) - 1);
|
---|
| 570 | }
|
---|
| 571 |
|
---|
| 572 | /*
|
---|
| 573 | * Push the event.
|
---|
| 574 | */
|
---|
| 575 | pVCpu->dbgf.s.aEvents[i].enmState = DBGFEVENTSTATE_CURRENT;
|
---|
| 576 | pVCpu->dbgf.s.aEvents[i].rip = rip;
|
---|
| 577 | pVCpu->dbgf.s.aEvents[i].Event.enmType = enmEvent;
|
---|
| 578 | pVCpu->dbgf.s.aEvents[i].Event.enmCtx = enmCtx;
|
---|
[73348] | 579 | pVCpu->dbgf.s.aEvents[i].Event.u.Generic.cArgs = cArgs;
|
---|
| 580 | pVCpu->dbgf.s.aEvents[i].Event.u.Generic.auArgs[0] = uEventArg0;
|
---|
| 581 | if (cArgs > 1)
|
---|
| 582 | {
|
---|
| 583 | AssertStmt(cArgs < RT_ELEMENTS(pVCpu->dbgf.s.aEvents[i].Event.u.Generic.auArgs),
|
---|
| 584 | cArgs = RT_ELEMENTS(pVCpu->dbgf.s.aEvents[i].Event.u.Generic.auArgs));
|
---|
| 585 | for (unsigned iArg = 1; iArg < cArgs; iArg++)
|
---|
| 586 | pVCpu->dbgf.s.aEvents[i].Event.u.Generic.auArgs[iArg] = va_arg(va, uint64_t);
|
---|
| 587 | }
|
---|
[59073] | 588 | pVCpu->dbgf.s.cEvents = i + 1;
|
---|
| 589 |
|
---|
[61628] | 590 | VMCPU_FF_SET(pVCpu, VMCPU_FF_DBGF);
|
---|
[73348] | 591 | va_end(va);
|
---|
[59073] | 592 | return VINF_EM_DBG_EVENT;
|
---|
| 593 | }
|
---|
| 594 |
|
---|
[73348] | 595 | va_end(va);
|
---|
[58998] | 596 | return VINF_SUCCESS;
|
---|
| 597 | }
|
---|
| 598 |
|
---|