mirror of
https://github.com/arehnman/yttrium-virtio-gpu.git
synced 2026-10-31 02:05:04 +00:00
Update _tagPacketOffloadRequest enum. Signed-off-by: Kostiantyn Kostiuk <[email protected]>
2236 lines
80 KiB
C++
Executable File
2236 lines
80 KiB
C++
Executable File
/*
|
|
* This file contains NDIS driver procedures, common for NDIS5 and NDIS6
|
|
*
|
|
* Copyright (c) 2008-2017 Red Hat, Inc.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met :
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and / or other materials provided with the distribution.
|
|
* 3. Neither the names of the copyright holders nor the names of their contributors
|
|
* may be used to endorse or promote products derived from this software
|
|
* without specific prior written permission.
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
* ARE DISCLAIMED.IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
* SUCH DAMAGE.
|
|
*/
|
|
#include "ndis56common.h"
|
|
#include "virtio_net.h"
|
|
#include "virtio_ring.h"
|
|
#include "kdebugprint.h"
|
|
#include "ParaNdis_DebugHistory.h"
|
|
#include "Trace.h"
|
|
#ifdef NETKVM_WPP_ENABLED
|
|
#include "ParaNdis_Common.tmh"
|
|
#endif
|
|
|
|
static VOID ParaNdis_UpdateMAC(PARANDIS_ADAPTER *pContext);
|
|
|
|
static __inline pRxNetDescriptor ReceiveQueueGetBuffer(PPARANDIS_RECEIVE_QUEUE pQueue);
|
|
|
|
#define MAX_VLAN_ID 4094
|
|
|
|
/**********************************************************
|
|
Validates MAC address
|
|
Valid MAC address is not broadcast, not multicast, not empty
|
|
if bLocal is set, it must be LOCAL
|
|
if not, is must be non-local or local
|
|
Parameters:
|
|
PUCHAR pcMacAddress - MAC address to validate
|
|
BOOLEAN bLocal - TRUE, if we validate locally administered address
|
|
Return value:
|
|
TRUE if valid
|
|
***********************************************************/
|
|
static BOOLEAN ParaNdis_ValidateMacAddress(PUCHAR pcMacAddress, BOOLEAN bLocal)
|
|
{
|
|
BOOLEAN bLA = FALSE, bEmpty, bBroadcast, bMulticast = FALSE;
|
|
bBroadcast = ETH_IS_BROADCAST(pcMacAddress);
|
|
bLA = !bBroadcast && ETH_IS_LOCALLY_ADMINISTERED(pcMacAddress);
|
|
bMulticast = !bBroadcast && ETH_IS_MULTICAST(pcMacAddress);
|
|
bEmpty = ETH_IS_EMPTY(pcMacAddress);
|
|
return !bBroadcast && !bEmpty && !bMulticast && (!bLocal || bLA);
|
|
}
|
|
|
|
typedef struct _tagConfigurationEntry
|
|
{
|
|
const char *Name;
|
|
ULONG ulValue;
|
|
ULONG ulMinimal;
|
|
ULONG ulMaximal;
|
|
}tConfigurationEntry;
|
|
|
|
typedef struct _tagConfigurationEntries
|
|
{
|
|
tConfigurationEntry PhysicalMediaType;
|
|
tConfigurationEntry PrioritySupport;
|
|
tConfigurationEntry isLogEnabled;
|
|
tConfigurationEntry debugLevel;
|
|
tConfigurationEntry TxCapacity;
|
|
tConfigurationEntry RxCapacity;
|
|
tConfigurationEntry OffloadTxChecksum;
|
|
tConfigurationEntry OffloadTxLSO;
|
|
tConfigurationEntry OffloadRxCS;
|
|
tConfigurationEntry stdIpcsV4;
|
|
tConfigurationEntry stdTcpcsV4;
|
|
tConfigurationEntry stdTcpcsV6;
|
|
tConfigurationEntry stdUdpcsV4;
|
|
tConfigurationEntry stdUdpcsV6;
|
|
tConfigurationEntry stdLsoV1;
|
|
tConfigurationEntry stdLsoV2ip4;
|
|
tConfigurationEntry stdLsoV2ip6;
|
|
tConfigurationEntry PriorityVlanTagging;
|
|
tConfigurationEntry VlanId;
|
|
tConfigurationEntry JumboPacket;
|
|
tConfigurationEntry NumberOfHandledRXPacketsInDPC;
|
|
#if PARANDIS_SUPPORT_RSS
|
|
tConfigurationEntry RSSOffloadSupported;
|
|
tConfigurationEntry NumRSSQueues;
|
|
#endif
|
|
#if PARANDIS_SUPPORT_RSC
|
|
tConfigurationEntry RSCIPv4Supported;
|
|
tConfigurationEntry RSCIPv6Supported;
|
|
#endif
|
|
#if PARANDIS_SUPPORT_USO
|
|
tConfigurationEntry USOv4Supported;
|
|
tConfigurationEntry USOv6Supported;
|
|
#endif
|
|
}tConfigurationEntries;
|
|
|
|
static const tConfigurationEntries defaultConfiguration =
|
|
{
|
|
{ "*PhysicalMediaType", 0, 0, 0xff },
|
|
{ "Priority", 0, 0, 1 },
|
|
{ "DoLog", 1, 0, 1 },
|
|
{ "DebugLevel", 2, 0, 8 },
|
|
{ "TxCapacity", 1024, 16, 1024 },
|
|
{ "RxCapacity", 256, 32, 1024 },
|
|
{ "Offload.TxChecksum", 0, 0, 31},
|
|
{ "Offload.TxLSO", 0, 0, 2},
|
|
{ "Offload.RxCS", 0, 0, 31},
|
|
{ "*IPChecksumOffloadIPv4", 3, 0, 3 },
|
|
{ "*TCPChecksumOffloadIPv4",3, 0, 3 },
|
|
{ "*TCPChecksumOffloadIPv6",3, 0, 3 },
|
|
{ "*UDPChecksumOffloadIPv4",3, 0, 3 },
|
|
{ "*UDPChecksumOffloadIPv6",3, 0, 3 },
|
|
{ "*LsoV1IPv4", 1, 0, 1 },
|
|
{ "*LsoV2IPv4", 1, 0, 1 },
|
|
{ "*LsoV2IPv6", 1, 0, 1 },
|
|
{ "*PriorityVLANTag", 3, 0, 3},
|
|
{ "VlanId", 0, 0, MAX_VLAN_ID},
|
|
{ "*JumboPacket", 1514, 590, 65500},
|
|
{ "NumberOfHandledRXPacketsInDPC", MAX_RX_LOOPS, 1, 10000},
|
|
#if PARANDIS_SUPPORT_RSS
|
|
{ "*RSS", 1, 0, 1},
|
|
{ "*NumRssQueues", 16, 1, PARANDIS_RSS_MAX_RECEIVE_QUEUES},
|
|
#endif
|
|
#if PARANDIS_SUPPORT_RSC
|
|
{ "*RscIPv4", 1, 0, 1},
|
|
{ "*RscIPv6", 1, 0, 1},
|
|
#endif
|
|
#if PARANDIS_SUPPORT_USO
|
|
{ "*UsoIPv4", 1, 0, 1},
|
|
{ "*UsoIPv6", 1, 0, 1},
|
|
#endif
|
|
};
|
|
|
|
static void ParaNdis_ResetVirtIONetDevice(PARANDIS_ADAPTER *pContext)
|
|
{
|
|
virtio_device_reset(&pContext->IODevice);
|
|
DPrintf(0, "[%s] Done\n", __FUNCTION__);
|
|
|
|
KeMemoryBarrier();
|
|
|
|
pContext->bDeviceInitialized = FALSE;
|
|
|
|
/* reset all the features in the device */
|
|
pContext->ulCurrentVlansFilterSet = 0;
|
|
}
|
|
|
|
/**********************************************************
|
|
Gets integer value for specifies in pEntry->Name name
|
|
Parameters:
|
|
NDIS_HANDLE cfg previously open configuration
|
|
tConfigurationEntry *pEntry - Entry to fill value in
|
|
***********************************************************/
|
|
static void GetConfigurationEntry(NDIS_HANDLE cfg, tConfigurationEntry *pEntry)
|
|
{
|
|
NDIS_STATUS status;
|
|
const char *statusName;
|
|
NDIS_STRING name = {0};
|
|
PNDIS_CONFIGURATION_PARAMETER pParam = NULL;
|
|
NDIS_PARAMETER_TYPE ParameterType = NdisParameterInteger;
|
|
NdisInitializeString(&name, (PUCHAR)pEntry->Name);
|
|
NdisReadConfiguration(
|
|
&status,
|
|
&pParam,
|
|
cfg,
|
|
&name,
|
|
ParameterType);
|
|
if (status == NDIS_STATUS_SUCCESS)
|
|
{
|
|
ULONG ulValue = pParam->ParameterData.IntegerData;
|
|
if (ulValue >= pEntry->ulMinimal && ulValue <= pEntry->ulMaximal)
|
|
{
|
|
pEntry->ulValue = ulValue;
|
|
statusName = "value";
|
|
}
|
|
else
|
|
{
|
|
statusName = "out of range";
|
|
}
|
|
DPrintf(0, "[%s] %s read for %s - current value = 0x%x, input value = 0x%x",
|
|
__FUNCTION__,
|
|
statusName,
|
|
pEntry->Name,
|
|
pEntry->ulValue,
|
|
ulValue);
|
|
}
|
|
else
|
|
{
|
|
DPrintf(0, "[%s] nothing read for %s - current value = 0x%x",
|
|
__FUNCTION__,
|
|
pEntry->Name,
|
|
pEntry->ulValue);
|
|
}
|
|
if (name.Buffer) NdisFreeString(name);
|
|
}
|
|
|
|
static void DisableLSOv4Permanently(PARANDIS_ADAPTER *pContext, LPCSTR procname, LPCSTR reason)
|
|
{
|
|
if (pContext->Offload.flagsValue & osbT4Lso)
|
|
{
|
|
DPrintf(0, "[%s] Warning: %s", procname, reason);
|
|
pContext->Offload.flagsValue &= ~osbT4Lso;
|
|
ParaNdis_ResetOffloadSettings(pContext, NULL, NULL);
|
|
}
|
|
}
|
|
|
|
static void DisableLSOv6Permanently(PARANDIS_ADAPTER *pContext, LPCSTR procname, LPCSTR reason)
|
|
{
|
|
if (pContext->Offload.flagsValue & osbT6Lso)
|
|
{
|
|
DPrintf(0, "[%s] Warning: %s\n", procname, reason);
|
|
pContext->Offload.flagsValue &= ~osbT6Lso;
|
|
ParaNdis_ResetOffloadSettings(pContext, NULL, NULL);
|
|
}
|
|
}
|
|
|
|
/**********************************************************
|
|
Loads NIC parameters from adapter registry key
|
|
Parameters:
|
|
context
|
|
PUCHAR *ppNewMACAddress - pointer to hold MAC address if configured from host
|
|
***********************************************************/
|
|
static void ReadNicConfiguration(PARANDIS_ADAPTER *pContext, PUCHAR pNewMACAddress)
|
|
{
|
|
NDIS_HANDLE cfg;
|
|
tConfigurationEntries *pConfiguration = (tConfigurationEntries *) ParaNdis_AllocateMemory(pContext, sizeof(tConfigurationEntries));
|
|
if (pConfiguration)
|
|
{
|
|
*pConfiguration = defaultConfiguration;
|
|
cfg = ParaNdis_OpenNICConfiguration(pContext);
|
|
if (cfg)
|
|
{
|
|
GetConfigurationEntry(cfg, &pConfiguration->PhysicalMediaType);
|
|
GetConfigurationEntry(cfg, &pConfiguration->isLogEnabled);
|
|
GetConfigurationEntry(cfg, &pConfiguration->debugLevel);
|
|
GetConfigurationEntry(cfg, &pConfiguration->PrioritySupport);
|
|
GetConfigurationEntry(cfg, &pConfiguration->TxCapacity);
|
|
GetConfigurationEntry(cfg, &pConfiguration->RxCapacity);
|
|
GetConfigurationEntry(cfg, &pConfiguration->OffloadTxChecksum);
|
|
GetConfigurationEntry(cfg, &pConfiguration->OffloadTxLSO);
|
|
GetConfigurationEntry(cfg, &pConfiguration->OffloadRxCS);
|
|
GetConfigurationEntry(cfg, &pConfiguration->stdIpcsV4);
|
|
GetConfigurationEntry(cfg, &pConfiguration->stdTcpcsV4);
|
|
GetConfigurationEntry(cfg, &pConfiguration->stdTcpcsV6);
|
|
GetConfigurationEntry(cfg, &pConfiguration->stdUdpcsV4);
|
|
GetConfigurationEntry(cfg, &pConfiguration->stdUdpcsV6);
|
|
GetConfigurationEntry(cfg, &pConfiguration->stdLsoV1);
|
|
GetConfigurationEntry(cfg, &pConfiguration->stdLsoV2ip4);
|
|
GetConfigurationEntry(cfg, &pConfiguration->stdLsoV2ip6);
|
|
GetConfigurationEntry(cfg, &pConfiguration->PriorityVlanTagging);
|
|
GetConfigurationEntry(cfg, &pConfiguration->VlanId);
|
|
GetConfigurationEntry(cfg, &pConfiguration->JumboPacket);
|
|
GetConfigurationEntry(cfg, &pConfiguration->NumberOfHandledRXPacketsInDPC);
|
|
#if PARANDIS_SUPPORT_RSS
|
|
GetConfigurationEntry(cfg, &pConfiguration->RSSOffloadSupported);
|
|
GetConfigurationEntry(cfg, &pConfiguration->NumRSSQueues);
|
|
#endif
|
|
#if PARANDIS_SUPPORT_RSC
|
|
GetConfigurationEntry(cfg, &pConfiguration->RSCIPv4Supported);
|
|
GetConfigurationEntry(cfg, &pConfiguration->RSCIPv6Supported);
|
|
#endif
|
|
#if PARANDIS_SUPPORT_USO
|
|
GetConfigurationEntry(cfg, &pConfiguration->USOv4Supported);
|
|
GetConfigurationEntry(cfg, &pConfiguration->USOv6Supported);
|
|
#endif
|
|
|
|
bDebugPrint = pConfiguration->isLogEnabled.ulValue;
|
|
virtioDebugLevel = pConfiguration->debugLevel.ulValue;
|
|
pContext->physicalMediaType = (NDIS_PHYSICAL_MEDIUM)pConfiguration->PhysicalMediaType.ulValue;
|
|
pContext->maxFreeTxDescriptors = pConfiguration->TxCapacity.ulValue;
|
|
pContext->NetMaxReceiveBuffers = pConfiguration->RxCapacity.ulValue;
|
|
pContext->uNumberOfHandledRXPacketsInDPC = pConfiguration->NumberOfHandledRXPacketsInDPC.ulValue;
|
|
pContext->bDoSupportPriority = pConfiguration->PrioritySupport.ulValue != 0;
|
|
pContext->Offload.flagsValue = 0;
|
|
// TX caps: 1 - TCP, 2 - UDP, 4 - IP, 8 - TCPv6, 16 - UDPv6
|
|
if (pConfiguration->OffloadTxChecksum.ulValue & 1) pContext->Offload.flagsValue |= osbT4TcpChecksum | osbT4TcpOptionsChecksum;
|
|
if (pConfiguration->OffloadTxChecksum.ulValue & 2) pContext->Offload.flagsValue |= osbT4UdpChecksum;
|
|
if (pConfiguration->OffloadTxChecksum.ulValue & 4) pContext->Offload.flagsValue |= osbT4IpChecksum | osbT4IpOptionsChecksum;
|
|
if (pConfiguration->OffloadTxChecksum.ulValue & 8) pContext->Offload.flagsValue |= osbT6TcpChecksum | osbT6TcpOptionsChecksum;
|
|
if (pConfiguration->OffloadTxChecksum.ulValue & 16) pContext->Offload.flagsValue |= osbT6UdpChecksum;
|
|
if (pConfiguration->OffloadTxLSO.ulValue) pContext->Offload.flagsValue |= osbT4Lso | osbT4LsoIp | osbT4LsoTcp;
|
|
if (pConfiguration->OffloadTxLSO.ulValue > 1) pContext->Offload.flagsValue |= osbT6Lso | osbT6LsoTcpOptions;
|
|
// RX caps: 1 - TCP, 2 - UDP, 4 - IP, 8 - TCPv6, 16 - UDPv6
|
|
if (pConfiguration->OffloadRxCS.ulValue & 1) pContext->Offload.flagsValue |= osbT4RxTCPChecksum | osbT4RxTCPOptionsChecksum;
|
|
if (pConfiguration->OffloadRxCS.ulValue & 2) pContext->Offload.flagsValue |= osbT4RxUDPChecksum;
|
|
if (pConfiguration->OffloadRxCS.ulValue & 4) pContext->Offload.flagsValue |= osbT4RxIPChecksum | osbT4RxIPOptionsChecksum;
|
|
if (pConfiguration->OffloadRxCS.ulValue & 8) pContext->Offload.flagsValue |= osbT6RxTCPChecksum | osbT6RxTCPOptionsChecksum;
|
|
if (pConfiguration->OffloadRxCS.ulValue & 16) pContext->Offload.flagsValue |= osbT6RxUDPChecksum;
|
|
/* full packet size that can be configured as GSO for VIRTIO is short */
|
|
/* NDIS test fails sometimes fails on segments 50-60K */
|
|
pContext->Offload.maxPacketSize = PARANDIS_MAX_LSO_SIZE;
|
|
/* InitialOffloadParameters is used only internally */
|
|
pContext->InitialOffloadParameters.Header.Type = NDIS_OBJECT_TYPE_DEFAULT;
|
|
pContext->InitialOffloadParameters.Header.Revision = NDIS_OFFLOAD_PARAMETERS_REVISION_1;
|
|
pContext->InitialOffloadParameters.Header.Size = NDIS_SIZEOF_OFFLOAD_PARAMETERS_REVISION_1;
|
|
pContext->InitialOffloadParameters.IPv4Checksum = (UCHAR)pConfiguration->stdIpcsV4.ulValue;
|
|
pContext->InitialOffloadParameters.TCPIPv4Checksum = (UCHAR)pConfiguration->stdTcpcsV4.ulValue;
|
|
pContext->InitialOffloadParameters.TCPIPv6Checksum = (UCHAR)pConfiguration->stdTcpcsV6.ulValue;
|
|
pContext->InitialOffloadParameters.UDPIPv4Checksum = (UCHAR)pConfiguration->stdUdpcsV4.ulValue;
|
|
pContext->InitialOffloadParameters.UDPIPv6Checksum = (UCHAR)pConfiguration->stdUdpcsV6.ulValue;
|
|
pContext->InitialOffloadParameters.LsoV1 = (UCHAR)pConfiguration->stdLsoV1.ulValue;
|
|
pContext->InitialOffloadParameters.LsoV2IPv4 = (UCHAR)pConfiguration->stdLsoV2ip4.ulValue;
|
|
pContext->InitialOffloadParameters.LsoV2IPv6 = (UCHAR)pConfiguration->stdLsoV2ip6.ulValue;
|
|
#if PARANDIS_SUPPORT_USO
|
|
pContext->InitialOffloadParameters.Header.Revision = NDIS_OFFLOAD_PARAMETERS_REVISION_5;
|
|
pContext->InitialOffloadParameters.Header.Size = NDIS_SIZEOF_OFFLOAD_PARAMETERS_REVISION_5;
|
|
if (pConfiguration->USOv4Supported.ulValue)
|
|
{
|
|
// Uncomment later
|
|
// pContext->InitialOffloadParameters.UdpSegmentation.IPv4 = (UCHAR)pConfiguration->USOv4Supported.ulValue;
|
|
// pContext->Offload.flagsValue |= osbT4Uso;
|
|
}
|
|
if (pConfiguration->USOv6Supported.ulValue)
|
|
{
|
|
// Uncomment later
|
|
// pContext->InitialOffloadParameters.UdpSegmentation.IPv6 = (UCHAR)pConfiguration->USOv6Supported.ulValue;
|
|
// pContext->Offload.flagsValue |= osbT6Uso;
|
|
}
|
|
#endif
|
|
pContext->ulPriorityVlanSetting = pConfiguration->PriorityVlanTagging.ulValue;
|
|
pContext->VlanId = pConfiguration->VlanId.ulValue & 0xfff;
|
|
pContext->MaxPacketSize.nMaxDataSize = pConfiguration->JumboPacket.ulValue - ETH_HEADER_SIZE;
|
|
#if PARANDIS_SUPPORT_RSS
|
|
pContext->bRSSOffloadSupported = pConfiguration->RSSOffloadSupported.ulValue ? TRUE : FALSE;
|
|
pContext->RSSMaxQueuesNumber = (CCHAR) pConfiguration->NumRSSQueues.ulValue;
|
|
#endif
|
|
#if PARANDIS_SUPPORT_RSC
|
|
pContext->RSC.bIPv4SupportedSW = (UCHAR)pConfiguration->RSCIPv4Supported.ulValue;
|
|
pContext->RSC.bIPv6SupportedSW = (UCHAR)pConfiguration->RSCIPv6Supported.ulValue;
|
|
#endif
|
|
if (!pContext->bDoSupportPriority)
|
|
pContext->ulPriorityVlanSetting = 0;
|
|
// if Vlan not supported
|
|
if (!IsVlanSupported(pContext)) {
|
|
pContext->VlanId = 0;
|
|
}
|
|
|
|
{
|
|
NDIS_STATUS status;
|
|
PVOID p;
|
|
UINT len = 0;
|
|
NdisReadNetworkAddress(&status, &p, &len, cfg);
|
|
if (status == NDIS_STATUS_SUCCESS && len == ETH_ALEN)
|
|
{
|
|
NdisMoveMemory(pNewMACAddress, p, len);
|
|
}
|
|
else if (len && len != ETH_ALEN)
|
|
{
|
|
DPrintf(0, "[%s] MAC address has wrong length of %d\n", __FUNCTION__, len);
|
|
}
|
|
else
|
|
{
|
|
DPrintf(4, "[%s] Nothing read for MAC, error %X\n", __FUNCTION__, status);
|
|
}
|
|
}
|
|
NdisCloseConfiguration(cfg);
|
|
}
|
|
NdisFreeMemory(pConfiguration, 0, 0);
|
|
}
|
|
}
|
|
|
|
void ParaNdis_ResetOffloadSettings(PARANDIS_ADAPTER *pContext, tOffloadSettingsFlags *pDest, PULONG from)
|
|
{
|
|
if (!pDest) pDest = &pContext->Offload.flags;
|
|
if (!from) from = &pContext->Offload.flagsValue;
|
|
|
|
pDest->fTxIPChecksum = !!(*from & osbT4IpChecksum);
|
|
pDest->fTxTCPChecksum = !!(*from & osbT4TcpChecksum);
|
|
pDest->fTxUDPChecksum = !!(*from & osbT4UdpChecksum);
|
|
pDest->fTxTCPOptions = !!(*from & osbT4TcpOptionsChecksum);
|
|
pDest->fTxIPOptions = !!(*from & osbT4IpOptionsChecksum);
|
|
|
|
pDest->fTxLso = !!(*from & osbT4Lso);
|
|
pDest->fTxLsoIP = !!(*from & osbT4LsoIp);
|
|
pDest->fTxLsoTCP = !!(*from & osbT4LsoTcp);
|
|
|
|
pDest->fRxIPChecksum = !!(*from & osbT4RxIPChecksum);
|
|
pDest->fRxIPOptions = !!(*from & osbT4RxIPOptionsChecksum);
|
|
pDest->fRxTCPChecksum = !!(*from & osbT4RxTCPChecksum);
|
|
pDest->fRxTCPOptions = !!(*from & osbT4RxTCPOptionsChecksum);
|
|
pDest->fRxUDPChecksum = !!(*from & osbT4RxUDPChecksum);
|
|
|
|
pDest->fTxTCPv6Checksum = !!(*from & osbT6TcpChecksum);
|
|
pDest->fTxTCPv6Options = !!(*from & osbT6TcpOptionsChecksum);
|
|
pDest->fTxUDPv6Checksum = !!(*from & osbT6UdpChecksum);
|
|
pDest->fTxIPv6Ext = !!(*from & osbT6IpExtChecksum);
|
|
|
|
pDest->fTxLsov6 = !!(*from & osbT6Lso);
|
|
pDest->fTxLsov6IP = !!(*from & osbT6LsoIpExt);
|
|
pDest->fTxLsov6TCP = !!(*from & osbT6LsoTcpOptions);
|
|
|
|
pDest->fRxTCPv6Checksum = !!(*from & osbT6RxTCPChecksum);
|
|
pDest->fRxTCPv6Options = !!(*from & osbT6RxTCPOptionsChecksum);
|
|
pDest->fRxUDPv6Checksum = !!(*from & osbT6RxUDPChecksum);
|
|
pDest->fRxIPv6Ext = !!(*from & osbT6RxIpExtChecksum);
|
|
|
|
pDest->fUsov4 = !!(*from & osbT4Uso);
|
|
pDest->fUsov6 = !!(*from & osbT6Uso);
|
|
}
|
|
|
|
static void DumpVirtIOFeatures(PPARANDIS_ADAPTER pContext)
|
|
{
|
|
static const struct { ULONG bitmask; PCHAR Name; } Features[] =
|
|
{
|
|
|
|
{VIRTIO_NET_F_CSUM, "VIRTIO_NET_F_CSUM" },
|
|
{VIRTIO_NET_F_GUEST_CSUM, "VIRTIO_NET_F_GUEST_CSUM" },
|
|
{VIRTIO_NET_F_MTU, "VIRTIO_NET_F_MTU" },
|
|
{VIRTIO_NET_F_MAC, "VIRTIO_NET_F_MAC" },
|
|
{VIRTIO_NET_F_GSO, "VIRTIO_NET_F_GSO" },
|
|
{VIRTIO_NET_F_GUEST_TSO4, "VIRTIO_NET_F_GUEST_TSO4"},
|
|
{VIRTIO_NET_F_GUEST_TSO6, "VIRTIO_NET_F_GUEST_TSO6"},
|
|
{VIRTIO_NET_F_GUEST_ECN, "VIRTIO_NET_F_GUEST_ECN"},
|
|
{VIRTIO_NET_F_GUEST_UFO, "VIRTIO_NET_F_GUEST_UFO"},
|
|
{VIRTIO_NET_F_HOST_TSO4, "VIRTIO_NET_F_HOST_TSO4"},
|
|
{VIRTIO_NET_F_HOST_TSO6, "VIRTIO_NET_F_HOST_TSO6"},
|
|
{VIRTIO_NET_F_HOST_ECN, "VIRTIO_NET_F_HOST_ECN"},
|
|
{VIRTIO_NET_F_HOST_UFO, "VIRTIO_NET_F_HOST_UFO"},
|
|
{VIRTIO_NET_F_MRG_RXBUF, "VIRTIO_NET_F_MRG_RXBUF"},
|
|
{VIRTIO_NET_F_STATUS, "VIRTIO_NET_F_STATUS"},
|
|
{VIRTIO_NET_F_CTRL_VQ, "VIRTIO_NET_F_CTRL_VQ"},
|
|
{VIRTIO_NET_F_CTRL_RX, "VIRTIO_NET_F_CTRL_RX"},
|
|
{VIRTIO_NET_F_CTRL_VLAN, "VIRTIO_NET_F_CTRL_VLAN"},
|
|
{VIRTIO_NET_F_CTRL_RX_EXTRA, "VIRTIO_NET_F_CTRL_RX_EXTRA"},
|
|
{VIRTIO_NET_F_GUEST_ANNOUNCE, "VIRTIO_NET_F_GUEST_ANNOUNCE"},
|
|
{VIRTIO_NET_F_CTRL_MAC_ADDR, "VIRTIO_NET_F_CTRL_MAC_ADDR"},
|
|
{VIRTIO_NET_F_MQ, "VIRTIO_NET_F_MQ"},
|
|
{VIRTIO_RING_F_INDIRECT_DESC, "VIRTIO_RING_F_INDIRECT_DESC"},
|
|
{VIRTIO_F_ANY_LAYOUT, "VIRTIO_F_ANY_LAYOUT"},
|
|
{VIRTIO_RING_F_EVENT_IDX, "VIRTIO_RING_F_EVENT_IDX"},
|
|
{VIRTIO_F_VERSION_1, "VIRTIO_F_VERSION_1"},
|
|
{VIRTIO_F_RING_PACKED, "VIRTIO_F_RING_PACKED"},
|
|
{VIRTIO_F_ACCESS_PLATFORM, "VIRTIO_F_ACCESS_PLATFORM"},
|
|
{VIRTIO_NET_F_CTRL_GUEST_OFFLOADS, "VIRTIO_NET_F_CTRL_GUEST_OFFLOADS" },
|
|
{VIRTIO_NET_F_RSC_EXT, "VIRTIO_NET_F_RSC_EXT" },
|
|
{VIRTIO_NET_F_RSS, "VIRTIO_NET_F_RSS" },
|
|
{VIRTIO_NET_F_HASH_REPORT, "VIRTIO_NET_F_HASH_REPORT" },
|
|
{VIRTIO_NET_F_STANDBY, "VIRTIO_NET_F_STANDBY" },
|
|
};
|
|
UINT i;
|
|
for (i = 0; i < sizeof(Features)/sizeof(Features[0]); ++i)
|
|
{
|
|
if (virtio_is_feature_enabled(pContext->u64HostFeatures, Features[i].bitmask))
|
|
{
|
|
DPrintf(0, "VirtIO Host Feature %s\n", Features[i].Name);
|
|
}
|
|
}
|
|
}
|
|
|
|
static BOOLEAN
|
|
AckFeature(PPARANDIS_ADAPTER pContext, UINT64 Feature)
|
|
{
|
|
if (virtio_is_feature_enabled(pContext->u64HostFeatures, Feature))
|
|
{
|
|
virtio_feature_enable(pContext->u64GuestFeatures, Feature);
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
/**********************************************************
|
|
Prints out statistics
|
|
***********************************************************/
|
|
static void PrintStatistics(PARANDIS_ADAPTER *pContext)
|
|
{
|
|
ULONG64 totalTxFrames =
|
|
pContext->Statistics.ifHCOutBroadcastPkts +
|
|
pContext->Statistics.ifHCOutMulticastPkts +
|
|
pContext->Statistics.ifHCOutUcastPkts;
|
|
ULONG64 totalRxFrames =
|
|
pContext->Statistics.ifHCInBroadcastPkts +
|
|
pContext->Statistics.ifHCInMulticastPkts +
|
|
pContext->Statistics.ifHCInUcastPkts;
|
|
|
|
#if 0 /* TODO - setup accessor functions*/
|
|
DPrintf(0, "[Diag!%X] RX buffers at VIRTIO %d of %d\n",
|
|
pContext->CurrentMacAddress[5],
|
|
pContext->RXPath.m_NetNofReceiveBuffers,
|
|
pContext->NetMaxReceiveBuffers);
|
|
|
|
DPrintf(0, "[Diag!] TX desc available %d/%d, buf %d\n",
|
|
pContext->TXPath.GetFreeTXDescriptors(),
|
|
pContext->maxFreeTxDescriptors,
|
|
pContext->TXPath.GetFreeHWBuffers());
|
|
#endif
|
|
DPrintf(0, "[Diag!] Bytes transmitted %I64u, received %I64u\n",
|
|
pContext->Statistics.ifHCOutOctets,
|
|
pContext->Statistics.ifHCInOctets);
|
|
DPrintf(0, "[Diag!] Tx frames %I64u, CSO %d, LSO %d\n",
|
|
totalTxFrames,
|
|
pContext->extraStatistics.framesCSOffload,
|
|
pContext->extraStatistics.framesLSO);
|
|
DPrintf(0, "[Diag!] Rx frames %I64u, Rx.Pri %d, RxHwCS.OK %d, FiltOut %d\n",
|
|
totalRxFrames, pContext->extraStatistics.framesRxPriority,
|
|
pContext->extraStatistics.framesRxCSHwOK, pContext->extraStatistics.framesFilteredOut);
|
|
}
|
|
|
|
static
|
|
VOID InitializeRSCState(PPARANDIS_ADAPTER pContext)
|
|
{
|
|
#if PARANDIS_SUPPORT_RSC
|
|
|
|
pContext->RSC.bIPv4Enabled = FALSE;
|
|
pContext->RSC.bIPv6Enabled = FALSE;
|
|
|
|
if(!pContext->bGuestChecksumSupported)
|
|
{
|
|
DPrintf(0, "[%s] Guest TSO cannot be enabled without guest checksum\n", __FUNCTION__);
|
|
return;
|
|
}
|
|
|
|
BOOLEAN bDynamicOffloadsPossible = pContext->bControlQueueSupported &&
|
|
AckFeature(pContext, VIRTIO_NET_F_CTRL_GUEST_OFFLOADS);
|
|
BOOLEAN bQemuRscSupport = bDynamicOffloadsPossible && AckFeature(pContext, VIRTIO_NET_F_RSC_EXT);
|
|
|
|
if(pContext->RSC.bIPv4SupportedSW)
|
|
{
|
|
pContext->RSC.bIPv4Enabled =
|
|
pContext->RSC.bIPv4SupportedHW =
|
|
AckFeature(pContext, VIRTIO_NET_F_GUEST_TSO4);
|
|
}
|
|
else
|
|
{
|
|
pContext->RSC.bIPv4SupportedHW =
|
|
virtio_is_feature_enabled(pContext->u64HostFeatures, VIRTIO_NET_F_GUEST_TSO4);
|
|
}
|
|
|
|
if(pContext->RSC.bIPv6SupportedSW)
|
|
{
|
|
pContext->RSC.bIPv6Enabled =
|
|
pContext->RSC.bIPv6SupportedHW =
|
|
AckFeature(pContext, VIRTIO_NET_F_GUEST_TSO6);
|
|
}
|
|
else
|
|
{
|
|
pContext->RSC.bIPv6SupportedHW =
|
|
virtio_is_feature_enabled(pContext->u64HostFeatures, VIRTIO_NET_F_GUEST_TSO6);
|
|
}
|
|
|
|
pContext->RSC.bHasDynamicConfig = bDynamicOffloadsPossible;
|
|
pContext->RSC.bQemuSupported = bQemuRscSupport;
|
|
|
|
DPrintf(0, "[%s] Guest TSO state: IP4=%d, IP6=%d, Dynamic=%d\n", __FUNCTION__,
|
|
pContext->RSC.bIPv4Enabled, pContext->RSC.bIPv6Enabled, pContext->RSC.bHasDynamicConfig);
|
|
|
|
DPrintf(0, "[%s] Guest QEMU RSC support state: %sresent\n", __FUNCTION__,
|
|
pContext->RSC.bQemuSupported ? "P" : "Not p");
|
|
#else
|
|
UNREFERENCED_PARAMETER(pContext);
|
|
#endif
|
|
}
|
|
|
|
static void
|
|
InitializeMaxMTUConfig(PPARANDIS_ADAPTER pContext)
|
|
{
|
|
pContext->bMaxMTUConfigSupported = AckFeature(pContext, VIRTIO_NET_F_MTU);
|
|
|
|
if (pContext->bMaxMTUConfigSupported)
|
|
{
|
|
virtio_get_config(
|
|
&pContext->IODevice,
|
|
ETH_ALEN + 2 * sizeof(USHORT),
|
|
&pContext->MaxPacketSize.nMaxDataSize,
|
|
sizeof(USHORT));
|
|
}
|
|
}
|
|
|
|
static void
|
|
InitializeLinkPropertiesConfig(PPARANDIS_ADAPTER pContext)
|
|
{
|
|
INT32 speed;
|
|
UINT8 duplexState;
|
|
|
|
const char *MediaDuplexStates[] =
|
|
{
|
|
"unknown",
|
|
"half",
|
|
"full"
|
|
};
|
|
|
|
pContext->bLinkPropertiesConfigSupported = AckFeature(pContext, VIRTIO_NET_F_SPEED_DUPLEX);
|
|
|
|
// Default link properties
|
|
pContext->LinkProperties.Speed = PARANDIS_DEFAULT_LINK_SPEED;
|
|
pContext->LinkProperties.DuplexState = MediaDuplexStateFull;
|
|
|
|
if (pContext->bLinkPropertiesConfigSupported)
|
|
{
|
|
virtio_get_config(
|
|
&pContext->IODevice,
|
|
FIELD_OFFSET(virtio_net_config, speed),
|
|
&speed,
|
|
sizeof(__u32));
|
|
virtio_get_config(
|
|
&pContext->IODevice,
|
|
FIELD_OFFSET(virtio_net_config, duplex),
|
|
&duplexState,
|
|
sizeof(__u8));
|
|
|
|
DPrintf(0, "[%s] Link properties in virtio configuration: %d:%d\n",
|
|
__FUNCTION__, speed, duplexState);
|
|
|
|
if (speed != VIRTIO_NET_SPEED_UNKNOWN && speed)
|
|
{
|
|
pContext->LinkProperties.Speed = ((ULONGLONG)speed) * 1000000;
|
|
}
|
|
|
|
if (duplexState == VIRTIO_NET_DUPLEX_HALF)
|
|
{
|
|
pContext->LinkProperties.DuplexState = MediaDuplexStateHalf;
|
|
}
|
|
else if (duplexState == VIRTIO_NET_DUPLEX_FULL)
|
|
{
|
|
pContext->LinkProperties.DuplexState = MediaDuplexStateFull;
|
|
}
|
|
|
|
}
|
|
|
|
DPrintf(0, "[%s] Speed=%llu Bit/s, Duplex=%s\n", __FUNCTION__,
|
|
pContext->LinkProperties.Speed, MediaDuplexStates[pContext->LinkProperties.DuplexState]);
|
|
}
|
|
|
|
|
|
static __inline void
|
|
DumpMac(int dbg_level, const char* calling_function, const char* header_str, UCHAR* mac)
|
|
{
|
|
DPrintf(dbg_level,"[%s] - %s: %02x-%02x-%02x-%02x-%02x-%02x\n",
|
|
calling_function, header_str, mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
|
|
|
|
}
|
|
|
|
static __inline void
|
|
SetDeviceMAC(PPARANDIS_ADAPTER pContext, PUCHAR pDeviceMAC)
|
|
{
|
|
if (pContext->bCfgMACAddrSupported &&
|
|
!pContext->bCtrlMACAddrSupported &&
|
|
!virtio_is_feature_enabled(pContext->u64HostFeatures, VIRTIO_F_VERSION_1))
|
|
{
|
|
virtio_set_config(
|
|
&pContext->IODevice,
|
|
0,
|
|
pDeviceMAC,
|
|
ETH_ALEN);
|
|
}
|
|
}
|
|
|
|
static void
|
|
InitializeMAC(PPARANDIS_ADAPTER pContext, PUCHAR pCurrentMAC)
|
|
{
|
|
//Acknowledge related features
|
|
pContext->bCfgMACAddrSupported = AckFeature(pContext, VIRTIO_NET_F_MAC);
|
|
pContext->bCtrlMACAddrSupported = pContext->bControlQueueSupported && AckFeature(pContext, VIRTIO_NET_F_CTRL_MAC_ADDR);
|
|
DPrintf(0, "[%s] - MAC address configuration options: configuration space %d, control queue %d", __FUNCTION__, pContext->bCfgMACAddrSupported, pContext->bCtrlMACAddrSupported);
|
|
|
|
//Read and validate permanent MAC address
|
|
if (pContext->bCfgMACAddrSupported)
|
|
{
|
|
virtio_get_config(
|
|
&pContext->IODevice,
|
|
0,
|
|
&pContext->PermanentMacAddress,
|
|
ETH_ALEN);
|
|
|
|
if (!ParaNdis_ValidateMacAddress(pContext->PermanentMacAddress, FALSE))
|
|
{
|
|
DumpMac(0, __FUNCTION__, "Invalid device MAC ignored", pContext->PermanentMacAddress);
|
|
NdisZeroMemory(pContext->PermanentMacAddress, sizeof(pContext->PermanentMacAddress));
|
|
}
|
|
}
|
|
|
|
if (ETH_IS_EMPTY(pContext->PermanentMacAddress))
|
|
{
|
|
pContext->PermanentMacAddress[0] = 0x02;
|
|
pContext->PermanentMacAddress[1] = 0x50;
|
|
pContext->PermanentMacAddress[2] = 0xF2;
|
|
pContext->PermanentMacAddress[3] = 0x00;
|
|
pContext->PermanentMacAddress[4] = 0x01;
|
|
pContext->PermanentMacAddress[5] = 0x80 | (UCHAR)(pContext->ulUniqueID & 0xFF);
|
|
DumpMac(0, __FUNCTION__, "No device MAC present, use default", pContext->PermanentMacAddress);
|
|
}
|
|
DumpMac(0, __FUNCTION__, "Permanent device MAC", pContext->PermanentMacAddress);
|
|
|
|
//In virtio 1.0 MAC in configuration space is read only
|
|
if (pContext->bCtrlMACAddrSupported ||
|
|
!virtio_is_feature_enabled(pContext->u64HostFeatures, VIRTIO_F_VERSION_1))
|
|
{
|
|
//Read and validate configured MAC address.
|
|
if (ParaNdis_ValidateMacAddress(pCurrentMAC, TRUE))
|
|
{
|
|
DPrintf(0, "[%s] MAC address from configuration used\n", __FUNCTION__);
|
|
ETH_COPY_NETWORK_ADDRESS(pContext->CurrentMacAddress, pCurrentMAC);
|
|
}
|
|
else
|
|
{
|
|
DPrintf(0, "[%s] - No valid MAC configured\n", __FUNCTION__);
|
|
ETH_COPY_NETWORK_ADDRESS(pContext->CurrentMacAddress, pContext->PermanentMacAddress);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
DPrintf(0, "[%s] MAC address from configuration will not be used. Setting MAC address through control queue is disabled.\n", __FUNCTION__);
|
|
ETH_COPY_NETWORK_ADDRESS(pContext->CurrentMacAddress, pContext->PermanentMacAddress);
|
|
}
|
|
|
|
//If control channel message for MAC address configuration is not supported
|
|
// Configure device with actual MAC address via configurations space
|
|
//Else actual MAC address will be configured later via control queue
|
|
SetDeviceMAC(pContext, pContext->CurrentMacAddress);
|
|
|
|
DumpMac(0, __FUNCTION__, "Actual MAC", pContext->CurrentMacAddress);
|
|
}
|
|
|
|
static __inline void
|
|
RestoreMAC(PPARANDIS_ADAPTER pContext)
|
|
{
|
|
SetDeviceMAC(pContext, pContext->PermanentMacAddress);
|
|
}
|
|
|
|
static NDIS_STATUS NTStatusToNdisStatus(NTSTATUS nt_status) {
|
|
switch (nt_status) {
|
|
case STATUS_SUCCESS:
|
|
return NDIS_STATUS_SUCCESS;
|
|
case STATUS_NOT_FOUND:
|
|
case STATUS_DEVICE_NOT_CONNECTED:
|
|
return NDIS_STATUS_ADAPTER_NOT_FOUND;
|
|
case STATUS_INSUFFICIENT_RESOURCES:
|
|
return NDIS_STATUS_RESOURCES;
|
|
case STATUS_INVALID_PARAMETER:
|
|
return NDIS_STATUS_INVALID_DEVICE_REQUEST;
|
|
default:
|
|
return NDIS_STATUS_FAILURE;
|
|
}
|
|
}
|
|
|
|
static void ReadLinkState(PARANDIS_ADAPTER *pContext)
|
|
{
|
|
if (pContext->bLinkDetectSupported)
|
|
{
|
|
USHORT linkStatus = 0;
|
|
virtio_get_config(&pContext->IODevice, ETH_ALEN, &linkStatus, sizeof(linkStatus));
|
|
pContext->bConnected = !!(linkStatus & VIRTIO_NET_S_LINK_UP);
|
|
pContext->bGuestAnnounced = !!(linkStatus & VIRTIO_NET_S_ANNOUNCE);
|
|
}
|
|
else
|
|
{
|
|
pContext->bConnected = TRUE;
|
|
}
|
|
}
|
|
|
|
/**********************************************************
|
|
Initializes the context structure
|
|
Major variables, received from NDIS on initialization, must be be set before this call
|
|
(for ex. pContext->MiniportHandle)
|
|
|
|
If this procedure fails, it is safe to call ParaNdis_CleanupContext
|
|
|
|
Parameters:
|
|
Return value:
|
|
SUCCESS, if resources are OK
|
|
NDIS_STATUS_RESOURCE_CONFLICT if not
|
|
***********************************************************/
|
|
NDIS_STATUS ParaNdis_InitializeContext(
|
|
PARANDIS_ADAPTER *pContext,
|
|
PNDIS_RESOURCE_LIST pResourceList)
|
|
{
|
|
NDIS_STATUS status = NDIS_STATUS_SUCCESS;
|
|
UCHAR CurrentMAC[ETH_ALEN] = {0};
|
|
ULONG dependentOptions;
|
|
|
|
DEBUG_ENTRY(0);
|
|
|
|
if (pContext->RSSParameters.FailedInitialization)
|
|
{
|
|
return NDIS_STATUS_RESOURCES;
|
|
}
|
|
|
|
ReadNicConfiguration(pContext, CurrentMAC);
|
|
|
|
pContext->fCurrentLinkState = MediaConnectStateUnknown;
|
|
|
|
if (pContext->PciResources.Init(pContext->MiniportHandle, pResourceList))
|
|
{
|
|
if (pContext->PciResources.GetInterruptFlags() & CM_RESOURCE_INTERRUPT_MESSAGE)
|
|
{
|
|
DPrintf(0, "[%s] Message interrupt assigned\n", __FUNCTION__);
|
|
pContext->bUsingMSIX = TRUE;
|
|
}
|
|
else
|
|
{
|
|
pContext->bSharedVectors = TRUE;
|
|
}
|
|
|
|
NTSTATUS nt_status = virtio_device_initialize(
|
|
&pContext->IODevice,
|
|
&ParaNdisSystemOps,
|
|
pContext,
|
|
pContext->bUsingMSIX ? true : false);
|
|
if (!NT_SUCCESS(nt_status))
|
|
{
|
|
DPrintf(0, "[%s] virtio_device_initialize failed with %x\n", __FUNCTION__, nt_status);
|
|
status = NTStatusToNdisStatus(nt_status);
|
|
DEBUG_EXIT_STATUS(0, status);
|
|
return status;
|
|
}
|
|
|
|
pContext->u64HostFeatures = virtio_get_features(&pContext->IODevice);
|
|
DumpVirtIOFeatures(pContext);
|
|
|
|
// Enable VIRTIO_F_ACCESS_PLATFORM feature on Windows 10 and Windows Server 2016
|
|
#if (WINVER == 0x0A00)
|
|
AckFeature(pContext, VIRTIO_F_ACCESS_PLATFORM);
|
|
#endif
|
|
pContext->bSuppressLinkUp = AckFeature(pContext, VIRTIO_NET_F_STANDBY);
|
|
|
|
pContext->bLinkDetectSupported = AckFeature(pContext, VIRTIO_NET_F_STATUS);
|
|
ReadLinkState(pContext);
|
|
DPrintf(0, "[%s] Link status on driver startup: %d\n", __FUNCTION__, pContext->bConnected);
|
|
pContext->bGuestAnnounced = false;
|
|
ParaNdis_SynchronizeLinkState(pContext, false);
|
|
|
|
InitializeLinkPropertiesConfig(pContext);
|
|
pContext->bControlQueueSupported = AckFeature(pContext, VIRTIO_NET_F_CTRL_VQ);
|
|
pContext->bGuestAnnounceSupported = pContext->bLinkDetectSupported && pContext->bControlQueueSupported && AckFeature(pContext, VIRTIO_NET_F_GUEST_ANNOUNCE);
|
|
InitializeMAC(pContext, CurrentMAC);
|
|
InitializeMaxMTUConfig(pContext);
|
|
|
|
pContext->bUseMergedBuffers = AckFeature(pContext, VIRTIO_NET_F_MRG_RXBUF);
|
|
pContext->nVirtioHeaderSize = (pContext->bUseMergedBuffers) ? sizeof(virtio_net_hdr_mrg_rxbuf) : sizeof(virtio_net_hdr);
|
|
AckFeature(pContext, VIRTIO_RING_F_EVENT_IDX);
|
|
}
|
|
else
|
|
{
|
|
DPrintf(0, "[%s] Error: Incomplete resources\n", __FUNCTION__);
|
|
status = NDIS_STATUS_RESOURCE_CONFLICT;
|
|
return status;
|
|
}
|
|
|
|
pContext->MaxPacketSize.nMaxFullSizeOS = pContext->MaxPacketSize.nMaxDataSize + ETH_HEADER_SIZE;
|
|
pContext->MaxPacketSize.nMaxFullSizeHwTx = pContext->MaxPacketSize.nMaxFullSizeOS;
|
|
#if PARANDIS_SUPPORT_RSC
|
|
pContext->MaxPacketSize.nMaxDataSizeHwRx = MAX_HW_RX_PACKET_SIZE;
|
|
pContext->MaxPacketSize.nMaxFullSizeOsRx = MAX_OS_RX_PACKET_SIZE;
|
|
#else
|
|
pContext->MaxPacketSize.nMaxDataSizeHwRx = pContext->MaxPacketSize.nMaxFullSizeOS + ETH_PRIORITY_HEADER_SIZE;
|
|
pContext->MaxPacketSize.nMaxFullSizeOsRx = pContext->MaxPacketSize.nMaxFullSizeOS;
|
|
#endif
|
|
if (pContext->ulPriorityVlanSetting)
|
|
pContext->MaxPacketSize.nMaxFullSizeHwTx = pContext->MaxPacketSize.nMaxFullSizeOS + ETH_PRIORITY_HEADER_SIZE;
|
|
|
|
#if PARANDIS_SUPPORT_RSS
|
|
pContext->bMultiQueue = pContext->bControlQueueSupported && AckFeature(pContext, VIRTIO_NET_F_MQ);
|
|
bool bRSS = virtio_is_feature_enabled(pContext->u64HostFeatures, VIRTIO_NET_F_RSS);
|
|
bool bHash = virtio_is_feature_enabled(pContext->u64HostFeatures, VIRTIO_NET_F_HASH_REPORT);
|
|
|
|
if ((bHash || bRSS) && pContext->bControlQueueSupported && virtio_is_feature_enabled(pContext->u64HostFeatures, VIRTIO_F_VERSION_1))
|
|
{
|
|
struct virtio_net_config cfg = {};
|
|
virtio_get_config(&pContext->IODevice, FIELD_OFFSET(struct virtio_net_config, duplex), &cfg.duplex, 8);
|
|
pContext->DeviceRSSCapabilities.SupportedHashes = cfg.supported_hash_types;
|
|
pContext->DeviceRSSCapabilities.MaxKeySize = cfg.rss_max_key_size;
|
|
pContext->DeviceRSSCapabilities.MaxIndirectEntries = cfg.rss_max_indirection_table_length;
|
|
|
|
ParaNdis6_CheckDeviceRSSCapabilities(pContext, bRSS, bHash);
|
|
|
|
if (bRSS)
|
|
{
|
|
pContext->bRSSSupportedByDevice = AckFeature(pContext, VIRTIO_NET_F_RSS);
|
|
pContext->bRSSSupportedByDevicePersistent = pContext->bRSSSupportedByDevice;
|
|
}
|
|
if (bHash)
|
|
{
|
|
pContext->bHashReportedByDevice = AckFeature(pContext, VIRTIO_NET_F_HASH_REPORT);
|
|
}
|
|
}
|
|
if (pContext->bRSSSupportedByDevice)
|
|
{
|
|
pContext->bMultiQueue = true;
|
|
}
|
|
if (pContext->bMultiQueue)
|
|
{
|
|
virtio_get_config(&pContext->IODevice, ETH_ALEN + sizeof(USHORT), &pContext->nHardwareQueues,
|
|
sizeof(pContext->nHardwareQueues));
|
|
}
|
|
else
|
|
{
|
|
pContext->nHardwareQueues = 1;
|
|
}
|
|
#else
|
|
pContext->nHardwareQueues = 1;
|
|
#endif
|
|
|
|
dependentOptions = osbT4TcpChecksum | osbT4UdpChecksum | osbT4TcpOptionsChecksum |
|
|
osbT6TcpChecksum | osbT6UdpChecksum | osbT6TcpOptionsChecksum | osbT6IpExtChecksum;
|
|
|
|
if((pContext->Offload.flagsValue & dependentOptions) && !AckFeature(pContext, VIRTIO_NET_F_CSUM))
|
|
{
|
|
DPrintf(0, "[%s] Host does not support CSUM, disabling CS offload\n", __FUNCTION__);
|
|
pContext->Offload.flagsValue &= ~dependentOptions;
|
|
}
|
|
|
|
pContext->bGuestChecksumSupported = AckFeature(pContext, VIRTIO_NET_F_GUEST_CSUM);
|
|
|
|
InitializeRSCState(pContext);
|
|
|
|
// now, after we checked the capabilities, we can initialize current
|
|
// configuration of offload tasks
|
|
ParaNdis_ResetOffloadSettings(pContext, NULL, NULL);
|
|
|
|
if (pContext->Offload.flags.fTxLso && !AckFeature(pContext, VIRTIO_NET_F_HOST_TSO4))
|
|
{
|
|
DisableLSOv4Permanently(pContext, __FUNCTION__, "Host does not support TSOv4\n");
|
|
}
|
|
|
|
if (pContext->Offload.flags.fTxLsov6 && !AckFeature(pContext, VIRTIO_NET_F_HOST_TSO6))
|
|
{
|
|
DisableLSOv6Permanently(pContext, __FUNCTION__, "Host does not support TSOv6");
|
|
}
|
|
|
|
if (pContext->Offload.flags.fUsov4 || pContext->Offload.flags.fUsov6)
|
|
{
|
|
LPCSTR message = NULL;
|
|
if (!CheckNdisVersion(6, 83))
|
|
{
|
|
message = "NDIS before 1903";
|
|
}
|
|
else if (virtio_is_feature_enabled(pContext->u64GuestFeatures, VIRTIO_NET_F_STANDBY))
|
|
{
|
|
message = "failover configuration";
|
|
}
|
|
else if (!AckFeature(pContext, VIRTIO_NET_F_HOST_USO))
|
|
{
|
|
message = "host without USO support";
|
|
}
|
|
if (message)
|
|
{
|
|
DPrintf(0, "[%s] USO is disabled due to %s\n", __FUNCTION__, message);
|
|
pContext->Offload.flagsValue &= ~osbT4Uso;
|
|
pContext->Offload.flagsValue &= ~osbT6Uso;
|
|
ParaNdis_ResetOffloadSettings(pContext, NULL, NULL);
|
|
}
|
|
}
|
|
|
|
pContext->bUseIndirect = AckFeature(pContext, VIRTIO_RING_F_INDIRECT_DESC);
|
|
pContext->bAnyLayout = AckFeature(pContext, VIRTIO_F_ANY_LAYOUT);
|
|
if (AckFeature(pContext, VIRTIO_F_VERSION_1))
|
|
{
|
|
pContext->nVirtioHeaderSize = pContext->bHashReportedByDevice ?
|
|
sizeof(virtio_net_hdr_v1_hash) : sizeof(virtio_net_hdr_v1);
|
|
|
|
pContext->bAnyLayout = true;
|
|
DPrintf(0, "[%s] Assuming VIRTIO_F_ANY_LAYOUT for V1 device\n", __FUNCTION__);
|
|
if (AckFeature(pContext, VIRTIO_F_RING_PACKED))
|
|
{
|
|
DPrintf(0, "[%s] Using PACKED ring\n", __FUNCTION__);
|
|
}
|
|
}
|
|
|
|
if (pContext->bControlQueueSupported)
|
|
{
|
|
pContext->bCtrlRXFiltersSupported = AckFeature(pContext, VIRTIO_NET_F_CTRL_RX);
|
|
pContext->bCtrlRXExtraFiltersSupported = AckFeature(pContext, VIRTIO_NET_F_CTRL_RX_EXTRA);
|
|
pContext->bCtrlVLANFiltersSupported = AckFeature(pContext, VIRTIO_NET_F_CTRL_VLAN);
|
|
}
|
|
|
|
if (status == NDIS_STATUS_SUCCESS)
|
|
{
|
|
NTSTATUS nt_status = virtio_set_features(&pContext->IODevice, pContext->u64GuestFeatures);
|
|
if (!NT_SUCCESS(nt_status))
|
|
{
|
|
DPrintf(0, "[%s] virtio_set_features failed with %x\n", __FUNCTION__, nt_status);
|
|
status = NTStatusToNdisStatus(nt_status);
|
|
}
|
|
}
|
|
|
|
DEBUG_EXIT_STATUS(0, status);
|
|
return status;
|
|
}
|
|
|
|
#if PARANDIS_SUPPORT_RSS
|
|
static USHORT DetermineQueueNumber(PARANDIS_ADAPTER *pContext)
|
|
{
|
|
if (!pContext->bUsingMSIX)
|
|
{
|
|
DPrintf(0, "[%s] No MSIX, using 1 queue\n", __FUNCTION__);
|
|
return 1;
|
|
}
|
|
|
|
if (pContext->bMultiQueue)
|
|
{
|
|
DPrintf(0, "[%s] Number of hardware queues = %d\n", __FUNCTION__, pContext->nHardwareQueues);
|
|
}
|
|
else
|
|
{
|
|
DPrintf(0, "[%s] - CTRL_MQ not acked, # of bundles set to 1\n", __FUNCTION__);
|
|
return 1;
|
|
}
|
|
|
|
USHORT nProcessors = USHORT(ParaNdis_GetSystemCPUCount() & 0xFFFF);
|
|
|
|
/* In virtio the type of the queue index is "short", thus this type casting */
|
|
USHORT nBundles = USHORT(((pContext->pMSIXInfoTable->MessageCount - 1) / 2) & 0xFFFF);
|
|
if (!nBundles && (pContext->pMSIXInfoTable->MessageCount == 1 || pContext->pMSIXInfoTable->MessageCount == 2))
|
|
{
|
|
DPrintf(0, "[%s] WARNING: %d MSIX message(s), performance will be reduced\n",
|
|
__FUNCTION__, pContext->pMSIXInfoTable->MessageCount);
|
|
nBundles = 1;
|
|
}
|
|
|
|
DPrintf(0, "[%s] %lu CPUs reported\n", __FUNCTION__, nProcessors);
|
|
DPrintf(0, "[%s] %lu MSIs, %u queues' bundles\n", __FUNCTION__, pContext->pMSIXInfoTable->MessageCount, nBundles);
|
|
|
|
USHORT nBundlesLimitByCPU = (pContext->nHardwareQueues < nProcessors) ? pContext->nHardwareQueues : nProcessors;
|
|
nBundles = (nBundles < nBundlesLimitByCPU) ? nBundles : nBundlesLimitByCPU;
|
|
|
|
DPrintf(0, "[%s] # of path bundles = %u\n", __FUNCTION__, nBundles);
|
|
|
|
return nBundles;
|
|
}
|
|
#else
|
|
static USHORT DetermineQueueNumber(PARANDIS_ADAPTER *pContext)
|
|
{
|
|
if (!pContext->bUsingMSIX)
|
|
{
|
|
DPrintf(0, "[%s] No MSIX, using 1 queue\n", __FUNCTION__);
|
|
return 1;
|
|
}
|
|
|
|
if (pContext->pMSIXInfoTable->MessageCount == 1 || pContext->pMSIXInfoTable->MessageCount > 2)
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
static NDIS_STATUS SetupDPCTarget(PARANDIS_ADAPTER *pContext)
|
|
{
|
|
ULONG i;
|
|
#if NDIS_SUPPORT_NDIS620
|
|
NDIS_STATUS status;
|
|
PROCESSOR_NUMBER procNumber;
|
|
#endif
|
|
|
|
for (i = 0; i < pContext->nPathBundles; i++)
|
|
{
|
|
#if NDIS_SUPPORT_NDIS620
|
|
status = KeGetProcessorNumberFromIndex(i, &procNumber);
|
|
if (status != NDIS_STATUS_SUCCESS)
|
|
{
|
|
DPrintf(0, "[%s] - KeGetProcessorNumberFromIndex failed for index %lu - %d\n", __FUNCTION__, i, status);
|
|
return status;
|
|
}
|
|
ParaNdis_ProcessorNumberToGroupAffinity(&pContext->pPathBundles[i].rxPath.DPCAffinity, &procNumber);
|
|
pContext->pPathBundles[i].txPath.DPCAffinity = pContext->pPathBundles[i].rxPath.DPCAffinity;
|
|
#elif NDIS_SUPPORT_NDIS6
|
|
pContext->pPathBundles[i].rxPath.DPCTargetProcessor = 1i64 << i;
|
|
pContext->pPathBundles[i].txPath.DPCTargetProcessor = pContext->pPathBundles[i].rxPath.DPCTargetProcessor;
|
|
#else
|
|
#error not supported
|
|
#endif
|
|
}
|
|
|
|
#if NDIS_SUPPORT_NDIS620
|
|
pContext->CXPath.DPCAffinity = pContext->pPathBundles[0].rxPath.DPCAffinity;
|
|
#elif NDIS_SUPPORT_NDIS6
|
|
pContext->CXPath.DPCTargetProcessor = pContext->pPathBundles[0].rxPath.DPCTargetProcessor;
|
|
#else
|
|
#error not yet defined
|
|
#endif
|
|
return NDIS_STATUS_SUCCESS;
|
|
}
|
|
|
|
/**********************************************************
|
|
Initializes VirtIO buffering and related stuff:
|
|
Allocates RX and TX queues and buffers
|
|
Parameters:
|
|
context
|
|
Return value:
|
|
TRUE if both queues are allocated
|
|
***********************************************************/
|
|
static NDIS_STATUS ParaNdis_VirtIONetInit(PARANDIS_ADAPTER *pContext)
|
|
{
|
|
NDIS_STATUS status = NDIS_STATUS_RESOURCES;
|
|
DEBUG_ENTRY(0);
|
|
UINT i;
|
|
USHORT nVirtIOQueues = pContext->nHardwareQueues * 2 + 2;
|
|
|
|
pContext->nPathBundles = DetermineQueueNumber(pContext);
|
|
if (pContext->nPathBundles == 0)
|
|
{
|
|
DPrintf(0, "[%s] - no I/O paths\n", __FUNCTION__);
|
|
return NDIS_STATUS_RESOURCES;
|
|
}
|
|
|
|
NTSTATUS nt_status = virtio_reserve_queue_memory(&pContext->IODevice, nVirtIOQueues);
|
|
if (!NT_SUCCESS(nt_status))
|
|
{
|
|
DPrintf(0, "[%s] - failed to reserve %u queues\n", __FUNCTION__, nVirtIOQueues);
|
|
return NTStatusToNdisStatus(nt_status);
|
|
}
|
|
|
|
if (pContext->bControlQueueSupported && pContext->CXPath.Create(2 * pContext->nHardwareQueues))
|
|
{
|
|
pContext->bCXPathCreated = TRUE;
|
|
}
|
|
else
|
|
{
|
|
DPrintf(0, "[%s] The Control vQueue does not work!\n", __FUNCTION__);
|
|
pContext->bCtrlRXFiltersSupported = pContext->bCtrlRXExtraFiltersSupported = FALSE;
|
|
pContext->bCtrlMACAddrSupported = pContext->bCtrlVLANFiltersSupported = FALSE;
|
|
pContext->bCXPathCreated = FALSE;
|
|
}
|
|
|
|
pContext->pPathBundles = (CPUPathBundle *)NdisAllocateMemoryWithTagPriority(pContext->MiniportHandle, pContext->nPathBundles * sizeof(*pContext->pPathBundles),
|
|
PARANDIS_MEMORY_TAG, NormalPoolPriority);
|
|
if (pContext->pPathBundles == nullptr)
|
|
{
|
|
DPrintf(0, "[%s] Path bundles allocation failed\n", __FUNCTION__);
|
|
return status;
|
|
}
|
|
|
|
for (i = 0; i < pContext->nPathBundles; i++)
|
|
{
|
|
new (pContext->pPathBundles + i) CPUPathBundle();
|
|
}
|
|
|
|
for (i = 0; i < pContext->nPathBundles; i++)
|
|
{
|
|
if (!pContext->pPathBundles[i].rxPath.Create(pContext, i * 2))
|
|
{
|
|
DPrintf(0, "%s: CParaNdisRX creation failed\n", __FUNCTION__);
|
|
return status;
|
|
}
|
|
pContext->pPathBundles[i].rxCreated = true;
|
|
|
|
if (!pContext->pPathBundles[i].txPath.Create(pContext, i * 2 + 1))
|
|
{
|
|
DPrintf(0, "%s: CParaNdisTX creation failed\n", __FUNCTION__);
|
|
return status;
|
|
}
|
|
pContext->pPathBundles[i].txCreated = true;
|
|
}
|
|
|
|
if (pContext->bCXPathCreated)
|
|
{
|
|
pContext->pPathBundles[0].cxPath = &pContext->CXPath;
|
|
}
|
|
|
|
status = NDIS_STATUS_SUCCESS;
|
|
|
|
return status;
|
|
}
|
|
|
|
static UINT8 ReadDeviceStatus(PARANDIS_ADAPTER *pContext)
|
|
{
|
|
return (UINT8) virtio_get_status(&pContext->IODevice);
|
|
}
|
|
|
|
static VOID ParaNdis_AddDriverOKStatus(PPARANDIS_ADAPTER pContext)
|
|
{
|
|
pContext->bDeviceInitialized = TRUE;
|
|
|
|
KeMemoryBarrier();
|
|
|
|
virtio_device_ready(&pContext->IODevice);
|
|
}
|
|
|
|
void ParaNdis_DeviceConfigureRSC(PARANDIS_ADAPTER *pContext)
|
|
{
|
|
DEBUG_ENTRY(0);
|
|
|
|
#if PARANDIS_SUPPORT_RSC
|
|
UINT64 GuestOffloads;
|
|
|
|
GuestOffloads = 1 << VIRTIO_NET_F_GUEST_CSUM |
|
|
((pContext->RSC.bIPv4Enabled) ? (1 << VIRTIO_NET_F_GUEST_TSO4) : 0) |
|
|
((pContext->RSC.bIPv6Enabled) ? (1 << VIRTIO_NET_F_GUEST_TSO6) : 0) |
|
|
((pContext->RSC.bQemuSupported) ? (1LL << VIRTIO_NET_F_RSC_EXT) : 0);
|
|
|
|
if (pContext->RSC.bHasDynamicConfig)
|
|
{
|
|
DPrintf(0, "Updating offload settings with %I64x\n", GuestOffloads);
|
|
pContext->CXPath.SendControlMessage(VIRTIO_NET_CTRL_GUEST_OFFLOADS, VIRTIO_NET_CTRL_GUEST_OFFLOADS_SET,
|
|
&GuestOffloads,
|
|
sizeof(GuestOffloads),
|
|
NULL, 0, 2);
|
|
}
|
|
else
|
|
{
|
|
DPrintf(0, "ERROR: Can't update offload settings dynamically!");
|
|
}
|
|
#else
|
|
UNREFERENCED_PARAMETER(pContext);
|
|
#endif /* PARANDIS_SUPPORT_RSC */
|
|
}
|
|
|
|
static NDIS_STATUS SetInitialDeviceRSS(PARANDIS_ADAPTER *pContext)
|
|
{
|
|
NDIS_STATUS status;
|
|
struct virtio_net_rss_config cfg = {};
|
|
max_tx_vq(&cfg) = (USHORT)pContext->nPathBundles;
|
|
DPrintf(0, "[%s]\n", __FUNCTION__);
|
|
if (!pContext->CXPath.SendControlMessage(VIRTIO_NET_CTRL_MQ,
|
|
VIRTIO_NET_CTRL_MQ_RSS_CONFIG, &cfg, sizeof(cfg), NULL, 0, 2))
|
|
{
|
|
status = NDIS_STATUS_DEVICE_FAILED;
|
|
}
|
|
else
|
|
{
|
|
status = NDIS_STATUS_SUCCESS;
|
|
}
|
|
return status;
|
|
}
|
|
|
|
NDIS_STATUS ParaNdis_DeviceConfigureMultiQueue(PARANDIS_ADAPTER *pContext)
|
|
{
|
|
NDIS_STATUS status = NDIS_STATUS_SUCCESS;
|
|
DEBUG_ENTRY(0);
|
|
|
|
if (pContext->nPathBundles > 1)
|
|
{
|
|
bool bHasMQ = pContext->u64GuestFeatures & (1LL << VIRTIO_NET_F_MQ);
|
|
u16 nPaths = u16(pContext->nPathBundles);
|
|
if (pContext->bRSSSupportedByDevice)
|
|
{
|
|
status = SetInitialDeviceRSS(pContext);
|
|
}
|
|
else if (bHasMQ &&
|
|
!pContext->CXPath.SendControlMessage(VIRTIO_NET_CTRL_MQ, VIRTIO_NET_CTRL_MQ_VQ_PAIRS_SET, &nPaths, sizeof(nPaths), NULL, 0, 2))
|
|
{
|
|
DPrintf(0, "[%s] - Sending MQ control message failed\n", __FUNCTION__);
|
|
status = NDIS_STATUS_DEVICE_FAILED;
|
|
}
|
|
}
|
|
|
|
DEBUG_EXIT_STATUS(0, status);
|
|
return status;
|
|
}
|
|
|
|
static VOID
|
|
ParaNdis_KickRX(PARANDIS_ADAPTER *pContext)
|
|
{
|
|
UINT i;
|
|
for (i = 0; i < pContext->nPathBundles; i++)
|
|
{
|
|
pContext->pPathBundles[i].rxPath.KickRXRing();
|
|
}
|
|
}
|
|
|
|
NDIS_STATUS ParaNdis_DeviceEnterD0(PARANDIS_ADAPTER *pContext)
|
|
{
|
|
NDIS_STATUS status = NDIS_STATUS_SUCCESS;
|
|
DEBUG_ENTRY(0);
|
|
|
|
ReadLinkState(pContext);
|
|
pContext->bEnableInterruptHandlingDPC = TRUE;
|
|
ParaNdis_SynchronizeLinkState(pContext);
|
|
ParaNdis_AddDriverOKStatus(pContext);
|
|
ParaNdis_DeviceConfigureMultiQueue(pContext);
|
|
ParaNdis_DeviceConfigureRSC(pContext);
|
|
ParaNdis_UpdateMAC(pContext);
|
|
ParaNdis_KickRX(pContext);
|
|
|
|
DEBUG_EXIT_STATUS(0, status);
|
|
return status;
|
|
}
|
|
|
|
/**********************************************************
|
|
Finishes initialization of context structure, calling also version dependent part
|
|
If this procedure failed, ParaNdis_CleanupContext must be called
|
|
Parameters:
|
|
context
|
|
Return value:
|
|
SUCCESS or some kind of failure
|
|
***********************************************************/
|
|
NDIS_STATUS ParaNdis_FinishInitialization(PARANDIS_ADAPTER *pContext)
|
|
{
|
|
NDIS_STATUS status = NDIS_STATUS_SUCCESS;
|
|
DEBUG_ENTRY(0);
|
|
|
|
status = ParaNdis_FinishSpecificInitialization(pContext);
|
|
DPrintf(0, "[%s] ParaNdis_FinishSpecificInitialization passed, status = %d\n", __FUNCTION__, status);
|
|
|
|
|
|
if (status == NDIS_STATUS_SUCCESS)
|
|
{
|
|
status = ParaNdis_VirtIONetInit(pContext);
|
|
DPrintf(0, "[%s] ParaNdis_VirtIONetInit passed, status = %d\n", __FUNCTION__, status);
|
|
}
|
|
|
|
if (status == NDIS_STATUS_SUCCESS && pContext->bUsingMSIX)
|
|
{
|
|
status = ParaNdis_ConfigureMSIXVectors(pContext);
|
|
DPrintf(0, "[%s] ParaNdis_ConfigureMSIXVectors passed, status = %d\n", __FUNCTION__, status);
|
|
}
|
|
|
|
if (status == NDIS_STATUS_SUCCESS)
|
|
{
|
|
status = SetupDPCTarget(pContext);
|
|
DPrintf(0, "[%s] SetupDPCTarget passed, status = %d\n", __FUNCTION__, status);
|
|
}
|
|
|
|
if (status == NDIS_STATUS_SUCCESS)
|
|
{
|
|
ParaNdis_DeviceEnterD0(pContext);
|
|
}
|
|
DEBUG_EXIT_STATUS(0, status);
|
|
return status;
|
|
}
|
|
|
|
/**********************************************************
|
|
Releases VirtIO related resources - queues and buffers
|
|
Parameters:
|
|
context
|
|
Return value:
|
|
***********************************************************/
|
|
static void VirtIONetRelease(PARANDIS_ADAPTER *pContext)
|
|
{
|
|
ULONG i;
|
|
DEBUG_ENTRY(0);
|
|
|
|
/* list NetReceiveBuffersWaiting must be free */
|
|
|
|
#ifdef PARANDIS_SUPPORT_RSS
|
|
for (i = 0; i < ARRAYSIZE(pContext->ReceiveQueues); i++)
|
|
{
|
|
pRxNetDescriptor pBufferDescriptor;
|
|
|
|
while (NULL != (pBufferDescriptor = ReceiveQueueGetBuffer(pContext->ReceiveQueues + i)))
|
|
{
|
|
pBufferDescriptor->Queue->ReuseReceiveBuffer(pBufferDescriptor);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
for (i = 0; i < pContext->nPathBundles; i++)
|
|
{
|
|
pRxNetDescriptor pBufferDescriptor;
|
|
|
|
while (NULL != (pBufferDescriptor = ReceiveQueueGetBuffer(&pContext->pPathBundles[i].rxPath.UnclassifiedPacketsQueue())))
|
|
{
|
|
pBufferDescriptor->Queue->ReuseReceiveBuffer(pBufferDescriptor);
|
|
}
|
|
}
|
|
|
|
RestoreMAC(pContext);
|
|
|
|
for (i = 0; i < pContext->nPathBundles; i++)
|
|
{
|
|
if (pContext->pPathBundles[i].txCreated)
|
|
{
|
|
pContext->pPathBundles[i].txPath.Shutdown();
|
|
}
|
|
|
|
if (pContext->pPathBundles[i].rxCreated)
|
|
{
|
|
pContext->pPathBundles[i].rxPath.Shutdown();
|
|
|
|
/* this can be freed, queue shut down */
|
|
pContext->pPathBundles[i].rxPath.FreeRxDescriptorsFromList();
|
|
}
|
|
}
|
|
|
|
if (pContext->bCXPathCreated)
|
|
{
|
|
pContext->CXPath.Shutdown();
|
|
}
|
|
|
|
PrintStatistics(pContext);
|
|
}
|
|
|
|
static void PreventDPCServicing(PARANDIS_ADAPTER *pContext)
|
|
{
|
|
LONG inside;
|
|
pContext->bEnableInterruptHandlingDPC = FALSE;
|
|
KeMemoryBarrier();
|
|
do
|
|
{
|
|
inside = InterlockedIncrement(&pContext->counterDPCInside);
|
|
InterlockedDecrement(&pContext->counterDPCInside);
|
|
if (inside > 1)
|
|
{
|
|
DPrintf(0, "[%s] waiting!\n", __FUNCTION__);
|
|
NdisMSleep(20000);
|
|
}
|
|
} while (inside > 1);
|
|
}
|
|
|
|
/**********************************************************
|
|
Frees all the resources allocated when the context initialized,
|
|
calling also version-dependent part
|
|
Parameters:
|
|
context
|
|
***********************************************************/
|
|
static VOID ParaNdis_CleanupContext(PARANDIS_ADAPTER *pContext)
|
|
{
|
|
/* disable any interrupt generation */
|
|
if (pContext->bDeviceInitialized)
|
|
{
|
|
ParaNdis_ResetVirtIONetDevice(pContext);
|
|
}
|
|
|
|
PreventDPCServicing(pContext);
|
|
|
|
/****************************************
|
|
ensure all the incoming packets returned,
|
|
free all the buffers and their descriptors
|
|
*****************************************/
|
|
|
|
ParaNdis_SetLinkState(pContext, MediaConnectStateUnknown);
|
|
VirtIONetRelease(pContext);
|
|
|
|
ParaNdis_FinalizeCleanup(pContext);
|
|
|
|
pContext->m_StateMachine.NotifyHalted();
|
|
|
|
if (pContext->pPathBundles != NULL)
|
|
{
|
|
UINT i;
|
|
|
|
for (i = 0; i < pContext->nPathBundles; i++)
|
|
{
|
|
pContext->pPathBundles[i].~CPUPathBundle();
|
|
}
|
|
NdisFreeMemoryWithTagPriority(pContext->MiniportHandle, pContext->pPathBundles, PARANDIS_MEMORY_TAG);
|
|
pContext->pPathBundles = nullptr;
|
|
}
|
|
|
|
if (pContext->RSS2QueueMap)
|
|
{
|
|
NdisFreeMemoryWithTagPriority(pContext->MiniportHandle, pContext->RSS2QueueMap, PARANDIS_MEMORY_TAG);
|
|
pContext->RSS2QueueMap = nullptr;
|
|
pContext->RSS2QueueLength = 0;
|
|
}
|
|
|
|
virtio_device_shutdown(&pContext->IODevice);
|
|
|
|
}
|
|
|
|
|
|
/**********************************************************
|
|
System shutdown handler (shutdown, restart, bugcheck)
|
|
Parameters:
|
|
context
|
|
***********************************************************/
|
|
VOID ParaNdis_OnShutdown(PARANDIS_ADAPTER *pContext)
|
|
{
|
|
DEBUG_ENTRY(0); // this is only for kdbg :)
|
|
ParaNdis_ResetVirtIONetDevice(pContext);
|
|
|
|
pContext->m_StateMachine.NotifyShutdown();
|
|
}
|
|
|
|
static ULONG ShallPassPacket(PARANDIS_ADAPTER *pContext, PNET_PACKET_INFO pPacketInfo)
|
|
{
|
|
ULONG i;
|
|
|
|
if (pPacketInfo->dataLength > pContext->MaxPacketSize.nMaxFullSizeOsRx + ETH_PRIORITY_HEADER_SIZE)
|
|
return FALSE;
|
|
|
|
if ((pPacketInfo->dataLength > pContext->MaxPacketSize.nMaxFullSizeOsRx) && !pPacketInfo->hasVlanHeader)
|
|
return FALSE;
|
|
|
|
if (IsVlanSupported(pContext) && pPacketInfo->hasVlanHeader)
|
|
{
|
|
if (pContext->VlanId && pContext->VlanId != pPacketInfo->Vlan.VlanId)
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
if (pContext->PacketFilter & NDIS_PACKET_TYPE_PROMISCUOUS)
|
|
return TRUE;
|
|
|
|
if(pPacketInfo->isUnicast)
|
|
{
|
|
ULONG Res;
|
|
|
|
if(!(pContext->PacketFilter & NDIS_PACKET_TYPE_DIRECTED))
|
|
return FALSE;
|
|
|
|
ETH_COMPARE_NETWORK_ADDRESSES_EQ_SAFE(pPacketInfo->ethDestAddr, pContext->CurrentMacAddress, &Res);
|
|
return !Res;
|
|
}
|
|
|
|
if(pPacketInfo->isBroadcast)
|
|
return (pContext->PacketFilter & NDIS_PACKET_TYPE_BROADCAST);
|
|
|
|
// Multi-cast
|
|
|
|
if(pContext->PacketFilter & NDIS_PACKET_TYPE_ALL_MULTICAST)
|
|
return TRUE;
|
|
|
|
if(!(pContext->PacketFilter & NDIS_PACKET_TYPE_MULTICAST))
|
|
return FALSE;
|
|
|
|
for (i = 0; i < pContext->MulticastData.nofMulticastEntries; i++)
|
|
{
|
|
ULONG Res;
|
|
PUCHAR CurrMcastAddr = &pContext->MulticastData.MulticastList[i*ETH_ALEN];
|
|
|
|
ETH_COMPARE_NETWORK_ADDRESSES_EQ_SAFE(pPacketInfo->ethDestAddr, CurrMcastAddr, &Res);
|
|
|
|
if(!Res)
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static __inline
|
|
CCHAR GetReceiveQueueForCurrentCPU(PARANDIS_ADAPTER *pContext)
|
|
{
|
|
#if PARANDIS_SUPPORT_RSS
|
|
return ParaNdis6_RSSGetCurrentCpuReceiveQueue(&pContext->RSSParameters);
|
|
#else
|
|
UNREFERENCED_PARAMETER(pContext);
|
|
|
|
return PARANDIS_RECEIVE_NO_QUEUE;
|
|
#endif
|
|
}
|
|
|
|
static __inline
|
|
pRxNetDescriptor ReceiveQueueGetBuffer(PPARANDIS_RECEIVE_QUEUE pQueue)
|
|
{
|
|
PLIST_ENTRY pListEntry = NdisInterlockedRemoveHeadList(&pQueue->BuffersList, &pQueue->Lock);
|
|
return pListEntry ? CONTAINING_RECORD(pListEntry, RxNetDescriptor, ReceiveQueueListEntry) : NULL;
|
|
}
|
|
|
|
static __inline
|
|
BOOLEAN ReceiveQueueHasBuffers(PPARANDIS_RECEIVE_QUEUE pQueue)
|
|
{
|
|
BOOLEAN res;
|
|
|
|
NdisAcquireSpinLock(&pQueue->Lock);
|
|
res = !IsListEmpty(&pQueue->BuffersList);
|
|
NdisReleaseSpinLock(&pQueue->Lock);
|
|
|
|
return res;
|
|
}
|
|
|
|
static VOID
|
|
UpdateReceiveSuccessStatistics(PPARANDIS_ADAPTER pContext,
|
|
PNET_PACKET_INFO pPacketInfo,
|
|
UINT nCoalescedSegmentsCount)
|
|
{
|
|
pContext->Statistics.ifHCInOctets += pPacketInfo->dataLength;
|
|
|
|
if(pPacketInfo->isUnicast)
|
|
{
|
|
pContext->Statistics.ifHCInUcastPkts += nCoalescedSegmentsCount;
|
|
pContext->Statistics.ifHCInUcastOctets += pPacketInfo->dataLength;
|
|
}
|
|
else if (pPacketInfo->isBroadcast)
|
|
{
|
|
pContext->Statistics.ifHCInBroadcastPkts += nCoalescedSegmentsCount;
|
|
pContext->Statistics.ifHCInBroadcastOctets += pPacketInfo->dataLength;
|
|
}
|
|
else if (pPacketInfo->isMulticast)
|
|
{
|
|
pContext->Statistics.ifHCInMulticastPkts += nCoalescedSegmentsCount;
|
|
pContext->Statistics.ifHCInMulticastOctets += pPacketInfo->dataLength;
|
|
}
|
|
else
|
|
{
|
|
NETKVM_ASSERT(FALSE);
|
|
}
|
|
}
|
|
|
|
static __inline VOID
|
|
UpdateReceiveFailStatistics(PPARANDIS_ADAPTER pContext, UINT nCoalescedSegmentsCount)
|
|
{
|
|
pContext->Statistics.ifInErrors++;
|
|
pContext->Statistics.ifInDiscards += nCoalescedSegmentsCount;
|
|
}
|
|
|
|
static void ProcessReceiveQueue(PARANDIS_ADAPTER *pContext,
|
|
PULONG pnPacketsToIndicateLeft,
|
|
PPARANDIS_RECEIVE_QUEUE pTargetReceiveQueue,
|
|
PNET_BUFFER_LIST *indicate,
|
|
PNET_BUFFER_LIST *indicateTail,
|
|
ULONG *nIndicate)
|
|
{
|
|
pRxNetDescriptor pBufferDescriptor;
|
|
|
|
while( (*pnPacketsToIndicateLeft > 0) &&
|
|
(NULL != (pBufferDescriptor = ReceiveQueueGetBuffer(pTargetReceiveQueue))) )
|
|
{
|
|
PNET_PACKET_INFO pPacketInfo = &pBufferDescriptor->PacketInfo;
|
|
|
|
if(ParaNdis_IsTxRxPossible(pContext) && ShallPassPacket(pContext, pPacketInfo))
|
|
{
|
|
UINT nCoalescedSegmentsCount;
|
|
PNET_BUFFER_LIST packet = ParaNdis_PrepareReceivedPacket(pContext, pBufferDescriptor, &nCoalescedSegmentsCount);
|
|
if(packet != NULL)
|
|
{
|
|
UpdateReceiveSuccessStatistics(pContext, pPacketInfo, nCoalescedSegmentsCount);
|
|
if (*indicate == nullptr)
|
|
{
|
|
*indicate = *indicateTail = packet;
|
|
}
|
|
else
|
|
{
|
|
NET_BUFFER_LIST_NEXT_NBL(*indicateTail) = packet;
|
|
*indicateTail = packet;
|
|
}
|
|
|
|
NET_BUFFER_LIST_NEXT_NBL(*indicateTail) = NULL;
|
|
(*pnPacketsToIndicateLeft)--;
|
|
(*nIndicate)++;
|
|
}
|
|
else
|
|
{
|
|
UpdateReceiveFailStatistics(pContext, nCoalescedSegmentsCount);
|
|
pBufferDescriptor->Queue->ReuseReceiveBuffer(pBufferDescriptor);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
pContext->extraStatistics.framesFilteredOut++;
|
|
pBufferDescriptor->Queue->ReuseReceiveBuffer(pBufferDescriptor);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/* DPC throttling implementation.
|
|
|
|
The main loop of the function RxDPCWorkBody finishes under light traffic
|
|
when there are no more packets in the virtqueue receive queue. Under the
|
|
heavy traffic, the situation is more complicated: the ready-to process packets
|
|
are fetched from the virtqueue's receive queue, indicated toward the OS upper
|
|
layer in the ProcessReceiveQueue and, depending on the relative speed of
|
|
a virtual NIC and guest OS, may be returned by the upper layer to the driver
|
|
with ParaNdis6_ReturnNetBufferLists and reinserted into the virtqueue for
|
|
reading.
|
|
|
|
Under these conditions, the RxDPCWorkBody's loop terminates because
|
|
ProcessReceiveQueue is limited by the nPacketsToIndicate parameter, accepting
|
|
the configuration value for DPC throttling. ProcessReceiveQueue decreases
|
|
the nPacketsToIndicate's value each time the packet is indicated toward the
|
|
OS's upper layer and stops indicating when nPacketsToIndicate drops to zero.
|
|
|
|
When nPacketsToIndicate reaches zero, the loop operates in the following way:
|
|
ProcessRxRing fetches the ready-to-process packet from virtqueue and places
|
|
them into receiving queues, but the packets are not indicated by
|
|
ProcessReceiveQueue; OS has no packets to be reinserted into the virtqueue,
|
|
virtqueue eventually becomes empty and RxDPCWorkBody's loop exits */
|
|
|
|
static
|
|
BOOLEAN RxDPCWorkBody(PARANDIS_ADAPTER *pContext, CPUPathBundle *pathBundle, ULONG nPacketsToIndicate)
|
|
{
|
|
BOOLEAN res = FALSE;
|
|
bool rxPathOwner = false;
|
|
PNET_BUFFER_LIST indicate, indicateTail;
|
|
ULONG nIndicate;
|
|
|
|
CCHAR CurrCpuReceiveQueue = GetReceiveQueueForCurrentCPU(pContext);
|
|
|
|
indicate = nullptr;
|
|
indicateTail = nullptr;
|
|
nIndicate = 0;
|
|
|
|
/* pathBundle is passed from ParaNdis_DPCWorkBody and may be NULL
|
|
if case DPC handler is scheduled by RSS to the CPU with
|
|
associated queues */
|
|
if (pathBundle != nullptr)
|
|
{
|
|
rxPathOwner = pathBundle->rxPath.UnclassifiedPacketsQueue().Ownership.Acquire();
|
|
|
|
pathBundle->rxPath.ProcessRxRing(CurrCpuReceiveQueue);
|
|
|
|
if (rxPathOwner)
|
|
{
|
|
ProcessReceiveQueue(pContext, &nPacketsToIndicate, &pathBundle->rxPath.UnclassifiedPacketsQueue(),
|
|
&indicate, &indicateTail, &nIndicate);
|
|
}
|
|
}
|
|
|
|
#ifdef PARANDIS_SUPPORT_RSS
|
|
if (CurrCpuReceiveQueue != PARANDIS_RECEIVE_NO_QUEUE)
|
|
{
|
|
ProcessReceiveQueue(pContext, &nPacketsToIndicate, &pContext->ReceiveQueues[CurrCpuReceiveQueue],
|
|
&indicate, &indicateTail, &nIndicate);
|
|
res |= ReceiveQueueHasBuffers(&pContext->ReceiveQueues[CurrCpuReceiveQueue]);
|
|
}
|
|
#endif
|
|
|
|
if (nIndicate)
|
|
{
|
|
if(pContext->m_RxStateMachine.RegisterOutstandingItems(nIndicate))
|
|
{
|
|
NdisMIndicateReceiveNetBufferLists(pContext->MiniportHandle,
|
|
indicate, 0, nIndicate, NDIS_RECEIVE_FLAGS_DISPATCH_LEVEL);
|
|
}
|
|
else
|
|
{
|
|
ParaNdis_ReuseRxNBLs(indicate);
|
|
}
|
|
}
|
|
|
|
if (rxPathOwner)
|
|
{
|
|
pathBundle->rxPath.UnclassifiedPacketsQueue().Ownership.Release();
|
|
}
|
|
|
|
// we do not need to make a check of rx queue restart etc. if we already know
|
|
// that we need to respawn the DPC to get more data from the queue
|
|
if (pathBundle != nullptr && res == 0)
|
|
{
|
|
res |= pathBundle->rxPath.RestartQueue() |
|
|
ReceiveQueueHasBuffers(&pathBundle->rxPath.UnclassifiedPacketsQueue());
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
void ParaNdis_ReuseRxNBLs(PNET_BUFFER_LIST pNBL)
|
|
{
|
|
while (pNBL)
|
|
{
|
|
PNET_BUFFER_LIST pTemp = pNBL;
|
|
pRxNetDescriptor pBuffersDescriptor = (pRxNetDescriptor)pNBL->MiniportReserved[0];
|
|
DPrintf(3, " Returned NBL of pBuffersDescriptor %p!\n", pBuffersDescriptor);
|
|
pNBL = NET_BUFFER_LIST_NEXT_NBL(pNBL);
|
|
NET_BUFFER_LIST_NEXT_NBL(pTemp) = NULL;
|
|
NdisFreeNetBufferList(pTemp);
|
|
pBuffersDescriptor->Queue->ReuseReceiveBuffer(pBuffersDescriptor);
|
|
}
|
|
}
|
|
|
|
void ParaNdis_CXDPCWorkBody(PARANDIS_ADAPTER *pContext)
|
|
{
|
|
InterlockedIncrement(&pContext->counterDPCInside);
|
|
if (pContext->bEnableInterruptHandlingDPC)
|
|
{
|
|
UINT8 status = 0;
|
|
status = ReadDeviceStatus(pContext);
|
|
|
|
if (virtio_is_feature_enabled(pContext->u64HostFeatures, VIRTIO_F_VERSION_1) &&
|
|
(status & VIRTIO_CONFIG_S_NEEDS_RESET))
|
|
{
|
|
DPrintf(0, "Received VIRTIO_CONFIG_S_NEEDS_RESET event");
|
|
pContext->m_StateMachine.NotifyDeviceNeedsReset();
|
|
pContext->bDeviceNeedsReset = TRUE;
|
|
}
|
|
|
|
ReadLinkState(pContext);
|
|
if (pContext->bLinkDetectSupported)
|
|
{
|
|
ReadLinkState(pContext);
|
|
ParaNdis_SynchronizeLinkState(pContext);
|
|
}
|
|
if (pContext->bGuestAnnounceSupported && pContext->bGuestAnnounced)
|
|
{
|
|
ParaNdis_SendGratuitousArpPacket(pContext);
|
|
pContext->CXPath.SendControlMessage(VIRTIO_NET_CTRL_ANNOUNCE, VIRTIO_NET_CTRL_ANNOUNCE_ACK, NULL, 0, NULL, 0, 0);
|
|
pContext->bGuestAnnounced = FALSE;
|
|
}
|
|
}
|
|
InterlockedDecrement(&pContext->counterDPCInside);
|
|
}
|
|
|
|
bool ParaNdis_RXTXDPCWorkBody(PARANDIS_ADAPTER *pContext, ULONG ulMaxPacketsToIndicate)
|
|
{
|
|
bool stillRequiresProcessing = false;
|
|
UINT numOfPacketsToIndicate = min(ulMaxPacketsToIndicate, pContext->uNumberOfHandledRXPacketsInDPC);
|
|
|
|
DEBUG_ENTRY(5);
|
|
|
|
InterlockedIncrement(&pContext->counterDPCInside);
|
|
|
|
CPUPathBundle *pathBundle = nullptr;
|
|
|
|
if (pContext->nPathBundles == 1)
|
|
{
|
|
pathBundle = pContext->pPathBundles;
|
|
}
|
|
else
|
|
{
|
|
ULONG procIndex = ParaNdis_GetCurrentCPUIndex();
|
|
if (procIndex < pContext->nPathBundles)
|
|
{
|
|
pathBundle = pContext->pPathBundles + procIndex;
|
|
}
|
|
}
|
|
/* When DPC is scheduled for RSS processing, it may be assigned to CPU that has no
|
|
correspondent path, so pathBundle may remain null. */
|
|
|
|
if (pContext->bEnableInterruptHandlingDPC)
|
|
{
|
|
if (RxDPCWorkBody(pContext, pathBundle, numOfPacketsToIndicate))
|
|
{
|
|
stillRequiresProcessing = true;
|
|
}
|
|
|
|
if (pathBundle != nullptr && pathBundle->txPath.DoPendingTasks(nullptr))
|
|
{
|
|
stillRequiresProcessing = true;
|
|
}
|
|
}
|
|
InterlockedDecrement(&pContext->counterDPCInside);
|
|
|
|
if (pContext->bSharedVectors && pathBundle)
|
|
{
|
|
ParaNdis_CXDPCWorkBody(pContext);
|
|
}
|
|
return stillRequiresProcessing;
|
|
}
|
|
|
|
/**********************************************************
|
|
Common handler of multicast address configuration
|
|
Parameters:
|
|
PVOID Buffer array of addresses from NDIS
|
|
ULONG BufferSize size of incoming buffer
|
|
PUINT pBytesRead update on success
|
|
PUINT pBytesNeeded update on wrong buffer size
|
|
Return value:
|
|
SUCCESS or kind of failure
|
|
***********************************************************/
|
|
NDIS_STATUS ParaNdis_SetMulticastList(
|
|
PARANDIS_ADAPTER *pContext,
|
|
PVOID Buffer,
|
|
ULONG BufferSize,
|
|
PUINT pBytesRead,
|
|
PUINT pBytesNeeded)
|
|
{
|
|
NDIS_STATUS status;
|
|
ULONG length = BufferSize;
|
|
if (length > sizeof(pContext->MulticastData.MulticastList))
|
|
{
|
|
status = NDIS_STATUS_MULTICAST_FULL;
|
|
*pBytesNeeded = sizeof(pContext->MulticastData.MulticastList);
|
|
}
|
|
else if (length % ETH_ALEN)
|
|
{
|
|
status = NDIS_STATUS_INVALID_LENGTH;
|
|
*pBytesNeeded = (length / ETH_ALEN) * ETH_ALEN;
|
|
}
|
|
else
|
|
{
|
|
NdisZeroMemory(pContext->MulticastData.MulticastList, sizeof(pContext->MulticastData.MulticastList));
|
|
if (length)
|
|
NdisMoveMemory(pContext->MulticastData.MulticastList, Buffer, length);
|
|
pContext->MulticastData.nofMulticastEntries = length / ETH_ALEN;
|
|
DPrintf(1, "[%s] New multicast list of %d bytes\n", __FUNCTION__, length);
|
|
*pBytesRead = length;
|
|
status = NDIS_STATUS_SUCCESS;
|
|
}
|
|
return status;
|
|
}
|
|
|
|
/**********************************************************
|
|
Common handler of PnP events
|
|
Parameters:
|
|
Return value:
|
|
***********************************************************/
|
|
VOID ParaNdis_OnPnPEvent(
|
|
PARANDIS_ADAPTER *pContext,
|
|
NDIS_DEVICE_PNP_EVENT pEvent,
|
|
PVOID pInfo,
|
|
ULONG ulSize)
|
|
{
|
|
const char *pName = "";
|
|
|
|
UNREFERENCED_PARAMETER(pInfo);
|
|
UNREFERENCED_PARAMETER(ulSize);
|
|
|
|
DEBUG_ENTRY(0);
|
|
#undef MAKECASE
|
|
#define MAKECASE(x) case (x): pName = #x; break;
|
|
switch (pEvent)
|
|
{
|
|
MAKECASE(NdisDevicePnPEventQueryRemoved)
|
|
MAKECASE(NdisDevicePnPEventRemoved)
|
|
MAKECASE(NdisDevicePnPEventSurpriseRemoved)
|
|
MAKECASE(NdisDevicePnPEventQueryStopped)
|
|
MAKECASE(NdisDevicePnPEventStopped)
|
|
MAKECASE(NdisDevicePnPEventPowerProfileChanged)
|
|
MAKECASE(NdisDevicePnPEventFilterListChanged)
|
|
default:
|
|
break;
|
|
}
|
|
ParaNdis_DebugHistory(pContext, _etagHistoryLogOperation::hopPnpEvent, NULL, pEvent, 0, 0);
|
|
DPrintf(0, "[%s] (%s)\n", __FUNCTION__, pName);
|
|
if (pEvent == NdisDevicePnPEventSurpriseRemoved)
|
|
{
|
|
// on simulated surprise removal (under PnpTest) we need to reset the device
|
|
// to prevent any access of device queues to memory buffers
|
|
pContext->bSurprizeRemoved = TRUE;
|
|
pContext->m_StateMachine.NotifySupriseRemoved();
|
|
ParaNdis_ResetVirtIONetDevice(pContext);
|
|
}
|
|
pContext->PnpEvents[pContext->nPnpEventIndex++] = pEvent;
|
|
if (pContext->nPnpEventIndex > sizeof(pContext->PnpEvents)/sizeof(pContext->PnpEvents[0]))
|
|
pContext->nPnpEventIndex = 0;
|
|
}
|
|
|
|
static VOID ParaNdis_DeviceFiltersUpdateRxMode(PARANDIS_ADAPTER *pContext)
|
|
{
|
|
u8 val;
|
|
ULONG f = pContext->PacketFilter;
|
|
val = (f & NDIS_PACKET_TYPE_PROMISCUOUS) ? 1 : 0;
|
|
pContext->CXPath.SendControlMessage(VIRTIO_NET_CTRL_RX, VIRTIO_NET_CTRL_RX_PROMISC, &val, sizeof(val), NULL, 0, 2);
|
|
val = (f & NDIS_PACKET_TYPE_ALL_MULTICAST) ? 1 : 0;
|
|
pContext->CXPath.SendControlMessage(VIRTIO_NET_CTRL_RX, VIRTIO_NET_CTRL_RX_ALLMULTI, &val, sizeof(val), NULL, 0, 2);
|
|
|
|
if (pContext->bCtrlRXExtraFiltersSupported)
|
|
{
|
|
val = (f & (NDIS_PACKET_TYPE_MULTICAST | NDIS_PACKET_TYPE_ALL_MULTICAST)) ? 0 : 1;
|
|
pContext->CXPath.SendControlMessage(VIRTIO_NET_CTRL_RX, VIRTIO_NET_CTRL_RX_NOMULTI, &val, sizeof(val), NULL, 0, 2);
|
|
val = (f & NDIS_PACKET_TYPE_DIRECTED) ? 0 : 1;
|
|
pContext->CXPath.SendControlMessage(VIRTIO_NET_CTRL_RX, VIRTIO_NET_CTRL_RX_NOUNI, &val, sizeof(val), NULL, 0, 2);
|
|
val = (f & NDIS_PACKET_TYPE_BROADCAST) ? 0 : 1;
|
|
pContext->CXPath.SendControlMessage(VIRTIO_NET_CTRL_RX, VIRTIO_NET_CTRL_RX_NOBCAST, &val, sizeof(val), NULL, 0, 2);
|
|
}
|
|
}
|
|
|
|
static VOID ParaNdis_DeviceFiltersUpdateAddresses(PARANDIS_ADAPTER *pContext)
|
|
{
|
|
u32 u32UniCastEntries = 0;
|
|
pContext->CXPath.SendControlMessage(VIRTIO_NET_CTRL_MAC, VIRTIO_NET_CTRL_MAC_TABLE_SET,
|
|
&u32UniCastEntries,
|
|
sizeof(u32UniCastEntries),
|
|
&pContext->MulticastData,
|
|
sizeof(pContext->MulticastData.nofMulticastEntries) + (ULONGLONG)pContext->MulticastData.nofMulticastEntries * ETH_ALEN,
|
|
2);
|
|
}
|
|
|
|
static VOID SetSingleVlanFilter(PARANDIS_ADAPTER *pContext, ULONG vlanId, BOOLEAN bOn, int levelIfOK)
|
|
{
|
|
u16 val = vlanId & 0xfff;
|
|
UCHAR cmd = bOn ? VIRTIO_NET_CTRL_VLAN_ADD : VIRTIO_NET_CTRL_VLAN_DEL;
|
|
pContext->CXPath.SendControlMessage(VIRTIO_NET_CTRL_VLAN, cmd, &val, sizeof(val), NULL, 0, levelIfOK);
|
|
}
|
|
|
|
static VOID SetAllVlanFilters(PARANDIS_ADAPTER *pContext, BOOLEAN bOn)
|
|
{
|
|
ULONG i;
|
|
for (i = 0; i <= MAX_VLAN_ID; ++i)
|
|
SetSingleVlanFilter(pContext, i, bOn, 7);
|
|
}
|
|
|
|
/*
|
|
possible values of filter set (pContext->ulCurrentVlansFilterSet):
|
|
0 - all disabled
|
|
1..4094 - one selected enabled
|
|
4095 - all enabled
|
|
Note that only 0th vlan can't be enabled
|
|
*/
|
|
VOID ParaNdis_DeviceFiltersUpdateVlanId(PARANDIS_ADAPTER *pContext)
|
|
{
|
|
if (pContext->bCtrlVLANFiltersSupported)
|
|
{
|
|
ULONG newFilterSet;
|
|
if (IsVlanSupported(pContext))
|
|
newFilterSet = pContext->VlanId ? pContext->VlanId : (MAX_VLAN_ID + 1);
|
|
else
|
|
newFilterSet = IsPrioritySupported(pContext) ? (MAX_VLAN_ID + 1) : 0;
|
|
if (newFilterSet != pContext->ulCurrentVlansFilterSet)
|
|
{
|
|
if (pContext->ulCurrentVlansFilterSet > MAX_VLAN_ID)
|
|
SetAllVlanFilters(pContext, FALSE);
|
|
else if (pContext->ulCurrentVlansFilterSet)
|
|
SetSingleVlanFilter(pContext, pContext->ulCurrentVlansFilterSet, FALSE, 2);
|
|
|
|
pContext->ulCurrentVlansFilterSet = newFilterSet;
|
|
|
|
if (pContext->ulCurrentVlansFilterSet > MAX_VLAN_ID)
|
|
SetAllVlanFilters(pContext, TRUE);
|
|
else if (pContext->ulCurrentVlansFilterSet)
|
|
SetSingleVlanFilter(pContext, pContext->ulCurrentVlansFilterSet, TRUE, 2);
|
|
}
|
|
}
|
|
}
|
|
|
|
VOID ParaNdis_UpdateDeviceFilters(PARANDIS_ADAPTER *pContext)
|
|
{
|
|
if (pContext->bCtrlRXFiltersSupported)
|
|
{
|
|
ParaNdis_DeviceFiltersUpdateRxMode(pContext);
|
|
ParaNdis_DeviceFiltersUpdateAddresses(pContext);
|
|
}
|
|
|
|
ParaNdis_DeviceFiltersUpdateVlanId(pContext);
|
|
}
|
|
|
|
static VOID
|
|
ParaNdis_UpdateMAC(PARANDIS_ADAPTER *pContext)
|
|
{
|
|
if (pContext->bCtrlMACAddrSupported)
|
|
{
|
|
pContext->CXPath.SendControlMessage(VIRTIO_NET_CTRL_MAC, VIRTIO_NET_CTRL_MAC_ADDR_SET,
|
|
pContext->CurrentMacAddress,
|
|
ETH_ALEN,
|
|
NULL, 0, 4);
|
|
}
|
|
}
|
|
|
|
NDIS_STATUS ParaNdis_PowerOn(PARANDIS_ADAPTER *pContext)
|
|
{
|
|
UINT i;
|
|
|
|
DEBUG_ENTRY(0);
|
|
ParaNdis_DebugHistory(pContext, _etagHistoryLogOperation::hopPowerOn, NULL, 1, 0, 0);
|
|
|
|
pContext->m_StateMachine.NotifyPowerOn();
|
|
|
|
ParaNdis_ResetVirtIONetDevice(pContext);
|
|
virtio_add_status(&pContext->IODevice, VIRTIO_CONFIG_S_ACKNOWLEDGE | VIRTIO_CONFIG_S_DRIVER);
|
|
/* virtio_get_features must be called with any mask once upon device initialization:
|
|
otherwise the device will not work properly */
|
|
(void)virtio_get_features(&pContext->IODevice);
|
|
NTSTATUS nt_status = virtio_set_features(&pContext->IODevice, pContext->u64GuestFeatures);
|
|
if (!NT_SUCCESS(nt_status))
|
|
{
|
|
DPrintf(0, "[%s] virtio_set_features failed with %x\n", __FUNCTION__, nt_status);
|
|
pContext->m_StateMachine.NotifyResumed();
|
|
return NTStatusToNdisStatus(nt_status);
|
|
}
|
|
|
|
for (i = 0; i < pContext->nPathBundles; i++)
|
|
{
|
|
pContext->pPathBundles[i].txPath.Renew();
|
|
pContext->pPathBundles[i].rxPath.Renew();
|
|
}
|
|
if (pContext->bCXPathCreated)
|
|
{
|
|
pContext->CXPath.Renew();
|
|
}
|
|
|
|
ParaNdis_RestoreDeviceConfigurationAfterReset(pContext);
|
|
|
|
for (i = 0; i < pContext->nPathBundles; i++)
|
|
{
|
|
pContext->pPathBundles[i].rxPath.PopulateQueue();
|
|
}
|
|
|
|
ParaNdis_DeviceEnterD0(pContext);
|
|
ParaNdis_UpdateDeviceFilters(pContext);
|
|
|
|
pContext->m_StateMachine.NotifyResumed();
|
|
|
|
ParaNdis_DebugHistory(pContext, _etagHistoryLogOperation::hopPowerOn, NULL, 0, 0, 0);
|
|
|
|
return NDIS_STATUS_SUCCESS;
|
|
}
|
|
|
|
VOID ParaNdis_PowerOff(PARANDIS_ADAPTER *pContext)
|
|
{
|
|
DEBUG_ENTRY(0);
|
|
ParaNdis_DebugHistory(pContext, _etagHistoryLogOperation::hopPowerOff, NULL, 1, 0, 0);
|
|
|
|
pContext->m_StateMachine.NotifySuspended();
|
|
|
|
pContext->bConnected = FALSE;
|
|
|
|
ParaNdis_ResetVirtIONetDevice(pContext);
|
|
|
|
#if !NDIS_SUPPORT_NDIS620
|
|
// WLK tests for Windows 2008 require media disconnect indication
|
|
// on power off. HCK tests for newer versions require media state unknown
|
|
// indication only and fail on disconnect indication
|
|
ParaNdis_SetLinkState(pContext, MediaConnectStateDisconnected);
|
|
#endif
|
|
ParaNdis_SetLinkState(pContext, MediaConnectStateUnknown);
|
|
|
|
PreventDPCServicing(pContext);
|
|
|
|
/*******************************************************************
|
|
shutdown queues to have all the receive buffers under our control
|
|
all the transmit buffers move to list of free buffers
|
|
********************************************************************/
|
|
|
|
for (UINT i = 0; i < pContext->nPathBundles; i++)
|
|
{
|
|
pContext->pPathBundles[i].txPath.Shutdown();
|
|
pContext->pPathBundles[i].rxPath.Shutdown();
|
|
}
|
|
|
|
if (pContext->bCXPathCreated)
|
|
{
|
|
pContext->CXPath.Shutdown();
|
|
}
|
|
|
|
ParaNdis_DebugHistory(pContext, _etagHistoryLogOperation::hopPowerOff, NULL, 0, 0, 0);
|
|
}
|
|
|
|
void ParaNdis_CallOnBugCheck(PARANDIS_ADAPTER *pContext)
|
|
{
|
|
if (pContext->bDeviceInitialized)
|
|
{
|
|
#ifdef DBG_USE_VIRTIO_PCI_ISR_FOR_HOST_REPORT
|
|
WriteVirtIODeviceByte(pContext->IODevice->isr, 1);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
tChecksumCheckResult ParaNdis_CheckRxChecksum(
|
|
PARANDIS_ADAPTER *pContext,
|
|
ULONG virtioFlags,
|
|
tCompletePhysicalAddress *pPacketPages,
|
|
PNET_PACKET_INFO pPacketInfo,
|
|
ULONG ulDataOffset,
|
|
BOOLEAN verifyLength)
|
|
{
|
|
tOffloadSettingsFlags f = pContext->Offload.flags;
|
|
tChecksumCheckResult res;
|
|
tTcpIpPacketParsingResult ppr;
|
|
ULONG ulPacketLength = pPacketInfo->dataLength;
|
|
ULONG flagsToCalculate = 0;
|
|
res.value = 0;
|
|
|
|
//VIRTIO_NET_HDR_F_NEEDS_CSUM - we need to calculate TCP/UDP CS
|
|
//VIRTIO_NET_HDR_F_DATA_VALID - host tells us TCP/UDP CS is OK
|
|
|
|
if (f.fRxIPChecksum) flagsToCalculate |= tPacketOffloadRequest::pcrIpChecksum; // check only
|
|
|
|
if (!(virtioFlags & VIRTIO_NET_HDR_F_DATA_VALID))
|
|
{
|
|
if (virtioFlags & VIRTIO_NET_HDR_F_NEEDS_CSUM)
|
|
{
|
|
flagsToCalculate |= tPacketOffloadRequest::pcrFixXxpChecksum | tPacketOffloadRequest::pcrTcpChecksum | tPacketOffloadRequest::pcrUdpChecksum;
|
|
}
|
|
else
|
|
{
|
|
if (f.fRxTCPChecksum) flagsToCalculate |= tPacketOffloadRequest::pcrTcpV4Checksum;
|
|
if (f.fRxUDPChecksum) flagsToCalculate |= tPacketOffloadRequest::pcrUdpV4Checksum;
|
|
if (f.fRxTCPv6Checksum) flagsToCalculate |= tPacketOffloadRequest::pcrTcpV6Checksum;
|
|
if (f.fRxUDPv6Checksum) flagsToCalculate |= tPacketOffloadRequest::pcrUdpV6Checksum;
|
|
}
|
|
}
|
|
|
|
if (pPacketInfo->isIP4 || pPacketInfo->isIP6)
|
|
{
|
|
ppr = ParaNdis_CheckSumVerify(pPacketPages, ulPacketLength - ETH_HEADER_SIZE,
|
|
ulDataOffset + ETH_HEADER_SIZE, flagsToCalculate,
|
|
verifyLength, __FUNCTION__);
|
|
}
|
|
else
|
|
{
|
|
ppr.value = 0;
|
|
ppr.ipStatus = static_cast<ULONG>(ppResult::ppresNotIP);
|
|
}
|
|
|
|
if (ppr.ipCheckSum == ppResult::ppresIPTooShort || ppr.xxpStatus == ppResult::ppresXxpIncomplete)
|
|
{
|
|
res.flags.IpOK = FALSE;
|
|
res.flags.IpFailed = TRUE;
|
|
return res;
|
|
}
|
|
|
|
if (virtioFlags & VIRTIO_NET_HDR_F_DATA_VALID)
|
|
{
|
|
pContext->extraStatistics.framesRxCSHwOK++;
|
|
ppr.xxpCheckSum = static_cast<ULONG>(ppResult::ppresCSOK);
|
|
}
|
|
|
|
if (ppr.ipStatus == ppResult::ppresIPV4 && !ppr.IsFragment)
|
|
{
|
|
if (f.fRxIPChecksum)
|
|
{
|
|
res.flags.IpOK = ppr.ipCheckSum == ppResult::ppresCSOK;
|
|
res.flags.IpFailed = ppr.ipCheckSum == ppResult::ppresCSBad;
|
|
}
|
|
if(ppr.xxpStatus == ppResult::ppresXxpKnown)
|
|
{
|
|
if(ppr.TcpUdp == ppResult::ppresIsTCP) /* TCP */
|
|
{
|
|
if (f.fRxTCPChecksum)
|
|
{
|
|
res.flags.TcpOK = ppr.xxpCheckSum == ppResult::ppresCSOK || ppr.fixedXxpCS;
|
|
res.flags.TcpFailed = !res.flags.TcpOK;
|
|
}
|
|
}
|
|
else /* UDP */
|
|
{
|
|
if (f.fRxUDPChecksum)
|
|
{
|
|
res.flags.UdpOK = ppr.xxpCheckSum == ppResult::ppresCSOK || ppr.fixedXxpCS;
|
|
res.flags.UdpFailed = !res.flags.UdpOK;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if (ppr.ipStatus == ppResult::ppresIPV6)
|
|
{
|
|
if(ppr.xxpStatus == ppResult::ppresXxpKnown)
|
|
{
|
|
if(ppr.TcpUdp == ppResult::ppresIsTCP) /* TCP */
|
|
{
|
|
if (f.fRxTCPv6Checksum)
|
|
{
|
|
res.flags.TcpOK = ppr.xxpCheckSum == ppResult::ppresCSOK || ppr.fixedXxpCS;
|
|
res.flags.TcpFailed = !res.flags.TcpOK;
|
|
}
|
|
}
|
|
else /* UDP */
|
|
{
|
|
if (f.fRxUDPv6Checksum)
|
|
{
|
|
res.flags.UdpOK = ppr.xxpCheckSum == ppResult::ppresCSOK || ppr.fixedXxpCS;
|
|
res.flags.UdpFailed = !res.flags.UdpOK;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
void ParaNdis_PrintCharArray(int DebugPrintLevel, const CCHAR *data, size_t length)
|
|
{
|
|
ParaNdis_PrintTable<80, 10>(DebugPrintLevel, data, length, "%d", [](const CCHAR *p) { return *p; });
|
|
}
|
|
|
|
_PARANDIS_ADAPTER::~_PARANDIS_ADAPTER()
|
|
{
|
|
guestAnnouncePackets.Clear();
|
|
ParaNdis_CleanupContext(this);
|
|
}
|