diff options
Diffstat (limited to 'src/VBox/Devices/GIMDev')
-rw-r--r-- | src/VBox/Devices/GIMDev/DrvUDP.cpp | 277 | ||||
-rw-r--r-- | src/VBox/Devices/GIMDev/GIMDev.cpp | 499 | ||||
-rw-r--r-- | src/VBox/Devices/GIMDev/Makefile.kup | 0 |
3 files changed, 776 insertions, 0 deletions
diff --git a/src/VBox/Devices/GIMDev/DrvUDP.cpp b/src/VBox/Devices/GIMDev/DrvUDP.cpp new file mode 100644 index 00000000..9772a25f --- /dev/null +++ b/src/VBox/Devices/GIMDev/DrvUDP.cpp @@ -0,0 +1,277 @@ +/* $Id: DrvUDP.cpp $ */ +/** @file + * UDP socket stream driver. + */ + +/* + * Copyright (C) 2015-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 <https://www.gnu.org/licenses>. + * + * SPDX-License-Identifier: GPL-3.0-only + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#define LOG_GROUP LOG_GROUP_DRV_UDP +#include <VBox/log.h> +#include <VBox/vmm/pdmdrv.h> + +#include "VBoxDD.h" + +#include <iprt/socket.h> +#include <iprt/udp.h> +#include <iprt/uuid.h> + + +/********************************************************************************************************************************* +* Defined Constants And Macros * +*********************************************************************************************************************************/ +/** Converts a pointer to DRVUDP::IStream to a PDRVUDP. */ +#define PDMISTREAM_2_DRVUDP(pInterface) ( (PDRVUDP)((uintptr_t)pInterface - RT_UOFFSETOF(DRVUDP, IStream)) ) + + +/********************************************************************************************************************************* +* Structures and Typedefs * +*********************************************************************************************************************************/ +/** + * UDP driver instance data. + * + * @implements PDMISTREAM + */ +typedef struct DRVUDP +{ + /** The stream interface. */ + PDMISTREAM IStream; + /** Pointer to the driver instance. */ + PPDMDRVINS pDrvIns; + /** The server port. */ + uint16_t uServerPort; + /** The server address. */ + char *pszServerAddress; + /** The resolved server address struct. */ + RTNETADDR ServerAddr; + /** The UDP socket. */ + RTSOCKET hSocket; +} DRVUDP, *PDRVUDP; + + +/********************************************************************************************************************************* +* Internal Functions * +*********************************************************************************************************************************/ + + +/** @interface_method_impl{PDMISTREAM,pfnRead} */ +static DECLCALLBACK(int) drvUDPRead(PPDMISTREAM pInterface, void *pvBuf, size_t *pcbRead) +{ + int rc = VINF_SUCCESS; + PDRVUDP pThis = PDMISTREAM_2_DRVUDP(pInterface); + LogFlowFunc(("pvBuf=%p *pcbRead=%#x (%s:%u)\n", pvBuf, *pcbRead, pThis->pszServerAddress, pThis->uServerPort)); + + Assert(pvBuf); + Assert(pcbRead); + if (pThis->hSocket != NIL_RTSOCKET) + { + size_t cbReallyRead = 0; + rc = RTSocketRead(pThis->hSocket, pvBuf, *pcbRead, &cbReallyRead); + if (RT_SUCCESS(rc)) + *pcbRead = cbReallyRead; + } + else + rc = VERR_NET_NOT_SOCKET; + + LogFlowFunc(("*pcbRead=%zu returns %Rrc\n", *pcbRead, rc)); + return rc; +} + + +/** @interface_method_impl{PDMISTREAM,pfnWrite} */ +static DECLCALLBACK(int) drvUDPWrite(PPDMISTREAM pInterface, const void *pvBuf, size_t *pcbWrite) +{ + int rc = VINF_SUCCESS; + PDRVUDP pThis = PDMISTREAM_2_DRVUDP(pInterface); + LogFlowFunc(("pvBuf=%p *pcbWrite=%#x (%s:%u)\n", pvBuf, *pcbWrite, pThis->pszServerAddress, pThis->uServerPort)); + + Assert(pvBuf); + Assert(pcbWrite); + if (pThis->hSocket != NIL_RTSOCKET) + { + size_t cbBuf = *pcbWrite; + rc = RTSocketWriteTo(pThis->hSocket, pvBuf, cbBuf, NULL /*pDstAddr*/); + if (RT_SUCCESS(rc)) + *pcbWrite = cbBuf; + } + else + rc = VERR_NET_NOT_SOCKET; + + LogFlowFunc(("*pcbWrite=%zu returns %Rrc\n", *pcbWrite, rc)); + return rc; +} + + +/** + * @interface_method_impl{PDMIBASE,pfnQueryInterface} + */ +static DECLCALLBACK(void *) drvUDPQueryInterface(PPDMIBASE pInterface, const char *pszIID) +{ + PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface); + PDRVUDP pThis = PDMINS_2_DATA(pDrvIns, PDRVUDP); + PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase); + PDMIBASE_RETURN_INTERFACE(pszIID, PDMISTREAM, &pThis->IStream); + return NULL; +} + + +/* -=-=-=-=- PDMDRVREG -=-=-=-=- */ + +/** + * Destruct a UDP socket stream driver instance. + * + * Most VM resources are freed by the VM. This callback is provided so that + * any non-VM resources can be freed correctly. + * + * @param pDrvIns The driver instance data. + */ +static DECLCALLBACK(void) drvUDPDestruct(PPDMDRVINS pDrvIns) +{ + PDRVUDP pThis = PDMINS_2_DATA(pDrvIns, PDRVUDP); + LogFlowFunc(("\n")); + PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns); + + if (pThis->hSocket != NIL_RTSOCKET) + { + /* + * We shutdown the socket here to poke out any blocking socket reads. The caller + * on the other thread/s need to ensure that they do -not- invoke drvUDPRead() + * or drvUDPWrite() after this. + */ + RTSocketRetain(pThis->hSocket); + RTSocketShutdown(pThis->hSocket, true, true); + RTSocketClose(pThis->hSocket); + pThis->hSocket = NIL_RTSOCKET; + LogRel(("DrvUDP#%u: Closed socket to %s:%u\n", pThis->pDrvIns->iInstance, pThis->pszServerAddress, pThis->uServerPort)); + } + + PDMDrvHlpMMHeapFree(pDrvIns, pThis->pszServerAddress); + pThis->pszServerAddress = NULL; +} + + +/** + * Construct a UDP socket stream driver instance. + * + * @copydoc FNPDMDRVCONSTRUCT + */ +static DECLCALLBACK(int) drvUDPConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags) +{ + RT_NOREF1(fFlags); + PDRVUDP pThis = PDMINS_2_DATA(pDrvIns, PDRVUDP); + PCPDMDRVHLPR3 pHlp = pDrvIns->pHlpR3; + + PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns); + + /* + * Init the static parts. + */ + pThis->pDrvIns = pDrvIns; + /* IBase */ + pDrvIns->IBase.pfnQueryInterface = drvUDPQueryInterface; + /* IStream */ + pThis->IStream.pfnRead = drvUDPRead; + pThis->IStream.pfnWrite = drvUDPWrite; + + /* + * Validate and read the configuration. + */ + PDMDRV_VALIDATE_CONFIG_RETURN(pDrvIns, "ServerAddress|ServerPort", ""); + + int rc = pHlp->pfnCFGMQueryStringAlloc(pCfg, "ServerAddress", &pThis->pszServerAddress); + if (RT_FAILURE(rc)) + return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS, + N_("Configuration error: querying \"ServerAddress\" resulted in %Rrc"), rc); + rc = pHlp->pfnCFGMQueryU16(pCfg, "ServerPort", &pThis->uServerPort); + if (RT_FAILURE(rc)) + return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS, + N_("Configuration error: querying \"ServerPort\" resulted in %Rrc"), rc); + + /* + * Create the socket and connect. + */ + rc = RTUdpCreateClientSocket(pThis->pszServerAddress, pThis->uServerPort, NULL, &pThis->hSocket); + if (RT_SUCCESS(rc)) + LogRel(("DrvUDP#%u: Connected socket to %s:%u\n", + pThis->pDrvIns->iInstance, pThis->pszServerAddress, pThis->uServerPort)); + else + LogRel(("DrvUDP#%u: Failed to create/connect socket to %s:%u rc=%Rrc\n", + pThis->pDrvIns->iInstance, pThis->pszServerAddress, pThis->uServerPort, rc)); + return VINF_SUCCESS; +} + + +/** + * UDP socket driver registration record. + */ +const PDMDRVREG g_DrvUDP = +{ + /* u32Version */ + PDM_DRVREG_VERSION, + /* szName */ + "UDP", + /* szRCMod */ + "", + /* szR0Mod */ + "", + /* pszDescription */ + "UDP socket stream driver.", + /* fFlags */ + PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT, + /* fClass. */ + PDM_DRVREG_CLASS_STREAM, + /* cMaxInstances */ + ~0U, + /* cbInstance */ + sizeof(DRVUDP), + /* pfnConstruct */ + drvUDPConstruct, + /* pfnDestruct */ + drvUDPDestruct, + /* pfnRelocate */ + NULL, + /* pfnIOCtl */ + NULL, + /* pfnPowerOn */ + NULL, + /* pfnReset */ + NULL, + /* pfnSuspend */ + NULL, + /* pfnResume */ + NULL, + /* pfnAttach */ + NULL, + /* pfnDetach */ + NULL, + /* pfnPowerOff */ + NULL, + /* pfnSoftReset */ + NULL, + /* u32EndVersion */ + PDM_DRVREG_VERSION +}; + diff --git a/src/VBox/Devices/GIMDev/GIMDev.cpp b/src/VBox/Devices/GIMDev/GIMDev.cpp new file mode 100644 index 00000000..49d675be --- /dev/null +++ b/src/VBox/Devices/GIMDev/GIMDev.cpp @@ -0,0 +1,499 @@ +/* $Id: GIMDev.cpp $ */ +/** @file + * Guest Interface Manager Device. + */ + +/* + * Copyright (C) 2014-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 <https://www.gnu.org/licenses>. + * + * SPDX-License-Identifier: GPL-3.0-only + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#define LOG_GROUP LOG_GROUP_DEV_GIM +#include <VBox/vmm/pdmdev.h> +#include <VBox/vmm/gim.h> + +#include "VBoxDD.h" +#include <iprt/alloc.h> +#include <iprt/semaphore.h> +#include <iprt/uuid.h> + + +/********************************************************************************************************************************* +* Defined Constants And Macros * +*********************************************************************************************************************************/ +#define GIMDEV_DEBUG_LUN 998 + + +/********************************************************************************************************************************* +* Structures and Typedefs * +*********************************************************************************************************************************/ +/** + * GIM device. + */ +typedef struct GIMDEV +{ + /** Pointer to the device instance. + * @note Only for getting our bearings when arriving in an interface method. */ + PPDMDEVINSR3 pDevIns; + + /** LUN\#998: The debug interface. */ + PDMIBASE IDbgBase; + /** LUN\#998: The stream port interface. */ + PDMISTREAM IDbgStreamPort; + /** Pointer to the attached base debug driver. */ + R3PTRTYPE(PPDMIBASE) pDbgDrvBase; + /** The debug receive thread. */ + RTTHREAD hDbgRecvThread; + /** Flag to indicate shutdown of the debug receive thread. */ + bool volatile fDbgRecvThreadShutdown; + bool afAlignment1[ARCH_BITS / 8 - 1]; + /** The debug setup parameters. */ + GIMDEBUGSETUP DbgSetup; + /** The debug transfer struct. */ + GIMDEBUG Dbg; +} GIMDEV; +/** Pointer to the GIM device state. */ +typedef GIMDEV *PGIMDEV; +AssertCompileMemberAlignment(GIMDEV, IDbgBase, 8); + +#ifndef VBOX_DEVICE_STRUCT_TESTCASE + +#ifdef IN_RING3 + + +/* -=-=-=-=-=-=-=-=- PDMIBASE on LUN#GIMDEV_DEBUG_LUN -=-=-=-=-=-=-=-=- */ + +/** + * @interface_method_impl{PDMIBASE,pfnQueryInterface} + */ +static DECLCALLBACK(void *) gimdevR3QueryInterface(PPDMIBASE pInterface, const char *pszIID) +{ + PGIMDEV pThis = RT_FROM_MEMBER(pInterface, GIMDEV, IDbgBase); + PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pThis->IDbgBase); + PDMIBASE_RETURN_INTERFACE(pszIID, PDMISTREAM, &pThis->IDbgStreamPort); + return NULL; +} + + +static DECLCALLBACK(int) gimDevR3DbgRecvThread(RTTHREAD hThreadSelf, void *pvUser) +{ + RT_NOREF1(hThreadSelf); + + /* + * Validate. + */ + PPDMDEVINS pDevIns = (PPDMDEVINS)pvUser; + AssertReturn(pDevIns, VERR_INVALID_PARAMETER); + PDMDEV_CHECK_VERSIONS_RETURN(pDevIns); + + PGIMDEV pThis = PDMDEVINS_2_DATA(pDevIns, PGIMDEV); + AssertReturn(pThis, VERR_INVALID_POINTER); + AssertReturn(pThis->DbgSetup.cbDbgRecvBuf, VERR_INTERNAL_ERROR); + AssertReturn(pThis->Dbg.hDbgRecvThreadSem != NIL_RTSEMEVENTMULTI, VERR_INTERNAL_ERROR_2); + AssertReturn(pThis->Dbg.pvDbgRecvBuf, VERR_INTERNAL_ERROR_3); + + PVM pVM = PDMDevHlpGetVM(pDevIns); + AssertReturn(pVM, VERR_INVALID_POINTER); + + PPDMISTREAM pDbgDrvStream = pThis->Dbg.pDbgDrvStream; + AssertReturn(pDbgDrvStream, VERR_INVALID_POINTER); + + for (;;) + { + /* + * Read incoming debug data. + */ + size_t cbRead = pThis->DbgSetup.cbDbgRecvBuf; + int rc = pDbgDrvStream->pfnRead(pDbgDrvStream, pThis->Dbg.pvDbgRecvBuf, &cbRead); + if ( RT_SUCCESS(rc) + && cbRead > 0) + { + /* + * Notify the consumer thread. + */ + if (ASMAtomicReadBool(&pThis->Dbg.fDbgRecvBufRead) == false) + { + if (pThis->DbgSetup.pfnDbgRecvBufAvail) + pThis->DbgSetup.pfnDbgRecvBufAvail(pVM); + pThis->Dbg.cbDbgRecvBufRead = cbRead; + RTSemEventMultiReset(pThis->Dbg.hDbgRecvThreadSem); + ASMAtomicWriteBool(&pThis->Dbg.fDbgRecvBufRead, true); + } + + /* + * Wait until the consumer thread has acknowledged reading of the + * current buffer or we're asked to shut down. + * + * It is important that we do NOT re-invoke 'pfnRead' before the + * current buffer is consumed, otherwise we risk data corruption. + */ + while ( ASMAtomicReadBool(&pThis->Dbg.fDbgRecvBufRead) == true + && !pThis->fDbgRecvThreadShutdown) + { + RTSemEventMultiWait(pThis->Dbg.hDbgRecvThreadSem, RT_INDEFINITE_WAIT); + } + } +#ifdef RT_OS_LINUX + else if (rc == VERR_NET_CONNECTION_REFUSED) + { + /* + * With the current, simplistic PDMISTREAM interface, this is the best we can do. + * Even using RTSocketSelectOne[Ex] on Linux returns immediately with 'ready-to-read' + * on localhost UDP sockets that are not connected on the other end. + */ + /** @todo Fix socket waiting semantics on localhost Linux unconnected UDP sockets. */ + RTThreadSleep(400); + } +#endif + else if ( rc != VINF_TRY_AGAIN + && rc != VERR_TRY_AGAIN + && rc != VERR_NET_CONNECTION_RESET_BY_PEER) + { + LogRel(("GIMDev: Debug thread terminating with rc=%Rrc\n", rc)); + break; + } + + if (pThis->fDbgRecvThreadShutdown) + { + LogRel(("GIMDev: Debug thread shutting down\n")); + break; + } + } + + return VINF_SUCCESS; +} + +/** + * @interface_method_impl{PDMDEVREG,pfnReset} + */ +static DECLCALLBACK(void) gimdevR3Reset(PPDMDEVINS pDevIns) +{ + NOREF(pDevIns); + /* We do not deregister any MMIO2 regions as the regions are expected to be static. */ +} + + + +/** + * @interface_method_impl{PDMDEVREG,pfnRelocate} + */ +static DECLCALLBACK(void) gimdevR3Relocate(PPDMDEVINS pDevIns, RTGCINTPTR offDelta) +{ + NOREF(pDevIns); + NOREF(offDelta); +#ifdef VBOX_WITH_RAW_MODE_KEEP +# error relocate pvPageRC +#endif +} + + +/** + * @interface_method_impl{PDMDEVREG,pfnDestruct} + */ +static DECLCALLBACK(int) gimdevR3Destruct(PPDMDEVINS pDevIns) +{ + PDMDEV_CHECK_VERSIONS_RETURN_QUIET(pDevIns); + PGIMDEV pThis = PDMDEVINS_2_DATA(pDevIns, PGIMDEV); + + /* + * Signal and wait for the debug thread to terminate. + */ + if (pThis->hDbgRecvThread != NIL_RTTHREAD) + { + pThis->fDbgRecvThreadShutdown = true; + if (pThis->Dbg.hDbgRecvThreadSem != NIL_RTSEMEVENT) + RTSemEventMultiSignal(pThis->Dbg.hDbgRecvThreadSem); + + int rc = RTThreadWait(pThis->hDbgRecvThread, 20000, NULL /*prc*/); + if (RT_SUCCESS(rc)) + pThis->hDbgRecvThread = NIL_RTTHREAD; + else + { + LogRel(("GIMDev: Debug thread did not terminate, rc=%Rrc!\n", rc)); + return VERR_RESOURCE_BUSY; + } + } + + /* + * Now clean up the semaphore & buffer now that the thread is gone. + */ + if (pThis->Dbg.hDbgRecvThreadSem != NIL_RTSEMEVENT) + { + RTSemEventMultiDestroy(pThis->Dbg.hDbgRecvThreadSem); + pThis->Dbg.hDbgRecvThreadSem = NIL_RTSEMEVENTMULTI; + } + if (pThis->Dbg.pvDbgRecvBuf) + { + RTMemFree(pThis->Dbg.pvDbgRecvBuf); + pThis->Dbg.pvDbgRecvBuf = NULL; + } + + return VINF_SUCCESS; +} + + +/** + * @interface_method_impl{PDMDEVREG,pfnConstruct} + */ +static DECLCALLBACK(int) gimdevR3Construct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfg) +{ + PDMDEV_CHECK_VERSIONS_RETURN(pDevIns); + PGIMDEV pThis = PDMDEVINS_2_DATA(pDevIns, PGIMDEV); + RT_NOREF2(iInstance, pCfg); + + Assert(iInstance == 0); + + /* + * Initialize relevant state bits. + */ + pThis->pDevIns = pDevIns; + pThis->hDbgRecvThread = NIL_RTTHREAD; + pThis->Dbg.hDbgRecvThreadSem = NIL_RTSEMEVENT; + + /* + * Get debug setup requirements from GIM. + */ + int rc = PDMDevHlpGIMGetDebugSetup(pDevIns, &pThis->DbgSetup); + if ( RT_SUCCESS(rc) + && pThis->DbgSetup.cbDbgRecvBuf > 0) + { + /* + * Attach the stream driver for the debug connection. + */ + PPDMISTREAM pDbgDrvStream = NULL; + pThis->IDbgBase.pfnQueryInterface = gimdevR3QueryInterface; + rc = PDMDevHlpDriverAttach(pDevIns, GIMDEV_DEBUG_LUN, &pThis->IDbgBase, &pThis->pDbgDrvBase, "GIM Debug Port"); + if (RT_SUCCESS(rc)) + { + pDbgDrvStream = PDMIBASE_QUERY_INTERFACE(pThis->pDbgDrvBase, PDMISTREAM); + if (pDbgDrvStream) + LogRel(("GIMDev: LUN#%u: Debug port configured\n", GIMDEV_DEBUG_LUN)); + else + { + LogRel(("GIMDev: LUN#%u: No unit\n", GIMDEV_DEBUG_LUN)); + rc = VERR_INTERNAL_ERROR_2; + } + } + else + { + pThis->pDbgDrvBase = NULL; + LogRel(("GIMDev: LUN#%u: No debug port configured! rc=%Rrc\n", GIMDEV_DEBUG_LUN, rc)); + } + + if (!pDbgDrvStream) + { + Assert(rc != VINF_SUCCESS); + return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS, + N_("Debug port configuration expected when GIM configured with debugging support")); + } + + void *pvDbgRecvBuf = RTMemAllocZ(pThis->DbgSetup.cbDbgRecvBuf); + if (RT_UNLIKELY(!pvDbgRecvBuf)) + { + LogRel(("GIMDev: Failed to alloc %u bytes for debug receive buffer\n", pThis->DbgSetup.cbDbgRecvBuf)); + return VERR_NO_MEMORY; + } + + /* + * Update the shared debug struct. + */ + pThis->Dbg.pDbgDrvStream = pDbgDrvStream; + pThis->Dbg.pvDbgRecvBuf = pvDbgRecvBuf; + pThis->Dbg.cbDbgRecvBufRead = 0; + pThis->Dbg.fDbgRecvBufRead = false; + + /* + * Create the semaphore and the debug receive thread itself. + */ + rc = RTSemEventMultiCreate(&pThis->Dbg.hDbgRecvThreadSem); + AssertRCReturn(rc, rc); + rc = RTThreadCreate(&pThis->hDbgRecvThread, gimDevR3DbgRecvThread, pDevIns, 0 /*cbStack*/, RTTHREADTYPE_IO, + RTTHREADFLAGS_WAITABLE, "GIMDebugRecv"); + if (RT_FAILURE(rc)) + { + RTSemEventMultiDestroy(pThis->Dbg.hDbgRecvThreadSem); + pThis->Dbg.hDbgRecvThreadSem = NIL_RTSEMEVENTMULTI; + + RTMemFree(pThis->Dbg.pvDbgRecvBuf); + pThis->Dbg.pvDbgRecvBuf = NULL; + return rc; + } + } + + /* + * Register this device with the GIM component. + */ + PDMDevHlpGIMDeviceRegister(pDevIns, pThis->DbgSetup.cbDbgRecvBuf ? &pThis->Dbg : NULL); + + /* + * Get the MMIO2 regions from the GIM provider and make the registrations. + */ +/** @todo r=bird: consider ditching this as GIM doesn't actually make use of it */ + uint32_t cRegions = 0; + PGIMMMIO2REGION paRegions = PDMDevHlpGIMGetMmio2Regions(pDevIns, &cRegions); + if ( cRegions + && paRegions) + { + for (uint32_t i = 0; i < cRegions; i++) + { + PGIMMMIO2REGION pCur = &paRegions[i]; + Assert(pCur->iRegion < 8); + rc = PDMDevHlpMmio2Create(pDevIns, NULL, pCur->iRegion << 16, pCur->cbRegion, 0 /* fFlags */, pCur->szDescription, + &pCur->pvPageR3, &pCur->hMmio2); + AssertLogRelMsgRCReturn(rc, ("rc=%Rrc iRegion=%u cbRegion=%#x %s\n", + rc, pCur->iRegion, pCur->cbRegion, pCur->szDescription), + rc); + pCur->fRegistered = true; + pCur->pvPageR0 = NIL_RTR0PTR; +# ifdef VBOX_WITH_RAW_MODE_KEEP + pCur->pvPageRC = NIL_RTRCPTR; +# endif + + LogRel(("GIMDev: Registered %s\n", pCur->szDescription)); + } + } + else + Assert(cRegions == 0); + + /** @todo Register SSM: PDMDevHlpSSMRegister(). */ + /** @todo Register statistics: STAM_REG(). */ + /** @todo Register DBGFInfo: PDMDevHlpDBGFInfoRegister(). */ + + return VINF_SUCCESS; +} + + +#else /* !IN_RING3 */ + +/** + * @callback_method_impl{PDMDEVREGR0,pfnConstruct} + */ +static DECLCALLBACK(int) gimdevRZConstruct(PPDMDEVINS pDevIns) +{ + PDMDEV_CHECK_VERSIONS_RETURN(pDevIns); + //PGIMDEV pThis = PDMDEVINS_2_DATA(pDevIns, PGIMDEV); + + /* + * Map the MMIO2 regions into the context. + */ +/** @todo r=bird: consider ditching this as GIM doesn't actually make use of it */ + uint32_t cRegions = 0; + PGIMMMIO2REGION paRegions = PDMDevHlpGIMGetMmio2Regions(pDevIns, &cRegions); + if ( cRegions + && paRegions) + { + for (uint32_t i = 0; i < cRegions; i++) + { + PGIMMMIO2REGION pCur = &paRegions[i]; + int rc = PDMDevHlpMmio2SetUpContext(pDevIns, pCur->hMmio2, 0, 0, &pCur->CTX_SUFF(pvPage)); + AssertLogRelMsgRCReturn(rc, ("rc=%Rrc iRegion=%u cbRegion=%#x %s\n", + rc, pCur->iRegion, pCur->cbRegion, pCur->szDescription), + rc); + Assert(pCur->fRegistered); + } + } + else + Assert(cRegions == 0); + + return VINF_SUCCESS; +} + +#endif /* !IN_RING3 */ + +/** + * The device registration structure. + */ +const PDMDEVREG g_DeviceGIMDev = +{ + /* .u32Version = */ PDM_DEVREG_VERSION, + /* .uReserved0 = */ 0, + /* .szName = */ "GIMDev", + /* .fFlags = */ PDM_DEVREG_FLAGS_DEFAULT_BITS | PDM_DEVREG_FLAGS_RZ | PDM_DEVREG_FLAGS_REQUIRE_R0 + | PDM_DEVREG_FLAGS_NEW_STYLE, + /* .fClass = */ PDM_DEVREG_CLASS_MISC, + /* .cMaxInstances = */ 1, + /* .uSharedVersion = */ 42, + /* .cbInstanceShared = */ sizeof(GIMDEV), + /* .cbInstanceCC = */ 0, + /* .cbInstanceRC = */ 0, + /* .cMaxPciDevices = */ 0, + /* .cMaxMsixVectors = */ 0, + /* .pszDescription = */ "VirtualBox GIM Device", +#if defined(IN_RING3) + /* .pszRCMod = */ "VBoxDDRC.rc", + /* .pszR0Mod = */ "VBoxDDR0.r0", + /* .pfnConstruct = */ gimdevR3Construct, + /* .pfnDestruct = */ gimdevR3Destruct, + /* .pfnRelocate = */ gimdevR3Relocate, + /* .pfnMemSetup = */ NULL, + /* .pfnPowerOn = */ NULL, + /* .pfnReset = */ gimdevR3Reset, + /* .pfnSuspend = */ NULL, + /* .pfnResume = */ NULL, + /* .pfnAttach = */ NULL, + /* .pfnDetach = */ NULL, + /* .pfnQueryInterface = */ NULL, + /* .pfnInitComplete = */ NULL, + /* .pfnPowerOff = */ NULL, + /* .pfnSoftReset = */ NULL, + /* .pfnReserved0 = */ NULL, + /* .pfnReserved1 = */ NULL, + /* .pfnReserved2 = */ NULL, + /* .pfnReserved3 = */ NULL, + /* .pfnReserved4 = */ NULL, + /* .pfnReserved5 = */ NULL, + /* .pfnReserved6 = */ NULL, + /* .pfnReserved7 = */ NULL, +#elif defined(IN_RING0) + /* .pfnEarlyConstruct = */ NULL, + /* .pfnConstruct = */ gimdevRZConstruct, + /* .pfnDestruct = */ NULL, + /* .pfnFinalDestruct = */ NULL, + /* .pfnRequest = */ NULL, + /* .pfnReserved0 = */ NULL, + /* .pfnReserved1 = */ NULL, + /* .pfnReserved2 = */ NULL, + /* .pfnReserved3 = */ NULL, + /* .pfnReserved4 = */ NULL, + /* .pfnReserved5 = */ NULL, + /* .pfnReserved6 = */ NULL, + /* .pfnReserved7 = */ NULL, +#elif defined(IN_RC) + /* .pfnConstruct = */ gimdevRZConstruct, + /* .pfnReserved0 = */ NULL, + /* .pfnReserved1 = */ NULL, + /* .pfnReserved2 = */ NULL, + /* .pfnReserved3 = */ NULL, + /* .pfnReserved4 = */ NULL, + /* .pfnReserved5 = */ NULL, + /* .pfnReserved6 = */ NULL, + /* .pfnReserved7 = */ NULL, +#else +# error "Not in IN_RING3, IN_RING0 or IN_RC!" +#endif + /* .u32VersionEnd = */ PDM_DEVREG_VERSION +}; + +#endif /* VBOX_DEVICE_STRUCT_TESTCASE */ + diff --git a/src/VBox/Devices/GIMDev/Makefile.kup b/src/VBox/Devices/GIMDev/Makefile.kup new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/src/VBox/Devices/GIMDev/Makefile.kup |