DevVirtioNet.cpp revision 75cb12092223b74f2f415d7e9ba54f10854bc43b
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync/* $Id$ */
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync/** @file
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * DevVirtioNet - Virtio Network Device
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync *
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync */
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync
c7814cf6e1240a519cbec0441e033d0e2470ed00vboxsync/*
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * Copyright (C) 2009 Sun Microsystems, Inc.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync *
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * This file is part of VirtualBox Open Source Edition (OSE), as
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * available from http://www.virtualbox.org. This file is free software;
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * you can redistribute it and/or modify it under the terms of the GNU
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * General Public License (GPL) as published by the Free Software
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * Foundation, in version 2 as it comes in the "COPYING" file of the
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync *
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * Clara, CA 95054 USA or visit http://www.sun.com if you need
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * additional information or have any questions.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync */
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync#define LOG_GROUP LOG_GROUP_DEV_VIRTIO_NET
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync#define VNET_GC_SUPPORT
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync#include <VBox/pdmdev.h>
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync#include <iprt/semaphore.h>
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync#ifdef IN_RING3
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync# include <iprt/mem.h>
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync#endif /* IN_RING3 */
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync#include "../Builtins.h"
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync#include "../VirtIO/Virtio.h"
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync#ifndef VBOX_DEVICE_STRUCT_TESTCASE
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync#define INSTANCE(pState) pState->VPCI.szInstance
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync#define IFACE_TO_STATE(pIface, ifaceName) \
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync ((VNETSTATE *)((char*)pIface - RT_OFFSETOF(VNETSTATE, ifaceName)))
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync#define STATUS pState->config.uStatus
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync#ifdef IN_RING3
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync#define VNET_PCI_SUBSYSTEM_ID 1 + VIRTIO_NET_ID
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync#define VNET_PCI_CLASS 0x0200
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync#define VNET_N_QUEUES 3
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync#define VNET_NAME_FMT "VNet%d"
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync#if 0
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync/* Virtio Block Device */
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync#define VNET_PCI_SUBSYSTEM_ID 1 + VIRTIO_BLK_ID
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync#define VNET_PCI_CLASS 0x0180
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync#define VNET_N_QUEUES 2
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync#define VNET_NAME_FMT "VBlk%d"
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync#endif
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync#endif /* IN_RING3 */
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync/* Forward declarations ******************************************************/
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsyncRT_C_DECLS_BEGIN
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsyncPDMBOTHCBDECL(int) vnetIOPortIn (PPDMDEVINS pDevIns, void *pvUser, RTIOPORT port, uint32_t *pu32, unsigned cb);
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsyncPDMBOTHCBDECL(int) vnetIOPortOut(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT port, uint32_t u32, unsigned cb);
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsyncRT_C_DECLS_END
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync#endif /* VBOX_DEVICE_STRUCT_TESTCASE */
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync#define VNET_TX_DELAY 150 /* 150 microseconds */
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync#define VNET_MAX_FRAME_SIZE 65536 // TODO: Is it the right limit?
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync#define VNET_MAC_FILTER_LEN 32
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync#define VNET_MAX_VID (1 << 12)
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync/* Virtio net features */
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync#define VNET_F_CSUM 0x00000001 /* Host handles pkts w/ partial csum */
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync#define VNET_F_GUEST_CSUM 0x00000002 /* Guest handles pkts w/ partial csum */
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync#define VNET_F_MAC 0x00000020 /* Host has given MAC address. */
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync#define VNET_F_GSO 0x00000040 /* Host handles pkts w/ any GSO type */
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync#define VNET_F_GUEST_TSO4 0x00000080 /* Guest can handle TSOv4 in. */
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync#define VNET_F_GUEST_TSO6 0x00000100 /* Guest can handle TSOv6 in. */
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync#define VNET_F_GUEST_ECN 0x00000200 /* Guest can handle TSO[6] w/ ECN in. */
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync#define VNET_F_GUEST_UFO 0x00000400 /* Guest can handle UFO in. */
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync#define VNET_F_HOST_TSO4 0x00000800 /* Host can handle TSOv4 in. */
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync#define VNET_F_HOST_TSO6 0x00001000 /* Host can handle TSOv6 in. */
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync#define VNET_F_HOST_ECN 0x00002000 /* Host can handle TSO[6] w/ ECN in. */
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync#define VNET_F_HOST_UFO 0x00004000 /* Host can handle UFO in. */
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync#define VNET_F_MRG_RXBUF 0x00008000 /* Host can merge receive buffers. */
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync#define VNET_F_STATUS 0x00010000 /* virtio_net_config.status available */
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync#define VNET_F_CTRL_VQ 0x00020000 /* Control channel available */
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync#define VNET_F_CTRL_RX 0x00040000 /* Control channel RX mode support */
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync#define VNET_F_CTRL_VLAN 0x00080000 /* Control channel VLAN filtering */
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync#define VNET_S_LINK_UP 1
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync#ifdef _MSC_VER
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsyncstruct VNetPCIConfig
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync#else /* !_MSC_VER */
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsyncstruct __attribute__ ((__packed__)) VNetPCIConfig
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync#endif /* !_MSC_VER */
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync{
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync RTMAC mac;
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync uint16_t uStatus;
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync};
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsyncAssertCompileMemberOffset(struct VNetPCIConfig, uStatus, 6);
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync/**
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * Device state structure. Holds the current state of device.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync */
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsyncstruct VNetState_st
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync{
ad27e1d5e48ca41245120c331cc88b50464813cevboxsync /* VPCISTATE must be the first member! */
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync VPCISTATE VPCI;
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync// PDMCRITSECT csRx; /**< Protects RX queue. */
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync PDMINETWORKPORT INetworkPort;
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync PDMINETWORKCONFIG INetworkConfig;
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync R3PTRTYPE(PPDMIBASE) pDrvBase; /**< Attached network driver. */
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync R3PTRTYPE(PPDMINETWORKCONNECTOR) pDrv; /**< Connector of attached network driver. */
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync R3PTRTYPE(PPDMQUEUE) pCanRxQueueR3; /**< Rx wakeup signaller - R3. */
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync R0PTRTYPE(PPDMQUEUE) pCanRxQueueR0; /**< Rx wakeup signaller - R0. */
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync RCPTRTYPE(PPDMQUEUE) pCanRxQueueRC; /**< Rx wakeup signaller - RC. */
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync#if HC_ARCH_BITS == 64
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync uint32_t padding;
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync#endif
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync /** transmit buffer */
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync R3PTRTYPE(uint8_t*) pTxBuf;
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync /**< Link Up(/Restore) Timer. */
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync PTMTIMERR3 pLinkUpTimer;
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync#ifdef VNET_TX_DELAY
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync /**< Transmit Delay Timer - R3. */
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync PTMTIMERR3 pTxTimerR3;
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync /**< Transmit Delay Timer - R0. */
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync PTMTIMERR0 pTxTimerR0;
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync /**< Transmit Delay Timer - GC. */
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync PTMTIMERRC pTxTimerRC;
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync#if HC_ARCH_BITS == 64
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync uint32_t padding2;
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync#endif
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync uint32_t u32i;
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync uint32_t u32AvgDiff;
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync uint32_t u32MinDiff;
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync uint32_t u32MaxDiff;
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync uint64_t u64NanoTS;
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync#endif /* VNET_TX_DELAY */
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync /** Indicates transmission in progress -- only one thread is allowed. */
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync uint32_t uIsTransmitting;
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync /** PCI config area holding MAC address as well as TBD. */
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync struct VNetPCIConfig config;
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync /** MAC address obtained from the configuration. */
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync RTMAC macConfigured;
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync /** True if physical cable is attached in configuration. */
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync bool fCableConnected;
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync /** Number of packet being sent/received to show in debug log. */
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync uint32_t u32PktNo;
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync /** N/A: */
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync bool volatile fMaybeOutOfSpace;
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync /** Promiscuous mode -- RX filter accepts all packets. */
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync bool fPromiscuous;
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync /** AllMulti mode -- RX filter accepts all multicast packets. */
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync bool fAllMulti;
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync /** The number of actually used slots in aMacTable. */
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync uint32_t nMacFilterEntries;
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync /** Array of MAC addresses accepted by RX filter. */
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync RTMAC aMacFilter[VNET_MAC_FILTER_LEN];
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync /** Bit array of VLAN filter, one bit per VLAN ID. */
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync uint8_t aVlanFilter[VNET_MAX_VID / sizeof(uint8_t)];
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync#if HC_ARCH_BITS != 64
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync uint32_t padding3;
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync#endif
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync R3PTRTYPE(PVQUEUE) pRxQueue;
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync R3PTRTYPE(PVQUEUE) pTxQueue;
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync R3PTRTYPE(PVQUEUE) pCtlQueue;
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync /* Receive-blocking-related fields ***************************************/
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync /** EMT: Gets signalled when more RX descriptors become available. */
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync RTSEMEVENT hEventMoreRxDescAvail;
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync /* Statistic fields ******************************************************/
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync STAMCOUNTER StatReceiveBytes;
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync STAMCOUNTER StatTransmitBytes;
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync#if defined(VBOX_WITH_STATISTICS)
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync STAMPROFILE StatReceive;
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync STAMPROFILE StatReceiveStore;
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync STAMPROFILEADV StatTransmit;
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync STAMPROFILE StatTransmitSend;
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync STAMPROFILE StatRxOverflow;
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync STAMCOUNTER StatRxOverflowWakeup;
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync#endif /* VBOX_WITH_STATISTICS */
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync};
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsynctypedef struct VNetState_st VNETSTATE;
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsynctypedef VNETSTATE *PVNETSTATE;
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync#ifndef VBOX_DEVICE_STRUCT_TESTCASE
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync#define VNETHDR_GSO_NONE 0
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsyncstruct VNetHdr
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync{
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync uint8_t u8Flags;
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync uint8_t u8GSOType;
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync uint16_t u16HdrLen;
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync uint16_t u16GSOSize;
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync uint16_t u16CSumStart;
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync uint16_t u16CSumOffset;
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync};
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsynctypedef struct VNetHdr VNETHDR;
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsynctypedef VNETHDR *PVNETHDR;
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsyncAssertCompileSize(VNETHDR, 10);
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsyncAssertCompileMemberOffset(VNETSTATE, VPCI, 0);
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync#define VNET_OK 0
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync#define VNET_ERROR 1
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsynctypedef uint8_t VNETCTLACK;
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync#define VNET_CTRL_CLS_RX_MODE 0
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync#define VNET_CTRL_CMD_RX_MODE_PROMISC 0
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync#define VNET_CTRL_CMD_RX_MODE_ALLMULTI 1
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync#define VNET_CTRL_CLS_MAC 1
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync#define VNET_CTRL_CMD_MAC_TABLE_SET 0
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync#define VNET_CTRL_CLS_VLAN 2
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync#define VNET_CTRL_CMD_VLAN_ADD 0
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync#define VNET_CTRL_CMD_VLAN_DEL 1
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsyncstruct VNetCtlHdr
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync{
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync uint8_t u8Class;
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync uint8_t u8Command;
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync};
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsynctypedef struct VNetCtlHdr VNETCTLHDR;
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsynctypedef VNETCTLHDR *PVNETCTLHDR;
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsyncAssertCompileSize(VNETCTLHDR, 2);
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsyncDECLINLINE(int) vnetCsEnter(PVNETSTATE pState, int rcBusy)
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync{
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync return vpciCsEnter(&pState->VPCI, rcBusy);
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync}
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsyncDECLINLINE(void) vnetCsLeave(PVNETSTATE pState)
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync{
83dc9ca94cd3c31dabc33a35b945de124d43aaeavboxsync vpciCsLeave(&pState->VPCI);
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync}
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsyncDECLINLINE(int) vnetCsRxEnter(PVNETSTATE pState, int rcBusy)
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync{
83dc9ca94cd3c31dabc33a35b945de124d43aaeavboxsync // STAM_PROFILE_START(&pState->CTXSUFF(StatCsRx), a);
83dc9ca94cd3c31dabc33a35b945de124d43aaeavboxsync // int rc = PDMCritSectEnter(&pState->csRx, rcBusy);
fb1975a6972d89de9e515bed0248db93f04ec9d8vboxsync // STAM_PROFILE_STOP(&pState->CTXSUFF(StatCsRx), a);
83dc9ca94cd3c31dabc33a35b945de124d43aaeavboxsync // return rc;
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync return VINF_SUCCESS;
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync}
83dc9ca94cd3c31dabc33a35b945de124d43aaeavboxsync
83dc9ca94cd3c31dabc33a35b945de124d43aaeavboxsyncDECLINLINE(void) vnetCsRxLeave(PVNETSTATE pState)
83dc9ca94cd3c31dabc33a35b945de124d43aaeavboxsync{
83dc9ca94cd3c31dabc33a35b945de124d43aaeavboxsync // PDMCritSectLeave(&pState->csRx);
83dc9ca94cd3c31dabc33a35b945de124d43aaeavboxsync}
83dc9ca94cd3c31dabc33a35b945de124d43aaeavboxsync
83dc9ca94cd3c31dabc33a35b945de124d43aaeavboxsync/**
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * Dump a packet to debug log.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync *
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * @param pState The device state structure.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * @param cpPacket The packet.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * @param cb The size of the packet.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * @param cszText A string denoting direction of packet transfer.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync */
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsyncDECLINLINE(void) vnetPacketDump(PVNETSTATE pState, const uint8_t *cpPacket, size_t cb, const char *cszText)
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync{
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync#ifdef DEBUG
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync Log(("%s %s packet #%d (%d bytes):\n",
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync INSTANCE(pState), cszText, ++pState->u32PktNo, cb));
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync //Log3(("%.*Rhxd\n", cb, cpPacket));
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync#endif
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync}
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsyncPDMBOTHCBDECL(uint32_t) vnetGetHostFeatures(void *pvState)
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync{
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync /* We support:
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * - Host-provided MAC address
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * - Link status reporting in config space
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * - Control queue
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * - RX mode setting
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * - MAC filter table
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * - VLAN filter
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync */
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync return VNET_F_MAC
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync | VNET_F_STATUS
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync | VNET_F_CTRL_VQ
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync | VNET_F_CTRL_RX
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync | VNET_F_CTRL_VLAN;
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync}
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsyncPDMBOTHCBDECL(uint32_t) vnetGetHostMinimalFeatures(void *pvState)
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync{
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync return VNET_F_MAC;
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync}
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsyncPDMBOTHCBDECL(void) vnetSetHostFeatures(void *pvState, uint32_t uFeatures)
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync{
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync // TODO: Nothing to do here yet
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync VNETSTATE *pState = (VNETSTATE *)pvState;
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync LogFlow(("%s vnetSetHostFeatures: uFeatures=%x\n", INSTANCE(pState), uFeatures));
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync}
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsyncPDMBOTHCBDECL(int) vnetGetConfig(void *pvState, uint32_t port, uint32_t cb, void *data)
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync{
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync VNETSTATE *pState = (VNETSTATE *)pvState;
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync if (port + cb > sizeof(struct VNetPCIConfig))
83dc9ca94cd3c31dabc33a35b945de124d43aaeavboxsync {
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync Log(("%s vnetGetConfig: Read beyond the config structure is attempted (port=%RTiop cb=%x).\n", INSTANCE(pState), port, cb));
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync return VERR_INTERNAL_ERROR;
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync }
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync memcpy(data, ((uint8_t*)&pState->config) + port, cb);
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync return VINF_SUCCESS;
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync}
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsyncPDMBOTHCBDECL(int) vnetSetConfig(void *pvState, uint32_t port, uint32_t cb, void *data)
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync{
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync VNETSTATE *pState = (VNETSTATE *)pvState;
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync if (port + cb > sizeof(struct VNetPCIConfig))
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync {
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync Log(("%s vnetGetConfig: Write beyond the config structure is attempted (port=%RTiop cb=%x).\n", INSTANCE(pState), port, cb));
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync return VERR_INTERNAL_ERROR;
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync }
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync memcpy(((uint8_t*)&pState->config) + port, data, cb);
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync return VINF_SUCCESS;
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync}
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync/**
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * Hardware reset. Revert all registers to initial values.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync *
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * @param pState The device state structure.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync */
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsyncPDMBOTHCBDECL(void) vnetReset(void *pvState)
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync{
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync VNETSTATE *pState = (VNETSTATE*)pvState;
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync Log(("%s Reset triggered\n", INSTANCE(pState)));
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync int rc = vnetCsRxEnter(pState, VERR_SEM_BUSY);
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync if (RT_UNLIKELY(rc != VINF_SUCCESS))
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync {
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync LogRel(("vnetReset failed to enter RX critical section!\n"));
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync return;
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync }
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync vpciReset(&pState->VPCI);
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync vnetCsRxLeave(pState);
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync // TODO: Implement reset
46fd1b35e55cbd736b7abe0d856a940f0336eb81vboxsync if (pState->fCableConnected)
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync STATUS = VNET_S_LINK_UP;
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync else
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync STATUS = 0;
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync /*
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * By default we pass all packets up since the older guests cannot control
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * virtio mode.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync */
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync pState->fPromiscuous = true;
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync pState->fAllMulti = false;
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync pState->nMacFilterEntries = 0;
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync memset(pState->aMacFilter, 0, VNET_MAC_FILTER_LEN * sizeof(RTMAC));
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync memset(pState->aVlanFilter, 0, sizeof(pState->aVlanFilter));
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync pState->uIsTransmitting = 0;
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync}
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync#ifdef IN_RING3
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync/**
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * Wakeup the RX thread.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync */
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsyncstatic void vnetWakeupReceive(PPDMDEVINS pDevIns)
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync{
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync VNETSTATE *pState = PDMINS_2_DATA(pDevIns, VNETSTATE *);
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync if ( pState->fMaybeOutOfSpace
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync && pState->hEventMoreRxDescAvail != NIL_RTSEMEVENT)
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync {
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync STAM_COUNTER_INC(&pState->StatRxOverflowWakeup);
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync Log(("%s Waking up Out-of-RX-space semaphore\n", INSTANCE(pState)));
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync RTSemEventSignal(pState->hEventMoreRxDescAvail);
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync }
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync}
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync/**
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * Link Up Timer handler.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync *
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * @param pDevIns Pointer to device instance structure.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * @param pTimer Pointer to the timer.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * @param pvUser NULL.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * @thread EMT
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync */
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsyncstatic DECLCALLBACK(void) vnetLinkUpTimer(PPDMDEVINS pDevIns, PTMTIMER pTimer, void *pvUser)
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync{
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync VNETSTATE *pState = (VNETSTATE *)pvUser;
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync int rc = vnetCsEnter(pState, VERR_SEM_BUSY);
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync if (RT_UNLIKELY(rc != VINF_SUCCESS))
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync return;
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync STATUS |= VNET_S_LINK_UP;
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync vpciRaiseInterrupt(&pState->VPCI, VERR_SEM_BUSY, VPCI_ISR_CONFIG);
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync vnetWakeupReceive(pDevIns);
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync vnetCsLeave(pState);
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync}
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync/**
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * Handler for the wakeup signaller queue.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync */
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsyncstatic DECLCALLBACK(bool) vnetCanRxQueueConsumer(PPDMDEVINS pDevIns, PPDMQUEUEITEMCORE pItem)
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync{
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync vnetWakeupReceive(pDevIns);
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync return true;
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync}
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync#endif /* IN_RING3 */
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync/**
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * This function is called when the driver becomes ready.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync *
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * @param pState The device state structure.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync */
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsyncPDMBOTHCBDECL(void) vnetReady(void *pvState)
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync{
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync VNETSTATE *pState = (VNETSTATE*)pvState;
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync Log(("%s Driver became ready, waking up RX thread...\n", INSTANCE(pState)));
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync#ifdef IN_RING3
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync vnetWakeupReceive(pState->VPCI.CTX_SUFF(pDevIns));
ad27e1d5e48ca41245120c331cc88b50464813cevboxsync#else
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync PPDMQUEUEITEMCORE pItem = PDMQueueAlloc(pState->CTX_SUFF(pCanRxQueue));
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync if (pItem)
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync PDMQueueInsert(pState->CTX_SUFF(pCanRxQueue), pItem);
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync#endif
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync}
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync/**
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * Port I/O Handler for IN operations.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync *
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * @returns VBox status code.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync *
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * @param pDevIns The device instance.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * @param pvUser Pointer to the device state structure.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * @param port Port number used for the IN operation.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * @param pu32 Where to store the result.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * @param cb Number of bytes read.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * @thread EMT
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync */
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsyncPDMBOTHCBDECL(int) vnetIOPortIn(PPDMDEVINS pDevIns, void *pvUser,
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync RTIOPORT port, uint32_t *pu32, unsigned cb)
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync{
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync return vpciIOPortIn(pDevIns, pvUser, port, pu32, cb,
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync vnetGetHostFeatures,
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync vnetGetConfig);
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync}
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync/**
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * Port I/O Handler for OUT operations.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync *
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * @returns VBox status code.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync *
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * @param pDevIns The device instance.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * @param pvUser User argument.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * @param Port Port number used for the IN operation.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * @param u32 The value to output.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * @param cb The value size in bytes.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * @thread EMT
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync */
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsyncPDMBOTHCBDECL(int) vnetIOPortOut(PPDMDEVINS pDevIns, void *pvUser,
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync RTIOPORT port, uint32_t u32, unsigned cb)
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync{
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync return vpciIOPortOut(pDevIns, pvUser, port, u32, cb,
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync vnetGetHostMinimalFeatures,
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync vnetGetHostFeatures,
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync vnetSetHostFeatures,
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync vnetReset,
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync vnetReady,
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync vnetSetConfig);
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync}
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync#ifdef IN_RING3
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync/**
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * Check if the device can receive data now.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * This must be called before the pfnRecieve() method is called.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync *
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * @remarks As a side effect this function enables queue notification
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * if it cannot receive because the queue is empty.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * It disables notification if it can receive.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync *
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * @returns VERR_NET_NO_BUFFER_SPACE if it cannot.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * @param pInterface Pointer to the interface structure containing the called function pointer.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * @thread RX
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync */
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsyncstatic int vnetCanReceive(VNETSTATE *pState)
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync{
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync int rc = vnetCsRxEnter(pState, VERR_SEM_BUSY);
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync AssertRCReturn(rc, rc);
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync LogFlow(("%s vnetCanReceive\n", INSTANCE(pState)));
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync if (!(pState->VPCI.uStatus & VPCI_STATUS_DRV_OK))
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync rc = VERR_NET_NO_BUFFER_SPACE;
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync else if (!vqueueIsReady(&pState->VPCI, pState->pRxQueue))
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync rc = VERR_NET_NO_BUFFER_SPACE;
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync else if (vqueueIsEmpty(&pState->VPCI, pState->pRxQueue))
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync {
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync vringSetNotification(&pState->VPCI, &pState->pRxQueue->VRing, true);
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync rc = VERR_NET_NO_BUFFER_SPACE;
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync }
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync else
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync {
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync vringSetNotification(&pState->VPCI, &pState->pRxQueue->VRing, false);
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync rc = VINF_SUCCESS;
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync }
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync LogFlow(("%s vnetCanReceive -> %Vrc\n", INSTANCE(pState), rc));
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync vnetCsRxLeave(pState);
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync return rc;
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync}
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsyncstatic DECLCALLBACK(int) vnetWaitReceiveAvail(PPDMINETWORKPORT pInterface, RTMSINTERVAL cMillies)
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync{
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync VNETSTATE *pState = IFACE_TO_STATE(pInterface, INetworkPort);
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync LogFlow(("%s vnetWaitReceiveAvail(cMillies=%u)\n", INSTANCE(pState), cMillies));
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync int rc = vnetCanReceive(pState);
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync if (RT_SUCCESS(rc))
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync return VINF_SUCCESS;
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync if (RT_UNLIKELY(cMillies == 0))
83dc9ca94cd3c31dabc33a35b945de124d43aaeavboxsync return VERR_NET_NO_BUFFER_SPACE;
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync rc = VERR_INTERRUPTED;
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync ASMAtomicXchgBool(&pState->fMaybeOutOfSpace, true);
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync STAM_PROFILE_START(&pState->StatRxOverflow, a);
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync VMSTATE enmVMState;
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync while (RT_LIKELY( (enmVMState = PDMDevHlpVMState(pState->VPCI.CTX_SUFF(pDevIns))) == VMSTATE_RUNNING
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync || enmVMState == VMSTATE_RUNNING_LS))
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync {
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync int rc2 = vnetCanReceive(pState);
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync if (RT_SUCCESS(rc2))
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync {
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync rc = VINF_SUCCESS;
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync break;
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync }
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync Log(("%s vnetWaitReceiveAvail: waiting cMillies=%u...\n",
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync INSTANCE(pState), cMillies));
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync RTSemEventWait(pState->hEventMoreRxDescAvail, cMillies);
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync }
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync STAM_PROFILE_STOP(&pState->StatRxOverflow, a);
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync ASMAtomicXchgBool(&pState->fMaybeOutOfSpace, false);
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync LogFlow(("%s vnetWaitReceiveAvail -> %d\n", INSTANCE(pState), rc));
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync return rc;
ad27e1d5e48ca41245120c331cc88b50464813cevboxsync}
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync/**
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * Provides interfaces to the driver.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync *
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * @returns Pointer to interface. NULL if the interface is not supported.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * @param pInterface Pointer to this interface structure.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * @param enmInterface The requested interface identification.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * @thread EMT
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync */
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsyncstatic DECLCALLBACK(void *) vnetQueryInterface(struct PDMIBASE *pInterface, PDMINTERFACE enmInterface)
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync{
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync VNETSTATE *pState = IFACE_TO_STATE(pInterface, VPCI.IBase);
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync Assert(&pState->VPCI.IBase == pInterface);
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync switch (enmInterface)
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync {
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync case PDMINTERFACE_NETWORK_PORT:
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync return &pState->INetworkPort;
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync case PDMINTERFACE_NETWORK_CONFIG:
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync return &pState->INetworkConfig;
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync default:
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync return vpciQueryInterface(pInterface, enmInterface);
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync }
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync}
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync/**
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * Returns true if it is a broadcast packet.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync *
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * @returns true if destination address indicates broadcast.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * @param pvBuf The ethernet packet.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync */
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsyncDECLINLINE(bool) vnetIsBroadcast(const void *pvBuf)
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync{
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync static const uint8_t s_abBcastAddr[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync return memcmp(pvBuf, s_abBcastAddr, sizeof(s_abBcastAddr)) == 0;
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync}
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync/**
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * Returns true if it is a multicast packet.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync *
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * @remarks returns true for broadcast packets as well.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * @returns true if destination address indicates multicast.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * @param pvBuf The ethernet packet.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync */
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsyncDECLINLINE(bool) vnetIsMulticast(const void *pvBuf)
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync{
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync return (*(char*)pvBuf) & 1;
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync}
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync/**
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * Determines if the packet is to be delivered to upper layer.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync *
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * @returns true if packet is intended for this node.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * @param pState Pointer to the state structure.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * @param pvBuf The ethernet packet.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * @param cb Number of bytes available in the packet.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync */
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsyncstatic bool vnetAddressFilter(PVNETSTATE pState, const void *pvBuf, size_t cb)
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync{
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync if (pState->fPromiscuous)
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync return true;
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync /* Ignore everything outside of our VLANs */
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync uint16_t *u16Ptr = (uint16_t*)pvBuf;
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync /* Compare TPID with VLAN Ether Type */
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync if ( u16Ptr[6] == RT_H2BE_U16(0x8100)
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync && !ASMBitTest(pState->aVlanFilter, RT_BE2H_U16(u16Ptr[7]) & 0xFFF))
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync return false;
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync if (vnetIsBroadcast(pvBuf))
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync return true;
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync
83dc9ca94cd3c31dabc33a35b945de124d43aaeavboxsync if (pState->fAllMulti && vnetIsMulticast(pvBuf))
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync return true;
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync if (!memcmp(pState->config.mac.au8, pvBuf, sizeof(RTMAC)))
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync return true;
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync for (unsigned i = 0; i < pState->nMacFilterEntries; i++)
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync if (!memcmp(&pState->aMacFilter[i], pvBuf, sizeof(RTMAC)))
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync return true;
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync return false;
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync}
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync/**
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * Pad and store received packet.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync *
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * @remarks Make sure that the packet appears to upper layer as one coming
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * from real Ethernet: pad it and insert FCS.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync *
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * @returns VBox status code.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * @param pState The device state structure.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * @param pvBuf The available data.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * @param cb Number of bytes available in the buffer.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * @thread RX
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync */
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsyncstatic int vnetHandleRxPacket(PVNETSTATE pState, const void *pvBuf, size_t cb)
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync{
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync VNETHDR hdr;
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync hdr.u8Flags = 0;
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync hdr.u8GSOType = VNETHDR_GSO_NONE;
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync vnetPacketDump(pState, (const uint8_t*)pvBuf, cb, "<-- Incoming");
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync unsigned int uOffset = 0;
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync for (unsigned int nElem = 0; uOffset < cb; nElem++)
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync {
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync VQUEUEELEM elem;
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync unsigned int nSeg = 0, uElemSize = 0;
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync if (!vqueueGet(&pState->VPCI, pState->pRxQueue, &elem))
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync {
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync Log(("%s vnetHandleRxPacket: Suddenly there is no space in receive queue!\n", INSTANCE(pState)));
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync return VERR_INTERNAL_ERROR;
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync }
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync if (elem.nIn < 1)
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync {
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync Log(("%s vnetHandleRxPacket: No writable descriptors in receive queue!\n", INSTANCE(pState)));
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync return VERR_INTERNAL_ERROR;
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync }
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync if (nElem == 0)
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync {
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync /* The very first segment of the very first element gets the header. */
83dc9ca94cd3c31dabc33a35b945de124d43aaeavboxsync if (elem.aSegsIn[nSeg].cb != sizeof(VNETHDR))
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync {
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync Log(("%s vnetHandleRxPacket: The first descriptor does match the header size!\n", INSTANCE(pState)));
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync return VERR_INTERNAL_ERROR;
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync }
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync elem.aSegsIn[nSeg++].pv = &hdr;
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync uElemSize += sizeof(VNETHDR);
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync }
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync while (nSeg < elem.nIn && uOffset < cb)
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync {
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync unsigned int uSize = RT_MIN(elem.aSegsIn[nSeg].cb, cb - uOffset);
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync elem.aSegsIn[nSeg++].pv = (uint8_t*)pvBuf + uOffset;
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync uOffset += uSize;
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync uElemSize += uSize;
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync }
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync STAM_PROFILE_START(&pState->StatReceiveStore, a);
f313ed48a0cc2d9d12580dc9412291ae15773f02vboxsync vqueuePut(&pState->VPCI, pState->pRxQueue, &elem, uElemSize);
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync STAM_PROFILE_STOP(&pState->StatReceiveStore, a);
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync }
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync vqueueSync(&pState->VPCI, pState->pRxQueue);
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync return VINF_SUCCESS;
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync}
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync/**
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * Receive data from the network.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync *
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * @returns VBox status code.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * @param pInterface Pointer to the interface structure containing the called function pointer.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * @param pvBuf The available data.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * @param cb Number of bytes available in the buffer.
f313ed48a0cc2d9d12580dc9412291ae15773f02vboxsync * @thread RX
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync */
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsyncstatic DECLCALLBACK(int) vnetReceive(PPDMINETWORKPORT pInterface, const void *pvBuf, size_t cb)
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync{
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync VNETSTATE *pState = IFACE_TO_STATE(pInterface, INetworkPort);
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync Log2(("%s vnetReceive: pvBuf=%p cb=%u\n", INSTANCE(pState), pvBuf, cb));
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync int rc = vnetCanReceive(pState);
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync if (RT_FAILURE(rc))
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync return rc;
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync /* Drop packets if VM is not running or cable is disconnected. */
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync VMSTATE enmVMState = PDMDevHlpVMState(pState->VPCI.CTX_SUFF(pDevIns));
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync if (( enmVMState != VMSTATE_RUNNING
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync && enmVMState != VMSTATE_RUNNING_LS)
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync || !(STATUS & VNET_S_LINK_UP))
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync return VINF_SUCCESS;
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync STAM_PROFILE_START(&pState->StatReceive, a);
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync vpciSetReadLed(&pState->VPCI, true);
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync if (vnetAddressFilter(pState, pvBuf, cb))
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync {
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync rc = vnetCsRxEnter(pState, VERR_SEM_BUSY);
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync if (RT_SUCCESS(rc))
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync {
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync rc = vnetHandleRxPacket(pState, pvBuf, cb);
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync STAM_REL_COUNTER_ADD(&pState->StatReceiveBytes, cb);
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync vnetCsRxLeave(pState);
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync }
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync }
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync vpciSetReadLed(&pState->VPCI, false);
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync STAM_PROFILE_STOP(&pState->StatReceive, a);
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync return rc;
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync}
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync/**
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * Gets the current Media Access Control (MAC) address.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync *
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * @returns VBox status code.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * @param pInterface Pointer to the interface structure containing the called function pointer.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * @param pMac Where to store the MAC address.
230bd8589bba39933ac5ec21482d6186d675e604vboxsync * @thread EMT
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync */
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsyncstatic DECLCALLBACK(int) vnetGetMac(PPDMINETWORKCONFIG pInterface, PRTMAC pMac)
230bd8589bba39933ac5ec21482d6186d675e604vboxsync{
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync VNETSTATE *pState = IFACE_TO_STATE(pInterface, INetworkConfig);
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync memcpy(pMac, pState->config.mac.au8, sizeof(RTMAC));
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync return VINF_SUCCESS;
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync}
ad27e1d5e48ca41245120c331cc88b50464813cevboxsync
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync/**
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * Gets the new link state.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync *
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * @returns The current link state.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * @param pInterface Pointer to the interface structure containing the called function pointer.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * @thread EMT
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync */
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsyncstatic DECLCALLBACK(PDMNETWORKLINKSTATE) vnetGetLinkState(PPDMINETWORKCONFIG pInterface)
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync{
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync VNETSTATE *pState = IFACE_TO_STATE(pInterface, INetworkConfig);
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync if (STATUS & VNET_S_LINK_UP)
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync return PDMNETWORKLINKSTATE_UP;
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync return PDMNETWORKLINKSTATE_DOWN;
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync}
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync/**
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * Sets the new link state.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync *
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * @returns VBox status code.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * @param pInterface Pointer to the interface structure containing the called function pointer.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * @param enmState The new link state
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync */
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsyncstatic DECLCALLBACK(int) vnetSetLinkState(PPDMINETWORKCONFIG pInterface, PDMNETWORKLINKSTATE enmState)
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync{
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync VNETSTATE *pState = IFACE_TO_STATE(pInterface, INetworkConfig);
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync bool fOldUp = !!(STATUS & VNET_S_LINK_UP);
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync bool fNewUp = enmState == PDMNETWORKLINKSTATE_UP;
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync if (fNewUp != fOldUp)
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync {
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync if (fNewUp)
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync {
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync Log(("%s Link is up\n", INSTANCE(pState)));
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync STATUS |= VNET_S_LINK_UP;
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync vpciRaiseInterrupt(&pState->VPCI, VERR_SEM_BUSY, VPCI_ISR_CONFIG);
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync }
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync else
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync {
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync Log(("%s Link is down\n", INSTANCE(pState)));
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync STATUS &= ~VNET_S_LINK_UP;
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync vpciRaiseInterrupt(&pState->VPCI, VERR_SEM_BUSY, VPCI_ISR_CONFIG);
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync }
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync if (pState->pDrv)
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync pState->pDrv->pfnNotifyLinkChanged(pState->pDrv, enmState);
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync }
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync return VINF_SUCCESS;
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync}
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsyncstatic DECLCALLBACK(void) vnetQueueReceive(void *pvState, PVQUEUE pQueue)
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync{
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync VNETSTATE *pState = (VNETSTATE*)pvState;
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync Log(("%s Receive buffers has been added, waking up receive thread.\n", INSTANCE(pState)));
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync vnetWakeupReceive(pState->VPCI.CTX_SUFF(pDevIns));
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync}
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsyncstatic DECLCALLBACK(void) vnetTransmitPendingPackets(PVNETSTATE pState, PVQUEUE pQueue)
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync{
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync /*
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * Only one thread is allowed to transmit at a time, others should skip
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * transmission as the packets will be picked up by the transmitting
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync * thread.
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync */
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync if (!ASMAtomicCmpXchgU32(&pState->uIsTransmitting, 1, 0))
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync return;
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync if ((pState->VPCI.uStatus & VPCI_STATUS_DRV_OK) == 0)
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync {
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync Log(("%s Ignoring transmit requests from non-existent driver (status=0x%x).\n",
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync INSTANCE(pState), pState->VPCI.uStatus));
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync return;
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync }
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync Log3(("%s vnetTransmitPendingPackets: About to trasmit %d pending packets\n", INSTANCE(pState),
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync vringReadAvailIndex(&pState->VPCI, &pState->pTxQueue->VRing) - pState->pTxQueue->uNextAvailIndex));
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync vpciSetWriteLed(&pState->VPCI, true);
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync VQUEUEELEM elem;
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync while (vqueueGet(&pState->VPCI, pQueue, &elem))
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync {
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync unsigned int uOffset = 0;
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync if (elem.nOut < 2 || elem.aSegsOut[0].cb != sizeof(VNETHDR))
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync {
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync Log(("%s vnetQueueTransmit: The first segment is not the header! (%u < 2 || %u != %u).\n",
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync INSTANCE(pState), elem.nOut, elem.aSegsOut[0].cb, sizeof(VNETHDR)));
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync break; /* For now we simply ignore the header, but it must be there anyway! */
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync }
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync else
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync {
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync STAM_PROFILE_ADV_START(&pState->StatTransmit, a);
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync /* Assemble a complete frame. */
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync for (unsigned int i = 1; i < elem.nOut && uOffset < VNET_MAX_FRAME_SIZE; i++)
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync {
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync unsigned int uSize = elem.aSegsOut[i].cb;
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync if (uSize > VNET_MAX_FRAME_SIZE - uOffset)
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync {
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync Log(("%s vnetQueueTransmit: Packet is too big (>64k), truncating...\n", INSTANCE(pState)));
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync uSize = VNET_MAX_FRAME_SIZE - uOffset;
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync }
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync PDMDevHlpPhysRead(pState->VPCI.CTX_SUFF(pDevIns), elem.aSegsOut[i].addr,
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync pState->pTxBuf + uOffset, uSize);
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync uOffset += uSize;
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync }
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync if (pState->pDrv)
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync {
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync vnetPacketDump(pState, pState->pTxBuf, uOffset, "--> Outgoing");
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync STAM_PROFILE_START(&pState->StatTransmitSend, a);
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync int rc = pState->pDrv->pfnSend(pState->pDrv, pState->pTxBuf, uOffset);
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync STAM_PROFILE_STOP(&pState->StatTransmitSend, a);
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync STAM_REL_COUNTER_ADD(&pState->StatTransmitBytes, uOffset);
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync }
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync }
1c2c968fd241148110002d75b2c0fdeddc211e14vboxsync vqueuePut(&pState->VPCI, pQueue, &elem, sizeof(VNETHDR) + uOffset);
vqueueSync(&pState->VPCI, pQueue);
STAM_PROFILE_ADV_STOP(&pState->StatTransmit, a);
}
vpciSetWriteLed(&pState->VPCI, false);
ASMAtomicWriteU32(&pState->uIsTransmitting, 0);
}
#ifdef VNET_TX_DELAY
static DECLCALLBACK(void) vnetQueueTransmit(void *pvState, PVQUEUE pQueue)
{
VNETSTATE *pState = (VNETSTATE*)pvState;
if (TMTimerIsActive(pState->CTX_SUFF(pTxTimer)))
{
int rc = TMTimerStop(pState->CTX_SUFF(pTxTimer));
Log3(("%s vnetQueueTransmit: Got kicked with notification disabled, "
"re-enable notification and flush TX queue\n", INSTANCE(pState)));
vnetTransmitPendingPackets(pState, pQueue);
if (RT_FAILURE(vnetCsEnter(pState, VERR_SEM_BUSY)))
LogRel(("vnetQueueTransmit: Failed to enter critical section!/n"));
else
{
vringSetNotification(&pState->VPCI, &pState->pTxQueue->VRing, true);
vnetCsLeave(pState);
}
}
else
{
if (RT_FAILURE(vnetCsEnter(pState, VERR_SEM_BUSY)))
LogRel(("vnetQueueTransmit: Failed to enter critical section!/n"));
else
{
vringSetNotification(&pState->VPCI, &pState->pTxQueue->VRing, false);
TMTimerSetMicro(pState->CTX_SUFF(pTxTimer), VNET_TX_DELAY);
pState->u64NanoTS = RTTimeNanoTS();
vnetCsLeave(pState);
}
}
}
/**
* Transmit Delay Timer handler.
*
* @remarks We only get here when the timer expires.
*
* @param pDevIns Pointer to device instance structure.
* @param pTimer Pointer to the timer.
* @param pvUser NULL.
* @thread EMT
*/
static DECLCALLBACK(void) vnetTxTimer(PPDMDEVINS pDevIns, PTMTIMER pTimer, void *pvUser)
{
VNETSTATE *pState = (VNETSTATE*)pvUser;
uint32_t u32MicroDiff = (uint32_t)((RTTimeNanoTS() - pState->u64NanoTS)/1000);
if (u32MicroDiff < pState->u32MinDiff)
pState->u32MinDiff = u32MicroDiff;
if (u32MicroDiff > pState->u32MaxDiff)
pState->u32MaxDiff = u32MicroDiff;
pState->u32AvgDiff = (pState->u32AvgDiff * pState->u32i + u32MicroDiff) / (pState->u32i + 1);
pState->u32i++;
Log3(("vnetTxTimer: Expired, diff %9d usec, avg %9d usec, min %9d usec, max %9d usec\n",
u32MicroDiff, pState->u32AvgDiff, pState->u32MinDiff, pState->u32MaxDiff));
// Log3(("%s vnetTxTimer: Expired\n", INSTANCE(pState)));
vnetTransmitPendingPackets(pState, pState->pTxQueue);
if (RT_FAILURE(vnetCsEnter(pState, VERR_SEM_BUSY)))
{
LogRel(("vnetTxTimer: Failed to enter critical section!/n"));
return;
}
vringSetNotification(&pState->VPCI, &pState->pTxQueue->VRing, true);
vnetCsLeave(pState);
}
#else /* !VNET_TX_DELAY */
static DECLCALLBACK(void) vnetQueueTransmit(void *pvState, PVQUEUE pQueue)
{
VNETSTATE *pState = (VNETSTATE*)pvState;
vnetTransmitPendingPackets(pState, pQueue);
}
#endif /* !VNET_TX_DELAY */
static uint8_t vnetControlRx(PVNETSTATE pState, PVNETCTLHDR pCtlHdr, PVQUEUEELEM pElem)
{
uint8_t u8Ack = VNET_OK;
uint8_t fOn;
PDMDevHlpPhysRead(pState->VPCI.CTX_SUFF(pDevIns),
pElem->aSegsOut[1].addr,
&fOn, sizeof(fOn));
Log(("%s vnetControlRx: uCommand=%u fOn=%u\n", INSTANCE(pState), pCtlHdr->u8Command, fOn));
switch (pCtlHdr->u8Command)
{
case VNET_CTRL_CMD_RX_MODE_PROMISC:
pState->fPromiscuous = !!fOn;
break;
case VNET_CTRL_CMD_RX_MODE_ALLMULTI:
pState->fAllMulti = !!fOn;
break;
default:
u8Ack = VNET_ERROR;
}
return u8Ack;
}
static uint8_t vnetControlMac(PVNETSTATE pState, PVNETCTLHDR pCtlHdr, PVQUEUEELEM pElem)
{
uint32_t nMacs = 0;
if (pCtlHdr->u8Command != VNET_CTRL_CMD_MAC_TABLE_SET
|| pElem->nOut != 3
|| pElem->aSegsOut[1].cb < sizeof(nMacs)
|| pElem->aSegsOut[2].cb < sizeof(nMacs))
{
Log(("%s vnetControlMac: Segment layout is wrong "
"(u8Command=%u nOut=%u cb1=%u cb2=%u)\n", INSTANCE(pState),
pCtlHdr->u8Command, pElem->nOut,
pElem->aSegsOut[1].cb, pElem->aSegsOut[2].cb));
return VNET_ERROR;
}
/* Load unicast addresses */
PDMDevHlpPhysRead(pState->VPCI.CTX_SUFF(pDevIns),
pElem->aSegsOut[1].addr,
&nMacs, sizeof(nMacs));
if (pElem->aSegsOut[1].cb < nMacs * sizeof(RTMAC) + sizeof(nMacs))
{
Log(("%s vnetControlMac: The unicast mac segment is too small "
"(nMacs=%u cb=%u)\n", INSTANCE(pState), pElem->aSegsOut[1].cb));
return VNET_ERROR;
}
if (nMacs > VNET_MAC_FILTER_LEN)
{
Log(("%s vnetControlMac: MAC table is too big, have to use promiscuous"
" mode (nMacs=%u)\n", INSTANCE(pState), nMacs));
pState->fPromiscuous = true;
}
else
{
if (nMacs)
PDMDevHlpPhysRead(pState->VPCI.CTX_SUFF(pDevIns),
pElem->aSegsOut[1].addr + sizeof(nMacs),
pState->aMacFilter, nMacs * sizeof(RTMAC));
pState->nMacFilterEntries = nMacs;
#ifdef DEBUG
Log(("%s vnetControlMac: unicast macs:\n", INSTANCE(pState)));
for(unsigned i = 0; i < nMacs; i++)
Log((" %RTmac\n", &pState->aMacFilter[i]));
#endif /* DEBUG */
}
/* Load multicast addresses */
PDMDevHlpPhysRead(pState->VPCI.CTX_SUFF(pDevIns),
pElem->aSegsOut[2].addr,
&nMacs, sizeof(nMacs));
if (pElem->aSegsOut[2].cb < nMacs * sizeof(RTMAC) + sizeof(nMacs))
{
Log(("%s vnetControlMac: The multicast mac segment is too small "
"(nMacs=%u cb=%u)\n", INSTANCE(pState), pElem->aSegsOut[2].cb));
return VNET_ERROR;
}
if (nMacs > VNET_MAC_FILTER_LEN - pState->nMacFilterEntries)
{
Log(("%s vnetControlMac: MAC table is too big, have to use allmulti"
" mode (nMacs=%u)\n", INSTANCE(pState), nMacs));
pState->fAllMulti = true;
}
else
{
if (nMacs)
PDMDevHlpPhysRead(pState->VPCI.CTX_SUFF(pDevIns),
pElem->aSegsOut[2].addr + sizeof(nMacs),
&pState->aMacFilter[pState->nMacFilterEntries],
nMacs * sizeof(RTMAC));
#ifdef DEBUG
Log(("%s vnetControlMac: multicast macs:\n", INSTANCE(pState)));
for(unsigned i = 0; i < nMacs; i++)
Log((" %RTmac\n",
&pState->aMacFilter[i+pState->nMacFilterEntries]));
#endif /* DEBUG */
pState->nMacFilterEntries += nMacs;
}
return VNET_OK;
}
static uint8_t vnetControlVlan(PVNETSTATE pState, PVNETCTLHDR pCtlHdr, PVQUEUEELEM pElem)
{
uint8_t u8Ack = VNET_OK;
uint16_t u16Vid;
if (pElem->nOut != 2 || pElem->aSegsOut[1].cb != sizeof(u16Vid))
{
Log(("%s vnetControlVlan: Segment layout is wrong "
"(u8Command=%u nOut=%u cb=%u)\n", INSTANCE(pState),
pCtlHdr->u8Command, pElem->nOut, pElem->aSegsOut[1].cb));
return VNET_ERROR;
}
PDMDevHlpPhysRead(pState->VPCI.CTX_SUFF(pDevIns),
pElem->aSegsOut[1].addr,
&u16Vid, sizeof(u16Vid));
if (u16Vid >= VNET_MAX_VID)
{
Log(("%s vnetControlVlan: VLAN ID is out of range "
"(VID=%u)\n", INSTANCE(pState), u16Vid));
return VNET_ERROR;
}
Log(("%s vnetControlVlan: uCommand=%u VID=%u\n", INSTANCE(pState),
pCtlHdr->u8Command, u16Vid));
switch (pCtlHdr->u8Command)
{
case VNET_CTRL_CMD_VLAN_ADD:
ASMBitSet(pState->aVlanFilter, u16Vid);
break;
case VNET_CTRL_CMD_VLAN_DEL:
ASMBitClear(pState->aVlanFilter, u16Vid);
break;
default:
u8Ack = VNET_ERROR;
}
return u8Ack;
}
static DECLCALLBACK(void) vnetQueueControl(void *pvState, PVQUEUE pQueue)
{
VNETSTATE *pState = (VNETSTATE*)pvState;
uint8_t u8Ack;
VQUEUEELEM elem;
while (vqueueGet(&pState->VPCI, pQueue, &elem))
{
unsigned int uOffset = 0;
if (elem.nOut < 1 || elem.aSegsOut[0].cb < sizeof(VNETCTLHDR))
{
Log(("%s vnetQueueControl: The first 'out' segment is not the "
"header! (%u < 1 || %u < %u).\n", INSTANCE(pState), elem.nOut,
elem.aSegsOut[0].cb,sizeof(VNETCTLHDR)));
break; /* Skip the element and hope the next one is good. */
}
else if ( elem.nIn < 1
|| elem.aSegsIn[elem.nIn - 1].cb < sizeof(VNETCTLACK))
{
Log(("%s vnetQueueControl: The last 'in' segment is too small "
"to hold the acknowledge! (%u < 1 || %u < %u).\n",
INSTANCE(pState), elem.nIn, elem.aSegsIn[elem.nIn - 1].cb,
sizeof(VNETCTLACK)));
break; /* Skip the element and hope the next one is good. */
}
else
{
VNETCTLHDR CtlHdr;
PDMDevHlpPhysRead(pState->VPCI.CTX_SUFF(pDevIns),
elem.aSegsOut[0].addr,
&CtlHdr, sizeof(CtlHdr));
switch (CtlHdr.u8Class)
{
case VNET_CTRL_CLS_RX_MODE:
u8Ack = vnetControlRx(pState, &CtlHdr, &elem);
break;
case VNET_CTRL_CLS_MAC:
u8Ack = vnetControlMac(pState, &CtlHdr, &elem);
break;
case VNET_CTRL_CLS_VLAN:
u8Ack = vnetControlVlan(pState, &CtlHdr, &elem);
break;
default:
u8Ack = VNET_ERROR;
}
Log(("%s Processed control message %u, ack=%u.\n", INSTANCE(pState),
CtlHdr.u8Class, u8Ack));
PDMDevHlpPhysWrite(pState->VPCI.CTX_SUFF(pDevIns),
elem.aSegsIn[elem.nIn - 1].addr,
&u8Ack, sizeof(u8Ack));
}
vqueuePut(&pState->VPCI, pQueue, &elem, sizeof(u8Ack));
vqueueSync(&pState->VPCI, pQueue);
}
}
/**
* Saves the configuration.
*
* @param pState The VNET state.
* @param pSSM The handle to the saved state.
*/
static void vnetSaveConfig(VNETSTATE *pState, PSSMHANDLE pSSM)
{
SSMR3PutMem(pSSM, &pState->macConfigured, sizeof(pState->macConfigured));
}
/**
* Live save - save basic configuration.
*
* @returns VBox status code.
* @param pDevIns The device instance.
* @param pSSM The handle to the saved state.
* @param uPass
*/
static DECLCALLBACK(int) vnetLiveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uPass)
{
VNETSTATE *pState = PDMINS_2_DATA(pDevIns, VNETSTATE*);
vnetSaveConfig(pState, pSSM);
return VINF_SSM_DONT_CALL_AGAIN;
}
/**
* Prepares for state saving.
*
* @returns VBox status code.
* @param pDevIns The device instance.
* @param pSSM The handle to the saved state.
*/
static DECLCALLBACK(int) vnetSavePrep(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
{
VNETSTATE* pState = PDMINS_2_DATA(pDevIns, VNETSTATE*);
int rc = vnetCsRxEnter(pState, VERR_SEM_BUSY);
if (RT_UNLIKELY(rc != VINF_SUCCESS))
return rc;
vnetCsRxLeave(pState);
return VINF_SUCCESS;
}
/**
* Saves the state of device.
*
* @returns VBox status code.
* @param pDevIns The device instance.
* @param pSSM The handle to the saved state.
*/
static DECLCALLBACK(int) vnetSaveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
{
VNETSTATE* pState = PDMINS_2_DATA(pDevIns, VNETSTATE*);
/* Save config first */
vnetSaveConfig(pState, pSSM);
/* Save the common part */
int rc = vpciSaveExec(&pState->VPCI, pSSM);
AssertRCReturn(rc, rc);
/* Save device-specific part */
rc = SSMR3PutMem( pSSM, pState->config.mac.au8, sizeof(pState->config.mac));
AssertRCReturn(rc, rc);
rc = SSMR3PutBool(pSSM, pState->fPromiscuous);
AssertRCReturn(rc, rc);
rc = SSMR3PutBool(pSSM, pState->fAllMulti);
AssertRCReturn(rc, rc);
rc = SSMR3PutU32( pSSM, pState->nMacFilterEntries);
AssertRCReturn(rc, rc);
rc = SSMR3PutMem( pSSM, pState->aMacFilter,
pState->nMacFilterEntries * sizeof(RTMAC));
AssertRCReturn(rc, rc);
rc = SSMR3PutMem( pSSM, pState->aVlanFilter, sizeof(pState->aVlanFilter));
AssertRCReturn(rc, rc);
Log(("%s State has been saved\n", INSTANCE(pState)));
return VINF_SUCCESS;
}
/**
* Serializes the receive thread, it may be working inside the critsect.
*
* @returns VBox status code.
* @param pDevIns The device instance.
* @param pSSM The handle to the saved state.
*/
static DECLCALLBACK(int) vnetLoadPrep(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
{
VNETSTATE* pState = PDMINS_2_DATA(pDevIns, VNETSTATE*);
int rc = vnetCsRxEnter(pState, VERR_SEM_BUSY);
if (RT_UNLIKELY(rc != VINF_SUCCESS))
return rc;
vnetCsRxLeave(pState);
return VINF_SUCCESS;
}
/* Takes down the link temporarily if it's current status is up.
*
* This is used during restore and when replumbing the network link.
*
* The temporary link outage is supposed to indicate to the OS that all network
* connections have been lost and that it for instance is appropriate to
* renegotiate any DHCP lease.
*
* @param pThis The PCNet instance data.
*/
static void vnetTempLinkDown(PVNETSTATE pState)
{
if (STATUS & VNET_S_LINK_UP)
{
STATUS &= ~VNET_S_LINK_UP;
vpciRaiseInterrupt(&pState->VPCI, VERR_SEM_BUSY, VPCI_ISR_CONFIG);
/* Restore the link back in 5 seconds. */
int rc = TMTimerSetMillies(pState->pLinkUpTimer, 5000);
AssertRC(rc);
}
}
/**
* Restore previously saved state of device.
*
* @returns VBox status code.
* @param pDevIns The device instance.
* @param pSSM The handle to the saved state.
* @param uVersion The data unit version number.
* @param uPass The data pass.
*/
static DECLCALLBACK(int) vnetLoadExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uVersion, uint32_t uPass)
{
VNETSTATE *pState = PDMINS_2_DATA(pDevIns, VNETSTATE*);
int rc;
/* config checks */
RTMAC macConfigured;
rc = SSMR3GetMem(pSSM, &macConfigured, sizeof(macConfigured));
AssertRCReturn(rc, rc);
if (memcmp(&macConfigured, &pState->macConfigured, sizeof(macConfigured))
&& (uPass == 0 || !PDMDevHlpVMTeleportedAndNotFullyResumedYet(pDevIns)))
LogRel(("%s: The mac address differs: config=%RTmac saved=%RTmac\n", INSTANCE(pState), &pState->macConfigured, &macConfigured));
rc = vpciLoadExec(&pState->VPCI, pSSM, uVersion, uPass, VNET_N_QUEUES);
AssertRCReturn(rc, rc);
if (uPass == SSM_PASS_FINAL)
{
rc = SSMR3GetMem( pSSM, pState->config.mac.au8,
sizeof(pState->config.mac));
AssertRCReturn(rc, rc);
if (uVersion > VIRTIO_SAVEDSTATE_VERSION_3_1_BETA1)
{
rc = SSMR3GetBool(pSSM, &pState->fPromiscuous);
AssertRCReturn(rc, rc);
rc = SSMR3GetBool(pSSM, &pState->fAllMulti);
AssertRCReturn(rc, rc);
rc = SSMR3GetU32(pSSM, &pState->nMacFilterEntries);
AssertRCReturn(rc, rc);
rc = SSMR3GetMem(pSSM, pState->aMacFilter,
pState->nMacFilterEntries * sizeof(RTMAC));
AssertRCReturn(rc, rc);
/* Clear the rest. */
if (pState->nMacFilterEntries < VNET_MAC_FILTER_LEN)
memset(&pState->aMacFilter[pState->nMacFilterEntries],
0,
(VNET_MAC_FILTER_LEN - pState->nMacFilterEntries)
* sizeof(RTMAC));
rc = SSMR3GetMem(pSSM, pState->aVlanFilter,
sizeof(pState->aVlanFilter));
AssertRCReturn(rc, rc);
}
else
{
pState->fPromiscuous = true;
pState->fAllMulti = false;
pState->nMacFilterEntries = 0;
memset(pState->aMacFilter, 0, VNET_MAC_FILTER_LEN * sizeof(RTMAC));
memset(pState->aVlanFilter, 0, sizeof(pState->aVlanFilter));
}
}
return rc;
}
/**
* Link status adjustments after loading.
*
* @returns VBox status code.
* @param pDevIns The device instance.
* @param pSSM The handle to the saved state.
*/
static DECLCALLBACK(int) vnetLoadDone(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
{
VNETSTATE *pState = PDMINS_2_DATA(pDevIns, VNETSTATE*);
/*
* Indicate link down to the guest OS that all network connections have
* been lost, unless we've been teleported here.
*/
if (!PDMDevHlpVMTeleportedAndNotFullyResumedYet(pDevIns))
vnetTempLinkDown(pState);
return VINF_SUCCESS;
}
/**
* Map PCI I/O region.
*
* @return VBox status code.
* @param pPciDev Pointer to PCI device. Use pPciDev->pDevIns to get the device instance.
* @param iRegion The region number.
* @param GCPhysAddress Physical address of the region. If iType is PCI_ADDRESS_SPACE_IO, this is an
* I/O port, else it's a physical address.
* This address is *NOT* relative to pci_mem_base like earlier!
* @param cb Region size.
* @param enmType One of the PCI_ADDRESS_SPACE_* values.
* @thread EMT
*/
static DECLCALLBACK(int) vnetMap(PPCIDEVICE pPciDev, int iRegion,
RTGCPHYS GCPhysAddress, uint32_t cb, PCIADDRESSSPACE enmType)
{
int rc;
VNETSTATE *pState = PDMINS_2_DATA(pPciDev->pDevIns, VNETSTATE*);
if (enmType != PCI_ADDRESS_SPACE_IO)
{
/* We should never get here */
AssertMsgFailed(("Invalid PCI address space param in map callback"));
return VERR_INTERNAL_ERROR;
}
pState->VPCI.addrIOPort = (RTIOPORT)GCPhysAddress;
rc = PDMDevHlpIOPortRegister(pPciDev->pDevIns, pState->VPCI.addrIOPort,
cb, 0, vnetIOPortOut, vnetIOPortIn,
NULL, NULL, "VirtioNet");
#ifdef VNET_GC_SUPPORT
AssertRCReturn(rc, rc);
rc = PDMDevHlpIOPortRegisterR0(pPciDev->pDevIns, pState->VPCI.addrIOPort,
cb, 0, "vnetIOPortOut", "vnetIOPortIn",
NULL, NULL, "VirtioNet");
AssertRCReturn(rc, rc);
rc = PDMDevHlpIOPortRegisterGC(pPciDev->pDevIns, pState->VPCI.addrIOPort,
cb, 0, "vnetIOPortOut", "vnetIOPortIn",
NULL, NULL, "VirtioNet");
#endif
AssertRC(rc);
return rc;
}
/**
* Construct a device instance for a VM.
*
* @returns VBox status.
* @param pDevIns The device instance data.
* If the registration structure is needed, pDevIns->pDevReg points to it.
* @param iInstance Instance number. Use this to figure out which registers and such to use.
* The device number is also found in pDevIns->iInstance, but since it's
* likely to be freqently used PDM passes it as parameter.
* @param pCfgHandle Configuration node handle for the device. Use this to obtain the configuration
* of the device instance. It's also found in pDevIns->pCfgHandle, but like
* iInstance it's expected to be used a bit in this function.
* @thread EMT
*/
static DECLCALLBACK(int) vnetConstruct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfgHandle)
{
VNETSTATE* pState = PDMINS_2_DATA(pDevIns, VNETSTATE*);
int rc;
/* Initialize PCI part first. */
pState->VPCI.IBase.pfnQueryInterface = vnetQueryInterface;
rc = vpciConstruct(pDevIns, &pState->VPCI, iInstance,
VNET_NAME_FMT, VNET_PCI_SUBSYSTEM_ID,
VNET_PCI_CLASS, VNET_N_QUEUES);
pState->pRxQueue = vpciAddQueue(&pState->VPCI, 256, vnetQueueReceive, "RX ");
pState->pTxQueue = vpciAddQueue(&pState->VPCI, 256, vnetQueueTransmit, "TX ");
pState->pCtlQueue = vpciAddQueue(&pState->VPCI, 16, vnetQueueControl, "CTL");
Log(("%s Constructing new instance\n", INSTANCE(pState)));
pState->hEventMoreRxDescAvail = NIL_RTSEMEVENT;
/*
* Validate configuration.
*/
if (!CFGMR3AreValuesValid(pCfgHandle, "MAC\0" "CableConnected\0" "LineSpeed\0"))
return PDMDEV_SET_ERROR(pDevIns, VERR_PDM_DEVINS_UNKNOWN_CFG_VALUES,
N_("Invalid configuration for VirtioNet device"));
/* Get config params */
rc = CFGMR3QueryBytes(pCfgHandle, "MAC", pState->macConfigured.au8,
sizeof(pState->macConfigured));
if (RT_FAILURE(rc))
return PDMDEV_SET_ERROR(pDevIns, rc,
N_("Configuration error: Failed to get MAC address"));
rc = CFGMR3QueryBool(pCfgHandle, "CableConnected", &pState->fCableConnected);
if (RT_FAILURE(rc))
return PDMDEV_SET_ERROR(pDevIns, rc,
N_("Configuration error: Failed to get the value of 'CableConnected'"));
/* Initialize PCI config space */
memcpy(pState->config.mac.au8, pState->macConfigured.au8, sizeof(pState->config.mac.au8));
pState->config.uStatus = 0;
/* Initialize state structure */
pState->u32PktNo = 1;
/* Interfaces */
pState->INetworkPort.pfnWaitReceiveAvail = vnetWaitReceiveAvail;
pState->INetworkPort.pfnReceive = vnetReceive;
pState->INetworkConfig.pfnGetMac = vnetGetMac;
pState->INetworkConfig.pfnGetLinkState = vnetGetLinkState;
pState->INetworkConfig.pfnSetLinkState = vnetSetLinkState;
pState->pTxBuf = (uint8_t *)RTMemAllocZ(VNET_MAX_FRAME_SIZE);
AssertMsgReturn(pState->pTxBuf,
("Cannot allocate TX buffer for virtio-net device\n"), VERR_NO_MEMORY);
/* Initialize critical section. */
// char szTmp[sizeof(pState->VPCI.szInstance) + 2];
// RTStrPrintf(szTmp, sizeof(szTmp), "%sRX", pState->VPCI.szInstance);
// rc = PDMDevHlpCritSectInit(pDevIns, &pState->csRx, szTmp);
// if (RT_FAILURE(rc))
// return rc;
/* Map our ports to IO space. */
rc = PDMDevHlpPCIIORegionRegister(pDevIns, 0,
VPCI_CONFIG + sizeof(VNetPCIConfig),
PCI_ADDRESS_SPACE_IO, vnetMap);
if (RT_FAILURE(rc))
return rc;
/* Register save/restore state handlers. */
rc = PDMDevHlpSSMRegisterEx(pDevIns, VIRTIO_SAVEDSTATE_VERSION, sizeof(VNETSTATE), NULL,
NULL, vnetLiveExec, NULL,
vnetSavePrep, vnetSaveExec, NULL,
vnetLoadPrep, vnetLoadExec, vnetLoadDone);
if (RT_FAILURE(rc))
return rc;
/* Create the RX notifier signaller. */
rc = PDMDevHlpPDMQueueCreate(pDevIns, sizeof(PDMQUEUEITEMCORE), 1, 0,
vnetCanRxQueueConsumer, true, "VNet-Rcv", &pState->pCanRxQueueR3);
if (RT_FAILURE(rc))
return rc;
pState->pCanRxQueueR0 = PDMQueueR0Ptr(pState->pCanRxQueueR3);
pState->pCanRxQueueRC = PDMQueueRCPtr(pState->pCanRxQueueR3);
/* Create Link Up Timer */
rc = PDMDevHlpTMTimerCreate(pDevIns, TMCLOCK_VIRTUAL, vnetLinkUpTimer, pState,
TMTIMER_FLAGS_DEFAULT_CRIT_SECT, /** @todo check locking here. */
"VirtioNet Link Up Timer", &pState->pLinkUpTimer);
if (RT_FAILURE(rc))
return rc;
#ifdef VNET_TX_DELAY
/* Create Transmit Delay Timer */
rc = PDMDevHlpTMTimerCreate(pDevIns, TMCLOCK_VIRTUAL, vnetTxTimer, pState,
TMTIMER_FLAGS_DEFAULT_CRIT_SECT, /** @todo check locking here. */
"VirtioNet TX Delay Timer", &pState->pTxTimerR3);
if (RT_FAILURE(rc))
return rc;
pState->pTxTimerR0 = TMTimerR0Ptr(pState->pTxTimerR3);
pState->pTxTimerRC = TMTimerRCPtr(pState->pTxTimerR3);
pState->u32i = pState->u32AvgDiff = pState->u32MaxDiff = 0;
pState->u32MinDiff = ~0;
#endif /* VNET_TX_DELAY */
rc = PDMDevHlpDriverAttach(pDevIns, 0, &pState->VPCI.IBase, &pState->pDrvBase, "Network Port");
if (RT_SUCCESS(rc))
{
if (rc == VINF_NAT_DNS)
{
PDMDevHlpVMSetRuntimeError(pDevIns, 0 /*fFlags*/, "NoDNSforNAT",
N_("A Domain Name Server (DNS) for NAT networking could not be determined. Ensure that your host is correctly connected to an ISP. If you ignore this warning the guest will not be able to perform nameserver lookups and it will probably observe delays if trying so"));
}
pState->pDrv = (PPDMINETWORKCONNECTOR)
pState->pDrvBase->pfnQueryInterface(pState->pDrvBase, PDMINTERFACE_NETWORK_CONNECTOR);
if (!pState->pDrv)
{
AssertMsgFailed(("%s Failed to obtain the PDMINTERFACE_NETWORK_CONNECTOR interface!\n"));
return VERR_PDM_MISSING_INTERFACE_BELOW;
}
}
else if (rc == VERR_PDM_NO_ATTACHED_DRIVER)
{
Log(("%s This adapter is not attached to any network!\n", INSTANCE(pState)));
}
else
return PDMDEV_SET_ERROR(pDevIns, rc, N_("Failed to attach the network LUN"));
rc = RTSemEventCreate(&pState->hEventMoreRxDescAvail);
if (RT_FAILURE(rc))
return rc;
vnetReset(pState);
PDMDevHlpSTAMRegisterF(pDevIns, &pState->StatReceiveBytes, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES, "Amount of data received", "/Devices/VNet%d/ReceiveBytes", iInstance);
PDMDevHlpSTAMRegisterF(pDevIns, &pState->StatTransmitBytes, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES, "Amount of data transmitted", "/Devices/VNet%d/TransmitBytes", iInstance);
#if defined(VBOX_WITH_STATISTICS)
PDMDevHlpSTAMRegisterF(pDevIns, &pState->StatReceive, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling receive", "/Devices/VNet%d/Receive/Total", iInstance);
PDMDevHlpSTAMRegisterF(pDevIns, &pState->StatReceiveStore, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling receive storing", "/Devices/VNet%d/Receive/Store", iInstance);
PDMDevHlpSTAMRegisterF(pDevIns, &pState->StatRxOverflow, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_OCCURENCE, "Profiling RX overflows", "/Devices/VNet%d/RxOverflow", iInstance);
PDMDevHlpSTAMRegisterF(pDevIns, &pState->StatRxOverflowWakeup, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, "Nr of RX overflow wakeups", "/Devices/VNet%d/RxOverflowWakeup", iInstance);
PDMDevHlpSTAMRegisterF(pDevIns, &pState->StatTransmit, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling transmits in HC", "/Devices/VNet%d/Transmit/Total", iInstance);
PDMDevHlpSTAMRegisterF(pDevIns, &pState->StatTransmitSend, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling send transmit in HC", "/Devices/VNet%d/Transmit/Send", iInstance);
#endif /* VBOX_WITH_STATISTICS */
return VINF_SUCCESS;
}
/**
* Destruct a device instance.
*
* We need to free non-VM resources only.
*
* @returns VBox status.
* @param pDevIns The device instance data.
* @thread EMT
*/
static DECLCALLBACK(int) vnetDestruct(PPDMDEVINS pDevIns)
{
VNETSTATE* pState = PDMINS_2_DATA(pDevIns, VNETSTATE*);
LogRel(("TxTimer stats (avg/min/max): %7d usec %7d usec %7d usec\n",
pState->u32AvgDiff, pState->u32MinDiff, pState->u32MaxDiff));
Log(("%s Destroying instance\n", INSTANCE(pState)));
if (pState->hEventMoreRxDescAvail != NIL_RTSEMEVENT)
{
RTSemEventSignal(pState->hEventMoreRxDescAvail);
RTSemEventDestroy(pState->hEventMoreRxDescAvail);
pState->hEventMoreRxDescAvail = NIL_RTSEMEVENT;
}
if (pState->pTxBuf)
{
RTMemFree(pState->pTxBuf);
pState->pTxBuf = NULL;
}
// if (PDMCritSectIsInitialized(&pState->csRx))
// PDMR3CritSectDelete(&pState->csRx);
return vpciDestruct(&pState->VPCI);
}
/**
* Device relocation callback.
*
* When this callback is called the device instance data, and if the
* device have a GC component, is being relocated, or/and the selectors
* have been changed. The device must use the chance to perform the
* necessary pointer relocations and data updates.
*
* Before the GC code is executed the first time, this function will be
* called with a 0 delta so GC pointer calculations can be one in one place.
*
* @param pDevIns Pointer to the device instance.
* @param offDelta The relocation delta relative to the old location.
*
* @remark A relocation CANNOT fail.
*/
static DECLCALLBACK(void) vnetRelocate(PPDMDEVINS pDevIns, RTGCINTPTR offDelta)
{
VNETSTATE* pState = PDMINS_2_DATA(pDevIns, VNETSTATE*);
vpciRelocate(pDevIns, offDelta);
pState->pCanRxQueueRC = PDMQueueRCPtr(pState->pCanRxQueueR3);
#ifdef VNET_TX_DELAY
pState->pTxTimerRC = TMTimerRCPtr(pState->pTxTimerR3);
#endif /* VNET_TX_DELAY */
// TBD
}
/**
* @copydoc FNPDMDEVSUSPEND
*/
static DECLCALLBACK(void) vnetSuspend(PPDMDEVINS pDevIns)
{
/* Poke thread waiting for buffer space. */
vnetWakeupReceive(pDevIns);
}
#ifdef VBOX_DYNAMIC_NET_ATTACH
/**
* Detach notification.
*
* One port on the network card has been disconnected from the network.
*
* @param pDevIns The device instance.
* @param iLUN The logical unit which is being detached.
* @param fFlags Flags, combination of the PDMDEVATT_FLAGS_* \#defines.
*/
static DECLCALLBACK(void) vnetDetach(PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags)
{
VNETSTATE *pState = PDMINS_2_DATA(pDevIns, VNETSTATE*);
Log(("%s vnetDetach:\n", INSTANCE(pState)));
AssertLogRelReturnVoid(iLUN == 0);
int rc = vnetCsEnter(pState, VERR_SEM_BUSY);
if (RT_FAILURE(rc))
{
LogRel(("vnetDetach failed to enter critical section!\n"));
return;
}
/*
* Zero some important members.
*/
pState->pDrvBase = NULL;
pState->pDrv = NULL;
vnetCsLeave(pState);
}
/**
* Attach the Network attachment.
*
* One port on the network card has been connected to a network.
*
* @returns VBox status code.
* @param pDevIns The device instance.
* @param iLUN The logical unit which is being attached.
* @param fFlags Flags, combination of the PDMDEVATT_FLAGS_* \#defines.
*
* @remarks This code path is not used during construction.
*/
static DECLCALLBACK(int) vnetAttach(PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags)
{
VNETSTATE *pState = PDMINS_2_DATA(pDevIns, VNETSTATE*);
LogFlow(("%s vnetAttach:\n", INSTANCE(pState)));
AssertLogRelReturn(iLUN == 0, VERR_PDM_NO_SUCH_LUN);
int rc = vnetCsEnter(pState, VERR_SEM_BUSY);
if (RT_FAILURE(rc))
{
LogRel(("vnetAttach failed to enter critical section!\n"));
return rc;
}
/*
* Attach the driver.
*/
rc = PDMDevHlpDriverAttach(pDevIns, 0, &pState->VPCI.IBase, &pState->pDrvBase, "Network Port");
if (RT_SUCCESS(rc))
{
if (rc == VINF_NAT_DNS)
{
#ifdef RT_OS_LINUX
PDMDevHlpVMSetRuntimeError(pDevIns, 0 /*fFlags*/, "NoDNSforNAT",
N_("A Domain Name Server (DNS) for NAT networking could not be determined. Please check your /etc/resolv.conf for <tt>nameserver</tt> entries. Either add one manually (<i>man resolv.conf</i>) or ensure that your host is correctly connected to an ISP. If you ignore this warning the guest will not be able to perform nameserver lookups and it will probably observe delays if trying so"));
#else
PDMDevHlpVMSetRuntimeError(pDevIns, 0 /*fFlags*/, "NoDNSforNAT",
N_("A Domain Name Server (DNS) for NAT networking could not be determined. Ensure that your host is correctly connected to an ISP. If you ignore this warning the guest will not be able to perform nameserver lookups and it will probably observe delays if trying so"));
#endif
}
pState->pDrv = (PPDMINETWORKCONNECTOR)pState->pDrvBase->pfnQueryInterface(pState->pDrvBase, PDMINTERFACE_NETWORK_CONNECTOR);
if (!pState->pDrv)
{
AssertMsgFailed(("Failed to obtain the PDMINTERFACE_NETWORK_CONNECTOR interface!\n"));
rc = VERR_PDM_MISSING_INTERFACE_BELOW;
}
}
else if (rc == VERR_PDM_NO_ATTACHED_DRIVER)
Log(("%s No attached driver!\n", INSTANCE(pState)));
/*
* Temporary set the link down if it was up so that the guest
* will know that we have change the configuration of the
* network card
*/
if (RT_SUCCESS(rc))
vnetTempLinkDown(pState);
vnetCsLeave(pState);
return rc;
}
#endif /* VBOX_DYNAMIC_NET_ATTACH */
/**
* @copydoc FNPDMDEVPOWEROFF
*/
static DECLCALLBACK(void) vnetPowerOff(PPDMDEVINS pDevIns)
{
/* Poke thread waiting for buffer space. */
vnetWakeupReceive(pDevIns);
}
/**
* The device registration structure.
*/
const PDMDEVREG g_DeviceVirtioNet =
{
/* Structure version. PDM_DEVREG_VERSION defines the current version. */
PDM_DEVREG_VERSION,
/* Device name. */
"virtio-net",
/* Name of guest context module (no path).
* Only evalutated if PDM_DEVREG_FLAGS_RC is set. */
"VBoxDDGC.gc",
/* Name of ring-0 module (no path).
* Only evalutated if PDM_DEVREG_FLAGS_RC is set. */
"VBoxDDR0.r0",
/* The description of the device. The UTF-8 string pointed to shall, like this structure,
* remain unchanged from registration till VM destruction. */
"Virtio Ethernet.\n",
/* Flags, combination of the PDM_DEVREG_FLAGS_* \#defines. */
#ifdef VNET_GC_SUPPORT
PDM_DEVREG_FLAGS_DEFAULT_BITS | PDM_DEVREG_FLAGS_RC | PDM_DEVREG_FLAGS_R0,
#else
PDM_DEVREG_FLAGS_DEFAULT_BITS,
#endif
/* Device class(es), combination of the PDM_DEVREG_CLASS_* \#defines. */
PDM_DEVREG_CLASS_NETWORK,
/* Maximum number of instances (per VM). */
8,
/* Size of the instance data. */
sizeof(VNETSTATE),
/* Construct instance - required. */
vnetConstruct,
/* Destruct instance - optional. */
vnetDestruct,
/* Relocation command - optional. */
vnetRelocate,
/* I/O Control interface - optional. */
NULL,
/* Power on notification - optional. */
NULL,
/* Reset notification - optional. */
NULL,
/* Suspend notification - optional. */
vnetSuspend,
/* Resume notification - optional. */
NULL,
#ifdef VBOX_DYNAMIC_NET_ATTACH
/* Attach command - optional. */
vnetAttach,
/* Detach notification - optional. */
vnetDetach,
#else /* !VBOX_DYNAMIC_NET_ATTACH */
/* Attach command - optional. */
NULL,
/* Detach notification - optional. */
NULL,
#endif /* !VBOX_DYNAMIC_NET_ATTACH */
/* Query a LUN base interface - optional. */
NULL,
/* Init complete notification - optional. */
NULL,
/* Power off notification - optional. */
vnetPowerOff,
/* pfnSoftReset */
NULL,
/* u32VersionEnd */
PDM_DEVREG_VERSION
};
#endif /* IN_RING3 */
#endif /* !VBOX_DEVICE_STRUCT_TESTCASE */