DBGFReg.cpp revision 8deff8528b8bc4b5c7c7cbbaae04f5112a88777c
083dd76e9fd7a829b1ed67ffc9003276643e7db1vboxsync/* $Id$ */
85c594c1140f082dd862abde9dc7825137a3d51avboxsync/** @file
083dd76e9fd7a829b1ed67ffc9003276643e7db1vboxsync * DBGF - Debugger Facility, Register Methods.
85c594c1140f082dd862abde9dc7825137a3d51avboxsync */
85c594c1140f082dd862abde9dc7825137a3d51avboxsync
85c594c1140f082dd862abde9dc7825137a3d51avboxsync/*
1c94c0a63ba68be1a7b2c640e70d7a06464e4fcavboxsync * Copyright (C) 2010-2011 Oracle Corporation
85c594c1140f082dd862abde9dc7825137a3d51avboxsync *
bd8e360cd1db83dcb2694ea9122ce3bc5bae678avboxsync * This file is part of VirtualBox Open Source Edition (OSE), as
bd8e360cd1db83dcb2694ea9122ce3bc5bae678avboxsync * available from http://www.virtualbox.org. This file is free software;
bd8e360cd1db83dcb2694ea9122ce3bc5bae678avboxsync * you can redistribute it and/or modify it under the terms of the GNU
bd8e360cd1db83dcb2694ea9122ce3bc5bae678avboxsync * General Public License (GPL) as published by the Free Software
bd8e360cd1db83dcb2694ea9122ce3bc5bae678avboxsync * Foundation, in version 2 as it comes in the "COPYING" file of the
bd8e360cd1db83dcb2694ea9122ce3bc5bae678avboxsync * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
bd8e360cd1db83dcb2694ea9122ce3bc5bae678avboxsync * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
1c94c0a63ba68be1a7b2c640e70d7a06464e4fcavboxsync */
1c94c0a63ba68be1a7b2c640e70d7a06464e4fcavboxsync
1c94c0a63ba68be1a7b2c640e70d7a06464e4fcavboxsync
1c94c0a63ba68be1a7b2c640e70d7a06464e4fcavboxsync/*******************************************************************************
85c594c1140f082dd862abde9dc7825137a3d51avboxsync* Header Files *
85c594c1140f082dd862abde9dc7825137a3d51avboxsync*******************************************************************************/
85c594c1140f082dd862abde9dc7825137a3d51avboxsync#define LOG_GROUP LOG_GROUP_DBGF
85c594c1140f082dd862abde9dc7825137a3d51avboxsync#include <VBox/vmm/dbgf.h>
85c594c1140f082dd862abde9dc7825137a3d51avboxsync#include "DBGFInternal.h"
85c594c1140f082dd862abde9dc7825137a3d51avboxsync#include <VBox/vmm/mm.h>
f6b7aa2d424718f1bf8ca45a37b219b986f1024bvboxsync#include <VBox/vmm/vm.h>
f6b7aa2d424718f1bf8ca45a37b219b986f1024bvboxsync#include <VBox/param.h>
f6b7aa2d424718f1bf8ca45a37b219b986f1024bvboxsync#include <VBox/err.h>
f6b7aa2d424718f1bf8ca45a37b219b986f1024bvboxsync#include <VBox/log.h>
85c594c1140f082dd862abde9dc7825137a3d51avboxsync#include <iprt/ctype.h>
85c594c1140f082dd862abde9dc7825137a3d51avboxsync#include <iprt/string.h>
85c594c1140f082dd862abde9dc7825137a3d51avboxsync
f6b7aa2d424718f1bf8ca45a37b219b986f1024bvboxsync
b29e03c9044019d9c77222336e8f8616052824f5vboxsync/*******************************************************************************
87b2fb3f8283472ba7010aedbf2b4dc12302155cvboxsync* Defined Constants And Macros *
87b2fb3f8283472ba7010aedbf2b4dc12302155cvboxsync*******************************************************************************/
11a862be79fe123488bccca60c06e92cdbfec6e8vboxsync/** Locks the register database for writing. */
87b2fb3f8283472ba7010aedbf2b4dc12302155cvboxsync#define DBGF_REG_DB_LOCK_WRITE(pVM) \
e38719852d98638514dba23fbacf53ad11361d6avboxsync do { \
85c594c1140f082dd862abde9dc7825137a3d51avboxsync int rcSem = RTSemRWRequestWrite((pVM)->dbgf.s.hRegDbLock, RT_INDEFINITE_WAIT); \
85c594c1140f082dd862abde9dc7825137a3d51avboxsync AssertRC(rcSem); \
85c594c1140f082dd862abde9dc7825137a3d51avboxsync } while (0)
85c594c1140f082dd862abde9dc7825137a3d51avboxsync
463d00559e51c6e08ccc9f5a77d2ee6d122b6e8cvboxsync/** Unlocks the register database after writing. */
463d00559e51c6e08ccc9f5a77d2ee6d122b6e8cvboxsync#define DBGF_REG_DB_UNLOCK_WRITE(pVM) \
463d00559e51c6e08ccc9f5a77d2ee6d122b6e8cvboxsync do { \
85c594c1140f082dd862abde9dc7825137a3d51avboxsync int rcSem = RTSemRWReleaseWrite((pVM)->dbgf.s.hRegDbLock); \
3598f07e0e71a448a04d478320a9ca6314160ff6vboxsync AssertRC(rcSem); \
083dd76e9fd7a829b1ed67ffc9003276643e7db1vboxsync } while (0)
85c594c1140f082dd862abde9dc7825137a3d51avboxsync
463d00559e51c6e08ccc9f5a77d2ee6d122b6e8cvboxsync/** Locks the register database for reading. */
463d00559e51c6e08ccc9f5a77d2ee6d122b6e8cvboxsync#define DBGF_REG_DB_LOCK_READ(pVM) \
463d00559e51c6e08ccc9f5a77d2ee6d122b6e8cvboxsync do { \
463d00559e51c6e08ccc9f5a77d2ee6d122b6e8cvboxsync int rcSem = RTSemRWRequestRead((pVM)->dbgf.s.hRegDbLock, RT_INDEFINITE_WAIT); \
463d00559e51c6e08ccc9f5a77d2ee6d122b6e8cvboxsync AssertRC(rcSem); \
8ab60c04baaf509b2aaae2a260adaf1281aaac03vboxsync } while (0)
8ab60c04baaf509b2aaae2a260adaf1281aaac03vboxsync
8ab60c04baaf509b2aaae2a260adaf1281aaac03vboxsync/** Unlocks the register database after reading. */
463d00559e51c6e08ccc9f5a77d2ee6d122b6e8cvboxsync#define DBGF_REG_DB_UNLOCK_READ(pVM) \
6edb4183bc898fddcd0987b6c5c3903b8246fe45vboxsync do { \
85c594c1140f082dd862abde9dc7825137a3d51avboxsync int rcSem = RTSemRWReleaseRead((pVM)->dbgf.s.hRegDbLock); \
85c594c1140f082dd862abde9dc7825137a3d51avboxsync AssertRC(rcSem); \
85c594c1140f082dd862abde9dc7825137a3d51avboxsync } while (0)
85c594c1140f082dd862abde9dc7825137a3d51avboxsync
34822e8b7d00c04a0bc98c0d1a565a00d9bb1fd9vboxsync
34822e8b7d00c04a0bc98c0d1a565a00d9bb1fd9vboxsync/** The max length of a set, register or sub-field name. */
fde449f361029c75f9bf28f145bd1ba7b36a9c77vboxsync#define DBGF_REG_MAX_NAME 40
fde449f361029c75f9bf28f145bd1ba7b36a9c77vboxsync
34822e8b7d00c04a0bc98c0d1a565a00d9bb1fd9vboxsync
85c594c1140f082dd862abde9dc7825137a3d51avboxsync/*******************************************************************************
34822e8b7d00c04a0bc98c0d1a565a00d9bb1fd9vboxsync* Structures and Typedefs *
507ebf9b3b77c84000a55645867db6617b5324bcvboxsync*******************************************************************************/
34822e8b7d00c04a0bc98c0d1a565a00d9bb1fd9vboxsync/**
34822e8b7d00c04a0bc98c0d1a565a00d9bb1fd9vboxsync * Register set registration record type.
34822e8b7d00c04a0bc98c0d1a565a00d9bb1fd9vboxsync */
34822e8b7d00c04a0bc98c0d1a565a00d9bb1fd9vboxsynctypedef enum DBGFREGSETTYPE
507ebf9b3b77c84000a55645867db6617b5324bcvboxsync{
507ebf9b3b77c84000a55645867db6617b5324bcvboxsync /** Invalid zero value. */
4ad933b349952e43d0b6ce21b3f5e2833ca04e85vboxsync DBGFREGSETTYPE_INVALID = 0,
85c594c1140f082dd862abde9dc7825137a3d51avboxsync /** CPU record. */
85c594c1140f082dd862abde9dc7825137a3d51avboxsync DBGFREGSETTYPE_CPU,
34822e8b7d00c04a0bc98c0d1a565a00d9bb1fd9vboxsync /** Device record. */
507ebf9b3b77c84000a55645867db6617b5324bcvboxsync DBGFREGSETTYPE_DEVICE,
34822e8b7d00c04a0bc98c0d1a565a00d9bb1fd9vboxsync /** End of valid record types. */
507ebf9b3b77c84000a55645867db6617b5324bcvboxsync DBGFREGSETTYPE_END
507ebf9b3b77c84000a55645867db6617b5324bcvboxsync} DBGFREGSETTYPE;
34822e8b7d00c04a0bc98c0d1a565a00d9bb1fd9vboxsync
98da2c2cca8786e117ad93a31c6b2c6c1c3cdc78vboxsync
507ebf9b3b77c84000a55645867db6617b5324bcvboxsync/**
507ebf9b3b77c84000a55645867db6617b5324bcvboxsync * Register set registration record.
507ebf9b3b77c84000a55645867db6617b5324bcvboxsync */
507ebf9b3b77c84000a55645867db6617b5324bcvboxsynctypedef struct DBGFREGSET
f6b7aa2d424718f1bf8ca45a37b219b986f1024bvboxsync{
f6b7aa2d424718f1bf8ca45a37b219b986f1024bvboxsync /** String space core. */
f6b7aa2d424718f1bf8ca45a37b219b986f1024bvboxsync RTSTRSPACECORE Core;
507ebf9b3b77c84000a55645867db6617b5324bcvboxsync /** The registration record type. */
fde449f361029c75f9bf28f145bd1ba7b36a9c77vboxsync DBGFREGSETTYPE enmType;
f6b7aa2d424718f1bf8ca45a37b219b986f1024bvboxsync /** The user argument for the callbacks. */
fde449f361029c75f9bf28f145bd1ba7b36a9c77vboxsync union
f6b7aa2d424718f1bf8ca45a37b219b986f1024bvboxsync {
85c594c1140f082dd862abde9dc7825137a3d51avboxsync /** The CPU view. */
34822e8b7d00c04a0bc98c0d1a565a00d9bb1fd9vboxsync PVMCPU pVCpu;
f6b7aa2d424718f1bf8ca45a37b219b986f1024bvboxsync /** The device view. */
fde449f361029c75f9bf28f145bd1ba7b36a9c77vboxsync PPDMDEVINS pDevIns;
f6b7aa2d424718f1bf8ca45a37b219b986f1024bvboxsync /** The general view. */
f6b7aa2d424718f1bf8ca45a37b219b986f1024bvboxsync void *pv;
f6b7aa2d424718f1bf8ca45a37b219b986f1024bvboxsync } uUserArg;
f6b7aa2d424718f1bf8ca45a37b219b986f1024bvboxsync
fde449f361029c75f9bf28f145bd1ba7b36a9c77vboxsync /** The register descriptors. */
f6b7aa2d424718f1bf8ca45a37b219b986f1024bvboxsync PCDBGFREGDESC paDescs;
f6b7aa2d424718f1bf8ca45a37b219b986f1024bvboxsync /** The number of register descriptors. */
f6b7aa2d424718f1bf8ca45a37b219b986f1024bvboxsync uint32_t cDescs;
f6b7aa2d424718f1bf8ca45a37b219b986f1024bvboxsync
f6b7aa2d424718f1bf8ca45a37b219b986f1024bvboxsync /** Array of lookup records. */
f6b7aa2d424718f1bf8ca45a37b219b986f1024bvboxsync struct DBGFREGLOOKUP *paLookupRecs;
f6b7aa2d424718f1bf8ca45a37b219b986f1024bvboxsync /** The number of lookup records. */
f6b7aa2d424718f1bf8ca45a37b219b986f1024bvboxsync uint32_t cLookupRecs;
f6b7aa2d424718f1bf8ca45a37b219b986f1024bvboxsync
f6b7aa2d424718f1bf8ca45a37b219b986f1024bvboxsync /** The register name prefix. */
f6b7aa2d424718f1bf8ca45a37b219b986f1024bvboxsync char szPrefix[1];
f6b7aa2d424718f1bf8ca45a37b219b986f1024bvboxsync} DBGFREGSET;
f6b7aa2d424718f1bf8ca45a37b219b986f1024bvboxsync/** Pointer to a register registration record. */
f6b7aa2d424718f1bf8ca45a37b219b986f1024bvboxsynctypedef DBGFREGSET *PDBGFREGSET;
f6b7aa2d424718f1bf8ca45a37b219b986f1024bvboxsync/** Pointer to a const register registration record. */
f6b7aa2d424718f1bf8ca45a37b219b986f1024bvboxsynctypedef DBGFREGSET const *PCDBGFREGSET;
f6b7aa2d424718f1bf8ca45a37b219b986f1024bvboxsync
f6b7aa2d424718f1bf8ca45a37b219b986f1024bvboxsync
85c594c1140f082dd862abde9dc7825137a3d51avboxsync/**
f6b7aa2d424718f1bf8ca45a37b219b986f1024bvboxsync * Register lookup record.
85c594c1140f082dd862abde9dc7825137a3d51avboxsync */
e38719852d98638514dba23fbacf53ad11361d6avboxsynctypedef struct DBGFREGLOOKUP
e38719852d98638514dba23fbacf53ad11361d6avboxsync{
85c594c1140f082dd862abde9dc7825137a3d51avboxsync /** The string space core. */
85c594c1140f082dd862abde9dc7825137a3d51avboxsync RTSTRSPACECORE Core;
85c594c1140f082dd862abde9dc7825137a3d51avboxsync /** Pointer to the set. */
85c594c1140f082dd862abde9dc7825137a3d51avboxsync PCDBGFREGSET pSet;
85c594c1140f082dd862abde9dc7825137a3d51avboxsync /** Pointer to the register descriptor. */
85c594c1140f082dd862abde9dc7825137a3d51avboxsync PCDBGFREGDESC pDesc;
85c594c1140f082dd862abde9dc7825137a3d51avboxsync /** If an alias this points to the alias descriptor, NULL if not. */
85c594c1140f082dd862abde9dc7825137a3d51avboxsync PCDBGFREGALIAS pAlias;
85c594c1140f082dd862abde9dc7825137a3d51avboxsync /** If a sub-field this points to the sub-field descriptor, NULL if not. */
85c594c1140f082dd862abde9dc7825137a3d51avboxsync PCDBGFREGSUBFIELD pSubField;
85c594c1140f082dd862abde9dc7825137a3d51avboxsync} DBGFREGLOOKUP;
85c594c1140f082dd862abde9dc7825137a3d51avboxsync/** Pointer to a register lookup record. */
85c594c1140f082dd862abde9dc7825137a3d51avboxsynctypedef DBGFREGLOOKUP *PDBGFREGLOOKUP;
85c594c1140f082dd862abde9dc7825137a3d51avboxsync/** Pointer to a const register lookup record. */
85c594c1140f082dd862abde9dc7825137a3d51avboxsynctypedef DBGFREGLOOKUP const *PCDBGFREGLOOKUP;
85c594c1140f082dd862abde9dc7825137a3d51avboxsync
85c594c1140f082dd862abde9dc7825137a3d51avboxsync
85c594c1140f082dd862abde9dc7825137a3d51avboxsync/**
85c594c1140f082dd862abde9dc7825137a3d51avboxsync * Initializes the register database.
85c594c1140f082dd862abde9dc7825137a3d51avboxsync *
85c594c1140f082dd862abde9dc7825137a3d51avboxsync * @returns VBox status code.
85c594c1140f082dd862abde9dc7825137a3d51avboxsync * @param pVM The VM handle.
85c594c1140f082dd862abde9dc7825137a3d51avboxsync */
85c594c1140f082dd862abde9dc7825137a3d51avboxsyncint dbgfR3RegInit(PVM pVM)
85c594c1140f082dd862abde9dc7825137a3d51avboxsync{
85c594c1140f082dd862abde9dc7825137a3d51avboxsync return RTSemRWCreate(&pVM->dbgf.s.hRegDbLock);
85c594c1140f082dd862abde9dc7825137a3d51avboxsync}
85c594c1140f082dd862abde9dc7825137a3d51avboxsync
85c594c1140f082dd862abde9dc7825137a3d51avboxsync
85c594c1140f082dd862abde9dc7825137a3d51avboxsync/**
85c594c1140f082dd862abde9dc7825137a3d51avboxsync * Terminates the register database.
85c594c1140f082dd862abde9dc7825137a3d51avboxsync *
85c594c1140f082dd862abde9dc7825137a3d51avboxsync * @param pVM The VM handle.
85c594c1140f082dd862abde9dc7825137a3d51avboxsync */
85c594c1140f082dd862abde9dc7825137a3d51avboxsyncvoid dbgfR3RegTerm(PVM pVM)
85c594c1140f082dd862abde9dc7825137a3d51avboxsync{
85c594c1140f082dd862abde9dc7825137a3d51avboxsync RTSemRWDestroy(pVM->dbgf.s.hRegDbLock);
85c594c1140f082dd862abde9dc7825137a3d51avboxsync pVM->dbgf.s.hRegDbLock = NIL_RTSEMRW;
85c594c1140f082dd862abde9dc7825137a3d51avboxsync}
85c594c1140f082dd862abde9dc7825137a3d51avboxsync
85c594c1140f082dd862abde9dc7825137a3d51avboxsync
85c594c1140f082dd862abde9dc7825137a3d51avboxsync/**
85c594c1140f082dd862abde9dc7825137a3d51avboxsync * Validates a register name.
85c594c1140f082dd862abde9dc7825137a3d51avboxsync *
85c594c1140f082dd862abde9dc7825137a3d51avboxsync * This is used for prefixes, aliases and field names.
85c594c1140f082dd862abde9dc7825137a3d51avboxsync *
85c594c1140f082dd862abde9dc7825137a3d51avboxsync * @returns true if valid, false if not.
999b9efb1c9a95fee642550c525ca0cf7c6f07b5vboxsync * @param pszName The register name to validate.
999b9efb1c9a95fee642550c525ca0cf7c6f07b5vboxsync */
f6b7aa2d424718f1bf8ca45a37b219b986f1024bvboxsyncstatic bool dbgfR3RegIsNameValid(const char *pszName)
999b9efb1c9a95fee642550c525ca0cf7c6f07b5vboxsync{
eb5d68001efb9346c3341aa46f2871b973b47107vboxsync const char *psz = pszName;
999b9efb1c9a95fee642550c525ca0cf7c6f07b5vboxsync if (!RT_C_IS_ALPHA(*psz))
eb5d68001efb9346c3341aa46f2871b973b47107vboxsync return false;
999b9efb1c9a95fee642550c525ca0cf7c6f07b5vboxsync char ch;
eb5d68001efb9346c3341aa46f2871b973b47107vboxsync while ((ch = *++psz))
999b9efb1c9a95fee642550c525ca0cf7c6f07b5vboxsync if ( !RT_C_IS_LOWER(ch)
999b9efb1c9a95fee642550c525ca0cf7c6f07b5vboxsync && !RT_C_IS_DIGIT(ch)
999b9efb1c9a95fee642550c525ca0cf7c6f07b5vboxsync && ch != '_')
999b9efb1c9a95fee642550c525ca0cf7c6f07b5vboxsync return false;
999b9efb1c9a95fee642550c525ca0cf7c6f07b5vboxsync if (psz - pszName > DBGF_REG_MAX_NAME)
eb5d68001efb9346c3341aa46f2871b973b47107vboxsync return false;
999b9efb1c9a95fee642550c525ca0cf7c6f07b5vboxsync return true;
999b9efb1c9a95fee642550c525ca0cf7c6f07b5vboxsync}
999b9efb1c9a95fee642550c525ca0cf7c6f07b5vboxsync
eb5d68001efb9346c3341aa46f2871b973b47107vboxsync
999b9efb1c9a95fee642550c525ca0cf7c6f07b5vboxsync/**
999b9efb1c9a95fee642550c525ca0cf7c6f07b5vboxsync * Common worker for registering a register set.
999b9efb1c9a95fee642550c525ca0cf7c6f07b5vboxsync *
eb5d68001efb9346c3341aa46f2871b973b47107vboxsync * @returns VBox status code.
999b9efb1c9a95fee642550c525ca0cf7c6f07b5vboxsync * @param pVM The VM handle.
999b9efb1c9a95fee642550c525ca0cf7c6f07b5vboxsync * @param paRegisters The register descriptors.
eb5d68001efb9346c3341aa46f2871b973b47107vboxsync * @param enmType The set type.
999b9efb1c9a95fee642550c525ca0cf7c6f07b5vboxsync * @param pvUserArg The user argument for the callbacks.
999b9efb1c9a95fee642550c525ca0cf7c6f07b5vboxsync * @param pszPrefix The name prefix.
eb5d68001efb9346c3341aa46f2871b973b47107vboxsync * @param iInstance The instance number to be appended to @a
999b9efb1c9a95fee642550c525ca0cf7c6f07b5vboxsync * pszPrefix when creating the set name.
999b9efb1c9a95fee642550c525ca0cf7c6f07b5vboxsync */
eb5d68001efb9346c3341aa46f2871b973b47107vboxsyncstatic int dbgfR3RegRegisterCommon(PVM pVM, PCDBGFREGDESC paRegisters, DBGFREGSETTYPE enmType, void *pvUserArg, const char *pszPrefix, uint32_t iInstance)
999b9efb1c9a95fee642550c525ca0cf7c6f07b5vboxsync{
999b9efb1c9a95fee642550c525ca0cf7c6f07b5vboxsync /*
eb5d68001efb9346c3341aa46f2871b973b47107vboxsync * Validate input.
f6b7aa2d424718f1bf8ca45a37b219b986f1024bvboxsync */
f6b7aa2d424718f1bf8ca45a37b219b986f1024bvboxsync /* The name components. */
f6b7aa2d424718f1bf8ca45a37b219b986f1024bvboxsync AssertMsgReturn(dbgfR3RegIsNameValid(pszPrefix), ("%s\n", pszPrefix), VERR_INVALID_NAME);
fde449f361029c75f9bf28f145bd1ba7b36a9c77vboxsync const char *psz = RTStrEnd(pszPrefix, RTSTR_MAX);
f6b7aa2d424718f1bf8ca45a37b219b986f1024bvboxsync bool const fNeedUnderscore = RT_C_IS_DIGIT(psz[-1]);
fde449f361029c75f9bf28f145bd1ba7b36a9c77vboxsync size_t const cchPrefix = psz - pszPrefix + fNeedUnderscore;
6edb4183bc898fddcd0987b6c5c3903b8246fe45vboxsync AssertMsgReturn(cchPrefix < RT_SIZEOFMEMB(DBGFREGSET, szPrefix) - 4 - 1, ("%s\n", pszPrefix), VERR_INVALID_NAME);
fde449f361029c75f9bf28f145bd1ba7b36a9c77vboxsync
fde449f361029c75f9bf28f145bd1ba7b36a9c77vboxsync AssertMsgReturn(iInstance <= 9999, ("%d\n", iInstance), VERR_INVALID_NAME);
85c594c1140f082dd862abde9dc7825137a3d51avboxsync
6edb4183bc898fddcd0987b6c5c3903b8246fe45vboxsync /* The descriptors. */
85c594c1140f082dd862abde9dc7825137a3d51avboxsync uint32_t cLookupRecs = 0;
85c594c1140f082dd862abde9dc7825137a3d51avboxsync uint32_t iDesc;
85c594c1140f082dd862abde9dc7825137a3d51avboxsync for (iDesc = 0; paRegisters[iDesc].pszName != NULL; iDesc++)
85c594c1140f082dd862abde9dc7825137a3d51avboxsync {
e38719852d98638514dba23fbacf53ad11361d6avboxsync AssertMsgReturn(dbgfR3RegIsNameValid(paRegisters[iDesc].pszName), ("%s (#%u)\n", paRegisters[iDesc].pszName, iDesc), VERR_INVALID_NAME);
85c594c1140f082dd862abde9dc7825137a3d51avboxsync
eb5d68001efb9346c3341aa46f2871b973b47107vboxsync if (enmType == DBGFREGSETTYPE_CPU)
eb5d68001efb9346c3341aa46f2871b973b47107vboxsync AssertMsgReturn((unsigned)paRegisters[iDesc].enmReg == iDesc && iDesc < (unsigned)DBGFREG_END,
eb5d68001efb9346c3341aa46f2871b973b47107vboxsync ("%d iDesc=%d\n", paRegisters[iDesc].enmReg, iDesc),
eb5d68001efb9346c3341aa46f2871b973b47107vboxsync VERR_INVALID_PARAMETER);
3598f07e0e71a448a04d478320a9ca6314160ff6vboxsync else
eb5d68001efb9346c3341aa46f2871b973b47107vboxsync AssertReturn(paRegisters[iDesc].enmReg == DBGFREG_END, VERR_INVALID_PARAMETER);
eb5d68001efb9346c3341aa46f2871b973b47107vboxsync AssertReturn( paRegisters[iDesc].enmType > DBGFREGVALTYPE_INVALID
eb5d68001efb9346c3341aa46f2871b973b47107vboxsync && paRegisters[iDesc].enmType < DBGFREGVALTYPE_END, VERR_INVALID_PARAMETER);
3598f07e0e71a448a04d478320a9ca6314160ff6vboxsync AssertMsgReturn(paRegisters[iDesc].fFlags & ~DBGFREG_FLAGS_READ_ONLY,
eb5d68001efb9346c3341aa46f2871b973b47107vboxsync ("%#x (#%u)\n", paRegisters[iDesc].fFlags, iDesc),
eb5d68001efb9346c3341aa46f2871b973b47107vboxsync VERR_INVALID_PARAMETER);
3598f07e0e71a448a04d478320a9ca6314160ff6vboxsync AssertPtrReturn(paRegisters[iDesc].pfnGet, VERR_INVALID_PARAMETER);
85c594c1140f082dd862abde9dc7825137a3d51avboxsync AssertPtrReturn(paRegisters[iDesc].pfnSet, VERR_INVALID_PARAMETER);
85c594c1140f082dd862abde9dc7825137a3d51avboxsync
85c594c1140f082dd862abde9dc7825137a3d51avboxsync uint32_t iAlias = 0;
85c594c1140f082dd862abde9dc7825137a3d51avboxsync PCDBGFREGALIAS paAliases = paRegisters[iDesc].paAliases;
85c594c1140f082dd862abde9dc7825137a3d51avboxsync if (paAliases)
85c594c1140f082dd862abde9dc7825137a3d51avboxsync {
f6b7aa2d424718f1bf8ca45a37b219b986f1024bvboxsync AssertPtrReturn(paAliases, VERR_INVALID_PARAMETER);
f6b7aa2d424718f1bf8ca45a37b219b986f1024bvboxsync for (; paAliases[iAlias].pszName; iAlias++)
f6b7aa2d424718f1bf8ca45a37b219b986f1024bvboxsync {
34822e8b7d00c04a0bc98c0d1a565a00d9bb1fd9vboxsync AssertMsgReturn(dbgfR3RegIsNameValid(paAliases[iAlias].pszName), ("%s (%s)\n", paAliases[iAlias].pszName, paRegisters[iDesc].pszName), VERR_INVALID_NAME);
34822e8b7d00c04a0bc98c0d1a565a00d9bb1fd9vboxsync AssertReturn( paAliases[iAlias].enmType > DBGFREGVALTYPE_INVALID
34822e8b7d00c04a0bc98c0d1a565a00d9bb1fd9vboxsync && paAliases[iAlias].enmType < DBGFREGVALTYPE_END, VERR_INVALID_PARAMETER);
f6b7aa2d424718f1bf8ca45a37b219b986f1024bvboxsync }
f6b7aa2d424718f1bf8ca45a37b219b986f1024bvboxsync }
fde449f361029c75f9bf28f145bd1ba7b36a9c77vboxsync
f6b7aa2d424718f1bf8ca45a37b219b986f1024bvboxsync uint32_t iSubField = 0;
f6b7aa2d424718f1bf8ca45a37b219b986f1024bvboxsync PCDBGFREGSUBFIELD paSubFields = paRegisters[iDesc].paSubFields;
f6b7aa2d424718f1bf8ca45a37b219b986f1024bvboxsync if (paSubFields)
f6b7aa2d424718f1bf8ca45a37b219b986f1024bvboxsync {
f6b7aa2d424718f1bf8ca45a37b219b986f1024bvboxsync AssertPtrReturn(paSubFields, VERR_INVALID_PARAMETER);
f6b7aa2d424718f1bf8ca45a37b219b986f1024bvboxsync for (; paSubFields[iSubField].pszName; iSubField++)
507ebf9b3b77c84000a55645867db6617b5324bcvboxsync {
f6b7aa2d424718f1bf8ca45a37b219b986f1024bvboxsync AssertMsgReturn(dbgfR3RegIsNameValid(paSubFields[iSubField].pszName), ("%s (%s)\n", paSubFields[iSubField].pszName, paRegisters[iDesc].pszName), VERR_INVALID_NAME);
f6b7aa2d424718f1bf8ca45a37b219b986f1024bvboxsync AssertReturn(paSubFields[iSubField].iFirstBit + paSubFields[iSubField].cBits <= 128, VERR_INVALID_PARAMETER);
f6b7aa2d424718f1bf8ca45a37b219b986f1024bvboxsync AssertReturn(paSubFields[iSubField].cBits + paSubFields[iSubField].cShift <= 128, VERR_INVALID_PARAMETER);
f6b7aa2d424718f1bf8ca45a37b219b986f1024bvboxsync AssertPtrNullReturn(paSubFields[iSubField].pfnGet, VERR_INVALID_POINTER);
f6b7aa2d424718f1bf8ca45a37b219b986f1024bvboxsync AssertPtrNullReturn(paSubFields[iSubField].pfnSet, VERR_INVALID_POINTER);
85c594c1140f082dd862abde9dc7825137a3d51avboxsync }
85c594c1140f082dd862abde9dc7825137a3d51avboxsync }
507ebf9b3b77c84000a55645867db6617b5324bcvboxsync
f6b7aa2d424718f1bf8ca45a37b219b986f1024bvboxsync cLookupRecs += (1 + iAlias) * (1 + iSubField);
f6b7aa2d424718f1bf8ca45a37b219b986f1024bvboxsync }
f6b7aa2d424718f1bf8ca45a37b219b986f1024bvboxsync
f6b7aa2d424718f1bf8ca45a37b219b986f1024bvboxsync /* Check the instance number of the CPUs. */
85c594c1140f082dd862abde9dc7825137a3d51avboxsync AssertReturn(enmType != DBGFREGSETTYPE_CPU || iInstance < pVM->cCpus, VERR_INVALID_CPU_ID);
85c594c1140f082dd862abde9dc7825137a3d51avboxsync
f6b7aa2d424718f1bf8ca45a37b219b986f1024bvboxsync /*
f6b7aa2d424718f1bf8ca45a37b219b986f1024bvboxsync * Allocate a new record and all associated lookup records.
f6b7aa2d424718f1bf8ca45a37b219b986f1024bvboxsync */
f6b7aa2d424718f1bf8ca45a37b219b986f1024bvboxsync size_t cbRegSet = RT_OFFSETOF(DBGFREGSET, szPrefix[cchPrefix + 4 + 1]);
f6b7aa2d424718f1bf8ca45a37b219b986f1024bvboxsync cbRegSet = RT_ALIGN_Z(cbRegSet, 32);
f6b7aa2d424718f1bf8ca45a37b219b986f1024bvboxsync size_t const offLookupRecArray = cbRegSet;
f6b7aa2d424718f1bf8ca45a37b219b986f1024bvboxsync cbRegSet += cLookupRecs * sizeof(DBGFREGLOOKUP);
f6b7aa2d424718f1bf8ca45a37b219b986f1024bvboxsync
f6b7aa2d424718f1bf8ca45a37b219b986f1024bvboxsync PDBGFREGSET pRegSet = (PDBGFREGSET)MMR3HeapAllocZ(pVM, MM_TAG_DBGF_REG, cbRegSet);
85c594c1140f082dd862abde9dc7825137a3d51avboxsync if (!pRegSet)
85c594c1140f082dd862abde9dc7825137a3d51avboxsync return VERR_NO_MEMORY;
85c594c1140f082dd862abde9dc7825137a3d51avboxsync
85c594c1140f082dd862abde9dc7825137a3d51avboxsync /*
85c594c1140f082dd862abde9dc7825137a3d51avboxsync * Initialize the new record.
e38719852d98638514dba23fbacf53ad11361d6avboxsync */
85c594c1140f082dd862abde9dc7825137a3d51avboxsync pRegSet->Core.pszString = pRegSet->szPrefix;
85c594c1140f082dd862abde9dc7825137a3d51avboxsync pRegSet->enmType = enmType;
e38719852d98638514dba23fbacf53ad11361d6avboxsync pRegSet->uUserArg.pv = pvUserArg;
85c594c1140f082dd862abde9dc7825137a3d51avboxsync pRegSet->paDescs = paRegisters;
85c594c1140f082dd862abde9dc7825137a3d51avboxsync pRegSet->cDescs = iDesc;
85c594c1140f082dd862abde9dc7825137a3d51avboxsync pRegSet->cLookupRecs = cLookupRecs;
e38719852d98638514dba23fbacf53ad11361d6avboxsync pRegSet->paLookupRecs = (PDBGFREGLOOKUP)((uintptr_t)pRegSet + offLookupRecArray);
85c594c1140f082dd862abde9dc7825137a3d51avboxsync if (fNeedUnderscore)
e38719852d98638514dba23fbacf53ad11361d6avboxsync RTStrPrintf(pRegSet->szPrefix, cchPrefix + 4 + 1, "%s_%u", pszPrefix, iInstance);
85c594c1140f082dd862abde9dc7825137a3d51avboxsync else
85c594c1140f082dd862abde9dc7825137a3d51avboxsync RTStrPrintf(pRegSet->szPrefix, cchPrefix + 4 + 1, "%s%u", pszPrefix, iInstance);
0c1bdc5adae416967cb64e09f8ec81a5b77fe31dvboxsync
85c594c1140f082dd862abde9dc7825137a3d51avboxsync
f6b7aa2d424718f1bf8ca45a37b219b986f1024bvboxsync /*
f6b7aa2d424718f1bf8ca45a37b219b986f1024bvboxsync * Initialize the lookup records.
f6b7aa2d424718f1bf8ca45a37b219b986f1024bvboxsync */
f6b7aa2d424718f1bf8ca45a37b219b986f1024bvboxsync char szName[DBGF_REG_MAX_NAME * 3 + 16];
f6b7aa2d424718f1bf8ca45a37b219b986f1024bvboxsync strcpy(szName, pRegSet->szPrefix);
f6b7aa2d424718f1bf8ca45a37b219b986f1024bvboxsync char *pszReg = strchr(szName, '\0');
f6b7aa2d424718f1bf8ca45a37b219b986f1024bvboxsync *pszReg++ = '.';
f6b7aa2d424718f1bf8ca45a37b219b986f1024bvboxsync
f6b7aa2d424718f1bf8ca45a37b219b986f1024bvboxsync int rc = VINF_SUCCESS;
f6b7aa2d424718f1bf8ca45a37b219b986f1024bvboxsync PDBGFREGLOOKUP pLookupRec = &pRegSet->paLookupRecs[0];
f6b7aa2d424718f1bf8ca45a37b219b986f1024bvboxsync for (iDesc = 0; paRegisters[iDesc].pszName != NULL && RT_SUCCESS(rc); iDesc++)
f6b7aa2d424718f1bf8ca45a37b219b986f1024bvboxsync {
f6b7aa2d424718f1bf8ca45a37b219b986f1024bvboxsync PCDBGFREGALIAS pCurAlias = NULL;
f6b7aa2d424718f1bf8ca45a37b219b986f1024bvboxsync PCDBGFREGALIAS pNextAlias = paRegisters[iDesc].paAliases;
f6b7aa2d424718f1bf8ca45a37b219b986f1024bvboxsync const char *pszRegName = paRegisters[iDesc].pszName;
85c594c1140f082dd862abde9dc7825137a3d51avboxsync while (RT_SUCCESS(rc))
85c594c1140f082dd862abde9dc7825137a3d51avboxsync {
85c594c1140f082dd862abde9dc7825137a3d51avboxsync size_t cchReg = strlen(paRegisters[iDesc].pszName);
e38719852d98638514dba23fbacf53ad11361d6avboxsync memcpy(pszReg, paRegisters[iDesc].pszName, cchReg + 1);
85c594c1140f082dd862abde9dc7825137a3d51avboxsync pLookupRec->Core.pszString = MMR3HeapStrDup(pVM, MM_TAG_DBGF_REG, szName);
85c594c1140f082dd862abde9dc7825137a3d51avboxsync if (!pLookupRec->Core.pszString)
85c594c1140f082dd862abde9dc7825137a3d51avboxsync rc = VERR_NO_STR_MEMORY;
85c594c1140f082dd862abde9dc7825137a3d51avboxsync pLookupRec->pSet = pRegSet;
85c594c1140f082dd862abde9dc7825137a3d51avboxsync pLookupRec->pDesc = &paRegisters[iDesc];
85c594c1140f082dd862abde9dc7825137a3d51avboxsync pLookupRec->pAlias = pCurAlias;
b29e03c9044019d9c77222336e8f8616052824f5vboxsync pLookupRec->pSubField = NULL;
1c8130d3c423590046a8ed4f3059de38ac5dcc14vboxsync
1c8130d3c423590046a8ed4f3059de38ac5dcc14vboxsync PCDBGFREGSUBFIELD paSubFields = paRegisters[iDesc].paSubFields;
1c8130d3c423590046a8ed4f3059de38ac5dcc14vboxsync if (paSubFields)
11a862be79fe123488bccca60c06e92cdbfec6e8vboxsync {
1c8130d3c423590046a8ed4f3059de38ac5dcc14vboxsync char *pszSub = &pszReg[cchReg];
507ebf9b3b77c84000a55645867db6617b5324bcvboxsync *pszSub++ = '.';
11a862be79fe123488bccca60c06e92cdbfec6e8vboxsync for (uint32_t iSubField = 0; paSubFields[iSubField].pszName && RT_SUCCESS(rc); iSubField++)
1c8130d3c423590046a8ed4f3059de38ac5dcc14vboxsync {
34822e8b7d00c04a0bc98c0d1a565a00d9bb1fd9vboxsync strcpy(pszSub, paSubFields[iSubField].pszName);
1c8130d3c423590046a8ed4f3059de38ac5dcc14vboxsync pLookupRec->Core.pszString = MMR3HeapStrDup(pVM, MM_TAG_DBGF_REG, szName);
34822e8b7d00c04a0bc98c0d1a565a00d9bb1fd9vboxsync if (!pLookupRec->Core.pszString)
34822e8b7d00c04a0bc98c0d1a565a00d9bb1fd9vboxsync rc = VERR_NO_STR_MEMORY;
34822e8b7d00c04a0bc98c0d1a565a00d9bb1fd9vboxsync pLookupRec->pSet = pRegSet;
1c8130d3c423590046a8ed4f3059de38ac5dcc14vboxsync pLookupRec->pDesc = &paRegisters[iDesc];
507ebf9b3b77c84000a55645867db6617b5324bcvboxsync pLookupRec->pAlias = pCurAlias;
507ebf9b3b77c84000a55645867db6617b5324bcvboxsync pLookupRec->pSubField = &paSubFields[iSubField];
507ebf9b3b77c84000a55645867db6617b5324bcvboxsync }
463d00559e51c6e08ccc9f5a77d2ee6d122b6e8cvboxsync }
34822e8b7d00c04a0bc98c0d1a565a00d9bb1fd9vboxsync
463d00559e51c6e08ccc9f5a77d2ee6d122b6e8cvboxsync /* next */
34822e8b7d00c04a0bc98c0d1a565a00d9bb1fd9vboxsync pCurAlias = pNextAlias++;
507ebf9b3b77c84000a55645867db6617b5324bcvboxsync if ( !pCurAlias
507ebf9b3b77c84000a55645867db6617b5324bcvboxsync || !pCurAlias->pszName)
507ebf9b3b77c84000a55645867db6617b5324bcvboxsync break;
507ebf9b3b77c84000a55645867db6617b5324bcvboxsync pszRegName = pCurAlias->pszName;
85c594c1140f082dd862abde9dc7825137a3d51avboxsync }
85c594c1140f082dd862abde9dc7825137a3d51avboxsync }
85c594c1140f082dd862abde9dc7825137a3d51avboxsync
85c594c1140f082dd862abde9dc7825137a3d51avboxsync if (RT_SUCCESS(rc))
85c594c1140f082dd862abde9dc7825137a3d51avboxsync {
85c594c1140f082dd862abde9dc7825137a3d51avboxsync /*
85c594c1140f082dd862abde9dc7825137a3d51avboxsync * Insert the record into the register set string space and optionally into
85c594c1140f082dd862abde9dc7825137a3d51avboxsync * the CPU register set cache.
*/
DBGF_REG_DB_LOCK_WRITE(pVM);
bool fInserted = RTStrSpaceInsert(&pVM->dbgf.s.RegSetSpace, &pRegSet->Core);
if (fInserted)
{
if (enmType == DBGFREGSETTYPE_CPU)
pVM->aCpus[iInstance].dbgf.s.pRegSet = pRegSet;
pVM->dbgf.s.cRegs += pRegSet->cDescs;
PDBGFREGLOOKUP paLookupRecs = pRegSet->paLookupRecs;
uint32_t iLookupRec = pRegSet->cLookupRecs;
while (iLookupRec-- > 0)
{
bool fInserted2 = RTStrSpaceInsert(&pVM->dbgf.s.RegSpace, &paLookupRecs[iLookupRec].Core);
AssertMsg(fInserted2, ("'%s'", paLookupRecs[iLookupRec].Core.pszString));
}
DBGF_REG_DB_UNLOCK_WRITE(pVM);
return VINF_SUCCESS;
}
DBGF_REG_DB_UNLOCK_WRITE(pVM);
rc = VERR_DUPLICATE;
}
/*
* Bail out.
*/
for (uint32_t i = 0; i < pRegSet->cLookupRecs; i++)
MMR3HeapFree((char *)pRegSet->paLookupRecs[i].Core.pszString);
MMR3HeapFree(pRegSet);
return rc;
}
/**
* Registers a set of registers for a CPU.
*
* @returns VBox status code.
* @param pVCpu The virtual CPU handle.
* @param paRegisters The register descriptors.
*/
VMMR3_INT_DECL(int) DBGFR3RegRegisterDevice(PVMCPU pVCpu, PCDBGFREGDESC paRegisters)
{
return dbgfR3RegRegisterCommon(pVCpu->pVMR3, paRegisters, DBGFREGSETTYPE_CPU, pVCpu, "cpu", pVCpu->idCpu);
}
/**
* Registers a set of registers for a device.
*
* @returns VBox status code.
* @param enmReg The register identifier.
* @param enmType The register type. This is for sort out
* aliases. Pass DBGFREGVALTYPE_INVALID to get
* the standard name.
*/
VMMR3DECL(int) DBGFR3RegRegisterDevice(PVM pVM, PCDBGFREGDESC paRegisters, PPDMDEVINS pDevIns, const char *pszPrefix, uint32_t iInstance)
{
VM_ASSERT_VALID_EXT_RETURN(pVM, VERR_INVALID_VM_HANDLE);
AssertPtrReturn(paRegisters, VERR_INVALID_POINTER);
AssertPtrReturn(pDevIns, VERR_INVALID_POINTER);
AssertPtrReturn(pszPrefix, VERR_INVALID_POINTER);
return dbgfR3RegRegisterCommon(pVM, paRegisters, DBGFREGSETTYPE_DEVICE, pDevIns, pszPrefix, iInstance);
}
/**
* Clears the register value variable.
*
* @param pValue The variable to clear.
*/
DECLINLINE(void) dbgfR3RegValClear(PDBGFREGVAL pValue)
{
pValue->au64[0] = 0;
pValue->au64[1] = 0;
}
/**
* Performs a cast between register value types.
*
* @retval VINF_SUCCESS
* @retval VINF_DBGF_ZERO_EXTENDED_REGISTER
* @retval VINF_DBGF_TRUNCATED_REGISTER
* @retval VERR_DBGF_UNSUPPORTED_CAST
*
* @param pValue The value to cast (input + output).
* @param enmFromType The input value.
* @param enmToType The desired output value.
*/
static int dbgfR3RegValCast(PDBGFREGVAL pValue, DBGFREGVALTYPE enmFromType, DBGFREGVALTYPE enmToType)
{
DBGFREGVAL const InVal = *pValue;
dbgfR3RegValClear(pValue);
/* Note! No default cases here as gcc warnings about missing enum values
are desired. */
switch (enmFromType)
{
case DBGFREGVALTYPE_U8:
switch (enmToType)
{
case DBGFREGVALTYPE_U8: pValue->u8 = InVal.u8; return VINF_SUCCESS;
case DBGFREGVALTYPE_U16: pValue->u16 = InVal.u8; return VINF_DBGF_ZERO_EXTENDED_REGISTER;
case DBGFREGVALTYPE_U32: pValue->u32 = InVal.u8; return VINF_DBGF_ZERO_EXTENDED_REGISTER;
case DBGFREGVALTYPE_U64: pValue->u64 = InVal.u8; return VINF_DBGF_ZERO_EXTENDED_REGISTER;
case DBGFREGVALTYPE_U128: pValue->u128.s.Lo = InVal.u8; return VINF_DBGF_ZERO_EXTENDED_REGISTER;
case DBGFREGVALTYPE_LRD: pValue->lrd = InVal.u8; return VINF_DBGF_ZERO_EXTENDED_REGISTER;
case DBGFREGVALTYPE_DTR: return VERR_DBGF_UNSUPPORTED_CAST;
case DBGFREGVALTYPE_32BIT_HACK:
case DBGFREGVALTYPE_END:
case DBGFREGVALTYPE_INVALID:
break;
}
break;
case DBGFREGVALTYPE_U16:
switch (enmToType)
{
case DBGFREGVALTYPE_U8: pValue->u8 = InVal.u16; return VINF_DBGF_TRUNCATED_REGISTER;
case DBGFREGVALTYPE_U16: pValue->u16 = InVal.u16; return VINF_SUCCESS;
case DBGFREGVALTYPE_U32: pValue->u32 = InVal.u16; return VINF_DBGF_ZERO_EXTENDED_REGISTER;
case DBGFREGVALTYPE_U64: pValue->u64 = InVal.u16; return VINF_DBGF_ZERO_EXTENDED_REGISTER;
case DBGFREGVALTYPE_U128: pValue->u128.s.Lo = InVal.u16; return VINF_DBGF_ZERO_EXTENDED_REGISTER;
case DBGFREGVALTYPE_LRD: pValue->lrd = InVal.u16; return VINF_DBGF_ZERO_EXTENDED_REGISTER;
case DBGFREGVALTYPE_DTR: return VERR_DBGF_UNSUPPORTED_CAST;
case DBGFREGVALTYPE_32BIT_HACK:
case DBGFREGVALTYPE_END:
case DBGFREGVALTYPE_INVALID:
break;
}
break;
case DBGFREGVALTYPE_U32:
switch (enmToType)
{
case DBGFREGVALTYPE_U8: pValue->u8 = InVal.u32; return VINF_DBGF_TRUNCATED_REGISTER;
case DBGFREGVALTYPE_U16: pValue->u16 = InVal.u32; return VINF_DBGF_TRUNCATED_REGISTER;
case DBGFREGVALTYPE_U32: pValue->u32 = InVal.u32; return VINF_SUCCESS;
case DBGFREGVALTYPE_U64: pValue->u64 = InVal.u32; return VINF_DBGF_ZERO_EXTENDED_REGISTER;
case DBGFREGVALTYPE_U128: pValue->u128.s.Lo = InVal.u32; return VINF_DBGF_ZERO_EXTENDED_REGISTER;
case DBGFREGVALTYPE_LRD: pValue->lrd = InVal.u32; return VINF_DBGF_ZERO_EXTENDED_REGISTER;
case DBGFREGVALTYPE_DTR: return VERR_DBGF_UNSUPPORTED_CAST;
case DBGFREGVALTYPE_32BIT_HACK:
case DBGFREGVALTYPE_END:
case DBGFREGVALTYPE_INVALID:
break;
}
break;
case DBGFREGVALTYPE_U64:
switch (enmToType)
{
case DBGFREGVALTYPE_U8: pValue->u8 = InVal.u64; return VINF_DBGF_TRUNCATED_REGISTER;
case DBGFREGVALTYPE_U16: pValue->u16 = InVal.u64; return VINF_DBGF_TRUNCATED_REGISTER;
case DBGFREGVALTYPE_U32: pValue->u32 = InVal.u64; return VINF_DBGF_TRUNCATED_REGISTER;
case DBGFREGVALTYPE_U64: pValue->u64 = InVal.u64; return VINF_SUCCESS;
case DBGFREGVALTYPE_U128: pValue->u128.s.Lo = InVal.u64; return VINF_DBGF_TRUNCATED_REGISTER;
case DBGFREGVALTYPE_LRD: pValue->lrd = InVal.u64; return VINF_DBGF_ZERO_EXTENDED_REGISTER;
case DBGFREGVALTYPE_DTR: return VERR_DBGF_UNSUPPORTED_CAST;
case DBGFREGVALTYPE_32BIT_HACK:
case DBGFREGVALTYPE_END:
case DBGFREGVALTYPE_INVALID:
break;
}
break;
case DBGFREGVALTYPE_U128:
switch (enmToType)
{
case DBGFREGVALTYPE_U8: pValue->u8 = InVal.u128.s.Lo; return VINF_DBGF_TRUNCATED_REGISTER;
case DBGFREGVALTYPE_U16: pValue->u16 = InVal.u128.s.Lo; return VINF_DBGF_TRUNCATED_REGISTER;
case DBGFREGVALTYPE_U32: pValue->u32 = InVal.u128.s.Lo; return VINF_DBGF_TRUNCATED_REGISTER;
case DBGFREGVALTYPE_U64: pValue->u64 = InVal.u128.s.Lo; return VINF_DBGF_TRUNCATED_REGISTER;
case DBGFREGVALTYPE_U128: pValue->u128 = InVal.u128; return VINF_SUCCESS;
case DBGFREGVALTYPE_LRD: pValue->lrd = InVal.u64; return VINF_DBGF_TRUNCATED_REGISTER;
case DBGFREGVALTYPE_DTR: return VERR_DBGF_UNSUPPORTED_CAST;
case DBGFREGVALTYPE_32BIT_HACK:
case DBGFREGVALTYPE_END:
case DBGFREGVALTYPE_INVALID:
break;
}
break;
case DBGFREGVALTYPE_LRD:
switch (enmToType)
{
case DBGFREGVALTYPE_U8: pValue->u8 = (uint8_t)InVal.lrd; return VINF_DBGF_TRUNCATED_REGISTER;
case DBGFREGVALTYPE_U16: pValue->u16 = (uint16_t)InVal.lrd; return VINF_DBGF_TRUNCATED_REGISTER;
case DBGFREGVALTYPE_U32: pValue->u32 = (uint32_t)InVal.lrd; return VINF_DBGF_TRUNCATED_REGISTER;
case DBGFREGVALTYPE_U64: pValue->u64 = (uint64_t)InVal.lrd; return VINF_DBGF_TRUNCATED_REGISTER;
case DBGFREGVALTYPE_U128:
pValue->u128.s.Lo = (uint64_t)InVal.lrd;
pValue->u128.s.Hi = (uint64_t)InVal.lrd / _4G / _4G;
return VINF_DBGF_TRUNCATED_REGISTER;
case DBGFREGVALTYPE_LRD: pValue->lrd = InVal.lrd; return VINF_SUCCESS;
case DBGFREGVALTYPE_DTR: return VERR_DBGF_UNSUPPORTED_CAST;
case DBGFREGVALTYPE_32BIT_HACK:
case DBGFREGVALTYPE_END:
case DBGFREGVALTYPE_INVALID:
break;
}
break;
case DBGFREGVALTYPE_DTR:
switch (enmToType)
{
case DBGFREGVALTYPE_U8: pValue->u8 = InVal.dtr.u64Base; return VINF_DBGF_TRUNCATED_REGISTER;
case DBGFREGVALTYPE_U16: pValue->u16 = InVal.dtr.u64Base; return VINF_DBGF_TRUNCATED_REGISTER;
case DBGFREGVALTYPE_U32: pValue->u32 = InVal.dtr.u64Base; return VINF_DBGF_TRUNCATED_REGISTER;
case DBGFREGVALTYPE_U64: pValue->u64 = InVal.dtr.u64Base; return VINF_DBGF_TRUNCATED_REGISTER;
case DBGFREGVALTYPE_U128: pValue->u128.s.Lo = InVal.dtr.u64Base; return VINF_DBGF_TRUNCATED_REGISTER;
case DBGFREGVALTYPE_LRD: pValue->lrd = InVal.dtr.u64Base; return VINF_DBGF_TRUNCATED_REGISTER;
case DBGFREGVALTYPE_DTR: pValue->dtr = InVal.dtr; return VINF_SUCCESS;
case DBGFREGVALTYPE_32BIT_HACK:
case DBGFREGVALTYPE_END:
case DBGFREGVALTYPE_INVALID:
break;
}
break;
case DBGFREGVALTYPE_INVALID:
case DBGFREGVALTYPE_END:
case DBGFREGVALTYPE_32BIT_HACK:
break;
}
AssertMsgFailed(("%d / %d\n", enmFromType, enmToType));
return VERR_DBGF_UNSUPPORTED_CAST;
}
/**
* Worker for the CPU register queries.
*
* @returns VBox status code.
* @retval VINF_SUCCESS
* @retval VERR_INVALID_VM_HANDLE
* @retval VERR_INVALID_CPU_ID
* @retval VERR_DBGF_REGISTER_NOT_FOUND
* @retval VERR_DBGF_UNSUPPORTED_CAST
* @retval VINF_DBGF_TRUNCATED_REGISTER
* @retval VINF_DBGF_ZERO_EXTENDED_REGISTER
*
* @param pVM The VM handle.
* @param idCpu The virtual CPU ID.
* @param enmReg The register to query.
* @param enmType The desired return type.
* @param pValue Where to return the register value.
*/
static DECLCALLBACK(int) dbgfR3RegCpuQueryWorker(PVM pVM, VMCPUID idCpu, DBGFREG enmReg, DBGFREGVALTYPE enmType, PDBGFREGVAL pValue)
{
int rc = VINF_SUCCESS;
DBGF_REG_DB_LOCK_READ(pVM);
/*
* Look up the register set of the specified CPU.
*/
PDBGFREGSET pSet = pVM->aCpus[idCpu].dbgf.s.pRegSet;
if (RT_LIKELY(pSet))
{
/*
* Look up the register and get the register value.
*/
if (RT_LIKELY(pSet->cDescs > (size_t)enmReg))
{
PCDBGFREGDESC pDesc = &pSet->paDescs[enmReg];
pValue->au64[0] = pValue->au64[1] = 0;
rc = pDesc->pfnGet(pSet->uUserArg.pv, pDesc, pValue);
if (RT_SUCCESS(rc))
{
/*
* Do the cast if the desired return type doesn't match what
* the getter returned.
*/
if (pDesc->enmType == enmType)
rc = VINF_SUCCESS;
else
rc = dbgfR3RegValCast(pValue, pDesc->enmType, enmType);
}
}
else
rc = VERR_DBGF_REGISTER_NOT_FOUND;
}
else
rc = VERR_INVALID_CPU_ID;
DBGF_REG_DB_UNLOCK_READ(pVM);
return rc;
}
/**
* Queries a 8-bit CPU register value.
*
* @retval VINF_SUCCESS
* @retval VERR_INVALID_VM_HANDLE
* @retval VERR_INVALID_CPU_ID
* @retval VERR_DBGF_REGISTER_NOT_FOUND
* @retval VERR_DBGF_UNSUPPORTED_CAST
* @retval VINF_DBGF_TRUNCATED_REGISTER
*
* @param pVM The VM handle.
* @param idCpu The target CPU ID.
* @param enmReg The register that's being queried.
* @param pu8 Where to store the register value.
*/
VMMR3DECL(int) DBGFR3RegCpuQueryU8(PVM pVM, VMCPUID idCpu, DBGFREG enmReg, uint8_t *pu8)
{
VM_ASSERT_VALID_EXT_RETURN(pVM, VERR_INVALID_VM_HANDLE);
AssertReturn(idCpu < pVM->cCpus, VERR_INVALID_CPU_ID);
DBGFREGVAL Value;
int rc = VMR3ReqCallWait(pVM, idCpu, (PFNRT)dbgfR3RegCpuQueryWorker, 5, pVM, idCpu, enmReg, DBGFREGVALTYPE_U8, &Value);
if (RT_SUCCESS(rc))
*pu8 = Value.u8;
else
*pu8 = 0;
return rc;
}
/**
* Queries a 16-bit CPU register value.
*
* @retval VINF_SUCCESS
* @retval VERR_INVALID_VM_HANDLE
* @retval VERR_INVALID_CPU_ID
* @retval VERR_DBGF_REGISTER_NOT_FOUND
* @retval VERR_DBGF_UNSUPPORTED_CAST
* @retval VINF_DBGF_TRUNCATED_REGISTER
* @retval VINF_DBGF_ZERO_EXTENDED_REGISTER
*
* @param pVM The VM handle.
* @param idCpu The target CPU ID.
* @param enmReg The register that's being queried.
* @param pu16 Where to store the register value.
*/
VMMR3DECL(int) DBGFR3RegCpuQueryU16(PVM pVM, VMCPUID idCpu, DBGFREG enmReg, uint16_t *pu16)
{
VM_ASSERT_VALID_EXT_RETURN(pVM, VERR_INVALID_VM_HANDLE);
AssertReturn(idCpu < pVM->cCpus, VERR_INVALID_CPU_ID);
DBGFREGVAL Value;
int rc = VMR3ReqCallWait(pVM, idCpu, (PFNRT)dbgfR3RegCpuQueryWorker, 5, pVM, idCpu, enmReg, DBGFREGVALTYPE_U16, &Value);
if (RT_SUCCESS(rc))
*pu16 = Value.u16;
else
*pu16 = 0;
return rc;
}
/**
* Queries a 32-bit CPU register value.
*
* @retval VINF_SUCCESS
* @retval VERR_INVALID_VM_HANDLE
* @retval VERR_INVALID_CPU_ID
* @retval VERR_DBGF_REGISTER_NOT_FOUND
* @retval VERR_DBGF_UNSUPPORTED_CAST
* @retval VINF_DBGF_TRUNCATED_REGISTER
* @retval VINF_DBGF_ZERO_EXTENDED_REGISTER
*
* @param pVM The VM handle.
* @param idCpu The target CPU ID.
* @param enmReg The register that's being queried.
* @param pu32 Where to store the register value.
*/
VMMR3DECL(int) DBGFR3RegCpuQueryU32(PVM pVM, VMCPUID idCpu, DBGFREG enmReg, uint32_t *pu32)
{
VM_ASSERT_VALID_EXT_RETURN(pVM, VERR_INVALID_VM_HANDLE);
AssertReturn(idCpu < pVM->cCpus, VERR_INVALID_CPU_ID);
DBGFREGVAL Value;
int rc = VMR3ReqCallWait(pVM, idCpu, (PFNRT)dbgfR3RegCpuQueryWorker, 5, pVM, idCpu, enmReg, DBGFREGVALTYPE_U32, &Value);
if (RT_SUCCESS(rc))
*pu32 = Value.u32;
else
*pu32 = 0;
return rc;
}
/**
* Queries a 64-bit CPU register value.
*
* @retval VINF_SUCCESS
* @retval VERR_INVALID_VM_HANDLE
* @retval VERR_INVALID_CPU_ID
* @retval VERR_DBGF_REGISTER_NOT_FOUND
* @retval VERR_DBGF_UNSUPPORTED_CAST
* @retval VINF_DBGF_TRUNCATED_REGISTER
* @retval VINF_DBGF_ZERO_EXTENDED_REGISTER
*
* @param pVM The VM handle.
* @param idCpu The target CPU ID.
* @param enmReg The register that's being queried.
* @param pu64 Where to store the register value.
*/
VMMR3DECL(int) DBGFR3RegCpuQueryU64(PVM pVM, VMCPUID idCpu, DBGFREG enmReg, uint64_t *pu64)
{
VM_ASSERT_VALID_EXT_RETURN(pVM, VERR_INVALID_VM_HANDLE);
AssertReturn(idCpu < pVM->cCpus, VERR_INVALID_CPU_ID);
DBGFREGVAL Value;
int rc = VMR3ReqCallWait(pVM, idCpu, (PFNRT)dbgfR3RegCpuQueryWorker, 5, pVM, idCpu, enmReg, DBGFREGVALTYPE_U64, &Value);
if (RT_SUCCESS(rc))
*pu64 = Value.u64;
else
*pu64 = 0;
return rc;
}
/**
* Wrapper around CPUMQueryGuestMsr for dbgfR3RegCpuQueryBatchWorker.
*
* @retval VINF_SUCCESS
* @retval VERR_DBGF_REGISTER_NOT_FOUND
*
* @param pVCpu The current CPU.
* @param pReg The where to store the register value and
* size.
* @param idMsr The MSR to get.
*/
static void dbgfR3RegGetMsrBatch(PVMCPU pVCpu, PDBGFREGENTRY pReg, uint32_t idMsr)
{
pReg->enmType = DBGFREGVALTYPE_U64;
int rc = CPUMQueryGuestMsr(pVCpu, idMsr, &pReg->Val.u64);
if (RT_FAILURE(rc))
{
AssertMsg(rc == VERR_CPUM_RAISE_GP_0, ("%Rrc\n", rc));
pReg->Val.u64 = 0;
}
}
static DECLCALLBACK(int) dbgfR3RegCpuQueryBatchWorker(PVM pVM, VMCPUID idCpu, PDBGFREGENTRY paRegs, size_t cRegs)
{
#if 0
PVMCPU pVCpu = &pVM->aCpus[idCpu];
PCCPUMCTX pCtx = CPUMQueryGuestCtxPtr(pVCpu);
PDBGFREGENTRY pReg = paRegs - 1;
while (cRegs-- > 0)
{
pReg++;
pReg->Val.au64[0] = 0;
pReg->Val.au64[1] = 0;
DBGFREG const enmReg = pReg->enmReg;
AssertMsgReturn(enmReg >= 0 && enmReg <= DBGFREG_END, ("%d (%#x)\n", enmReg, enmReg), VERR_DBGF_REGISTER_NOT_FOUND);
if (enmReg != DBGFREG_END)
{
PCDBGFREGDESC pDesc = &g_aDbgfRegDescs[enmReg];
if (!pDesc->pfnGet)
{
PCRTUINT128U pu = (PCRTUINT128U)((uintptr_t)pCtx + pDesc->offCtx);
pReg->enmType = pDesc->enmType;
switch (pDesc->enmType)
{
case DBGFREGVALTYPE_U8: pReg->Val.u8 = pu->au8[0]; break;
case DBGFREGVALTYPE_U16: pReg->Val.u16 = pu->au16[0]; break;
case DBGFREGVALTYPE_U32: pReg->Val.u32 = pu->au32[0]; break;
case DBGFREGVALTYPE_U64: pReg->Val.u64 = pu->au64[0]; break;
case DBGFREGVALTYPE_U128:
pReg->Val.au64[0] = pu->au64[0];
pReg->Val.au64[1] = pu->au64[1];
break;
case DBGFREGVALTYPE_LRD:
pReg->Val.au64[0] = pu->au64[0];
pReg->Val.au16[5] = pu->au16[5];
break;
default:
AssertMsgFailedReturn(("%s %d\n", pDesc->pszName, pDesc->enmType), VERR_INTERNAL_ERROR_3);
}
}
else
{
int rc = pDesc->pfnGet(pVCpu, pDesc, pCtx, &pReg->Val.u);
if (RT_FAILURE(rc))
return rc;
}
}
}
return VINF_SUCCESS;
#else
return VERR_NOT_IMPLEMENTED;
#endif
}
/**
* Query a batch of registers.
*
* @retval VINF_SUCCESS
* @retval VERR_INVALID_VM_HANDLE
* @retval VERR_INVALID_CPU_ID
* @retval VERR_DBGF_REGISTER_NOT_FOUND
*
* @param pVM The VM handle.
* @param idCpu The target CPU ID.
* @param paRegs Pointer to an array of @a cRegs elements. On
* input the enmReg members indicates which
* registers to query. On successful return the
* other members are set. DBGFREG_END can be used
* as a filler.
* @param cRegs The number of entries in @a paRegs.
*/
VMMR3DECL(int) DBGFR3RegCpuQueryBatch(PVM pVM, VMCPUID idCpu, PDBGFREGENTRY paRegs, size_t cRegs)
{
VM_ASSERT_VALID_EXT_RETURN(pVM, VERR_INVALID_VM_HANDLE);
AssertReturn(idCpu < pVM->cCpus, VERR_INVALID_CPU_ID);
if (!cRegs)
return VINF_SUCCESS;
AssertReturn(cRegs < _1M, VERR_OUT_OF_RANGE);
AssertPtrReturn(paRegs, VERR_INVALID_POINTER);
size_t iReg = cRegs;
while (iReg-- > 0)
{
DBGFREG enmReg = paRegs[iReg].enmReg;
AssertMsgReturn(enmReg < DBGFREG_END && enmReg >= DBGFREG_AL, ("%d (%#x)", enmReg, enmReg), VERR_DBGF_REGISTER_NOT_FOUND);
}
return VMR3ReqCallWait(pVM, idCpu, (PFNRT)dbgfR3RegCpuQueryBatchWorker, 4, pVM, idCpu, paRegs, cRegs);
}
/**
* Query a all registers for a Virtual CPU.
*
* @retval VINF_SUCCESS
* @retval VERR_INVALID_VM_HANDLE
* @retval VERR_INVALID_CPU_ID
*
* @param pVM The VM handle.
* @param idCpu The target CPU ID.
* @param paRegs Pointer to an array of @a cRegs elements.
* These will be filled with the CPU register
* values. Overflowing entries will be set to
* DBGFREG_END. The returned registers can be
* accessed by using the DBGFREG values as index.
* @param cRegs The number of entries in @a paRegs. The
* recommended value is DBGFREG_ALL_COUNT.
*/
VMMR3DECL(int) DBGFR3RegCpuQueryAll(PVM pVM, VMCPUID idCpu, PDBGFREGENTRY paRegs, size_t cRegs)
{
/*
* Validate input.
*/
VM_ASSERT_VALID_EXT_RETURN(pVM, VERR_INVALID_VM_HANDLE);
AssertReturn(idCpu < pVM->cCpus, VERR_INVALID_CPU_ID);
if (!cRegs)
return VINF_SUCCESS;
AssertReturn(cRegs < _1M, VERR_OUT_OF_RANGE);
AssertPtrReturn(paRegs, VERR_INVALID_POINTER);
/*
* Convert it into a batch query (lazy bird).
*/
unsigned iReg = 0;
while (iReg < cRegs && iReg < DBGFREG_ALL_COUNT)
{
paRegs[iReg].enmReg = (DBGFREG)iReg;
iReg++;
}
while (iReg < cRegs)
paRegs[iReg++].enmReg = DBGFREG_END;
return VMR3ReqCallWait(pVM, idCpu, (PFNRT)dbgfR3RegCpuQueryBatchWorker, 4, pVM, idCpu, paRegs, cRegs);
}
/**
* Gets the name of a register.
*
* @returns Pointer to read-only register name (lower case). NULL if the
* parameters are invalid.
*
* @param pVM The VM handle.
* @param enmReg The register identifier.
* @param enmType The register type. This is for sort out
* aliases. Pass DBGFREGVALTYPE_INVALID to get
* the standard name.
*/
VMMR3DECL(const char *) DBGFR3RegCpuName(PVM pVM, DBGFREG enmReg, DBGFREGVALTYPE enmType)
{
AssertReturn(enmReg >= DBGFREG_AL && enmReg < DBGFREG_END, NULL);
AssertReturn(enmType >= DBGFREGVALTYPE_INVALID && enmType < DBGFREGVALTYPE_END, NULL);
VM_ASSERT_VALID_EXT_RETURN(pVM, NULL);
PCDBGFREGSET pSet = pVM->aCpus[0].dbgf.s.pRegSet;
if (RT_UNLIKELY(!pSet))
return NULL;
PCDBGFREGDESC pDesc = &pSet->paDescs[enmReg];
PCDBGFREGALIAS pAlias = pDesc->paAliases;
if ( pAlias
&& pDesc->enmType != enmType
&& enmType != DBGFREGVALTYPE_INVALID)
{
while (pAlias->pszName)
{
if (pAlias->enmType == enmType)
return pAlias->pszName;
pAlias++;
}
}
return pDesc->pszName;
}
/**
* Fold the string to lower case and copy it into the destination buffer.
*
* @returns Number of folder characters, -1 on overflow.
* @param pszSrc The source string.
* @param cchSrc How much to fold and copy.
* @param pszDst The output buffer.
* @param cbDst The size of the output buffer.
*/
static ssize_t dbgfR3RegCopyToLower(const char *pszSrc, size_t cchSrc, char *pszDst, size_t cbDst)
{
ssize_t cchFolded = 0;
char ch;
while (cchSrc-- > 0 && (ch = *pszSrc++))
{
if (RT_UNLIKELY(cbDst <= 1))
return -1;
cbDst--;
char chLower = RT_C_TO_LOWER(ch);
cchFolded += chLower != ch;
*pszDst++ = chLower;
}
if (RT_UNLIKELY(!cbDst))
return -1;
*pszDst = '\0';
return cchFolded;
}
/**
* Resolves the register name.
*
* @returns Lookup record.
* @param pVM The VM handle.
* @param idDefCpu The default CPU ID set.
* @param pszReg The register name.
*/
static PCDBGFREGLOOKUP dbgfR3RegResolve(PVM pVM, VMCPUID idDefCpu, const char *pszReg)
{
DBGF_REG_DB_LOCK_READ(pVM);
/* Try looking up the name without any case folding or cpu prefixing. */
PCDBGFREGLOOKUP pLookupRec = (PCDBGFREGLOOKUP)RTStrSpaceGet(&pVM->dbgf.s.RegSpace, pszReg);
if (!pLookupRec)
{
char szName[DBGF_REG_MAX_NAME * 4 + 16];
/* Lower case it and try again. */
ssize_t cchFolded = dbgfR3RegCopyToLower(pszReg, RTSTR_MAX, szName, sizeof(szName) - DBGF_REG_MAX_NAME);
if (cchFolded > 0)
pLookupRec = (PCDBGFREGLOOKUP)RTStrSpaceGet(&pVM->dbgf.s.RegSpace, szName);
if ( !pLookupRec
&& cchFolded >= 0
&& idDefCpu != VMCPUID_ANY)
{
/* Prefix it with the specified CPU set. */
size_t cchCpuSet = RTStrPrintf(szName, sizeof(szName), "cpu%u.", idDefCpu);
dbgfR3RegCopyToLower(pszReg, RTSTR_MAX, &szName[cchCpuSet], sizeof(szName) - cchCpuSet);
pLookupRec = (PCDBGFREGLOOKUP)RTStrSpaceGet(&pVM->dbgf.s.RegSpace, szName);
}
}
DBGF_REG_DB_UNLOCK_READ(pVM);
return pLookupRec;
}
/**
* Performs a left shift on a RTUINT128U value.
*
* @returns pVal.
* @param pVal The value to shift (input/output).
* @param cBits The number of bits to shift it. Negative
* numbers are treated as right shifts.
*/
static PRTUINT128U dbgfR3RegU128_ShiftLeft(PRTUINT128U pVal, int cBits)
{
RTUINT128U const InVal = *pVal;
if (cBits >= 0)
{
if (cBits >= 128)
pVal->s.Lo = pVal->s.Hi = 0;
else if (cBits >= 64)
{
pVal->s.Lo = 0;
pVal->s.Hi = InVal.s.Lo << (cBits - 64);
}
else
{
pVal->s.Hi = InVal.s.Hi << cBits;
pVal->s.Hi |= InVal.s.Lo >> (64 - cBits);
pVal->s.Lo = InVal.s.Lo << cBits;
}
}
else
{
/* (right shift) */
cBits = -cBits;
if (cBits >= 128)
pVal->s.Lo = pVal->s.Hi = 0;
else if (cBits >= 64)
{
pVal->s.Hi = 0;
pVal->s.Lo = InVal.s.Hi >> (cBits - 64);
}
else
{
pVal->s.Lo = InVal.s.Lo >> cBits;
pVal->s.Lo |= InVal.s.Hi << (64 - cBits);
pVal->s.Hi = InVal.s.Hi >> cBits;
}
}
return pVal;
}
/**
* ANDs the RTUINT128U value against a bitmask made up of the first @a cBits
* bits.
*
* @returns pVal.
* @param pVal The value to shift (input/output).
* @param cBits The number of bits in the AND mask.
*/
static PRTUINT128U dbgfR3RegU128_AndNFirstBits(PRTUINT128U pVal, unsigned cBits)
{
if (cBits <= 64)
{
pVal->s.Hi = 0;
pVal->s.Lo &= RT_BIT_64(cBits) - 1;
}
else if (cBits < 128)
pVal->s.Hi &= RT_BIT_64(cBits - 64) - 1;
return pVal;
}
/**
* On CPU worker for the register queries, used by dbgfR3RegNmQueryWorker.
*
* @returns VBox status code.
*
* @param pVM The VM handle.
* @param pLookupRec The register lookup record.
* @param enmType The desired return type.
* @param pValue Where to return the register value.
* @param penmType Where to store the register value type.
* Optional.
*/
static DECLCALLBACK(int) dbgfR3RegNmQueryWorkerOnCpu(PVM pVM, PCDBGFREGLOOKUP pLookupRec, DBGFREGVALTYPE enmType,
PDBGFREGVAL pValue, PDBGFREGVALTYPE penmType)
{
PCDBGFREGDESC pDesc = pLookupRec->pDesc;
PCDBGFREGSET pSet = pLookupRec->pSet;
PCDBGFREGSUBFIELD pSubField = pLookupRec->pSubField;
DBGFREGVALTYPE enmValueType = pDesc->enmType;
int rc;
/*
* Get the register or sub-field value.
*/
dbgfR3RegValClear(pValue);
if (!pSubField)
rc = pDesc->pfnGet(pSet->uUserArg.pv, pDesc, pValue);
else
{
if (pSubField->pfnGet)
{
rc = pSubField->pfnGet(pSet->uUserArg.pv, pSubField, &pValue->u128);
enmValueType = DBGFREGVALTYPE_U128;
}
else
{
rc = pDesc->pfnGet(pSet->uUserArg.pv, pDesc, pValue);
if (RT_SUCCESS(rc))
{
rc = dbgfR3RegValCast(pValue, enmValueType, DBGFREGVALTYPE_U128);
if (RT_SUCCESS(rc))
{
dbgfR3RegU128_ShiftLeft(&pValue->u128, -pSubField->iFirstBit);
dbgfR3RegU128_AndNFirstBits(&pValue->u128, pSubField->cBits);
if (pSubField->cShift)
dbgfR3RegU128_ShiftLeft(&pValue->u128, pSubField->cShift);
}
}
}
if (RT_SUCCESS(rc))
{
unsigned const cBits = pSubField->cBits + pSubField->cShift;
if (cBits <= 8)
enmValueType = DBGFREGVALTYPE_U8;
else if (cBits <= 16)
enmValueType = DBGFREGVALTYPE_U16;
else if (cBits <= 32)
enmValueType = DBGFREGVALTYPE_U32;
else if (cBits <= 64)
enmValueType = DBGFREGVALTYPE_U64;
else
enmValueType = DBGFREGVALTYPE_U128;
rc = dbgfR3RegValCast(pValue, DBGFREGVALTYPE_U128, enmValueType);
}
}
if (RT_SUCCESS(rc))
{
/*
* Do the cast if the desired return type doesn't match what
* the getter returned.
*/
if ( enmValueType == enmType
|| enmType == DBGFREGVALTYPE_END)
{
rc = VINF_SUCCESS;
if (penmType)
*penmType = enmValueType;
}
else
{
rc = dbgfR3RegValCast(pValue, enmValueType, enmType);
if (penmType)
*penmType = RT_SUCCESS(rc) ? enmType : enmValueType;
}
}
return rc;
}
/**
* Worker for the register queries.
*
* @returns VBox status code.
* @retval VINF_SUCCESS
* @retval VERR_INVALID_VM_HANDLE
* @retval VERR_INVALID_CPU_ID
* @retval VERR_DBGF_REGISTER_NOT_FOUND
* @retval VERR_DBGF_UNSUPPORTED_CAST
* @retval VINF_DBGF_TRUNCATED_REGISTER
* @retval VINF_DBGF_ZERO_EXTENDED_REGISTER
*
* @param pVM The VM handle.
* @param idDefCpu The virtual CPU ID for the default CPU register
* set.
* @param pszReg The register to query.
* @param enmType The desired return type.
* @param pValue Where to return the register value.
* @param penmType Where to store the register value type.
* Optional.
*/
static DECLCALLBACK(int) dbgfR3RegNmQueryWorker(PVM pVM, VMCPUID idDefCpu, const char *pszReg, DBGFREGVALTYPE enmType,
PDBGFREGVAL pValue, PDBGFREGVALTYPE penmType)
{
/*
* Validate input.
*/
VM_ASSERT_VALID_EXT_RETURN(pVM, VERR_INVALID_VM_HANDLE);
AssertReturn(idDefCpu < pVM->cCpus || idDefCpu == VMCPUID_ANY, VERR_INVALID_CPU_ID);
AssertPtrReturn(pszReg, VERR_INVALID_POINTER);
Assert(enmType > DBGFREGVALTYPE_INVALID && enmType <= DBGFREGVALTYPE_END);
AssertPtr(pValue);
/*
* Resolve the register and call the getter on the relevant CPU.
*/
PCDBGFREGLOOKUP pLookupRec = dbgfR3RegResolve(pVM, idDefCpu, pszReg);
if (pLookupRec)
{
if (pLookupRec->pSet->enmType == DBGFREGSETTYPE_CPU)
idDefCpu = pLookupRec->pSet->uUserArg.pVCpu->idCpu;
return VMR3ReqCallWait(pVM, idDefCpu, (PFNRT)dbgfR3RegNmQueryWorkerOnCpu, 5, pVM, pLookupRec, enmType, pValue, penmType);
}
return VERR_DBGF_REGISTER_NOT_FOUND;
}
/**
* Queries a descriptor table register value.
*
* @retval VINF_SUCCESS
* @retval VERR_INVALID_VM_HANDLE
* @retval VERR_INVALID_CPU_ID
* @retval VERR_DBGF_REGISTER_NOT_FOUND
*
* @param pVM The VM handle.
* @param idDefCpu The default target CPU ID, VMCPUID_ANY if not
* applicable.
* @param pszReg The register that's being queried. Except for
* CPU registers, this must be on the form
* "set.reg[.sub]".
* @param pValue Where to store the register value.
* @param penmType Where to store the register value type.
*/
VMMR3DECL(int) DBGFR3RegNmQuery(PVM pVM, VMCPUID idDefCpu, const char *pszReg, PDBGFREGVAL pValue, PDBGFREGVALTYPE penmType)
{
return dbgfR3RegNmQueryWorker(pVM, idDefCpu, pszReg, DBGFREGVALTYPE_END, pValue, penmType);
}
/**
* Queries a 8-bit register value.
*
* @retval VINF_SUCCESS
* @retval VERR_INVALID_VM_HANDLE
* @retval VERR_INVALID_CPU_ID
* @retval VERR_DBGF_REGISTER_NOT_FOUND
* @retval VERR_DBGF_UNSUPPORTED_CAST
* @retval VINF_DBGF_TRUNCATED_REGISTER
*
* @param pVM The VM handle.
* @param idDefCpu The default target CPU ID, VMCPUID_ANY if not
* applicable.
* @param pszReg The register that's being queried. Except for
* CPU registers, this must be on the form
* "set.reg[.sub]".
* @param pu8 Where to store the register value.
*/
VMMR3DECL(int) DBGFR3RegNmQueryU8(PVM pVM, VMCPUID idDefCpu, const char *pszReg, uint8_t *pu8)
{
DBGFREGVAL Value;
int rc = dbgfR3RegNmQueryWorker(pVM, idDefCpu, pszReg, DBGFREGVALTYPE_U8, &Value, NULL);
if (RT_SUCCESS(rc))
*pu8 = Value.u8;
else
*pu8 = 0;
return rc;
}
/**
* Queries a 16-bit register value.
*
* @retval VINF_SUCCESS
* @retval VERR_INVALID_VM_HANDLE
* @retval VERR_INVALID_CPU_ID
* @retval VERR_DBGF_REGISTER_NOT_FOUND
* @retval VERR_DBGF_UNSUPPORTED_CAST
* @retval VINF_DBGF_TRUNCATED_REGISTER
* @retval VINF_DBGF_ZERO_EXTENDED_REGISTER
*
* @param pVM The VM handle.
* @param idDefCpu The default target CPU ID, VMCPUID_ANY if not
* applicable.
* @param pszReg The register that's being queried. Except for
* CPU registers, this must be on the form
* "set.reg[.sub]".
* @param pu16 Where to store the register value.
*/
VMMR3DECL(int) DBGFR3RegNmQueryU16(PVM pVM, VMCPUID idDefCpu, const char *pszReg, uint16_t *pu16)
{
DBGFREGVAL Value;
int rc = dbgfR3RegNmQueryWorker(pVM, idDefCpu, pszReg, DBGFREGVALTYPE_U16, &Value, NULL);
if (RT_SUCCESS(rc))
*pu16 = Value.u16;
else
*pu16 = 0;
return rc;
}
/**
* Queries a 32-bit register value.
*
* @retval VINF_SUCCESS
* @retval VERR_INVALID_VM_HANDLE
* @retval VERR_INVALID_CPU_ID
* @retval VERR_DBGF_REGISTER_NOT_FOUND
* @retval VERR_DBGF_UNSUPPORTED_CAST
* @retval VINF_DBGF_TRUNCATED_REGISTER
* @retval VINF_DBGF_ZERO_EXTENDED_REGISTER
*
* @param pVM The VM handle.
* @param idDefCpu The default target CPU ID, VMCPUID_ANY if not
* applicable.
* @param pszReg The register that's being queried. Except for
* CPU registers, this must be on the form
* "set.reg[.sub]".
* @param pu32 Where to store the register value.
*/
VMMR3DECL(int) DBGFR3RegNmQueryU32(PVM pVM, VMCPUID idDefCpu, const char *pszReg, uint32_t *pu32)
{
DBGFREGVAL Value;
int rc = dbgfR3RegNmQueryWorker(pVM, idDefCpu, pszReg, DBGFREGVALTYPE_U32, &Value, NULL);
if (RT_SUCCESS(rc))
*pu32 = Value.u32;
else
*pu32 = 0;
return rc;
}
/**
* Queries a 64-bit register value.
*
* @retval VINF_SUCCESS
* @retval VERR_INVALID_VM_HANDLE
* @retval VERR_INVALID_CPU_ID
* @retval VERR_DBGF_REGISTER_NOT_FOUND
* @retval VERR_DBGF_UNSUPPORTED_CAST
* @retval VINF_DBGF_TRUNCATED_REGISTER
* @retval VINF_DBGF_ZERO_EXTENDED_REGISTER
*
* @param pVM The VM handle.
* @param idDefCpu The default target CPU ID, VMCPUID_ANY if not
* applicable.
* @param pszReg The register that's being queried. Except for
* CPU registers, this must be on the form
* "set.reg[.sub]".
* @param pu64 Where to store the register value.
*/
VMMR3DECL(int) DBGFR3RegNmQueryU64(PVM pVM, VMCPUID idDefCpu, const char *pszReg, uint64_t *pu64)
{
DBGFREGVAL Value;
int rc = dbgfR3RegNmQueryWorker(pVM, idDefCpu, pszReg, DBGFREGVALTYPE_U64, &Value, NULL);
if (RT_SUCCESS(rc))
*pu64 = Value.u64;
else
*pu64 = 0;
return rc;
}
/**
* Queries a 128-bit register value.
*
* @retval VINF_SUCCESS
* @retval VERR_INVALID_VM_HANDLE
* @retval VERR_INVALID_CPU_ID
* @retval VERR_DBGF_REGISTER_NOT_FOUND
* @retval VERR_DBGF_UNSUPPORTED_CAST
* @retval VINF_DBGF_TRUNCATED_REGISTER
* @retval VINF_DBGF_ZERO_EXTENDED_REGISTER
*
* @param pVM The VM handle.
* @param idDefCpu The default target CPU ID, VMCPUID_ANY if not
* applicable.
* @param pszReg The register that's being queried. Except for
* CPU registers, this must be on the form
* "set.reg[.sub]".
* @param pu128 Where to store the register value.
*/
VMMR3DECL(int) DBGFR3RegNmQueryU128(PVM pVM, VMCPUID idDefCpu, const char *pszReg, PRTUINT128U pu128)
{
DBGFREGVAL Value;
int rc = dbgfR3RegNmQueryWorker(pVM, idDefCpu, pszReg, DBGFREGVALTYPE_U128, &Value, NULL);
if (RT_SUCCESS(rc))
*pu128 = Value.u128;
else
pu128->s.Hi = pu128->s.Lo = 0;
return rc;
}
/**
* Queries a long double register value.
*
* @retval VINF_SUCCESS
* @retval VERR_INVALID_VM_HANDLE
* @retval VERR_INVALID_CPU_ID
* @retval VERR_DBGF_REGISTER_NOT_FOUND
* @retval VERR_DBGF_UNSUPPORTED_CAST
* @retval VINF_DBGF_TRUNCATED_REGISTER
* @retval VINF_DBGF_ZERO_EXTENDED_REGISTER
*
* @param pVM The VM handle.
* @param idDefCpu The default target CPU ID, VMCPUID_ANY if not
* applicable.
* @param pszReg The register that's being queried. Except for
* CPU registers, this must be on the form
* "set.reg[.sub]".
* @param plrd Where to store the register value.
*/
VMMR3DECL(int) DBGFR3RegNmQueryLrd(PVM pVM, VMCPUID idDefCpu, const char *pszReg, long double *plrd)
{
DBGFREGVAL Value;
int rc = dbgfR3RegNmQueryWorker(pVM, idDefCpu, pszReg, DBGFREGVALTYPE_LRD, &Value, NULL);
if (RT_SUCCESS(rc))
*plrd = Value.lrd;
else
*plrd = 0;
return rc;
}
/**
* Queries a descriptor table register value.
*
* @retval VINF_SUCCESS
* @retval VERR_INVALID_VM_HANDLE
* @retval VERR_INVALID_CPU_ID
* @retval VERR_DBGF_REGISTER_NOT_FOUND
* @retval VERR_DBGF_UNSUPPORTED_CAST
* @retval VINF_DBGF_TRUNCATED_REGISTER
* @retval VINF_DBGF_ZERO_EXTENDED_REGISTER
*
* @param pVM The VM handle.
* @param idDefCpu The default target CPU ID, VMCPUID_ANY if not
* applicable.
* @param pszReg The register that's being queried. Except for
* CPU registers, this must be on the form
* "set.reg[.sub]".
* @param pu64Base Where to store the register base value.
* @param pu32Limit Where to store the register limit value.
*/
VMMR3DECL(int) DBGFR3RegNmQueryXdtr(PVM pVM, VMCPUID idDefCpu, const char *pszReg, uint64_t *pu64Base, uint32_t *pu32Limit)
{
DBGFREGVAL Value;
int rc = dbgfR3RegNmQueryWorker(pVM, idDefCpu, pszReg, DBGFREGVALTYPE_DTR, &Value, NULL);
if (RT_SUCCESS(rc))
{
*pu64Base = Value.dtr.u64Base;
*pu32Limit = Value.dtr.u32Limit;
}
else
{
*pu64Base = 0;
*pu32Limit = 0;
}
return rc;
}
/// @todo VMMR3DECL(int) DBGFR3RegNmQueryBatch(PVM pVM,VMCPUID idDefCpu, DBGFREGENTRYNM paRegs, size_t cRegs);
/**
* Gets the number of registers returned by DBGFR3RegNmQueryAll.
*
* @returns VBox status code.
* @param pVM The VM handle.
* @param pcRegs Where to return the register count.
*/
VMMR3DECL(int) DBGFR3RegNmQueryAllCount(PVM pVM, size_t *pcRegs)
{
VM_ASSERT_VALID_EXT_RETURN(pVM, VERR_INVALID_VM_HANDLE);
*pcRegs = pVM->dbgf.s.cRegs;
return VINF_SUCCESS;
}
VMMR3DECL(int) DBGFR3RegNmQueryAll(PVM pVM, DBGFREGENTRYNM paRegs, size_t cRegs)
{
return VERR_NOT_IMPLEMENTED;
}
/// @todo VMMR3DECL(int) DBGFR3RegNmPrintf(PVM pVM, VMCPUID idDefCpu, char pszBuf, size_t cbBuf, const char *pszFormat, ...);
/// @todo VMMR3DECL(int) DBGFR3RegNmPrintfV(PVM pVM, VMCPUID idDefCpu, char pszBuf, size_t cbBuf, const char *pszFormat, ...);