Files
yttrium-virtio-gpu/NetKVM/wlh/ParaNdis6_Impl.cpp
T
2024-01-22 14:18:30 +02:00

1120 lines
39 KiB
C++
Executable File

/*
* This file contains NDIS6-specific implementation of driver's procedures.
*
* 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 "ParaNdis6.h"
#include "kdebugprint.h"
#include "ParaNdis_DebugHistory.h"
#include "Trace.h"
#ifdef NETKVM_WPP_ENABLED
#include "ParaNdis6_Impl.tmh"
#endif
static MINIPORT_DISABLE_INTERRUPT MiniportDisableInterruptEx;
static MINIPORT_ENABLE_INTERRUPT MiniportEnableInterruptEx;
static MINIPORT_INTERRUPT_DPC MiniportInterruptDPC;
static MINIPORT_ISR MiniportInterrupt;
static MINIPORT_ENABLE_MESSAGE_INTERRUPT MiniportEnableMSIInterrupt;
static MINIPORT_DISABLE_MESSAGE_INTERRUPT MiniportDisableMSIInterrupt;
static MINIPORT_MESSAGE_INTERRUPT MiniportMSIInterrupt;
static MINIPORT_MESSAGE_INTERRUPT_DPC MiniportMSIInterruptDpc;
static MINIPORT_PROCESS_SG_LIST ProcessSGListHandler;
static MINIPORT_ALLOCATE_SHARED_MEM_COMPLETE SharedMemAllocateCompleteHandler;
static MINIPORT_PROCESS_SG_LIST ProcessSGListHandler;
/**********************************************************
Implements general-purpose memory allocation routine
Parameters:
ULONG ulRequiredSize: block size
Return value:
PVOID allocated memory block
NULL on error
***********************************************************/
PVOID ParaNdis_AllocateMemoryRaw(NDIS_HANDLE MiniportHandle, ULONG ulRequiredSize)
{
return NdisAllocateMemoryWithTagPriority(
MiniportHandle,
ulRequiredSize,
PARANDIS_MEMORY_TAG,
NormalPoolPriority);
}
PVOID ParaNdis_AllocateMemory(PARANDIS_ADAPTER *pContext, ULONG ulRequiredSize)
{
return ParaNdis_AllocateMemoryRaw(pContext->MiniportHandle, ulRequiredSize);
}
/**********************************************************
Implements opening of adapter-specific configuration
Parameters:
Return value:
NDIS_HANDLE Handle of open configuration
NULL on error
***********************************************************/
NDIS_HANDLE ParaNdis_OpenNICConfiguration(PARANDIS_ADAPTER *pContext)
{
NDIS_CONFIGURATION_OBJECT co;
NDIS_HANDLE cfg;
NDIS_STATUS status;
DEBUG_ENTRY(2);
co.Header.Type = NDIS_OBJECT_TYPE_CONFIGURATION_OBJECT;
co.Header.Revision = NDIS_CONFIGURATION_OBJECT_REVISION_1;
co.Header.Size = NDIS_SIZEOF_CONFIGURATION_OBJECT_REVISION_1;
co.Flags = 0;
co.NdisHandle = pContext->MiniportHandle;
status = NdisOpenConfigurationEx(&co, &cfg);
if (status != NDIS_STATUS_SUCCESS)
cfg = NULL;
DEBUG_EXIT_STATUS(status == NDIS_STATUS_SUCCESS ? 2 : 0, status);
return cfg;
}
/**********************************************************
NDIS6 implementation of shared memory allocation
Parameters:
context
tCompletePhysicalAddress *pAddresses
the structure accumulates all our knowledge
about the allocation (size, addresses, cacheability etc)
Return value:
TRUE if the allocation was successful
***********************************************************/
BOOLEAN ParaNdis_InitialAllocatePhysicalMemory(
PARANDIS_ADAPTER *pContext,
ULONG ulSize,
tCompletePhysicalAddress *pAddresses)
{
NdisMAllocateSharedMemory(
pContext->MiniportHandle,
ulSize,
TRUE,
&pAddresses->Virtual,
&pAddresses->Physical);
if (pAddresses->Virtual != NULL)
{
pAddresses->size = ulSize;
pContext->extraStatistics.allocatedSharedMemory += ulSize;
return TRUE;
}
return FALSE;
}
/**********************************************************
NDIS6 implementation of shared memory freeing
Parameters:
context
tCompletePhysicalAddress *pAddresses
the structure accumulates all our knowledge
about the allocation (size, addresses, cacheability etc)
filled by ParaNdis_InitialAllocatePhysicalMemory
***********************************************************/
VOID ParaNdis_FreePhysicalMemory(
PARANDIS_ADAPTER *pContext,
tCompletePhysicalAddress *pAddresses)
{
NdisMFreeSharedMemory(
pContext->MiniportHandle,
pAddresses->size,
TRUE,
pAddresses->Virtual,
pAddresses->Physical);
}
#if (NDIS_SUPPORT_NDIS620)
typedef MINIPORT_SYNCHRONIZE_INTERRUPT_HANDLER NDIS_SYNC_PROC_TYPE;
#else
typedef PVOID NDIS_SYNC_PROC_TYPE;
#endif
BOOLEAN ParaNdis_SynchronizeWithInterrupt(
PARANDIS_ADAPTER *pContext,
ULONG messageId,
tSynchronizedProcedure procedure,
PVOID parameter)
{
NDIS_SYNC_PROC_TYPE syncProc;
syncProc = (NDIS_SYNC_PROC_TYPE) procedure;
return NdisMSynchronizeWithInterruptEx(pContext->InterruptHandle, messageId, syncProc, parameter);
}
/**********************************************************
NDIS-required procedure for hardware interrupt registration
Parameters:
IN PVOID MiniportInterruptContext (actually Adapter context)
***********************************************************/
static VOID MiniportDisableInterruptEx(IN PVOID MiniportInterruptContext)
{
DEBUG_ENTRY(0);
PARANDIS_ADAPTER *pContext = (PARANDIS_ADAPTER *)MiniportInterruptContext;
/* TODO - make sure that interrups are not reenabled by the DPC callback*/
for (UINT i = 0; i < pContext->nPathBundles; i++)
{
pContext->pPathBundles[i].txPath.DisableInterrupts();
pContext->pPathBundles[i].rxPath.DisableInterrupts();
}
if (pContext->bCXPathCreated)
{
pContext->CXPath.DisableInterrupts();
}
}
/**********************************************************
NDIS-required procedure for hardware interrupt registration
Parameters:
IN PVOID MiniportInterruptContext (actually Adapter context)
***********************************************************/
static VOID MiniportEnableInterruptEx(IN PVOID MiniportInterruptContext)
{
DEBUG_ENTRY(0);
PARANDIS_ADAPTER *pContext = (PARANDIS_ADAPTER *)MiniportInterruptContext;
for (UINT i = 0; i < pContext->nPathBundles; i++)
{
pContext->pPathBundles[i].txPath.EnableInterrupts();
pContext->pPathBundles[i].rxPath.EnableInterrupts();
}
if (pContext->bCXPathCreated)
{
pContext->CXPath.EnableInterrupts();
}
}
/**********************************************************
NDIS-required procedure for hardware interrupt handling
Parameters:
IN PVOID MiniportInterruptContext (actually Adapter context)
OUT PBOOLEAN QueueDefaultInterruptDpc - set to TRUE for default DPC spawning
OUT PULONG TargetProcessors
Return value:
TRUE if recognized
***********************************************************/
static BOOLEAN MiniportInterrupt(
IN PVOID MiniportInterruptContext,
OUT PBOOLEAN QueueDefaultInterruptDpc,
OUT PULONG TargetProcessors
)
{
PARANDIS_ADAPTER *pContext = (PARANDIS_ADAPTER *)MiniportInterruptContext;
ULONG status = virtio_read_isr_status(&pContext->IODevice);
*TargetProcessors = 0;
if((status == 0) ||
(status == VIRTIO_NET_INVALID_INTERRUPT_STATUS))
{
*QueueDefaultInterruptDpc = FALSE;
return FALSE;
}
PARANDIS_STORE_LAST_INTERRUPT_TIMESTAMP(pContext);
if(!pContext->bDeviceInitialized) {
*QueueDefaultInterruptDpc = FALSE;
return TRUE;
}
for (UINT i = 0; i < pContext->nPathBundles; i++)
{
pContext->pPathBundles[i].txPath.DisableInterrupts();
pContext->pPathBundles[i].rxPath.DisableInterrupts();
}
if (pContext->bCXPathCreated)
{
pContext->CXPath.DisableInterrupts();
}
*QueueDefaultInterruptDpc = TRUE;
pContext->ulIrqReceived += 1;
return true;
}
// This procedure must work the same way as
// ParaNdis_ConfigureMSIXVectors when spreads vectors over RX/TX/CX pathes.
// Returns respective TX or RX path if exists, then CX path if exists
// (i.e. returns CX path only if it has dedicated vector)
// otherwise (unlikely) returns NULL
static CParaNdisAbstractPath *GetPathByMessageId(PARANDIS_ADAPTER *pContext, ULONG MessageId)
{
CParaNdisAbstractPath *path;
UINT bundleId = MessageId / 2;
if (bundleId < pContext->nPathBundles)
{
if (MessageId & 1)
{
path = &(pContext->pPathBundles[bundleId].rxPath);
}
else
{
path = &(pContext->pPathBundles[bundleId].txPath);
}
}
else if (pContext->CXPath.getMessageIndex() == MessageId)
{
path = &pContext->CXPath;
}
else
{
path = NULL;
}
return path;
}
/**********************************************************
NDIS-required procedure for MSI hardware interrupt handling
Parameters:
IN PVOID MiniportInterruptContext (actually Adapter context)
IN ULONG MessageId - specific interrupt index
OUT PBOOLEAN QueueDefaultInterruptDpc - - set to TRUE for default DPC spawning
OUT PULONG TargetProcessors
Return value:
TRUE if recognized
***********************************************************/
static BOOLEAN MiniportMSIInterrupt(
IN PVOID MiniportInterruptContext,
IN ULONG MessageId,
OUT PBOOLEAN QueueDefaultInterruptDpc,
OUT PULONG TargetProcessors
)
{
PARANDIS_ADAPTER *pContext = (PARANDIS_ADAPTER *)MiniportInterruptContext;
PARANDIS_STORE_LAST_INTERRUPT_TIMESTAMP(pContext);
*TargetProcessors = 0;
*QueueDefaultInterruptDpc = FALSE;
if (!pContext->bDeviceInitialized) {
return TRUE;
}
CParaNdisAbstractPath *path = GetPathByMessageId(pContext, MessageId);
if (NULL == path) {
*QueueDefaultInterruptDpc = FALSE;
return TRUE;
}
path->SetLastInterruptTimestamp(pContext->LastInterruptTimeStamp);
path->DisableInterrupts();
// emit DPC for processing of TX/RX or DPC for processing of CX path
// note that in case the CX shares vector with TX/RX the CX DPC is not
// scheduled and CX path is processed by ParaNdis_RXTXDPCWorkBody
// (see pContext->bSharedVectors for such case)
if (!path->FireDPC(MessageId))
{
*QueueDefaultInterruptDpc = TRUE;
#if !NDIS_SUPPORT_NDIS620
*TargetProcessors = (ULONG)path->DPCTargetProcessor;
#endif
}
pContext->ulIrqReceived += 1;
return true;
}
#if NDIS_SUPPORT_NDIS620
static __inline
VOID GetAffinityForCurrentCpu(PGROUP_AFFINITY pAffinity)
{
PROCESSOR_NUMBER ProcNum;
KeGetCurrentProcessorNumberEx(&ProcNum);
pAffinity->Group = ProcNum.Group;
pAffinity->Mask = 1;
pAffinity->Mask <<= ProcNum.Number;
}
#endif
/**********************************************************
NDIS-required procedure for DPC handling
Parameters:
PVOID MiniportInterruptContext (Adapter context)
***********************************************************/
static VOID MiniportInterruptDPC(
IN NDIS_HANDLE MiniportInterruptContext,
IN PVOID MiniportDpcContext,
IN PVOID ReceiveThrottleParameters,
IN PVOID NdisReserved2
)
{
PARANDIS_ADAPTER *pContext = (PARANDIS_ADAPTER *)MiniportInterruptContext;
bool requiresDPCRescheduling;
#if NDIS_SUPPORT_NDIS620
PNDIS_RECEIVE_THROTTLE_PARAMETERS RxThrottleParameters = (PNDIS_RECEIVE_THROTTLE_PARAMETERS)ReceiveThrottleParameters;
DEBUG_ENTRY(5);
RxThrottleParameters->MoreNblsPending = 0;
requiresDPCRescheduling = ParaNdis_RXTXDPCWorkBody(pContext, RxThrottleParameters->MaxNblsToIndicate);
if (requiresDPCRescheduling)
{
GROUP_AFFINITY Affinity;
GetAffinityForCurrentCpu(&Affinity);
NdisMQueueDpcEx(pContext->InterruptHandle, 0, &Affinity, MiniportDpcContext);
}
#else /* NDIS 6.0*/
DEBUG_ENTRY(5);
UNREFERENCED_PARAMETER(ReceiveThrottleParameters);
requiresDPCRescheduling = ParaNdis_RXTXDPCWorkBody(pContext, PARANDIS_UNLIMITED_PACKETS_TO_INDICATE);
if (requiresDPCRescheduling)
{
DPrintf(4, "[%s] Queued additional DPC for %d\n", __FUNCTION__, requiresDPCRescheduling);
NdisMQueueDpc(pContext->InterruptHandle, 0, 1 << KeGetCurrentProcessorNumber(), MiniportDpcContext);
}
#endif /* NDIS_SUPPORT_NDIS620 */
UNREFERENCED_PARAMETER(NdisReserved2);
}
/**********************************************************
A CX procedure for MSI DPC handling
Parameters:
KDPC * Dpc - The dpc structure for CX
IN ULONG MessageId - specific interrupt index
***********************************************************/
VOID _Function_class_(KDEFERRED_ROUTINE) MiniportMSIInterruptCXDpc(
struct _KDPC *Dpc,
IN PVOID MiniportInterruptContext,
IN PVOID NdisReserved1,
IN PVOID NdisReserved2
)
{
PARANDIS_ADAPTER *pContext = (PARANDIS_ADAPTER *)MiniportInterruptContext;
ParaNdis_CXDPCWorkBody(pContext);
UNREFERENCED_PARAMETER(Dpc);
UNREFERENCED_PARAMETER(NdisReserved1);
UNREFERENCED_PARAMETER(NdisReserved2);
}
/**********************************************************
NDIS-required procedure for MSI DPC handling
Parameters:
PVOID MiniportInterruptContext (Adapter context)
IN ULONG MessageId - specific interrupt index
***********************************************************/
static VOID MiniportMSIInterruptDpc(
IN PVOID MiniportInterruptContext,
IN ULONG MessageId,
IN PVOID MiniportDpcContext,
#if NDIS_SUPPORT_NDIS620
IN PVOID ReceiveThrottleParameters,
IN PVOID NdisReserved2
#else
IN PULONG NdisReserved1,
IN PULONG NdisReserved2
#endif
)
{
PARANDIS_ADAPTER *pContext = (PARANDIS_ADAPTER *)MiniportInterruptContext;
bool requireDPCRescheduling;
#if NDIS_SUPPORT_NDIS620
PNDIS_RECEIVE_THROTTLE_PARAMETERS RxThrottleParameters = (PNDIS_RECEIVE_THROTTLE_PARAMETERS)ReceiveThrottleParameters;
RxThrottleParameters->MoreNblsPending = 0;
requireDPCRescheduling = ParaNdis_RXTXDPCWorkBody(pContext, RxThrottleParameters->MaxNblsToIndicate);
if (requireDPCRescheduling)
{
GROUP_AFFINITY Affinity;
GetAffinityForCurrentCpu(&Affinity);
NdisMQueueDpcEx(pContext->InterruptHandle, MessageId, &Affinity, MiniportDpcContext);
}
#else
UNREFERENCED_PARAMETER(NdisReserved1);
requireDPCRescheduling = ParaNdis_RXTXDPCWorkBody(pContext, PARANDIS_UNLIMITED_PACKETS_TO_INDICATE);
if (requireDPCRescheduling)
{
NdisMQueueDpc(pContext->InterruptHandle, MessageId, 1 << KeGetCurrentProcessorNumber(), MiniportDpcContext);
}
#endif
UNREFERENCED_PARAMETER(NdisReserved2);
}
static VOID MiniportDisableMSIInterrupt(
IN PVOID MiniportInterruptContext,
IN ULONG MessageId
)
{
PARANDIS_ADAPTER *pContext = (PARANDIS_ADAPTER *)MiniportInterruptContext;
/* TODO - How we prevent DPC procedure from re-enabling interrupt? */
CParaNdisAbstractPath *path = GetPathByMessageId(pContext, MessageId);
path->DisableInterrupts();
}
static VOID MiniportEnableMSIInterrupt(
IN PVOID MiniportInterruptContext,
IN ULONG MessageId
)
{
PARANDIS_ADAPTER *pContext = (PARANDIS_ADAPTER *)MiniportInterruptContext;
CParaNdisAbstractPath *path = GetPathByMessageId(pContext, MessageId);
path->EnableInterrupts();
}
/**********************************************************
NDIS required handler for run-time allocation of physical memory
Parameters:
Return value:
***********************************************************/
static VOID SharedMemAllocateCompleteHandler(
IN NDIS_HANDLE MiniportAdapterContext,
IN PVOID VirtualAddress,
IN PNDIS_PHYSICAL_ADDRESS PhysicalAddress,
IN ULONG Length,
IN PVOID Context
)
{
UNREFERENCED_PARAMETER(MiniportAdapterContext);
UNREFERENCED_PARAMETER(VirtualAddress);
UNREFERENCED_PARAMETER(PhysicalAddress);
UNREFERENCED_PARAMETER(Length);
UNREFERENCED_PARAMETER(Context);
}
/*
We enter this procedure in two cases:
- number of MSIX vectors is at least nPathBundles*2 + 1
- there is single MSIX vector and one path bundle
*/
NDIS_STATUS ParaNdis_ConfigureMSIXVectors(PARANDIS_ADAPTER *pContext)
{
NDIS_STATUS status = NDIS_STATUS_RESOURCES;
UINT i;
u16 nVectors = (u16)pContext->pMSIXInfoTable->MessageCount;
PIO_INTERRUPT_MESSAGE_INFO pTable = pContext->pMSIXInfoTable;
if (pTable && pTable->MessageCount)
{
status = NDIS_STATUS_SUCCESS;
DPrintf(0, "[%s] Using MSIX interrupts (%d messages, irql %d)\n",
__FUNCTION__, pTable->MessageCount, pTable->UnifiedIrql);
for (i = 0; i < pContext->pMSIXInfoTable->MessageCount; ++i)
{
DPrintf(0, "[%s] MSIX message%d=%08X=>%I64X\n",
__FUNCTION__, i,
pTable->MessageInfo[i].MessageData,
pTable->MessageInfo[i].MessageAddress.QuadPart);
}
for (UINT j = 0; j < pContext->nPathBundles && status == NDIS_STATUS_SUCCESS; ++j)
{
u16 vector = 2 * u16(j);
status = pContext->pPathBundles[j].txPath.SetupMessageIndex(vector);
if (status == NDIS_STATUS_SUCCESS)
{
if (nVectors > 1) vector++;
status = pContext->pPathBundles[j].rxPath.SetupMessageIndex(vector);
}
DPrintf(0, "[%s] Using messages %u/%u for RX/TX queue %u\n", __FUNCTION__,
pContext->pPathBundles[j].rxPath.getMessageIndex(),
pContext->pPathBundles[j].txPath.getMessageIndex(),
j);
}
if (status == NDIS_STATUS_SUCCESS && pContext->bCXPathCreated)
{
u16 nVector;
/*
Usually there is own vector for control queue.
In corner case of one or two vectors control queue uses the same vector as RX or TX
and does not spawn its own DPC for processing
*/
if (nVectors < 3)
{
nVector = nVectors - 1;
pContext->bSharedVectors = TRUE;
}
else
{
nVector = 2 * u16(pContext->nPathBundles);
}
status = pContext->CXPath.SetupMessageIndex(nVector);
}
}
DEBUG_EXIT_STATUS(0, status);
return status;
}
void ParaNdis_RestoreDeviceConfigurationAfterReset(
PARANDIS_ADAPTER *pContext)
{
if (pContext->bUsingMSIX)
{
ParaNdis_ConfigureMSIXVectors(pContext);
}
}
static void DebugParseOffloadBits()
{
NDIS_TCP_IP_CHECKSUM_NET_BUFFER_LIST_INFO info;
tChecksumCheckResult res;
ULONG val = 1;
int level = 1;
while (val)
{
info.Value = (PVOID)(ULONG_PTR)val;
if (info.Receive.IpChecksumFailed) DPrintf(level, "W.%X=IPCS failed\n", val);
if (info.Receive.IpChecksumSucceeded) DPrintf(level, "W.%X=IPCS OK\n", val);
if (info.Receive.TcpChecksumFailed) DPrintf(level, "W.%X=TCPCS failed\n", val);
if (info.Receive.TcpChecksumSucceeded) DPrintf(level, "W.%X=TCPCS OK\n", val);
if (info.Receive.UdpChecksumFailed) DPrintf(level, "W.%X=UDPCS failed\n", val);
if (info.Receive.UdpChecksumSucceeded) DPrintf(level, "W.%X=UDPCS OK\n", val);
val = val << 1;
}
val = 1;
while (val)
{
res.value = val;
if (res.flags.IpFailed) DPrintf(level, "C.%X=IPCS failed\n", val);
if (res.flags.IpOK) DPrintf(level, "C.%X=IPCS OK\n", val);
if (res.flags.TcpFailed) DPrintf(level, "C.%X=TCPCS failed\n", val);
if (res.flags.TcpOK) DPrintf(level, "C.%X=TCPCS OK\n", val);
if (res.flags.UdpFailed) DPrintf(level, "C.%X=UDPCS failed\n", val);
if (res.flags.UdpOK) DPrintf(level, "C.%X=UDPCS OK\n", val);
val = val << 1;
}
}
/**********************************************************
NDIS6-related final initialization:
Installing interrupt handler
Allocate buffer list pool
Parameters:
Return value:
***********************************************************/
NDIS_STATUS ParaNdis_FinishSpecificInitialization(PARANDIS_ADAPTER *pContext)
{
NDIS_STATUS status = NDIS_STATUS_SUCCESS;
NET_BUFFER_LIST_POOL_PARAMETERS PoolParams;
NDIS_MINIPORT_INTERRUPT_CHARACTERISTICS mic;
DEBUG_ENTRY(0);
NdisZeroMemory(&mic, sizeof(mic));
mic.Header.Type = NDIS_OBJECT_TYPE_MINIPORT_INTERRUPT;
mic.Header.Revision = NDIS_MINIPORT_INTERRUPT_REVISION_1;
mic.Header.Size = NDIS_SIZEOF_MINIPORT_INTERRUPT_CHARACTERISTICS_REVISION_1;
mic.DisableInterruptHandler = MiniportDisableInterruptEx;
mic.EnableInterruptHandler = MiniportEnableInterruptEx;
mic.InterruptDpcHandler = MiniportInterruptDPC;
mic.InterruptHandler = MiniportInterrupt;
if (pContext->bUsingMSIX)
{
mic.MsiSupported = TRUE;
mic.MsiSyncWithAllMessages = TRUE;
mic.EnableMessageInterruptHandler = MiniportEnableMSIInterrupt;
mic.DisableMessageInterruptHandler = MiniportDisableMSIInterrupt;
mic.MessageInterruptHandler = MiniportMSIInterrupt;
mic.MessageInterruptDpcHandler = MiniportMSIInterruptDpc;
}
PoolParams.Header.Type = NDIS_OBJECT_TYPE_DEFAULT;
PoolParams.Header.Size = NDIS_SIZEOF_NET_BUFFER_LIST_POOL_PARAMETERS_REVISION_1;
PoolParams.Header.Revision = NET_BUFFER_LIST_POOL_PARAMETERS_REVISION_1;
PoolParams.ProtocolId = NDIS_PROTOCOL_ID_DEFAULT;
PoolParams.fAllocateNetBuffer = TRUE;
PoolParams.ContextSize = 0;
PoolParams.PoolTag = PARANDIS_MEMORY_TAG;
PoolParams.DataSize = 0;
pContext->BufferListsPool = NdisAllocateNetBufferListPool(pContext->MiniportHandle, &PoolParams);
if (!pContext->BufferListsPool)
{
status = NDIS_STATUS_RESOURCES;
}
#if defined(NETKVM_COPY_RX_DATA)
if (status == NDIS_STATUS_SUCCESS)
{
PoolParams.DataSize = pContext->MaxPacketSize.nMaxFullSizeOsRx;
pContext->BufferListsPoolForArm = NdisAllocateNetBufferListPool(pContext->MiniportHandle, &PoolParams);
if (!pContext->BufferListsPoolForArm)
{
status = NDIS_STATUS_RESOURCES;
}
}
#endif
if (status == NDIS_STATUS_SUCCESS)
{
status = NdisMRegisterInterruptEx(pContext->MiniportHandle, pContext, &mic, &pContext->InterruptHandle);
}
if (pContext->bUsingMSIX)
{
DPrintf(0, "[%s] MSIX message table %savailable, count = %u\n", __FUNCTION__, (mic.MessageInfoTable == nullptr ? "not " : ""),
(mic.MessageInfoTable == nullptr ? 0 : mic.MessageInfoTable->MessageCount));
}
else
{
DPrintf(0, "[%s] Not using MSIX\n", __FUNCTION__);
}
if (status == NDIS_STATUS_SUCCESS)
{
NDIS_SG_DMA_DESCRIPTION sgDesc;
sgDesc.Header.Type = NDIS_OBJECT_TYPE_SG_DMA_DESCRIPTION;
sgDesc.Header.Revision = NDIS_SG_DMA_DESCRIPTION_REVISION_1;
sgDesc.Header.Size = NDIS_SIZEOF_SG_DMA_DESCRIPTION_REVISION_1;
sgDesc.Flags = NDIS_SG_DMA_64_BIT_ADDRESS;
#if (NDIS_SUPPORT_NDIS650)
sgDesc.Flags |= NDIS_SG_DMA_V3_HAL_API;
#endif
sgDesc.MaximumPhysicalMapping = 0x10000; // 64K
sgDesc.ProcessSGListHandler = ProcessSGListHandler;
sgDesc.SharedMemAllocateCompleteHandler = SharedMemAllocateCompleteHandler;
sgDesc.ScatterGatherListSize = 0; // OUT value
status = NdisMRegisterScatterGatherDma(pContext->MiniportHandle, &sgDesc, &pContext->DmaHandle);
if (status != NDIS_STATUS_SUCCESS)
{
DPrintf(0, "[%s] ERROR: NdisMRegisterScatterGatherDma failed (%X)!\n", __FUNCTION__, status);
}
else
{
DPrintf(0, "[%s] SG recommended size %d\n", __FUNCTION__, sgDesc.ScatterGatherListSize);
}
}
if (status == NDIS_STATUS_SUCCESS)
{
if (NDIS_CONNECT_MESSAGE_BASED == mic.InterruptType)
{
pContext->pMSIXInfoTable = mic.MessageInfoTable;
}
else if (pContext->bUsingMSIX)
{
DPrintf(0, "[%s] ERROR: Interrupt type %d, message table %p\n",
__FUNCTION__, mic.InterruptType, mic.MessageInfoTable);
status = NDIS_STATUS_RESOURCE_CONFLICT;
}
ParaNdis6_ApplyOffloadPersistentConfiguration(pContext);
DebugParseOffloadBits();
}
DEBUG_EXIT_STATUS(0, status);
return status;
}
/**********************************************************
NDIS6-related final initialization:
Uninstalling interrupt handler
Dellocate buffer list pool
Parameters:
context
***********************************************************/
VOID ParaNdis_FinalizeCleanup(PARANDIS_ADAPTER *pContext)
{
// we zero context members to be able examine them in the debugger/dump
if (pContext->InterruptHandle)
{
NdisMDeregisterInterruptEx(pContext->InterruptHandle);
pContext->InterruptHandle = NULL;
}
if (pContext->BufferListsPool)
{
NdisFreeNetBufferListPool(pContext->BufferListsPool);
pContext->BufferListsPool = NULL;
}
if (pContext->BufferListsPoolForArm)
{
NdisFreeNetBufferListPool(pContext->BufferListsPoolForArm);
pContext->BufferListsPoolForArm = NULL;
}
if (pContext->DmaHandle)
{
NdisMDeregisterScatterGatherDma(pContext->DmaHandle);
pContext->DmaHandle = NULL;
}
}
static
void ParaNdis_AdjustRxBufferHolderLength(
pRxNetDescriptor p,
ULONG ulDataOffset)
{
PMDL NextMdlLinkage = p->Holder;
ULONG ulBytesLeft = p->PacketInfo.dataLength + ulDataOffset;
ULONG ulPageDescIndex = PARANDIS_FIRST_RX_DATA_PAGE;
while(NextMdlLinkage != NULL)
{
ULONG ulThisMdlBytes = min(p->PhysicalPages[ulPageDescIndex].size, ulBytesLeft);
NdisAdjustMdlLength(NextMdlLinkage, ulThisMdlBytes);
ulBytesLeft -= ulThisMdlBytes;
NextMdlLinkage = NDIS_MDL_LINKAGE(NextMdlLinkage);
ulPageDescIndex++;
}
NETKVM_ASSERT(ulBytesLeft == 0);
}
#if PARANDIS_SUPPORT_RSS
static ULONG HashReportToHashType(USHORT report)
{
static const ULONG table[VIRTIO_NET_HASH_REPORT_MAX + 1] =
{
#if (NDIS_SUPPORT_NDIS680)
0, NDIS_HASH_IPV4, NDIS_HASH_TCP_IPV4, NDIS_HASH_UDP_IPV4,
NDIS_HASH_IPV6, NDIS_HASH_TCP_IPV6, NDIS_HASH_UDP_IPV6,
NDIS_HASH_IPV6_EX, NDIS_HASH_TCP_IPV6_EX, NDIS_HASH_UDP_IPV6_EX
#else
0, NDIS_HASH_IPV4, NDIS_HASH_TCP_IPV4, 0,
NDIS_HASH_IPV6, NDIS_HASH_TCP_IPV6, 0,
NDIS_HASH_IPV6_EX, NDIS_HASH_TCP_IPV6_EX, 0
#endif
};
if (report > VIRTIO_NET_HASH_REPORT_MAX)
{
return 0;
}
return table[report];
}
#endif
static __inline
VOID NBLSetRSSInfo(PPARANDIS_ADAPTER pContext, PNET_BUFFER_LIST pNBL, PNET_PACKET_INFO PacketInfo, PVOID virtioHeader)
{
#if PARANDIS_SUPPORT_RSS
CNdisRWLockState lockState;
pContext->RSSParameters.rwLock.acquireReadDpr(lockState);
if(pContext->RSSParameters.RSSMode != PARANDIS_RSS_MODE::PARANDIS_RSS_DISABLED)
{
NET_BUFFER_LIST_SET_HASH_TYPE (pNBL, PacketInfo->RSSHash.Type);
NET_BUFFER_LIST_SET_HASH_FUNCTION(pNBL, PacketInfo->RSSHash.Function);
NET_BUFFER_LIST_SET_HASH_VALUE (pNBL, PacketInfo->RSSHash.Value);
if (PacketInfo->RSSHash.Type && pContext->bHashReportedByDevice)
{
virtio_net_hdr_v1_hash *ph = (virtio_net_hdr_v1_hash *)virtioHeader;
ULONG val = HashReportToHashType(ph->hash_report);
if (val != PacketInfo->RSSHash.Type || ph->hash_value != PacketInfo->RSSHash.Value)
{
pContext->extraStatistics.framesRSSError++;
TraceNoPrefix(0, "%s: hash mistake: reported %d, correct %X\n", __FUNCTION__,
ph->hash_report, PacketInfo->RSSHash.Type);
}
}
}
pContext->RSSParameters.rwLock.releaseDpr(lockState);
#else
UNREFERENCED_PARAMETER(pContext);
UNREFERENCED_PARAMETER(pNBL);
UNREFERENCED_PARAMETER(PacketInfo);
UNREFERENCED_PARAMETER(virtioHeader);
#endif
}
static __inline
VOID NBLSet8021QInfo(PPARANDIS_ADAPTER pContext, PNET_BUFFER_LIST pNBL, PNET_PACKET_INFO pPacketInfo)
{
NDIS_NET_BUFFER_LIST_8021Q_INFO qInfo;
qInfo.Value = NULL;
if (IsPrioritySupported(pContext))
qInfo.TagHeader.UserPriority = pPacketInfo->Vlan.UserPriority;
if (IsVlanSupported(pContext))
qInfo.TagHeader.VlanId = pPacketInfo->Vlan.VlanId;
if(qInfo.Value != NULL)
pContext->extraStatistics.framesRxPriority++;
NET_BUFFER_LIST_INFO(pNBL, Ieee8021QNetBufferListInfo) = qInfo.Value;
}
#if PARANDIS_SUPPORT_RSC
static __inline
UINT PktGetTCPCoalescedSegmentsCount(PPARANDIS_ADAPTER pContext,
PNET_PACKET_INFO PacketInfo,
UINT mss)
{
// We have no exact data from the device, but can evaluate the number
// of coalesced segments according to mss (max segment size) value
// provided in the packet header. It includes only size of
// TCP payload and TCP payload of final packet was earlier
// splitted over TCP segments with payload of mss bytes each
ULONG TcpHeaderOffset = PacketInfo->L2HdrLen + PacketInfo->L3HdrLen;
auto TCPHdr = reinterpret_cast<TCPHeader *>RtlOffsetToPointer(PacketInfo->headersBuffer, TcpHeaderOffset);
ULONG IpAndTcpHeaderSize = PacketInfo->L3HdrLen + TCP_HEADER_LENGTH(TCPHdr);
ULONG payloadLen = PacketInfo->L2PayloadLen - IpAndTcpHeaderSize;
// if we have meaningless value from the device, use
// the best estimation - max possible TCP payload
// nMaxDataSize (typically 1500) starts after ethernet header
ULONG maxMss = pContext->MaxPacketSize.nMaxDataSize - IpAndTcpHeaderSize;
if (mss > maxMss || mss == 0)
{
mss = maxMss;
}
return payloadLen / mss + !!(payloadLen % mss);
}
static __inline
VOID NBLSetRSCInfo(PPARANDIS_ADAPTER pContext, PNET_BUFFER_LIST pNBL,
PNET_PACKET_INFO PacketInfo, UINT nCoalescedSegments, USHORT nDupAck)
{
NDIS_TCP_IP_CHECKSUM_NET_BUFFER_LIST_INFO qCSInfo;
qCSInfo.Value = NULL;
qCSInfo.Receive.IpChecksumSucceeded = TRUE;
qCSInfo.Receive.IpChecksumValueInvalid = TRUE;
qCSInfo.Receive.TcpChecksumSucceeded = TRUE;
qCSInfo.Receive.TcpChecksumValueInvalid = TRUE;
NET_BUFFER_LIST_INFO(pNBL, TcpIpChecksumNetBufferListInfo) = qCSInfo.Value;
NET_BUFFER_LIST_COALESCED_SEG_COUNT(pNBL) = (USHORT) nCoalescedSegments;
NET_BUFFER_LIST_DUP_ACK_COUNT(pNBL) = nDupAck;
NdisInterlockedAddLargeStatistic(&pContext->RSC.Statistics.CoalescedOctets, PacketInfo->L2PayloadLen);
NdisInterlockedAddLargeStatistic(&pContext->RSC.Statistics.CoalesceEvents, 1);
NdisInterlockedAddLargeStatistic(&pContext->RSC.Statistics.CoalescedPkts, nCoalescedSegments);
}
#endif
#if defined(NETKVM_COPY_RX_DATA)
static PNET_BUFFER_LIST CloneNblFreeOriginalForArm(PARANDIS_ADAPTER *pContext, PNET_BUFFER_LIST original, PVOID bufferDesc)
{
PNET_BUFFER_LIST pNewNbl;
if (!original)
{
return NULL;
}
pNewNbl = NdisAllocateNetBufferList(pContext->BufferListsPoolForArm, 0, NULL);
if (pNewNbl)
{
PNET_BUFFER src = NET_BUFFER_LIST_FIRST_NB(original);
PNET_BUFFER dest = NET_BUFFER_LIST_FIRST_NB(pNewNbl);
ULONG done = 0;
NET_BUFFER_DATA_LENGTH(dest) = NET_BUFFER_DATA_LENGTH(src);
NET_BUFFER_DATA_OFFSET(dest) = 0;
NET_BUFFER_CURRENT_MDL_OFFSET(dest) = 0;
NdisCopyFromNetBufferToNetBuffer(dest, 0, NET_BUFFER_DATA_LENGTH(src), src, 0, &done);
if (done == NET_BUFFER_DATA_LENGTH(src))
{
NdisCopyReceiveNetBufferListInfo(pNewNbl, original);
pNewNbl->MiniportReserved[0] = bufferDesc;
}
else
{
DPrintf(0, "[%s] ERROR: Can't copy data to NBL (%d != %d)\n",
__FUNCTION__, done, NET_BUFFER_DATA_LENGTH(src));
NdisFreeNetBufferList(pNewNbl);
pNewNbl = NULL;
}
}
else
{
DPrintf(0, "[%s] ERROR: Can't allocate NBL\n", __FUNCTION__);
}
NdisFreeNetBufferList(original);
return pNewNbl;
}
#else
#define CloneNblFreeOriginalForArm(ctx, org, bufDesc) (org)
#endif
/**********************************************************
NDIS6 implementation of packet indication
Parameters:
context
PVOID pBuffersDescriptor - VirtIO buffer descriptor of data buffer
BOOLEAN bPrepareOnly - only return NBL for further indication in batch
Return value:
TRUE is packet indicated
FALSE if not (in this case, the descriptor should be freed now)
If priority header is in the packet. it will be removed and *pLength decreased
***********************************************************/
tPacketIndicationType ParaNdis_PrepareReceivedPacket(
PARANDIS_ADAPTER *pContext,
pRxNetDescriptor pBuffersDesc,
PUINT pnCoalescedSegmentsCount)
{
PMDL pMDL = pBuffersDesc->Holder;
PNET_BUFFER_LIST pNBL = NULL;
*pnCoalescedSegmentsCount = 1;
if (pMDL)
{
ULONG nBytesStripped = 0;
PNET_PACKET_INFO pPacketInfo = &pBuffersDesc->PacketInfo;
if (pContext->ulPriorityVlanSetting && pPacketInfo->hasVlanHeader)
{
nBytesStripped = ParaNdis_StripVlanHeaderMoveHead(pPacketInfo);
}
ParaNdis_PadPacketToMinimalLength(pPacketInfo);
ParaNdis_AdjustRxBufferHolderLength(pBuffersDesc, nBytesStripped);
pNBL = NdisAllocateNetBufferAndNetBufferList(pContext->BufferListsPool, 0, 0, pMDL, nBytesStripped, pPacketInfo->dataLength);
if (pNBL)
{
virtio_net_hdr_mrg_rxbuf *pHeader = (virtio_net_hdr_mrg_rxbuf *) pBuffersDesc->PhysicalPages[0].Virtual;
tChecksumCheckResult csRes;
NDIS_TCP_IP_CHECKSUM_NET_BUFFER_LIST_INFO qCSInfo;
qCSInfo.Value = NULL;
pNBL->SourceHandle = pContext->MiniportHandle;
NBLSetRSSInfo(pContext, pNBL, pPacketInfo, pHeader);
NBLSet8021QInfo(pContext, pNBL, pPacketInfo);
pNBL->MiniportReserved[0] = pBuffersDesc;
#if PARANDIS_SUPPORT_RSC
csRes.value = 0;
csRes.flags.IpOK = true;
csRes.flags.TcpOK = true;
if (pHeader->hdr.gso_type != VIRTIO_NET_HDR_GSO_NONE)
{
USHORT nDupAcks = 0;
if (pHeader->hdr.flags & VIRTIO_NET_HDR_F_RSC_INFO)
{
*pnCoalescedSegmentsCount = pHeader->hdr.rsc_ext_num_packets;
nDupAcks = pHeader->hdr.rsc_ext_num_dupacks;
pContext->extraStatistics.framesCoalescedWindows++;
}
else
{
*pnCoalescedSegmentsCount = PktGetTCPCoalescedSegmentsCount(pContext, pPacketInfo, pHeader->hdr.gso_size);
pContext->extraStatistics.framesCoalescedHost++;
}
NBLSetRSCInfo(pContext, pNBL, pPacketInfo, *pnCoalescedSegmentsCount, 0);
// according to the spec the device does not calculate TCP checksum
qCSInfo.Receive.IpChecksumValueInvalid = true;
qCSInfo.Receive.TcpChecksumValueInvalid = true;
}
else
#endif
{
csRes = ParaNdis_CheckRxChecksum(
pContext,
pHeader->hdr.flags,
&pBuffersDesc->PhysicalPages[PARANDIS_FIRST_RX_DATA_PAGE],
pPacketInfo,
nBytesStripped, TRUE);
}
if (csRes.value)
{
qCSInfo.Receive.IpChecksumFailed = csRes.flags.IpFailed;
qCSInfo.Receive.IpChecksumSucceeded = csRes.flags.IpOK;
qCSInfo.Receive.TcpChecksumFailed = csRes.flags.TcpFailed;
qCSInfo.Receive.TcpChecksumSucceeded = csRes.flags.TcpOK;
qCSInfo.Receive.UdpChecksumFailed = csRes.flags.UdpFailed;
qCSInfo.Receive.UdpChecksumSucceeded = csRes.flags.UdpOK;
}
NET_BUFFER_LIST_INFO(pNBL, TcpIpChecksumNetBufferListInfo) = qCSInfo.Value;
DPrintf(1, "datalen %d, GSO type/flags %d:%d, mss %d, %d segments, CS %X->%X\n",
pPacketInfo->dataLength, pHeader->hdr.gso_type, pHeader->hdr.flags, pHeader->hdr.gso_size,
*pnCoalescedSegmentsCount, csRes.value, (ULONG)(ULONG_PTR)qCSInfo.Value);
pNBL->Status = NDIS_STATUS_SUCCESS;
#if defined(ENABLE_HISTORY_LOG)
{
tTcpIpPacketParsingResult packetReview = ParaNdis_CheckSumVerify(
RtlOffsetToPointer(pPacketInfo->headersBuffer, ETH_HEADER_SIZE),
pPacketInfo->dataLength,
pcrIpChecksum | pcrTcpChecksum | pcrUdpChecksum,
__FUNCTION__
);
ParaNdis_DebugHistory(pContext, hopPacketReceived, pNBL, pPacketInfo->dataLength, (ULONG)(ULONG_PTR)qInfo.Value, packetReview.value);
}
#endif
}
}
return CloneNblFreeOriginalForArm(pContext, pNBL, pBuffersDesc);
}
NDIS_STATUS ParaNdis_ExactSendFailureStatus(PARANDIS_ADAPTER *pContext)
{
NDIS_STATUS status = NDIS_STATUS_FAILURE;
if (!pContext->bConnected) status = NDIS_STATUS_MEDIA_DISCONNECTED;
if (pContext->bSurprizeRemoved) status = NDIS_STATUS_NOT_ACCEPTED;
return status;
}
BOOLEAN ParaNdis_IsTxRxPossible(PARANDIS_ADAPTER *pContext)
{
BOOLEAN b;
b = !pContext->bSurprizeRemoved && pContext->fCurrentLinkState == MediaConnectStateConnected;
return b;
}
/**********************************************************
NDIS required handler for run-time allocation of scatter-gather list
Parameters:
pSGL - scatter-hather list of elements (possible NULL when called directly)
Context - (tNetBufferEntry *) for specific NET_BUFFER in NBL
Called on DPC (DDK claims it)
***********************************************************/
VOID ProcessSGListHandler(
IN PDEVICE_OBJECT pDO,
IN PVOID Reserved,
IN PSCATTER_GATHER_LIST pSGL,
IN PVOID Context
)
{
UNREFERENCED_PARAMETER(Reserved);
UNREFERENCED_PARAMETER(pDO);
auto NBHolder = static_cast<CNB*>(Context);
NBHolder->MappingDone(pSGL);
}
/**********************************************************
Required procedure of NDIS
NDIS wants to cancel sending of each list which has specified CancelID
Can be tested only under NDIS Test
***********************************************************/
VOID ParaNdis6_CancelSendNetBufferLists(
NDIS_HANDLE miniportAdapterContext,
PVOID pCancelId)
{
PARANDIS_ADAPTER *pContext = (PARANDIS_ADAPTER *)miniportAdapterContext;
DEBUG_ENTRY(0);
for (UINT i = 0; i < pContext->nPathBundles; i++)
{
pContext->pPathBundles[i].txPath.CancelNBLs(pCancelId);
}
}