/* $Id: DBGFAddr.cpp $ */ /** @file * DBGF - Debugger Facility, Mixed Address Methods. */ /* * Copyright (C) 2006-2023 Oracle and/or its affiliates. * * This file is part of VirtualBox base platform packages, as * available from https://www.virtualbox.org. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation, in version 3 of the * License. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * * SPDX-License-Identifier: GPL-3.0-only */ /********************************************************************************************************************************* * Header Files * *********************************************************************************************************************************/ #define LOG_GROUP LOG_GROUP_DBGF #include #include #include #include #include #include "DBGFInternal.h" #include #include #include #include #include /** * Common worker for DBGFR3AddrFromSelOff and DBGFR3AddrFromSelInfoOff. */ static int dbgfR3AddrFromSelInfoOffWorker(PDBGFADDRESS pAddress, PCDBGFSELINFO pSelInfo, RTUINTPTR off) { if (pSelInfo->fFlags & (DBGFSELINFO_FLAGS_INVALID | DBGFSELINFO_FLAGS_NOT_PRESENT)) return pSelInfo->fFlags & DBGFSELINFO_FLAGS_NOT_PRESENT ? VERR_SELECTOR_NOT_PRESENT : VERR_INVALID_SELECTOR; /** @todo This all goes voodoo in long mode. */ /* check limit. */ if (DBGFSelInfoIsExpandDown(pSelInfo)) { if ( !pSelInfo->u.Raw.Gen.u1Granularity && off > UINT32_C(0xffff)) return VERR_OUT_OF_SELECTOR_BOUNDS; if (off <= pSelInfo->cbLimit) return VERR_OUT_OF_SELECTOR_BOUNDS; } else if (off > pSelInfo->cbLimit) return VERR_OUT_OF_SELECTOR_BOUNDS; pAddress->FlatPtr = pSelInfo->GCPtrBase + off; /** @todo fix all these selector tests! */ if ( !pSelInfo->GCPtrBase && pSelInfo->u.Raw.Gen.u1Granularity && pSelInfo->u.Raw.Gen.u1DefBig) pAddress->fFlags = DBGFADDRESS_FLAGS_FLAT; else if (pSelInfo->cbLimit <= UINT32_C(0xffff)) pAddress->fFlags = DBGFADDRESS_FLAGS_FAR16; else if (pSelInfo->cbLimit <= UINT32_C(0xffffffff)) pAddress->fFlags = DBGFADDRESS_FLAGS_FAR32; else pAddress->fFlags = DBGFADDRESS_FLAGS_FAR64; return VINF_SUCCESS; } /** * Creates a mixed address from a Sel:off pair. * * @returns VBox status code. * @param pUVM The user mode VM handle. * @param idCpu The CPU ID. * @param pAddress Where to store the mixed address. * @param Sel The selector part. * @param off The offset part. */ VMMR3DECL(int) DBGFR3AddrFromSelOff(PUVM pUVM, VMCPUID idCpu, PDBGFADDRESS pAddress, RTSEL Sel, RTUINTPTR off) { UVM_ASSERT_VALID_EXT_RETURN(pUVM, VERR_INVALID_VM_HANDLE); VM_ASSERT_VALID_EXT_RETURN(pUVM->pVM, VERR_INVALID_VM_HANDLE); AssertReturn(idCpu < pUVM->cCpus, VERR_INVALID_PARAMETER); pAddress->Sel = Sel; pAddress->off = off; if (Sel != DBGF_SEL_FLAT) { DBGFSELINFO SelInfo; int rc = DBGFR3SelQueryInfo(pUVM, idCpu, Sel, DBGFSELQI_FLAGS_DT_GUEST | DBGFSELQI_FLAGS_DT_ADJ_64BIT_MODE, &SelInfo); if (RT_FAILURE(rc)) return rc; rc = dbgfR3AddrFromSelInfoOffWorker(pAddress, &SelInfo, off); if (RT_FAILURE(rc)) return rc; } else { pAddress->FlatPtr = off; pAddress->fFlags = DBGFADDRESS_FLAGS_FLAT; } pAddress->fFlags |= DBGFADDRESS_FLAGS_VALID; return VINF_SUCCESS; } /** * Creates a mixed address from selector info and an offset into the segment * described by it. * * @returns VBox status code. * @param pUVM The user mode VM handle. * @param pAddress Where to store the mixed address. * @param pSelInfo The selector info. * @param off The offset part. */ VMMR3DECL(int) DBGFR3AddrFromSelInfoOff(PUVM pUVM, PDBGFADDRESS pAddress, PCDBGFSELINFO pSelInfo, RTUINTPTR off) { UVM_ASSERT_VALID_EXT_RETURN(pUVM, VERR_INVALID_VM_HANDLE); VM_ASSERT_VALID_EXT_RETURN(pUVM->pVM, VERR_INVALID_VM_HANDLE); pAddress->Sel = pSelInfo->Sel; pAddress->off = off; int rc = dbgfR3AddrFromSelInfoOffWorker(pAddress, pSelInfo, off); if (RT_FAILURE(rc)) return rc; pAddress->fFlags |= DBGFADDRESS_FLAGS_VALID; return VINF_SUCCESS; } /** * Creates a mixed address from a flat address. * * @returns pAddress. * @param pUVM The user mode VM handle. * @param pAddress Where to store the mixed address. * @param FlatPtr The flat pointer. */ VMMR3DECL(PDBGFADDRESS) DBGFR3AddrFromFlat(PUVM pUVM, PDBGFADDRESS pAddress, RTGCUINTPTR FlatPtr) { UVM_ASSERT_VALID_EXT_RETURN(pUVM, NULL); VM_ASSERT_VALID_EXT_RETURN(pUVM->pVM, NULL); pAddress->Sel = DBGF_SEL_FLAT; pAddress->off = FlatPtr; pAddress->FlatPtr = FlatPtr; pAddress->fFlags = DBGFADDRESS_FLAGS_FLAT | DBGFADDRESS_FLAGS_VALID; return pAddress; } /** * Creates a mixed address from a guest physical address. * * @returns pAddress. * @param pUVM The user mode VM handle. * @param pAddress Where to store the mixed address. * @param PhysAddr The guest physical address. */ VMMR3DECL(PDBGFADDRESS) DBGFR3AddrFromPhys(PUVM pUVM, PDBGFADDRESS pAddress, RTGCPHYS PhysAddr) { UVM_ASSERT_VALID_EXT_RETURN(pUVM, NULL); pAddress->Sel = DBGF_SEL_FLAT; pAddress->off = PhysAddr; pAddress->FlatPtr = PhysAddr; pAddress->fFlags = DBGFADDRESS_FLAGS_PHYS | DBGFADDRESS_FLAGS_VALID; return pAddress; } /** * Creates a mixed address from a flat host ring-0 address. * * @returns pAddress * @param pAddress Where to store the mixed address. * @param R0Ptr The host ring-0 address. */ VMMR3_INT_DECL(PDBGFADDRESS) DBGFR3AddrFromHostR0(PDBGFADDRESS pAddress, RTR0UINTPTR R0Ptr) { pAddress->FlatPtr = R0Ptr; pAddress->off = R0Ptr; pAddress->fFlags = DBGFADDRESS_FLAGS_RING0 | DBGFADDRESS_FLAGS_VALID; pAddress->Sel = DBGF_SEL_FLAT; return pAddress; } /** * Checks if the specified address is valid (checks the structure pointer too). * * @returns true if valid. * @returns false if invalid. * @param pUVM The user mode VM handle. * @param pAddress The address to validate. */ VMMR3DECL(bool) DBGFR3AddrIsValid(PUVM pUVM, PCDBGFADDRESS pAddress) { UVM_ASSERT_VALID_EXT_RETURN(pUVM, false); if (!RT_VALID_PTR(pAddress)) return false; if (!DBGFADDRESS_IS_VALID(pAddress)) return false; /* more? */ return true; } /** * Called on the EMT for the VCpu. * * @returns VBox status code. * @param pVCpu The cross context virtual CPU structure. * @param pAddress The address. * @param pGCPhys Where to return the physical address. */ static DECLCALLBACK(int) dbgfR3AddrToPhysOnVCpu(PVMCPU pVCpu, PCDBGFADDRESS pAddress, PRTGCPHYS pGCPhys) { VMCPU_ASSERT_EMT(pVCpu); /* This is just a wrapper because we cannot pass FlatPtr thru VMR3ReqCall directly. */ PGMPTWALK Walk; int const rc = PGMGstGetPage(pVCpu, pAddress->FlatPtr, &Walk); *pGCPhys = Walk.GCPhys; return rc; } /** * Converts an address to a guest physical address. * * @returns VBox status code. * @retval VINF_SUCCESS * @retval VERR_INVALID_PARAMETER if the address is invalid. * @retval VERR_INVALID_STATE if the VM is being terminated or if the virtual * CPU handle is invalid. * @retval VERR_NOT_SUPPORTED is the type of address cannot be converted. * @retval VERR_PAGE_NOT_PRESENT * @retval VERR_PAGE_TABLE_NOT_PRESENT * @retval VERR_PAGE_DIRECTORY_PTR_NOT_PRESENT * @retval VERR_PAGE_MAP_LEVEL4_NOT_PRESENT * * @param pUVM The user mode VM handle. * @param idCpu The ID of the CPU context to convert virtual * addresses. * @param pAddress The address. * @param pGCPhys Where to return the physical address. */ VMMR3DECL(int) DBGFR3AddrToPhys(PUVM pUVM, VMCPUID idCpu, PCDBGFADDRESS pAddress, PRTGCPHYS pGCPhys) { /* * Parameter validation. */ AssertPtr(pGCPhys); *pGCPhys = NIL_RTGCPHYS; AssertPtr(pAddress); AssertReturn(DBGFADDRESS_IS_VALID(pAddress), VERR_INVALID_PARAMETER); UVM_ASSERT_VALID_EXT_RETURN(pUVM, VERR_INVALID_STATE); PVM pVM = pUVM->pVM; VM_ASSERT_VALID_EXT_RETURN(pVM, VERR_INVALID_VM_HANDLE); AssertReturn(idCpu < pUVM->cCpus, VERR_INVALID_PARAMETER); /* * Convert by address type. */ int rc; if (pAddress->fFlags & DBGFADDRESS_FLAGS_PHYS) { *pGCPhys = pAddress->FlatPtr; rc = VINF_SUCCESS; } else { PVMCPU pVCpu = VMMGetCpuById(pVM, idCpu); if (VMCPU_IS_EMT(pVCpu)) rc = dbgfR3AddrToPhysOnVCpu(pVCpu, pAddress, pGCPhys); else rc = VMR3ReqPriorityCallWaitU(pUVM, pVCpu->idCpu, (PFNRT)dbgfR3AddrToPhysOnVCpu, 3, pVCpu, pAddress, pGCPhys); } return rc; } /** * Converts an address to a host physical address. * * @returns VBox status code. * @retval VINF_SUCCESS * @retval VERR_INVALID_PARAMETER if the address is invalid. * @retval VERR_INVALID_STATE if the VM is being terminated or if the virtual * CPU handle is invalid. * @retval VERR_NOT_SUPPORTED is the type of address cannot be converted. * @retval VERR_PAGE_NOT_PRESENT * @retval VERR_PAGE_TABLE_NOT_PRESENT * @retval VERR_PAGE_DIRECTORY_PTR_NOT_PRESENT * @retval VERR_PAGE_MAP_LEVEL4_NOT_PRESENT * @retval VERR_PGM_PHYS_PAGE_RESERVED * @retval VERR_PGM_INVALID_GC_PHYSICAL_ADDRESS * * @param pUVM The user mode VM handle. * @param idCpu The ID of the CPU context to convert virtual * addresses. * @param pAddress The address. * @param pHCPhys Where to return the physical address. */ VMMR3DECL(int) DBGFR3AddrToHostPhys(PUVM pUVM, VMCPUID idCpu, PDBGFADDRESS pAddress, PRTHCPHYS pHCPhys) { /* * Parameter validation. */ AssertPtr(pHCPhys); *pHCPhys = NIL_RTHCPHYS; AssertPtr(pAddress); AssertReturn(DBGFADDRESS_IS_VALID(pAddress), VERR_INVALID_PARAMETER); UVM_ASSERT_VALID_EXT_RETURN(pUVM, VERR_INVALID_STATE); PVM pVM = pUVM->pVM; VM_ASSERT_VALID_EXT_RETURN(pVM, VERR_INVALID_VM_HANDLE); AssertReturn(idCpu < pUVM->cCpus, VERR_INVALID_PARAMETER); /* * Convert it. */ RTGCPHYS GCPhys; int rc = DBGFR3AddrToPhys(pUVM, idCpu, pAddress, &GCPhys); if (RT_SUCCESS(rc)) rc = PGMPhysGCPhys2HCPhys(pVM, pAddress->FlatPtr, pHCPhys); return rc; } /** * Called on the EMT for the VCpu. * * @returns VBox status code. * * @param pUVM The user mode VM handle. * @param idCpu The ID of the CPU context. * @param pAddress The address. * @param fReadOnly Whether returning a read-only page is fine or not. * @param ppvR3Ptr Where to return the address. */ static DECLCALLBACK(int) dbgfR3AddrToVolatileR3PtrOnVCpu(PUVM pUVM, VMCPUID idCpu, PDBGFADDRESS pAddress, bool fReadOnly, void **ppvR3Ptr) { PVM pVM = pUVM->pVM; VM_ASSERT_VALID_EXT_RETURN(pVM, VERR_INVALID_VM_HANDLE); Assert(idCpu == VMMGetCpuId(pVM)); /* * This is a tad ugly, but it gets the job done. */ int rc; PGMPAGEMAPLOCK Lock; if (pAddress->fFlags & DBGFADDRESS_FLAGS_PHYS) { if (fReadOnly) rc = PGMPhysGCPhys2CCPtrReadOnly(pVM, pAddress->FlatPtr, (void const **)ppvR3Ptr, &Lock); else rc = PGMPhysGCPhys2CCPtr(pVM, pAddress->FlatPtr, ppvR3Ptr, &Lock); } else { PVMCPU pVCpu = VMMGetCpuById(pVM, idCpu); if (fReadOnly) rc = PGMPhysGCPtr2CCPtrReadOnly(pVCpu, pAddress->FlatPtr, (void const **)ppvR3Ptr, &Lock); else rc = PGMPhysGCPtr2CCPtr(pVCpu, pAddress->FlatPtr, ppvR3Ptr, &Lock); } if (RT_SUCCESS(rc)) PGMPhysReleasePageMappingLock(pVM, &Lock); return rc; } /** * Converts an address to a volatile host virtual address. * * @returns VBox status code. * @retval VINF_SUCCESS * @retval VERR_INVALID_PARAMETER if the address is invalid. * @retval VERR_INVALID_STATE if the VM is being terminated or if the virtual * CPU handle is invalid. * @retval VERR_NOT_SUPPORTED is the type of address cannot be converted. * @retval VERR_PAGE_NOT_PRESENT * @retval VERR_PAGE_TABLE_NOT_PRESENT * @retval VERR_PAGE_DIRECTORY_PTR_NOT_PRESENT * @retval VERR_PAGE_MAP_LEVEL4_NOT_PRESENT * @retval VERR_PGM_PHYS_PAGE_RESERVED * @retval VERR_PGM_INVALID_GC_PHYSICAL_ADDRESS * * @param pUVM The user mode VM handle. * @param idCpu The ID of the CPU context to convert virtual * addresses. * @param pAddress The address. * @param fReadOnly Whether returning a read-only page is fine or not. * If set to thru the page may have to be made writable * before we return. * @param ppvR3Ptr Where to return the address. */ VMMR3DECL(int) DBGFR3AddrToVolatileR3Ptr(PUVM pUVM, VMCPUID idCpu, PDBGFADDRESS pAddress, bool fReadOnly, void **ppvR3Ptr) { /* * Parameter validation. */ AssertPtr(ppvR3Ptr); *ppvR3Ptr = NULL; AssertPtr(pAddress); AssertReturn(DBGFADDRESS_IS_VALID(pAddress), VERR_INVALID_PARAMETER); UVM_ASSERT_VALID_EXT_RETURN(pUVM, VERR_INVALID_STATE); AssertReturn(idCpu < pUVM->cCpus, VERR_INVALID_PARAMETER); /* * Convert it. */ return VMR3ReqPriorityCallWaitU(pUVM, idCpu, (PFNRT)dbgfR3AddrToVolatileR3PtrOnVCpu, 5, pUVM, idCpu, pAddress, fReadOnly, ppvR3Ptr); } /** * Adds an offset to an address. * * @returns pAddress. * * @param pAddress The address. * @param uAddend How much to add. * * @remarks No address space or segment limit checks are performed, */ VMMR3DECL(PDBGFADDRESS) DBGFR3AddrAdd(PDBGFADDRESS pAddress, RTGCUINTPTR uAddend) { /* * Parameter validation. */ AssertPtrReturn(pAddress, NULL); AssertReturn(DBGFADDRESS_IS_VALID(pAddress), NULL); /* * Add the stuff. */ pAddress->off += uAddend; pAddress->FlatPtr += uAddend; return pAddress; } /** * Subtracts an offset from an address. * * @returns VINF_SUCCESS on success. * * @param pAddress The address. * @param uSubtrahend How much to subtract. * * @remarks No address space or segment limit checks are performed, */ VMMR3DECL(PDBGFADDRESS) DBGFR3AddrSub(PDBGFADDRESS pAddress, RTGCUINTPTR uSubtrahend) { /* * Parameter validation. */ AssertPtrReturn(pAddress, NULL); AssertReturn(DBGFADDRESS_IS_VALID(pAddress), NULL); /* * Add the stuff. */ pAddress->off -= uSubtrahend; pAddress->FlatPtr -= uSubtrahend; return pAddress; }