From f215e02bf85f68d3a6106c2a1f4f7f063f819064 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Thu, 11 Apr 2024 10:17:27 +0200 Subject: Adding upstream version 7.0.14-dfsg. Signed-off-by: Daniel Baumann --- src/VBox/Devices/Network/DrvDedicatedNic.cpp | 577 +++++++++++++++++++++++++++ 1 file changed, 577 insertions(+) create mode 100644 src/VBox/Devices/Network/DrvDedicatedNic.cpp (limited to 'src/VBox/Devices/Network/DrvDedicatedNic.cpp') diff --git a/src/VBox/Devices/Network/DrvDedicatedNic.cpp b/src/VBox/Devices/Network/DrvDedicatedNic.cpp new file mode 100644 index 00000000..c1de8109 --- /dev/null +++ b/src/VBox/Devices/Network/DrvDedicatedNic.cpp @@ -0,0 +1,577 @@ +/* $Id: DrvDedicatedNic.cpp $ */ +/** @file + * DrvDedicatedNic - Experimental network driver for using a dedicated (V)NIC. + */ + +/* + * Copyright (C) 2010-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_DEFAULT +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "VBoxDD.h" + + +/********************************************************************************************************************************* +* Structures and Typedefs * +*********************************************************************************************************************************/ +/** + * Instance data for the dedicated (V)NIC driver. + * + * @implements PDMINETWORKUP + */ +typedef struct DRVDEDICATEDNIC +{ + /** The network interface. */ + PDMINETWORKUP INetworkUpR3; + /** The network interface. */ + R3PTRTYPE(PPDMINETWORKDOWN) pIAboveNet; + /** The network config interface. + * Can (in theory at least) be NULL. */ + R3PTRTYPE(PPDMINETWORKCONFIG) pIAboveConfigR3; + /** Pointer to the driver instance. */ + PPDMDRVINSR3 pDrvInsR3; + /** Ring-3 base interface for the ring-0 context. */ + PDMIBASER0 IBaseR0; + /** Ring-3 base interface for the raw-mode context. */ + PDMIBASERC IBaseRC; + RTR3PTR R3PtrAlignment; + + + /** The network interface for the ring-0 context. */ + PDMINETWORKUPR0 INetworkUpR0; + /** Pointer to the driver instance. */ + PPDMDRVINSR0 pDrvInsR0; + RTR0PTR R0PtrAlignment; + + /** The interface we're talking to. */ + R0PTRTYPE(PINTNETTRUNKIFPORT) pIfPortR0; + /** Set if the link is up, clear if its down. */ + bool fLinkDown; + /** Set if the current transmit operation is done the XMIT thread. If clear, + * we assume its an EMT. */ + bool fXmitOnXmitThread; + /** The name of the interface that we're connected to. */ + char szIfName[128 + 8 - 2]; + + /** Critical section serializing transmission. */ + PDMCRITSECT XmitLock; + /** The transmit scatter gather buffer (ring-3 -> ring-0). */ + PDMSCATTERGATHER XmitSg; + /** The transmit GSO context (when applicable). */ + PDMNETWORKGSO XmitGso; + /** The transmit buffer (ring-3 -> ring-0). */ + uint8_t abXmitBuf[_64K]; + + /** The receive scatter gather buffer. */ + PDMSCATTERGATHER RecvSg; + /** The receive buffer (ring-0 -> ring-3). */ + uint8_t abRecvBuf[_64K]; + +} DRVDEDICATEDNIC; +/** Pointer to the instance data for the dedicated (V)NIC driver. */ +typedef DRVDEDICATEDNIC *PDRVDEDICATEDNIC; + +/** + * Ring-0 operations. + */ +typedef enum DRVDEDICATEDNICR0OP +{ + /** Invalid zero value.. */ + DRVDEDICATEDNICR0OP_INVALID = 0, + /** Initialize the connection to the NIC. */ + DRVDEDICATEDNICR0OP_INIT, + /** Terminate the connection to the NIC. */ + DRVDEDICATEDNICR0OP_TERM, + /** Suspend the operation. */ + DRVDEDICATEDNICR0OP_SUSPEND, + /** Resume the operation. */ + DRVDEDICATEDNICR0OP_RESUME, + /** Wait for and do receive work. + * We do this in ring-0 instead of ring-3 to save 1-2 buffer copies and + * unnecessary context switching. */ + DRVDEDICATEDNICR0OP_RECV, + /** Wait for and do transmit work. + * We do this in ring-0 instead of ring-3 to save 1-2 buffer copies and + * unnecessary context switching. */ + DRVDEDICATEDNICR0OP_SEND, + /** Changes the promiscuousness of the interface (guest point of view). */ + DRVDEDICATEDNICR0OP_PROMISC, + /** End of the valid operations. */ + DRVDEDICATEDNICR0OP_END, + /** The usual 32-bit hack. */ + DRVDEDICATEDNICR0OP_32BIT_HACK = 0x7fffffff +} DRVDEDICATEDNICR0OP; + + + +#ifdef IN_RING0 + +/** + * @interface_method_impl{FNPDMDRVREQHANDLERR0} + */ +PDMBOTHCBDECL(int) drvR0DedicatedNicReqHandler(PPDMDRVINS pDrvIns, uint32_t uOperation, uint64_t u64Arg) +{ + RT_NOREF_PV(pDrvIns); RT_NOREF_PV(u64Arg); + switch ((DRVDEDICATEDNICR0OP)uOperation) + { + case DRVDEDICATEDNICR0OP_INIT: + return VERR_NOT_IMPLEMENTED;//drvR0DedicatedNicReqInit(pDrvIns, u64Arg); + + case DRVDEDICATEDNICR0OP_TERM: + return VERR_NOT_IMPLEMENTED;//drvR0DedicatedNicReqTerm(pDrvIns); + + case DRVDEDICATEDNICR0OP_SUSPEND: + return VERR_NOT_IMPLEMENTED;//drvR0DedicatedNicReqSuspend(pDrvIns); + + case DRVDEDICATEDNICR0OP_RESUME: + return VERR_NOT_IMPLEMENTED;//drvR0DedicatedNicReqResume(pDrvIns); + + case DRVDEDICATEDNICR0OP_RECV: + return VERR_NOT_IMPLEMENTED;//drvR0DedicatedNicReqRecv(pDrvIns); + + case DRVDEDICATEDNICR0OP_SEND: + return VERR_NOT_IMPLEMENTED;//drvR0DedicatedNicReqSend(pDrvIns); + + case DRVDEDICATEDNICR0OP_PROMISC: + return VERR_NOT_IMPLEMENTED;//drvR0DedicatedNicReqPromisc(pDrvIns, !!u64Arg); + + case DRVDEDICATEDNICR0OP_END: + default: + return VERR_INVALID_FUNCTION; + } +} + +#endif /* IN_RING0 */ + + + +#if 0 /* currently unused */ + +/* -=-=-=-=- PDMINETWORKUP -=-=-=-=- */ + +/** + * @interface_method_impl{PDMINETWORKUP,pfnBeginXmit} + */ +PDMBOTHCBDECL(int) drvDedicatedNicUp_BeginXmit(PPDMINETWORKUP pInterface, bool fOnWorkerThread) +{ + PDRVDEDICATEDNIC pThis = RT_FROM_MEMBER(pInterface, DRVDEDICATEDNIC, CTX_SUFF(INetworkUp)); + int rc = PDMCritSectTryEnter(&pThis->XmitLock); + if (RT_SUCCESS(rc)) + ASMAtomicUoWriteBool(&pThis->fXmitOnXmitThread, fOnWorkerThread); + return rc; +} + + +/** + * @interface_method_impl{PDMINETWORKUP,pfnAllocBuf} + */ +PDMBOTHCBDECL(int) drvDedicatedNicUp_AllocBuf(PPDMINETWORKUP pInterface, size_t cbMin, + PCPDMNETWORKGSO pGso, PPPDMSCATTERGATHER ppSgBuf) +{ + PDRVDEDICATEDNIC pThis = RT_FROM_MEMBER(pInterface, DRVDEDICATEDNIC, CTX_SUFF(INetworkUp)); + Assert(PDMCritSectIsOwner(&pThis->XmitLock)); + + /* + * If the net is down, we can return immediately. + */ + if (pThis->fLinkDown) + return VERR_NET_DOWN; + +#ifdef IN_RING0 + /** @todo Ask the driver for a buffer, atomically if we're called on EMT. */ + RT_NOREF_PV(cbMin); RT_NOREF_PV(pGso); RT_NOREF_PV(ppSgBuf); + return VERR_TRY_AGAIN; + +#else /* IN_RING3 */ + /* + * Are we busy or is the request too big? + */ + if (RT_UNLIKELY((pThis->XmitSg.fFlags & PDMSCATTERGATHER_FLAGS_MAGIC_MASK) == PDMSCATTERGATHER_FLAGS_MAGIC)) + return VERR_TRY_AGAIN; + if (cbMin > sizeof(pThis->abXmitBuf)) + return VERR_NO_MEMORY; + + /* + * Initialize the S/G buffer and return. + */ + pThis->XmitSg.fFlags = PDMSCATTERGATHER_FLAGS_MAGIC | PDMSCATTERGATHER_FLAGS_OWNER_1; + pThis->XmitSg.cbUsed = 0; + pThis->XmitSg.cbAvailable = sizeof(pThis->abXmitBuf); + pThis->XmitSg.pvAllocator = NULL; + if (!pGso) + { + pThis->XmitSg.pvUser = NULL; + pThis->XmitGso.u8Type = PDMNETWORKGSOTYPE_INVALID; + } + else + { + pThis->XmitSg.pvUser = &pThis->XmitGso; + pThis->XmitGso = *pGso; + } + pThis->XmitSg.cSegs = 1; + pThis->XmitSg.aSegs[0].cbSeg = pThis->XmitSg.cbAvailable; + pThis->XmitSg.aSegs[0].pvSeg = &pThis->abXmitBuf[0]; + +# if 0 /* poison */ + memset(pThis->XmitSg.aSegs[0].pvSeg, 'F', pThis->XmitSg.aSegs[0].cbSeg); +# endif + + *ppSgBuf = &pThis->XmitSg; + return VINF_SUCCESS; +#endif /* IN_RING3 */ +} + + +/** + * @interface_method_impl{PDMINETWORKUP,pfnFreeBuf} + */ +PDMBOTHCBDECL(int) drvDedicatedNicUp_FreeBuf(PPDMINETWORKUP pInterface, PPDMSCATTERGATHER pSgBuf) +{ +#ifdef VBOX_STRICT + PDRVDEDICATEDNIC pThis = RT_FROM_MEMBER(pInterface, DRVDEDICATEDNIC, CTX_SUFF(INetworkUp)); + Assert(pSgBuf->fFlags == (PDMSCATTERGATHER_FLAGS_MAGIC | PDMSCATTERGATHER_FLAGS_OWNER_1)); + Assert(pSgBuf->cbUsed <= pSgBuf->cbAvailable); + Assert(PDMCritSectIsOwner(&pThis->XmitLock)); +#else + RT_NOREF1(pInterface); +#endif + + if (pSgBuf) + { +#ifdef IN_RING0 + // ... +#else + Assert(pSgBuf == &pThis->XmitSg); + Assert((pSgBuf->fFlags & PDMSCATTERGATHER_FLAGS_MAGIC_MASK) == PDMSCATTERGATHER_FLAGS_MAGIC); + pSgBuf->fFlags = 0; +#endif + } + + return VINF_SUCCESS; +} + + +/** + * @interface_method_impl{PDMINETWORKUP,pfnSendBuf} + */ +PDMBOTHCBDECL(int) drvDedicatedNicUp_SendBuf(PPDMINETWORKUP pInterface, PPDMSCATTERGATHER pSgBuf, bool fOnWorkerThread) +{ + PDRVDEDICATEDNIC pThis = RT_FROM_MEMBER(pInterface, DRVDEDICATEDNIC, CTX_SUFF(INetworkUp)); + STAM_PROFILE_START(&pThis->StatTransmit, a); + + AssertPtr(pSgBuf); + Assert(pSgBuf->fFlags == (PDMSCATTERGATHER_FLAGS_MAGIC | PDMSCATTERGATHER_FLAGS_OWNER_1)); + Assert(pSgBuf->cbUsed <= pSgBuf->cbAvailable); +#ifdef IN_RING0 + Assert(pSgBuf == &pThis->XmitSg); +#endif + Assert(PDMCritSectIsOwner(&pThis->XmitLock)); + +#ifdef IN_RING0 + /* + * Tell the driver to send the packet. + */ + RT_NOREF_PV(pThis); RT_NOREF_PV(pSgBuf); RT_NOREF_PV(fOnWorkerThread); + return VERR_INTERNAL_ERROR_4; + +#else /* IN_RING3 */ + NOREF(fOnWorkerThread); + + /* + * Call ring-0 to start the transfer. + */ + int rc = PDMDrvHlpCallR0(pThis->pDrvInsR3, DRVDEDICATEDNICR0OP_SEND, pSgBuf->cbUsed); + if (RT_FAILURE(rc) && rc != VERR_NET_DOWN) + rc = VERR_NET_NO_BUFFER_SPACE; + pSgBuf->fFlags = 0; + return rc; +#endif /* IN_RING3 */ +} + + +/** + * @interface_method_impl{PDMINETWORKUP,pfnEndXmit} + */ +PDMBOTHCBDECL(void) drvDedicatedNicUp_EndXmit(PPDMINETWORKUP pInterface) +{ + PDRVDEDICATEDNIC pThis = RT_FROM_MEMBER(pInterface, DRVDEDICATEDNIC, CTX_SUFF(INetworkUp)); + ASMAtomicUoWriteBool(&pThis->fXmitOnXmitThread, false); + PDMCritSectLeave(&pThis->XmitLock); +} + + +/** + * @interface_method_impl{PDMINETWORKUP,pfnSetPromiscuousMode} + */ +PDMBOTHCBDECL(void) drvDedicatedNicUp_SetPromiscuousMode(PPDMINETWORKUP pInterface, bool fPromiscuous) +{ + PDRVDEDICATEDNIC pThis = RT_FROM_MEMBER(pInterface, DRVDEDICATEDNIC, CTX_SUFF(INetworkUp)); + /** @todo enable/disable promiscuous mode (should be easy) */ + NOREF(pThis); RT_NOREF_PV(fPromiscuous); +} + +#endif /* unused */ +#ifdef IN_RING3 +# if 0 /* currently unused */ + +/** + * @interface_method_impl{PDMINETWORKUP,pfnNotifyLinkChanged} + */ +static DECLCALLBACK(void) drvR3DedicatedNicUp_NotifyLinkChanged(PPDMINETWORKUP pInterface, PDMNETWORKLINKSTATE enmLinkState) +{ + PDRVDEDICATEDNIC pThis = RT_FROM_MEMBER(pInterface, DRVDEDICATEDNIC, CTX_SUFF(INetworkUp)); + bool fLinkDown; + switch (enmLinkState) + { + case PDMNETWORKLINKSTATE_DOWN: + case PDMNETWORKLINKSTATE_DOWN_RESUME: + fLinkDown = true; + break; + default: + AssertMsgFailed(("enmLinkState=%d\n", enmLinkState)); + case PDMNETWORKLINKSTATE_UP: + fLinkDown = false; + break; + } + LogFlow(("drvR3DedicatedNicUp_NotifyLinkChanged: enmLinkState=%d %d->%d\n", enmLinkState, pThis->fLinkDown, fLinkDown)); + ASMAtomicWriteBool(&pThis->fLinkDown, fLinkDown); +} + + +/* -=-=-=-=- PDMIBASER0 -=-=-=-=- */ + +/** + * @interface_method_impl{PDMIBASER0,pfnQueryInterface} + */ +static DECLCALLBACK(RTR0PTR) drvR3DedicatedNicIBaseR0_QueryInterface(PPDMIBASER0 pInterface, const char *pszIID) +{ + PDRVDEDICATEDNIC pThis = RT_FROM_MEMBER(pInterface, DRVDEDICATEDNIC, IBaseR0); + PDMIBASER0_RETURN_INTERFACE(pThis->pDrvInsR3, pszIID, PDMINETWORKUP, &pThis->INetworkUpR0); + return NIL_RTR0PTR; +} + + +/* -=-=-=-=- PDMIBASE -=-=-=-=- */ + +/** + * @interface_method_impl{PDMIBASE,pfnQueryInterface} + */ +static DECLCALLBACK(void *) drvR3DedicatedNicIBase_QueryInterface(PPDMIBASE pInterface, const char *pszIID) +{ + PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface); + PDRVDEDICATEDNIC pThis = PDMINS_2_DATA(pDrvIns, PDRVDEDICATEDNIC); + + PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase); + PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASER0, &pThis->IBaseR0); + PDMIBASE_RETURN_INTERFACE(pszIID, PDMINETWORKUP, &pThis->INetworkUpR3); + return NULL; +} + +# endif /* Currently unused */ + + +/* -=-=-=-=- PDMDRVREG -=-=-=-=- */ + +/** + * @interface_method_impl{PDMDRVREG,pfnPowerOff} + */ +static DECLCALLBACK(void) drvR3DedicatedNicPowerOff(PPDMDRVINS pDrvIns) +{ + LogFlow(("drvR3DedicatedNicPowerOff\n")); + int rc = PDMDrvHlpCallR0(pDrvIns, DRVDEDICATEDNICR0OP_SUSPEND, 0); + AssertRC(rc); +} + + +/** + * @interface_method_impl{PDMDRVREG,pfnResume} + */ +static DECLCALLBACK(void) drvR3DedicatedNicResume(PPDMDRVINS pDrvIns) +{ + LogFlow(("drvR3DedicatedNicPowerResume\n")); + int rc = PDMDrvHlpCallR0(pDrvIns, DRVDEDICATEDNICR0OP_RESUME, 0); + AssertRC(rc); +} + + +/** + * @interface_method_impl{PDMDRVREG,pfnSuspend} + */ +static DECLCALLBACK(void) drvR3DedicatedNicSuspend(PPDMDRVINS pDrvIns) +{ + LogFlow(("drvR3DedicatedNicPowerSuspend\n")); + int rc = PDMDrvHlpCallR0(pDrvIns, DRVDEDICATEDNICR0OP_SUSPEND, 0); + AssertRC(rc); +} + + +/** + * @interface_method_impl{PDMDRVREG,pfnPowerOn} + */ +static DECLCALLBACK(void) drvR3DedicatedNicPowerOn(PPDMDRVINS pDrvIns) +{ + LogFlow(("drvR3DedicatedNicPowerOn\n")); + int rc = PDMDrvHlpCallR0(pDrvIns, DRVDEDICATEDNICR0OP_RESUME, 0); + AssertRC(rc); +} + + +/** + * @interface_method_impl{PDMDRVREG,pfnDestruct} + */ +static DECLCALLBACK(void) drvR3DedicatedNicDestruct(PPDMDRVINS pDrvIns) +{ + LogFlow(("drvR3DedicatedNicDestruct\n")); + PDRVDEDICATEDNIC pThis = PDMINS_2_DATA(pDrvIns, PDRVDEDICATEDNIC); + PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns); + + if (pThis->pIfPortR0) + { + int rc = PDMDrvHlpCallR0(pDrvIns, DRVDEDICATEDNICR0OP_TERM, 0); + AssertRC(rc);; + } +} + + +/** + * @interface_method_impl{PDMDRVREG,pfnConstruct} + */ +static DECLCALLBACK(int) drvR3DedicatedNicConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags) +{ + RT_NOREF(pCfg, fFlags); + PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns); + PDRVDEDICATEDNIC pThis = PDMINS_2_DATA(pDrvIns, PDRVDEDICATEDNIC); + + /* + * Init the static parts. + */ + pThis->pDrvInsR3 = pDrvIns; + pThis->pDrvInsR0 = PDMDRVINS_2_R0PTR(pDrvIns); +#if 0 + pThis->hRecvThread = NIL_RTTHREAD; + pThis->hRecvEvt = NIL_RTSEMEVENT; + pThis->pXmitThread = NULL; + pThis->hXmitEvt = NIL_SUPSEMEVENT; + pThis->pSupDrvSession = PDMDrvHlpGetSupDrvSession(pDrvIns); + pThis->hSgCache = NIL_RTMEMCACHE; + pThis->enmRecvState = RECVSTATE_SUSPENDED; + pThis->fActivateEarlyDeactivateLate = false; + /* IBase* */ + pDrvIns->IBase.pfnQueryInterface = drvR3DedicatedNicIBase_QueryInterface; + pThis->IBaseR0.pfnQueryInterface = drvR3DedicatedNicIBaseR0_QueryInterface; + pThis->IBaseRC.pfnQueryInterface = drvR3DedicatedNicIBaseRC_QueryInterface; + /* INetworkUp */ + pThis->INetworkUpR3.pfnBeginXmit = drvDedicatedNic_BeginXmit; + pThis->INetworkUpR3.pfnAllocBuf = drvDedicatedNic_AllocBuf; + pThis->INetworkUpR3.pfnFreeBuf = drvDedicatedNic_FreeBuf; + pThis->INetworkUpR3.pfnSendBuf = drvDedicatedNic_SendBuf; + pThis->INetworkUpR3.pfnEndXmit = drvDedicatedNic_EndXmit; + pThis->INetworkUpR3.pfnSetPromiscuousMode = drvDedicatedNic_SetPromiscuousMode; + pThis->INetworkUpR3.pfnNotifyLinkChanged = drvR3DedicatedNicUp_NotifyLinkChanged; +#endif + + /** @todo + * Need to create a generic way of calling into the ring-0 side of the driver so + * we can initialize the thing as well as send and receive. Hmm ... the + * sending could be done more efficiently from a ring-0 kernel thread actually + * (saves context switching and 1-2 copy operations). Ditto for receive, except + * we need to tie the thread to the process or we cannot access the guest ram so + * easily. + */ + + return VERR_NOT_IMPLEMENTED; +} + + + +/** + * Internal networking transport driver registration record. + */ +const PDMDRVREG g_DrvDedicatedNic = +{ + /* u32Version */ + PDM_DRVREG_VERSION, + /* szName */ + "DedicatedNic", + /* szRCMod */ + "", + /* szR0Mod */ + "VBoxDDR0.r0", + /* pszDescription */ + "Dedicated (V)NIC Driver", + /* fFlags */ + PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT | PDM_DRVREG_FLAGS_R0, + /* fClass. */ + PDM_DRVREG_CLASS_NETWORK, + /* cMaxInstances */ + ~0U, + /* cbInstance */ + sizeof(DRVDEDICATEDNIC), + /* pfnConstruct */ + drvR3DedicatedNicConstruct, + /* pfnDestruct */ + drvR3DedicatedNicDestruct, + /* pfnRelocate */ + NULL, + /* pfnIOCtl */ + NULL, + /* pfnPowerOn */ + drvR3DedicatedNicPowerOn, + /* pfnReset */ + NULL, + /* pfnSuspend */ + drvR3DedicatedNicSuspend, + /* pfnResume */ + drvR3DedicatedNicResume, + /* pfnAttach */ + NULL, + /* pfnDetach */ + NULL, + /* pfnPowerOff */ + drvR3DedicatedNicPowerOff, + /* pfnSoftReset */ + NULL, + /* u32EndVersion */ + PDM_DRVREG_VERSION +}; + +#endif /* IN_RING3 */ + -- cgit v1.2.3