1 /*
2  * This file contains NDIS driver procedures, common for NDIS5 and NDIS6
3  *
4  * Copyright (c) 2008-2017 Red Hat, Inc.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met :
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and / or other materials provided with the distribution.
14  * 3. Neither the names of the copyright holders nor the names of their contributors
15  *    may be used to endorse or promote products derived from this software
16  *    without specific prior written permission.
17  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED.IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE
21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  */
29 #include "ndis56common.h"
30 
31 #ifdef WPP_EVENT_TRACING
32 #include "ParaNdis-Common.tmh"
33 #endif
34 
35 static void ReuseReceiveBufferRegular(PARANDIS_ADAPTER *pContext, pIONetDescriptor pBuffersDescriptor);
36 static void ReuseReceiveBufferPowerOff(PARANDIS_ADAPTER *pContext, pIONetDescriptor pBuffersDescriptor);
37 
38 //#define ROUNDSIZE(sz) ((sz + 15) & ~15)
39 #define MAX_VLAN_ID     4095
40 
41 #if 0
42 void FORCEINLINE DebugDumpPacket(LPCSTR prefix, PVOID header, int level)
43 {
44     PUCHAR peth = (PUCHAR)header;
45     DPrintf(level, ("[%s] %02X%02X%02X%02X%02X%02X => %02X%02X%02X%02X%02X%02X", prefix,
46         peth[6], peth[7], peth[8], peth[9], peth[10], peth[11],
47         peth[0], peth[1], peth[2], peth[3], peth[4], peth[5]));
48 }
49 #else
50 void FORCEINLINE DebugDumpPacket(LPCSTR prefix, PVOID header, int level)
51 {
52 }
53 #endif
54 
55 
56 
57 /**********************************************************
58 Validates MAC address
59 Valid MAC address is not broadcast, not multicast, not empty
60 if bLocal is set, it must be LOCAL
61 if not, is must be non-local or local
62 Parameters:
63     PUCHAR pcMacAddress - MAC address to validate
64     BOOLEAN bLocal      - TRUE, if we validate locally administered address
65 Return value:
66     TRUE if valid
67 ***********************************************************/
68 BOOLEAN ParaNdis_ValidateMacAddress(PUCHAR pcMacAddress, BOOLEAN bLocal)
69 {
70     BOOLEAN bLA = FALSE, bEmpty, bBroadcast, bMulticast = FALSE;
71     bBroadcast = ETH_IS_BROADCAST(pcMacAddress);
72     bLA = !bBroadcast && ETH_IS_LOCALLY_ADMINISTERED(pcMacAddress);
73     bMulticast = !bBroadcast && ETH_IS_MULTICAST(pcMacAddress);
74     bEmpty = ETH_IS_EMPTY(pcMacAddress);
75     return !bBroadcast && !bEmpty && !bMulticast && (!bLocal || bLA);
76 }
77 
78 static eInspectedPacketType QueryPacketType(PVOID data)
79 {
80     if (ETH_IS_BROADCAST(data))
81         return iptBroadcast;
82     if (ETH_IS_MULTICAST(data))
83         return iptMulticast;
84     return iptUnicast;
85 }
86 
87 typedef struct _tagConfigurationEntry
88 {
89     const char      *Name;
90     ULONG           ulValue;
91     ULONG           ulMinimal;
92     ULONG           ulMaximal;
93 }tConfigurationEntry;
94 
95 typedef struct _tagConfigurationEntries
96 {
97     tConfigurationEntry isPromiscuous;
98     tConfigurationEntry PrioritySupport;
99     tConfigurationEntry ConnectRate;
100     tConfigurationEntry isLogEnabled;
101     tConfigurationEntry debugLevel;
102     tConfigurationEntry connectTimer;
103     tConfigurationEntry dpcChecker;
104     tConfigurationEntry TxCapacity;
105     tConfigurationEntry RxCapacity;
106     tConfigurationEntry InterruptRecovery;
107     tConfigurationEntry LogStatistics;
108     tConfigurationEntry PacketFiltering;
109     tConfigurationEntry ScatterGather;
110     tConfigurationEntry BatchReceive;
111     tConfigurationEntry OffloadTxChecksum;
112     tConfigurationEntry OffloadTxLSO;
113     tConfigurationEntry OffloadRxCS;
114     tConfigurationEntry OffloadGuestCS;
115     tConfigurationEntry UseSwTxChecksum;
116     tConfigurationEntry IPPacketsCheck;
117     tConfigurationEntry stdIpcsV4;
118     tConfigurationEntry stdTcpcsV4;
119     tConfigurationEntry stdTcpcsV6;
120     tConfigurationEntry stdUdpcsV4;
121     tConfigurationEntry stdUdpcsV6;
122     tConfigurationEntry stdLsoV1;
123     tConfigurationEntry stdLsoV2ip4;
124     tConfigurationEntry stdLsoV2ip6;
125     tConfigurationEntry PriorityVlanTagging;
126     tConfigurationEntry VlanId;
127     tConfigurationEntry UseMergeableBuffers;
128     tConfigurationEntry MTU;
129     tConfigurationEntry NumberOfHandledRXPackersInDPC;
130     tConfigurationEntry Indirect;
131 }tConfigurationEntries;
132 
133 static const tConfigurationEntries defaultConfiguration =
134 {
135     { "Promiscuous",    0,  0,  1 },
136     { "Priority",       0,  0,  1 },
137     { "ConnectRate",    100,10,10000 },
138     { "DoLog",          1,  0,  1 },
139     { "DebugLevel",     2,  0,  8 },
140     { "ConnectTimer",   0,  0,  300000 },
141     { "DpcCheck",       0,  0,  2 },
142     { "TxCapacity",     1024,   16, 1024 },
143     { "RxCapacity",     256, 32, 1024 },
144     { "InterruptRecovery",  0, 0, 1},
145     { "LogStatistics",  0, 0, 10000},
146     { "PacketFilter",   1, 0, 1},
147     { "Gather",         1, 0, 1},
148     { "BatchReceive",   1, 0, 1},
149     { "Offload.TxChecksum", 0, 0, 31},
150     { "Offload.TxLSO",  0, 0, 2},
151     { "Offload.RxCS",   0, 0, 31},
152     { "Offload.GuestCS", 0, 0, 1},
153     { "UseSwTxChecksum", 0, 0, 1 },
154     { "IPPacketsCheck", 0, 0, 3 },
155     { "*IPChecksumOffloadIPv4", 3, 0, 3 },
156     { "*TCPChecksumOffloadIPv4",3, 0, 3 },
157     { "*TCPChecksumOffloadIPv6",3, 0, 3 },
158     { "*UDPChecksumOffloadIPv4",3, 0, 3 },
159     { "*UDPChecksumOffloadIPv6",3, 0, 3 },
160     { "*LsoV1IPv4", 1, 0, 1 },
161     { "*LsoV2IPv4", 1, 0, 1 },
162     { "*LsoV2IPv6", 1, 0, 1 },
163     { "*PriorityVLANTag", 3, 0, 3},
164     { "VlanId", 0, 0, MAX_VLAN_ID},
165     { "MergeableBuf", 1, 0, 1},
166     { "MTU", 1500, 500, 65500},
167     { "NumberOfHandledRXPackersInDPC", MAX_RX_LOOPS, 1, 10000},
168     { "Indirect", 0, 0, 2},
169 };
170 
171 static void ParaNdis_ResetVirtIONetDevice(PARANDIS_ADAPTER *pContext)
172 {
173     virtio_device_reset(&pContext->IODevice);
174     DPrintf(0, ("[%s] Done", __FUNCTION__));
175     /* reset all the features in the device */
176     pContext->ulCurrentVlansFilterSet = 0;
177     pContext->ullGuestFeatures = 0;
178 #ifdef VIRTIO_RESET_VERIFY
179     if (1)
180     {
181         u8 devStatus;
182         devStatus = virtio_get_status(&pContext->IODevice);
183         if (devStatus)
184         {
185             DPrintf(0, ("[%s] Device status is still %02X", __FUNCTION__, (ULONG)devStatus));
186             virtio_device_reset(&pContext->IODevice);
187             devStatus = virtio_get_status(&pContext->IODevice);
188             DPrintf(0, ("[%s] Device status on retry %02X", __FUNCTION__, (ULONG)devStatus));
189         }
190     }
191 #endif
192 }
193 
194 /**********************************************************
195 Gets integer value for specifies in pEntry->Name name
196 Parameters:
197     NDIS_HANDLE cfg  previously open configuration
198     tConfigurationEntry *pEntry - Entry to fill value in
199 ***********************************************************/
200 static void GetConfigurationEntry(NDIS_HANDLE cfg, tConfigurationEntry *pEntry)
201 {
202     NDIS_STATUS status;
203     const char *statusName;
204     NDIS_STRING name = {0};
205     PNDIS_CONFIGURATION_PARAMETER pParam = NULL;
206     NDIS_PARAMETER_TYPE ParameterType = NdisParameterInteger;
207     NdisInitializeString(&name, (PUCHAR)pEntry->Name);
208     NdisReadConfiguration(
209         &status,
210         &pParam,
211         cfg,
212         &name,
213         ParameterType);
214     if (status == NDIS_STATUS_SUCCESS)
215     {
216         ULONG ulValue = pParam->ParameterData.IntegerData;
217         if (ulValue >= pEntry->ulMinimal && ulValue <= pEntry->ulMaximal)
218         {
219             pEntry->ulValue = ulValue;
220             statusName = "value";
221         }
222         else
223         {
224             statusName = "out of range";
225         }
226     }
227     else
228     {
229         statusName = "nothing";
230     }
231     DPrintf(2, ("[%s] %s read for %s - 0x%x",
232         __FUNCTION__,
233         statusName,
234         pEntry->Name,
235         pEntry->ulValue));
236     if (name.Buffer) NdisFreeString(name);
237 }
238 
239 static void DisableLSOv4Permanently(PARANDIS_ADAPTER *pContext, LPCSTR procname, LPCSTR reason)
240 {
241     if (pContext->Offload.flagsValue & osbT4Lso)
242     {
243         DPrintf(0, ("[%s] Warning: %s", procname, reason));
244         pContext->Offload.flagsValue &= ~osbT4Lso;
245         ParaNdis_ResetOffloadSettings(pContext, NULL, NULL);
246     }
247 }
248 
249 static void DisableLSOv6Permanently(PARANDIS_ADAPTER *pContext, LPCSTR procname, LPCSTR reason)
250 {
251     if (pContext->Offload.flagsValue & osbT6Lso)
252     {
253         DPrintf(0, ("[%s] Warning: %s", procname, reason));
254         pContext->Offload.flagsValue &= ~osbT6Lso;
255         ParaNdis_ResetOffloadSettings(pContext, NULL, NULL);
256     }
257 }
258 
259 static void DisableBothLSOPermanently(PARANDIS_ADAPTER *pContext, LPCSTR procname, LPCSTR reason)
260 {
261     if (pContext->Offload.flagsValue & (osbT4Lso | osbT6Lso))
262     {
263         DPrintf(0, ("[%s] Warning: %s", procname, reason));
264         pContext->Offload.flagsValue &= ~(osbT6Lso | osbT4Lso);
265         ParaNdis_ResetOffloadSettings(pContext, NULL, NULL);
266     }
267 }
268 
269 /**********************************************************
270 Loads NIC parameters from adapter registry key
271 Parameters:
272     context
273     PUCHAR *ppNewMACAddress - pointer to hold MAC address if configured from host
274 ***********************************************************/
275 static void ReadNicConfiguration(PARANDIS_ADAPTER *pContext, PUCHAR *ppNewMACAddress)
276 {
277     NDIS_HANDLE cfg;
278     tConfigurationEntries *pConfiguration = ParaNdis_AllocateMemory(pContext, sizeof(tConfigurationEntries));
279     if (pConfiguration)
280     {
281         *pConfiguration = defaultConfiguration;
282         cfg = ParaNdis_OpenNICConfiguration(pContext);
283         if (cfg)
284         {
285             GetConfigurationEntry(cfg, &pConfiguration->isLogEnabled);
286             GetConfigurationEntry(cfg, &pConfiguration->debugLevel);
287             GetConfigurationEntry(cfg, &pConfiguration->ConnectRate);
288             GetConfigurationEntry(cfg, &pConfiguration->PrioritySupport);
289             GetConfigurationEntry(cfg, &pConfiguration->isPromiscuous);
290             GetConfigurationEntry(cfg, &pConfiguration->TxCapacity);
291             GetConfigurationEntry(cfg, &pConfiguration->RxCapacity);
292             GetConfigurationEntry(cfg, &pConfiguration->connectTimer);
293             GetConfigurationEntry(cfg, &pConfiguration->dpcChecker);
294             GetConfigurationEntry(cfg, &pConfiguration->InterruptRecovery);
295             GetConfigurationEntry(cfg, &pConfiguration->LogStatistics);
296             GetConfigurationEntry(cfg, &pConfiguration->PacketFiltering);
297             GetConfigurationEntry(cfg, &pConfiguration->ScatterGather);
298             GetConfigurationEntry(cfg, &pConfiguration->BatchReceive);
299             GetConfigurationEntry(cfg, &pConfiguration->OffloadTxChecksum);
300             GetConfigurationEntry(cfg, &pConfiguration->OffloadTxLSO);
301             GetConfigurationEntry(cfg, &pConfiguration->OffloadRxCS);
302             GetConfigurationEntry(cfg, &pConfiguration->OffloadGuestCS);
303             GetConfigurationEntry(cfg, &pConfiguration->UseSwTxChecksum);
304             GetConfigurationEntry(cfg, &pConfiguration->IPPacketsCheck);
305             GetConfigurationEntry(cfg, &pConfiguration->stdIpcsV4);
306             GetConfigurationEntry(cfg, &pConfiguration->stdTcpcsV4);
307             GetConfigurationEntry(cfg, &pConfiguration->stdTcpcsV6);
308             GetConfigurationEntry(cfg, &pConfiguration->stdUdpcsV4);
309             GetConfigurationEntry(cfg, &pConfiguration->stdUdpcsV6);
310             GetConfigurationEntry(cfg, &pConfiguration->stdLsoV1);
311             GetConfigurationEntry(cfg, &pConfiguration->stdLsoV2ip4);
312             GetConfigurationEntry(cfg, &pConfiguration->stdLsoV2ip6);
313             GetConfigurationEntry(cfg, &pConfiguration->PriorityVlanTagging);
314             GetConfigurationEntry(cfg, &pConfiguration->VlanId);
315             GetConfigurationEntry(cfg, &pConfiguration->UseMergeableBuffers);
316             GetConfigurationEntry(cfg, &pConfiguration->MTU);
317             GetConfigurationEntry(cfg, &pConfiguration->NumberOfHandledRXPackersInDPC);
318             GetConfigurationEntry(cfg, &pConfiguration->Indirect);
319 
320     #if !defined(WPP_EVENT_TRACING)
321             bDebugPrint = pConfiguration->isLogEnabled.ulValue;
322             nDebugLevel = pConfiguration->debugLevel.ulValue;
323     #endif
324             // ignoring promiscuous setting, nothing to do with it
325             pContext->maxFreeTxDescriptors = pConfiguration->TxCapacity.ulValue;
326             pContext->NetMaxReceiveBuffers = pConfiguration->RxCapacity.ulValue;
327             pContext->ulMilliesToConnect = pConfiguration->connectTimer.ulValue;
328             pContext->nEnableDPCChecker = pConfiguration->dpcChecker.ulValue;
329             pContext->bDoInterruptRecovery = pConfiguration->InterruptRecovery.ulValue != 0;
330             pContext->Limits.nPrintDiagnostic = pConfiguration->LogStatistics.ulValue;
331             pContext->uNumberOfHandledRXPacketsInDPC = pConfiguration->NumberOfHandledRXPackersInDPC.ulValue;
332             pContext->bDoSupportPriority = pConfiguration->PrioritySupport.ulValue != 0;
333             pContext->ulFormalLinkSpeed  = pConfiguration->ConnectRate.ulValue;
334             pContext->ulFormalLinkSpeed *= 1000000;
335             pContext->bDoHwPacketFiltering = pConfiguration->PacketFiltering.ulValue != 0;
336             pContext->bUseScatterGather  = pConfiguration->ScatterGather.ulValue != 0;
337             pContext->bBatchReceive      = pConfiguration->BatchReceive.ulValue != 0;
338             pContext->bDoHardwareChecksum = pConfiguration->UseSwTxChecksum.ulValue == 0;
339             pContext->bDoGuestChecksumOnReceive = pConfiguration->OffloadGuestCS.ulValue != 0;
340             pContext->bDoIPCheckTx = pConfiguration->IPPacketsCheck.ulValue & 1;
341             pContext->bDoIPCheckRx = pConfiguration->IPPacketsCheck.ulValue & 2;
342             pContext->Offload.flagsValue = 0;
343             // TX caps: 1 - TCP, 2 - UDP, 4 - IP, 8 - TCPv6, 16 - UDPv6
344             if (pConfiguration->OffloadTxChecksum.ulValue & 1) pContext->Offload.flagsValue |= osbT4TcpChecksum | osbT4TcpOptionsChecksum;
345             if (pConfiguration->OffloadTxChecksum.ulValue & 2) pContext->Offload.flagsValue |= osbT4UdpChecksum;
346             if (pConfiguration->OffloadTxChecksum.ulValue & 4) pContext->Offload.flagsValue |= osbT4IpChecksum | osbT4IpOptionsChecksum;
347             if (pConfiguration->OffloadTxChecksum.ulValue & 8) pContext->Offload.flagsValue |= osbT6TcpChecksum | osbT6TcpOptionsChecksum;
348             if (pConfiguration->OffloadTxChecksum.ulValue & 16) pContext->Offload.flagsValue |= osbT6UdpChecksum;
349             if (pConfiguration->OffloadTxLSO.ulValue) pContext->Offload.flagsValue |= osbT4Lso | osbT4LsoIp | osbT4LsoTcp;
350             if (pConfiguration->OffloadTxLSO.ulValue > 1) pContext->Offload.flagsValue |= osbT6Lso | osbT6LsoTcpOptions;
351             // RX caps: 1 - TCP, 2 - UDP, 4 - IP, 8 - TCPv6, 16 - UDPv6
352             if (pConfiguration->OffloadRxCS.ulValue & 1) pContext->Offload.flagsValue |= osbT4RxTCPChecksum | osbT4RxTCPOptionsChecksum;
353             if (pConfiguration->OffloadRxCS.ulValue & 2) pContext->Offload.flagsValue |= osbT4RxUDPChecksum;
354             if (pConfiguration->OffloadRxCS.ulValue & 4) pContext->Offload.flagsValue |= osbT4RxIPChecksum | osbT4RxIPOptionsChecksum;
355             if (pConfiguration->OffloadRxCS.ulValue & 8) pContext->Offload.flagsValue |= osbT6RxTCPChecksum | osbT6RxTCPOptionsChecksum;
356             if (pConfiguration->OffloadRxCS.ulValue & 16) pContext->Offload.flagsValue |= osbT6RxUDPChecksum;
357             /* full packet size that can be configured as GSO for VIRTIO is short */
358             /* NDIS test fails sometimes fails on segments 50-60K */
359             pContext->Offload.maxPacketSize = PARANDIS_MAX_LSO_SIZE;
360             pContext->InitialOffloadParameters.IPv4Checksum = (UCHAR)pConfiguration->stdIpcsV4.ulValue;
361             pContext->InitialOffloadParameters.TCPIPv4Checksum = (UCHAR)pConfiguration->stdTcpcsV4.ulValue;
362             pContext->InitialOffloadParameters.TCPIPv6Checksum = (UCHAR)pConfiguration->stdTcpcsV6.ulValue;
363             pContext->InitialOffloadParameters.UDPIPv4Checksum = (UCHAR)pConfiguration->stdUdpcsV4.ulValue;
364             pContext->InitialOffloadParameters.UDPIPv6Checksum = (UCHAR)pConfiguration->stdUdpcsV6.ulValue;
365             pContext->InitialOffloadParameters.LsoV1 = (UCHAR)pConfiguration->stdLsoV1.ulValue;
366             pContext->InitialOffloadParameters.LsoV2IPv4 = (UCHAR)pConfiguration->stdLsoV2ip4.ulValue;
367             pContext->InitialOffloadParameters.LsoV2IPv6 = (UCHAR)pConfiguration->stdLsoV2ip6.ulValue;
368             pContext->ulPriorityVlanSetting = pConfiguration->PriorityVlanTagging.ulValue;
369             pContext->VlanId = pConfiguration->VlanId.ulValue & 0xfff;
370             pContext->bUseMergedBuffers = pConfiguration->UseMergeableBuffers.ulValue != 0;
371             pContext->MaxPacketSize.nMaxDataSize = pConfiguration->MTU.ulValue;
372             pContext->bUseIndirect = pConfiguration->Indirect.ulValue != 0;
373             if (!pContext->bDoSupportPriority)
374                 pContext->ulPriorityVlanSetting = 0;
375             // if Vlan not supported
376             if (!IsVlanSupported(pContext))
377                 pContext->VlanId = 0;
378             if (1)
379             {
380                 NDIS_STATUS status;
381                 PVOID p;
382                 UINT  len = 0;
383                 NdisReadNetworkAddress(&status, &p, &len, cfg);
384                 if (status == NDIS_STATUS_SUCCESS && len == sizeof(pContext->CurrentMacAddress))
385                 {
386                     *ppNewMACAddress = ParaNdis_AllocateMemory(pContext, sizeof(pContext->CurrentMacAddress));
387                     if (*ppNewMACAddress)
388                     {
389                         NdisMoveMemory(*ppNewMACAddress, p, len);
390                     }
391                     else
392                     {
393                         DPrintf(0, ("[%s] MAC address present, but some problem also...", __FUNCTION__));
394                     }
395                 }
396                 else if (len && len != sizeof(pContext->CurrentMacAddress))
397                 {
398                     DPrintf(0, ("[%s] MAC address has wrong length of %d", __FUNCTION__, len));
399                 }
400                 else
401                 {
402                     DPrintf(4, ("[%s] Nothing read for MAC, error %X", __FUNCTION__, status));
403                 }
404             }
405             NdisCloseConfiguration(cfg);
406         }
407         NdisFreeMemory(pConfiguration, 0, 0);
408     }
409 }
410 
411 void ParaNdis_ResetOffloadSettings(PARANDIS_ADAPTER *pContext, tOffloadSettingsFlags *pDest, PULONG from)
412 {
413     if (!pDest) pDest = &pContext->Offload.flags;
414     if (!from)  from = &pContext->Offload.flagsValue;
415 
416     pDest->fTxIPChecksum = !!(*from & osbT4IpChecksum);
417     pDest->fTxTCPChecksum = !!(*from & osbT4TcpChecksum);
418     pDest->fTxUDPChecksum = !!(*from & osbT4UdpChecksum);
419     pDest->fTxTCPOptions = !!(*from & osbT4TcpOptionsChecksum);
420     pDest->fTxIPOptions = !!(*from & osbT4IpOptionsChecksum);
421 
422     pDest->fTxLso = !!(*from & osbT4Lso);
423     pDest->fTxLsoIP = !!(*from & osbT4LsoIp);
424     pDest->fTxLsoTCP = !!(*from & osbT4LsoTcp);
425 
426     pDest->fRxIPChecksum = !!(*from & osbT4RxIPChecksum);
427     pDest->fRxIPOptions = !!(*from & osbT4RxIPOptionsChecksum);
428     pDest->fRxTCPChecksum = !!(*from & osbT4RxTCPChecksum);
429     pDest->fRxTCPOptions = !!(*from & osbT4RxTCPOptionsChecksum);
430     pDest->fRxUDPChecksum = !!(*from & osbT4RxUDPChecksum);
431 
432     pDest->fTxTCPv6Checksum = !!(*from & osbT6TcpChecksum);
433     pDest->fTxTCPv6Options = !!(*from & osbT6TcpOptionsChecksum);
434     pDest->fTxUDPv6Checksum = !!(*from & osbT6UdpChecksum);
435     pDest->fTxIPv6Ext = !!(*from & osbT6IpExtChecksum);
436 
437     pDest->fTxLsov6 = !!(*from & osbT6Lso);
438     pDest->fTxLsov6IP = !!(*from & osbT6LsoIpExt);
439     pDest->fTxLsov6TCP = !!(*from & osbT6LsoTcpOptions);
440 
441     pDest->fRxTCPv6Checksum = !!(*from & osbT6RxTCPChecksum);
442     pDest->fRxTCPv6Options = !!(*from & osbT6RxTCPOptionsChecksum);
443     pDest->fRxUDPv6Checksum = !!(*from & osbT6RxUDPChecksum);
444     pDest->fRxIPv6Ext = !!(*from & osbT6RxIpExtChecksum);
445 }
446 
447 /**********************************************************
448 Enumerates adapter resources and fills the structure holding them
449 Verifies that IO assigned and has correct size
450 Verifies that interrupt assigned
451 Parameters:
452     PNDIS_RESOURCE_LIST RList - list of resources, received from NDIS
453     tAdapterResources *pResources - structure to fill
454 Return value:
455     TRUE if everything is OK
456 ***********************************************************/
457 static BOOLEAN GetAdapterResources(NDIS_HANDLE MiniportHandle, PNDIS_RESOURCE_LIST RList, tAdapterResources *pResources)
458 {
459     UINT i;
460     int read, bar = -1;
461     PCI_COMMON_HEADER pci_config;
462     NdisZeroMemory(pResources, sizeof(*pResources));
463 
464     // read the PCI config space header
465     read = NdisReadPciSlotInformation(
466        MiniportHandle,
467        0 /* SlotNumber, reserved */,
468        0 /* Offset */,
469        &pci_config,
470        sizeof(pci_config));
471     if (read != sizeof(pci_config)) {
472        return FALSE;
473     }
474 
475     for (i = 0; i < RList->Count; ++i)
476     {
477         ULONG type = RList->PartialDescriptors[i].Type;
478         if (type == CmResourceTypePort)
479         {
480             PHYSICAL_ADDRESS Start = RList->PartialDescriptors[i].u.Port.Start;
481             ULONG len = RList->PartialDescriptors[i].u.Port.Length;
482             DPrintf(0, ("Found IO ports at %08lX(%d)", Start.LowPart, len));
483             bar = virtio_get_bar_index(&pci_config, Start);
484             if (bar < 0) {
485                break;
486             }
487             pResources->PciBars[bar].BasePA = Start;
488             pResources->PciBars[bar].uLength = len;
489             pResources->PciBars[bar].bPortSpace = TRUE;
490         }
491         else if (type == CmResourceTypeMemory)
492         {
493             PHYSICAL_ADDRESS Start = RList->PartialDescriptors[i].u.Memory.Start;
494             ULONG len = RList->PartialDescriptors[i].u.Memory.Length;
495             DPrintf(0, ("Found IO memory at %08I64X(%d)", Start.QuadPart, len));
496             bar = virtio_get_bar_index(&pci_config, Start);
497             if (bar < 0) {
498                break;
499             }
500             pResources->PciBars[bar].BasePA = Start;
501             pResources->PciBars[bar].uLength = len;
502             pResources->PciBars[bar].bPortSpace = FALSE;
503         }
504         else if (type == CmResourceTypeInterrupt)
505         {
506             pResources->Vector = RList->PartialDescriptors[i].u.Interrupt.Vector;
507             pResources->Level = RList->PartialDescriptors[i].u.Interrupt.Level;
508             pResources->Affinity = RList->PartialDescriptors[i].u.Interrupt.Affinity;
509             pResources->InterruptFlags = RList->PartialDescriptors[i].Flags;
510             DPrintf(0, ("Found Interrupt vector %d, level %d, affinity %X, flags %X",
511                 pResources->Vector, pResources->Level, (ULONG)pResources->Affinity, pResources->InterruptFlags));
512         }
513     }
514     return bar >= 0 && pResources->Vector;
515 }
516 
517 static void DumpVirtIOFeatures(PARANDIS_ADAPTER *pContext)
518 {
519     const struct {  ULONG bitmask;  const PCHAR Name; } Features[] =
520     {
521 
522         {VIRTIO_NET_F_CSUM, "VIRTIO_NET_F_CSUM" },
523         {VIRTIO_NET_F_GUEST_CSUM, "VIRTIO_NET_F_GUEST_CSUM" },
524         {VIRTIO_NET_F_MAC, "VIRTIO_NET_F_MAC" },
525         {VIRTIO_NET_F_GSO, "VIRTIO_NET_F_GSO" },
526         {VIRTIO_NET_F_GUEST_TSO4, "VIRTIO_NET_F_GUEST_TSO4"},
527         {VIRTIO_NET_F_GUEST_TSO6, "VIRTIO_NET_F_GUEST_TSO6"},
528         {VIRTIO_NET_F_GUEST_ECN, "VIRTIO_NET_F_GUEST_ECN"},
529         {VIRTIO_NET_F_GUEST_UFO, "VIRTIO_NET_F_GUEST_UFO"},
530         {VIRTIO_NET_F_HOST_TSO4, "VIRTIO_NET_F_HOST_TSO4"},
531         {VIRTIO_NET_F_HOST_TSO6, "VIRTIO_NET_F_HOST_TSO6"},
532         {VIRTIO_NET_F_HOST_ECN, "VIRTIO_NET_F_HOST_ECN"},
533         {VIRTIO_NET_F_HOST_UFO, "VIRTIO_NET_F_HOST_UFO"},
534         {VIRTIO_NET_F_MRG_RXBUF, "VIRTIO_NET_F_MRG_RXBUF"},
535         {VIRTIO_NET_F_STATUS, "VIRTIO_NET_F_STATUS"},
536         {VIRTIO_NET_F_CTRL_VQ, "VIRTIO_NET_F_CTRL_VQ"},
537         {VIRTIO_NET_F_CTRL_RX, "VIRTIO_NET_F_CTRL_RX"},
538         {VIRTIO_NET_F_CTRL_VLAN, "VIRTIO_NET_F_CTRL_VLAN"},
539         {VIRTIO_NET_F_CTRL_RX_EXTRA, "VIRTIO_NET_F_CTRL_RX_EXTRA"},
540         {VIRTIO_RING_F_INDIRECT_DESC, "VIRTIO_RING_F_INDIRECT_DESC"},
541         {VIRTIO_F_VERSION_1, "VIRTIO_F_VERSION_1" },
542         {VIRTIO_F_ANY_LAYOUT, "VIRTIO_F_ANY_LAYOUT" },
543     };
544     UINT i;
545     for (i = 0; i < sizeof(Features)/sizeof(Features[0]); ++i)
546     {
547         if (VirtIODeviceGetHostFeature(pContext, Features[i].bitmask))
548         {
549             DPrintf(0, ("VirtIO Host Feature %s", Features[i].Name));
550         }
551     }
552 }
553 
554 /**********************************************************
555     Only for test. Prints out if the interrupt line is ON
556 Parameters:
557 Return value:
558 ***********************************************************/
559 static void JustForCheckClearInterrupt(PARANDIS_ADAPTER *pContext, const char *Label)
560 {
561     if (pContext->bEnableInterruptChecking)
562     {
563         ULONG ulActive;
564         ulActive = virtio_read_isr_status(&pContext->IODevice);
565         if (ulActive)
566         {
567             DPrintf(0,("WARNING: Interrupt Line %d(%s)!", ulActive, Label));
568         }
569     }
570 }
571 
572 /**********************************************************
573 Prints out statistics
574 ***********************************************************/
575 static void PrintStatistics(PARANDIS_ADAPTER *pContext)
576 {
577     ULONG64 totalTxFrames =
578         pContext->Statistics.ifHCOutBroadcastPkts +
579         pContext->Statistics.ifHCOutMulticastPkts +
580         pContext->Statistics.ifHCOutUcastPkts;
581     ULONG64 totalRxFrames =
582         pContext->Statistics.ifHCInBroadcastPkts +
583         pContext->Statistics.ifHCInMulticastPkts +
584         pContext->Statistics.ifHCInUcastPkts;
585 
586     DPrintf(0, ("[Diag!%X] RX buffers at VIRTIO %d of %d",
587         pContext->CurrentMacAddress[5],
588         pContext->NetNofReceiveBuffers,
589         pContext->NetMaxReceiveBuffers));
590     DPrintf(0, ("[Diag!] TX desc available %d/%d, buf %d/min. %d",
591         pContext->nofFreeTxDescriptors,
592         pContext->maxFreeTxDescriptors,
593         pContext->nofFreeHardwareBuffers,
594         pContext->minFreeHardwareBuffers));
595     pContext->minFreeHardwareBuffers = pContext->nofFreeHardwareBuffers;
596     if (pContext->NetTxPacketsToReturn)
597     {
598         DPrintf(0, ("[Diag!] TX packets to return %d", pContext->NetTxPacketsToReturn));
599     }
600     DPrintf(0, ("[Diag!] Bytes transmitted %I64u, received %I64u",
601         pContext->Statistics.ifHCOutOctets,
602         pContext->Statistics.ifHCInOctets));
603     DPrintf(0, ("[Diag!] Tx frames %I64u, CSO %d, LSO %d, indirect %d",
604         totalTxFrames,
605         pContext->extraStatistics.framesCSOffload,
606         pContext->extraStatistics.framesLSO,
607         pContext->extraStatistics.framesIndirect));
608     DPrintf(0, ("[Diag!] Rx frames %I64u, Rx.Pri %d, RxHwCS.OK %d, FiltOut %d",
609         totalRxFrames, pContext->extraStatistics.framesRxPriority,
610         pContext->extraStatistics.framesRxCSHwOK, pContext->extraStatistics.framesFilteredOut));
611     if (pContext->extraStatistics.framesRxCSHwMissedBad || pContext->extraStatistics.framesRxCSHwMissedGood)
612     {
613         DPrintf(0, ("[Diag!] RxHwCS mistakes: missed bad %d, missed good %d",
614             pContext->extraStatistics.framesRxCSHwMissedBad, pContext->extraStatistics.framesRxCSHwMissedGood));
615     }
616 }
617 
618 static NDIS_STATUS NTStatusToNdisStatus(NTSTATUS nt_status) {
619     switch (nt_status) {
620     case STATUS_SUCCESS:
621         return NDIS_STATUS_SUCCESS;
622     case STATUS_NOT_FOUND:
623     case STATUS_DEVICE_NOT_CONNECTED:
624         return NDIS_STATUS_ADAPTER_NOT_FOUND;
625     case STATUS_INSUFFICIENT_RESOURCES:
626         return NDIS_STATUS_RESOURCES;
627     case STATUS_INVALID_PARAMETER:
628         return NDIS_STATUS_INVALID_DEVICE_REQUEST;
629     default:
630         return NDIS_STATUS_FAILURE;
631     }
632 }
633 
634 static NDIS_STATUS FinalizeFeatures(PARANDIS_ADAPTER *pContext)
635 {
636     NTSTATUS nt_status = virtio_set_features(&pContext->IODevice, pContext->ullGuestFeatures);
637     if (!NT_SUCCESS(nt_status)) {
638         DPrintf(0, ("[%s] virtio_set_features failed with %x\n", __FUNCTION__, nt_status));
639     }
640     return NTStatusToNdisStatus(nt_status);
641 }
642 
643 /**********************************************************
644 Initializes the context structure
645 Major variables, received from NDIS on initialization, must be be set before this call
646 (for ex. pContext->MiniportHandle)
647 
648 If this procedure fails, no need to call
649     ParaNdis_CleanupContext
650 
651 
652 Parameters:
653 Return value:
654     SUCCESS, if resources are OK
655     NDIS_STATUS_RESOURCE_CONFLICT if not
656 ***********************************************************/
657 NDIS_STATUS ParaNdis_InitializeContext(
658     PARANDIS_ADAPTER *pContext,
659     PNDIS_RESOURCE_LIST pResourceList)
660 {
661     NDIS_STATUS status = NDIS_STATUS_SUCCESS;
662     PUCHAR pNewMacAddress = NULL;
663     USHORT linkStatus = 0;
664     NTSTATUS nt_status;
665 
666     DEBUG_ENTRY(0);
667     /* read first PCI IO bar*/
668     //ulIOAddress = ReadPCIConfiguration(miniportAdapterHandle, 0x10);
669     /* check this is IO and assigned */
670     ReadNicConfiguration(pContext, &pNewMacAddress);
671     if (pNewMacAddress)
672     {
673         if (ParaNdis_ValidateMacAddress(pNewMacAddress, TRUE))
674         {
675             DPrintf(0, ("[%s] WARNING: MAC address reloaded", __FUNCTION__));
676             NdisMoveMemory(pContext->CurrentMacAddress, pNewMacAddress, sizeof(pContext->CurrentMacAddress));
677         }
678         else
679         {
680             DPrintf(0, ("[%s] WARNING: Invalid MAC address ignored", __FUNCTION__));
681         }
682         NdisFreeMemory(pNewMacAddress, 0, 0);
683     }
684 
685     pContext->MaxPacketSize.nMaxFullSizeOS = pContext->MaxPacketSize.nMaxDataSize + ETH_HEADER_SIZE;
686     pContext->MaxPacketSize.nMaxFullSizeHwTx = pContext->MaxPacketSize.nMaxFullSizeOS;
687     pContext->MaxPacketSize.nMaxFullSizeHwRx = pContext->MaxPacketSize.nMaxFullSizeOS + ETH_PRIORITY_HEADER_SIZE;
688     if (pContext->ulPriorityVlanSetting)
689         pContext->MaxPacketSize.nMaxFullSizeHwTx = pContext->MaxPacketSize.nMaxFullSizeHwRx;
690 
691     if (GetAdapterResources(pContext->MiniportHandle, pResourceList, &pContext->AdapterResources))
692     {
693         if (pContext->AdapterResources.InterruptFlags & CM_RESOURCE_INTERRUPT_MESSAGE)
694         {
695             DPrintf(0, ("[%s] Message interrupt assigned", __FUNCTION__));
696             pContext->bUsingMSIX = TRUE;
697         }
698 
699         nt_status = virtio_device_initialize(
700             &pContext->IODevice,
701             &ParaNdisSystemOps,
702             pContext,
703             pContext->bUsingMSIX);
704         if (!NT_SUCCESS(nt_status)) {
705             DPrintf(0, ("[%s] virtio_device_initialize failed with %x\n", __FUNCTION__, nt_status));
706             status = NTStatusToNdisStatus(nt_status);
707             DEBUG_EXIT_STATUS(0, status);
708             return status;
709         }
710 
711         pContext->bIODeviceInitialized = TRUE;
712         JustForCheckClearInterrupt(pContext, "init 0");
713         ParaNdis_ResetVirtIONetDevice(pContext);
714         JustForCheckClearInterrupt(pContext, "init 1");
715         virtio_add_status(&pContext->IODevice, VIRTIO_CONFIG_S_ACKNOWLEDGE);
716         JustForCheckClearInterrupt(pContext, "init 2");
717         virtio_add_status(&pContext->IODevice, VIRTIO_CONFIG_S_DRIVER);
718         pContext->ullHostFeatures = virtio_get_features(&pContext->IODevice);
719         DumpVirtIOFeatures(pContext);
720         JustForCheckClearInterrupt(pContext, "init 3");
721         pContext->bLinkDetectSupported = 0 != VirtIODeviceGetHostFeature(pContext, VIRTIO_NET_F_STATUS);
722 
723         if(pContext->bLinkDetectSupported) {
724             virtio_get_config(&pContext->IODevice, sizeof(pContext->CurrentMacAddress), &linkStatus, sizeof(linkStatus));
725             pContext->bConnected = (linkStatus & VIRTIO_NET_S_LINK_UP) != 0;
726             DPrintf(0, ("[%s] Link status on driver startup: %d", __FUNCTION__, pContext->bConnected));
727         }
728 
729         if (VirtIODeviceGetHostFeature(pContext, VIRTIO_F_VERSION_1))
730         {
731             // virtio 1.0 always uses the extended header
732             pContext->nVirtioHeaderSize = sizeof(virtio_net_hdr_ext);
733             VirtIODeviceEnableGuestFeature(pContext, VIRTIO_F_VERSION_1);
734         }
735         else
736         {
737             pContext->nVirtioHeaderSize = sizeof(virtio_net_hdr_basic);
738         }
739 
740         if (VirtIODeviceGetHostFeature(pContext, VIRTIO_F_ANY_LAYOUT))
741         {
742             VirtIODeviceEnableGuestFeature(pContext, VIRTIO_F_ANY_LAYOUT);
743         }
744         if (VirtIODeviceGetHostFeature(pContext, VIRTIO_RING_F_EVENT_IDX))
745         {
746             VirtIODeviceEnableGuestFeature(pContext, VIRTIO_RING_F_EVENT_IDX);
747         }
748 
749         if (!pContext->bUseMergedBuffers && VirtIODeviceGetHostFeature(pContext, VIRTIO_NET_F_MRG_RXBUF))
750         {
751             DPrintf(0, ("[%s] Not using mergeable buffers", __FUNCTION__));
752         }
753         else
754         {
755             pContext->bUseMergedBuffers = VirtIODeviceGetHostFeature(pContext, VIRTIO_NET_F_MRG_RXBUF) != 0;
756             if (pContext->bUseMergedBuffers)
757             {
758                 pContext->nVirtioHeaderSize = sizeof(virtio_net_hdr_ext);
759                 VirtIODeviceEnableGuestFeature(pContext, VIRTIO_NET_F_MRG_RXBUF);
760             }
761         }
762         if (VirtIODeviceGetHostFeature(pContext, VIRTIO_NET_F_MAC))
763         {
764             virtio_get_config(
765                 &pContext->IODevice,
766                 0, // + offsetof(struct virtio_net_config, mac)
767                 &pContext->PermanentMacAddress,
768                 ETH_LENGTH_OF_ADDRESS);
769             if (!ParaNdis_ValidateMacAddress(pContext->PermanentMacAddress, FALSE))
770             {
771                 DPrintf(0,("Invalid device MAC ignored(%02x-%02x-%02x-%02x-%02x-%02x)",
772                     pContext->PermanentMacAddress[0],
773                     pContext->PermanentMacAddress[1],
774                     pContext->PermanentMacAddress[2],
775                     pContext->PermanentMacAddress[3],
776                     pContext->PermanentMacAddress[4],
777                     pContext->PermanentMacAddress[5]));
778                 NdisZeroMemory(pContext->PermanentMacAddress, sizeof(pContext->PermanentMacAddress));
779             }
780         }
781 
782         if (ETH_IS_EMPTY(pContext->PermanentMacAddress))
783         {
784             DPrintf(0, ("No device MAC present, use default"));
785             pContext->PermanentMacAddress[0] = 0x02;
786             pContext->PermanentMacAddress[1] = 0x50;
787             pContext->PermanentMacAddress[2] = 0xF2;
788             pContext->PermanentMacAddress[3] = 0x00;
789             pContext->PermanentMacAddress[4] = 0x01;
790             pContext->PermanentMacAddress[5] = 0x80 | (UCHAR)(pContext->ulUniqueID & 0xFF);
791         }
792         DPrintf(0,("Device MAC = %02x-%02x-%02x-%02x-%02x-%02x",
793             pContext->PermanentMacAddress[0],
794             pContext->PermanentMacAddress[1],
795             pContext->PermanentMacAddress[2],
796             pContext->PermanentMacAddress[3],
797             pContext->PermanentMacAddress[4],
798             pContext->PermanentMacAddress[5]));
799 
800         if (ETH_IS_EMPTY(pContext->CurrentMacAddress))
801         {
802             NdisMoveMemory(
803                 &pContext->CurrentMacAddress,
804                 &pContext->PermanentMacAddress,
805                 ETH_LENGTH_OF_ADDRESS);
806         }
807         else
808         {
809             DPrintf(0,("Current MAC = %02x-%02x-%02x-%02x-%02x-%02x",
810                 pContext->CurrentMacAddress[0],
811                 pContext->CurrentMacAddress[1],
812                 pContext->CurrentMacAddress[2],
813                 pContext->CurrentMacAddress[3],
814                 pContext->CurrentMacAddress[4],
815                 pContext->CurrentMacAddress[5]));
816         }
817         if (VirtIODeviceGetHostFeature(pContext, VIRTIO_NET_F_CTRL_VQ)) {
818             pContext->bHasControlQueue = TRUE;
819             VirtIODeviceEnableGuestFeature(pContext, VIRTIO_NET_F_CTRL_VQ);
820         }
821     }
822     else
823     {
824         DPrintf(0, ("[%s] Error: Incomplete resources", __FUNCTION__));
825         status = NDIS_STATUS_RESOURCE_CONFLICT;
826     }
827 
828 
829     if (pContext->bDoHardwareChecksum)
830     {
831         ULONG dependentOptions;
832         dependentOptions = osbT4TcpChecksum | osbT4UdpChecksum | osbT4TcpOptionsChecksum;
833         if (!VirtIODeviceGetHostFeature(pContext, VIRTIO_NET_F_CSUM) &&
834             (pContext->Offload.flagsValue & dependentOptions))
835         {
836             DPrintf(0, ("[%s] Host does not support CSUM, disabling CS offload", __FUNCTION__) );
837             pContext->Offload.flagsValue &= ~dependentOptions;
838         }
839     }
840 
841     if (pContext->bDoGuestChecksumOnReceive && VirtIODeviceGetHostFeature(pContext, VIRTIO_NET_F_GUEST_CSUM))
842     {
843         DPrintf(0, ("[%s] Enabling guest checksum", __FUNCTION__) );
844         VirtIODeviceEnableGuestFeature(pContext, VIRTIO_NET_F_GUEST_CSUM);
845     }
846     else
847     {
848         pContext->bDoGuestChecksumOnReceive = FALSE;
849     }
850 
851     // now, after we checked the capabilities, we can initialize current
852     // configuration of offload tasks
853     ParaNdis_ResetOffloadSettings(pContext, NULL, NULL);
854     if (pContext->Offload.flags.fTxLso && !pContext->bUseScatterGather)
855     {
856         DisableBothLSOPermanently(pContext, __FUNCTION__, "SG is not active");
857     }
858     if (pContext->Offload.flags.fTxLso &&
859         !VirtIODeviceGetHostFeature(pContext, VIRTIO_NET_F_HOST_TSO4))
860     {
861         DisableLSOv4Permanently(pContext, __FUNCTION__, "Host does not support TSOv4");
862     }
863     if (pContext->Offload.flags.fTxLsov6 &&
864         !VirtIODeviceGetHostFeature(pContext, VIRTIO_NET_F_HOST_TSO6))
865     {
866         DisableLSOv6Permanently(pContext, __FUNCTION__, "Host does not support TSOv6");
867     }
868     if (pContext->bUseIndirect)
869     {
870         const char *reason = "";
871         if (!VirtIODeviceGetHostFeature(pContext, VIRTIO_RING_F_INDIRECT_DESC))
872         {
873             pContext->bUseIndirect = FALSE;
874             reason = "Host support";
875         }
876         else if (!pContext->bUseScatterGather)
877         {
878             pContext->bUseIndirect = FALSE;
879             reason = "SG";
880         }
881         DPrintf(0, ("[%s] %sable indirect Tx(!%s)", __FUNCTION__, pContext->bUseIndirect ? "En" : "Dis", reason) );
882     }
883 
884     if (VirtIODeviceGetHostFeature(pContext, VIRTIO_NET_F_CTRL_RX_EXTRA) &&
885         pContext->bDoHwPacketFiltering)
886     {
887         DPrintf(0, ("[%s] Using hardware packet filtering", __FUNCTION__));
888         pContext->bHasHardwareFilters = TRUE;
889     }
890 
891     status = FinalizeFeatures(pContext);
892 
893     pContext->ReuseBufferProc = (tReuseReceiveBufferProc)ReuseReceiveBufferRegular;
894 
895     NdisInitializeEvent(&pContext->ResetEvent);
896     DEBUG_EXIT_STATUS(0, status);
897     return status;
898 }
899 
900 /**********************************************************
901 Free the resources allocated for VirtIO buffer descriptor
902 Parameters:
903     PVOID pParam                pIONetDescriptor to free
904     BOOLEAN bRemoveFromList     TRUE, if also remove it from list
905 ***********************************************************/
906 static void VirtIONetFreeBufferDescriptor(PARANDIS_ADAPTER *pContext, pIONetDescriptor pBufferDescriptor)
907 {
908     if(pBufferDescriptor)
909     {
910         if (pBufferDescriptor->pHolder)
911             ParaNdis_UnbindBufferFromPacket(pContext, pBufferDescriptor);
912         if (pBufferDescriptor->DataInfo.Virtual)
913             ParaNdis_FreePhysicalMemory(pContext, &pBufferDescriptor->DataInfo);
914         if (pBufferDescriptor->HeaderInfo.Virtual)
915             ParaNdis_FreePhysicalMemory(pContext, &pBufferDescriptor->HeaderInfo);
916         NdisFreeMemory(pBufferDescriptor, 0, 0);
917     }
918 }
919 
920 /**********************************************************
921 Free all the buffer descriptors from specified list
922 Parameters:
923     PLIST_ENTRY pListRoot           list containing pIONetDescriptor structures
924     PNDIS_SPIN_LOCK pLock           lock to protest this list
925 Return value:
926 ***********************************************************/
927 static void FreeDescriptorsFromList(PARANDIS_ADAPTER *pContext, PLIST_ENTRY pListRoot, PNDIS_SPIN_LOCK pLock)
928 {
929     pIONetDescriptor pBufferDescriptor;
930     LIST_ENTRY TempList;
931     InitializeListHead(&TempList);
932     NdisAcquireSpinLock(pLock);
933     while(!IsListEmpty(pListRoot))
934     {
935         pBufferDescriptor = (pIONetDescriptor)RemoveHeadList(pListRoot);
936         InsertTailList(&TempList, &pBufferDescriptor->listEntry);
937     }
938     NdisReleaseSpinLock(pLock);
939     while(!IsListEmpty(&TempList))
940     {
941         pBufferDescriptor = (pIONetDescriptor)RemoveHeadList(&TempList);
942         VirtIONetFreeBufferDescriptor(pContext, pBufferDescriptor);
943     }
944 }
945 
946 static pIONetDescriptor AllocatePairOfBuffersOnInit(
947     PARANDIS_ADAPTER *pContext,
948     ULONG size1,
949     ULONG size2,
950     BOOLEAN bForTx)
951 {
952     pIONetDescriptor p;
953     p = (pIONetDescriptor)ParaNdis_AllocateMemory(pContext, sizeof(*p));
954     if (p)
955     {
956         BOOLEAN b1 = FALSE, b2 = FALSE;
957         NdisZeroMemory(p, sizeof(*p));
958         p->HeaderInfo.size = size1;
959         p->DataInfo.size   = size2;
960         p->HeaderInfo.IsCached = p->DataInfo.IsCached = 1;
961         p->HeaderInfo.IsTX = p->DataInfo.IsTX = bForTx;
962         p->nofUsedBuffers = 0;
963         b1 = ParaNdis_InitialAllocatePhysicalMemory(pContext, &p->HeaderInfo);
964         if (b1) b2 = ParaNdis_InitialAllocatePhysicalMemory(pContext, &p->DataInfo);
965         if (b1 && b2)
966         {
967             BOOLEAN b = bForTx || ParaNdis_BindBufferToPacket(pContext, p);
968             if (!b)
969             {
970                 DPrintf(0, ("[INITPHYS](%s) Failed to bind memory to net packet", bForTx ? "TX" : "RX"));
971                 VirtIONetFreeBufferDescriptor(pContext, p);
972                 p = NULL;
973             }
974         }
975         else
976         {
977             if (b1) ParaNdis_FreePhysicalMemory(pContext, &p->HeaderInfo);
978             if (b2) ParaNdis_FreePhysicalMemory(pContext, &p->DataInfo);
979             NdisFreeMemory(p, 0, 0);
980             p = NULL;
981             DPrintf(0, ("[INITPHYS](%s) Failed to allocate memory block", bForTx ? "TX" : "RX"));
982         }
983     }
984     if (p)
985     {
986         DPrintf(3, ("[INITPHYS](%s) Header v%p(p%08lX), Data v%p(p%08lX)", bForTx ? "TX" : "RX",
987             p->HeaderInfo.Virtual, p->HeaderInfo.Physical.LowPart,
988             p->DataInfo.Virtual, p->DataInfo.Physical.LowPart));
989     }
990     return p;
991 }
992 
993 /**********************************************************
994 Allocates TX buffers according to startup setting (pContext->maxFreeTxDescriptors as got from registry)
995 Buffers are chained in NetFreeSendBuffers
996 Parameters:
997     context
998 ***********************************************************/
999 static void PrepareTransmitBuffers(PARANDIS_ADAPTER *pContext)
1000 {
1001     UINT nBuffers, nMaxBuffers;
1002     DEBUG_ENTRY(4);
1003     nMaxBuffers = virtio_get_queue_size(pContext->NetSendQueue) / 2;
1004     if (nMaxBuffers > pContext->maxFreeTxDescriptors) nMaxBuffers = pContext->maxFreeTxDescriptors;
1005 
1006     for (nBuffers = 0; nBuffers < nMaxBuffers; ++nBuffers)
1007     {
1008         pIONetDescriptor pBuffersDescriptor =
1009             AllocatePairOfBuffersOnInit(
1010                 pContext,
1011                 pContext->nVirtioHeaderSize,
1012                 pContext->MaxPacketSize.nMaxFullSizeHwTx,
1013                 TRUE);
1014         if (!pBuffersDescriptor) break;
1015 
1016         NdisZeroMemory(pBuffersDescriptor->HeaderInfo.Virtual, pBuffersDescriptor->HeaderInfo.size);
1017         InsertTailList(&pContext->NetFreeSendBuffers, &pBuffersDescriptor->listEntry);
1018         pContext->nofFreeTxDescriptors++;
1019     }
1020 
1021     pContext->maxFreeTxDescriptors = pContext->nofFreeTxDescriptors;
1022     pContext->nofFreeHardwareBuffers = pContext->nofFreeTxDescriptors * 2;
1023     pContext->maxFreeHardwareBuffers = pContext->minFreeHardwareBuffers = pContext->nofFreeHardwareBuffers;
1024     DPrintf(0, ("[%s] available %d Tx descriptors, %d hw buffers",
1025         __FUNCTION__, pContext->nofFreeTxDescriptors, pContext->nofFreeHardwareBuffers));
1026 }
1027 
1028 static BOOLEAN AddRxBufferToQueue(PARANDIS_ADAPTER *pContext, pIONetDescriptor pBufferDescriptor)
1029 {
1030     UINT nBuffersToSubmit = 2;
1031     struct VirtIOBufferDescriptor sg[2];
1032     if (!pContext->bUseMergedBuffers)
1033     {
1034         sg[0].physAddr = pBufferDescriptor->HeaderInfo.Physical;
1035         sg[0].length = pBufferDescriptor->HeaderInfo.size;
1036         sg[1].physAddr = pBufferDescriptor->DataInfo.Physical;
1037         sg[1].length = pBufferDescriptor->DataInfo.size;
1038     }
1039     else
1040     {
1041         sg[0].physAddr = pBufferDescriptor->DataInfo.Physical;
1042         sg[0].length = pBufferDescriptor->DataInfo.size;
1043         nBuffersToSubmit = 1;
1044     }
1045     return 0 <= virtqueue_add_buf(
1046         pContext->NetReceiveQueue,
1047         sg,
1048         0,
1049         nBuffersToSubmit,
1050         pBufferDescriptor,
1051         NULL,
1052         0);
1053 }
1054 
1055 
1056 /**********************************************************
1057 Allocates maximum RX buffers for incoming packets
1058 Buffers are chained in NetReceiveBuffers
1059 Parameters:
1060     context
1061 ***********************************************************/
1062 static int PrepareReceiveBuffers(PARANDIS_ADAPTER *pContext)
1063 {
1064     int nRet = 0;
1065     UINT i;
1066     DEBUG_ENTRY(4);
1067 
1068     for (i = 0; i < pContext->NetMaxReceiveBuffers; ++i)
1069     {
1070         ULONG size1 = pContext->bUseMergedBuffers ? 4 : pContext->nVirtioHeaderSize;
1071         ULONG size2 = pContext->MaxPacketSize.nMaxFullSizeHwRx +
1072             (pContext->bUseMergedBuffers ? pContext->nVirtioHeaderSize : 0);
1073         pIONetDescriptor pBuffersDescriptor =
1074             AllocatePairOfBuffersOnInit(pContext, size1, size2, FALSE);
1075         if (!pBuffersDescriptor) break;
1076 
1077         if (!AddRxBufferToQueue(pContext, pBuffersDescriptor))
1078         {
1079             VirtIONetFreeBufferDescriptor(pContext, pBuffersDescriptor);
1080             break;
1081         }
1082 
1083         InsertTailList(&pContext->NetReceiveBuffers, &pBuffersDescriptor->listEntry);
1084 
1085         pContext->NetNofReceiveBuffers++;
1086     }
1087 
1088     pContext->NetMaxReceiveBuffers = pContext->NetNofReceiveBuffers;
1089     DPrintf(0, ("[%s] MaxReceiveBuffers %d\n", __FUNCTION__, pContext->NetMaxReceiveBuffers) );
1090 
1091     virtqueue_kick(pContext->NetReceiveQueue);
1092 
1093     return nRet;
1094 }
1095 
1096 static NDIS_STATUS FindNetQueues(PARANDIS_ADAPTER *pContext)
1097 {
1098     struct virtqueue *queues[3];
1099     unsigned nvqs = pContext->bHasControlQueue ? 3 : 2;
1100     NTSTATUS status;
1101 
1102     // We work with two or three virtqueues, 0 - receive, 1 - send, 2 - control
1103     status = virtio_find_queues(
1104        &pContext->IODevice,
1105        nvqs,
1106        queues);
1107     if (!NT_SUCCESS(status)) {
1108        DPrintf(0, ("[%s] virtio_find_queues failed with %x\n", __FUNCTION__, status));
1109        return NTStatusToNdisStatus(status);
1110     }
1111 
1112     pContext->NetReceiveQueue = queues[0];
1113     pContext->NetSendQueue = queues[1];
1114     if (pContext->bHasControlQueue) {
1115        pContext->NetControlQueue = queues[2];
1116     }
1117 
1118     return NDIS_STATUS_SUCCESS;
1119 }
1120 
1121 // called on PASSIVE upon unsuccessful Init or upon Halt
1122 static void DeleteNetQueues(PARANDIS_ADAPTER *pContext)
1123 {
1124     virtio_delete_queues(&pContext->IODevice);
1125 }
1126 
1127 /**********************************************************
1128 Initializes VirtIO buffering and related stuff:
1129 Allocates RX and TX queues and buffers
1130 Parameters:
1131     context
1132 Return value:
1133     TRUE if both queues are allocated
1134 ***********************************************************/
1135 static NDIS_STATUS ParaNdis_VirtIONetInit(PARANDIS_ADAPTER *pContext)
1136 {
1137     NDIS_STATUS status;
1138     DEBUG_ENTRY(0);
1139 
1140     pContext->ControlData.IsCached = 1;
1141     pContext->ControlData.size = 512;
1142 
1143     status = FindNetQueues(pContext);
1144     if (status != NDIS_STATUS_SUCCESS) {
1145         return status;
1146     }
1147 
1148     if (pContext->NetReceiveQueue && pContext->NetSendQueue)
1149     {
1150         PrepareTransmitBuffers(pContext);
1151         PrepareReceiveBuffers(pContext);
1152 
1153         if (pContext->NetControlQueue)
1154             ParaNdis_InitialAllocatePhysicalMemory(pContext, &pContext->ControlData);
1155         if (!pContext->NetControlQueue || !pContext->ControlData.Virtual)
1156         {
1157             DPrintf(0, ("[%s] The Control vQueue does not work!\n", __FUNCTION__) );
1158             pContext->bHasHardwareFilters = FALSE;
1159         }
1160         if (pContext->nofFreeTxDescriptors &&
1161             pContext->NetMaxReceiveBuffers &&
1162             pContext->maxFreeHardwareBuffers)
1163         {
1164             pContext->sgTxGatherTable = ParaNdis_AllocateMemory(pContext,
1165                 pContext->maxFreeHardwareBuffers * sizeof(pContext->sgTxGatherTable[0]));
1166             if (!pContext->sgTxGatherTable)
1167             {
1168                 DisableBothLSOPermanently(pContext, __FUNCTION__, "Can not allocate SG table");
1169             }
1170             status = NDIS_STATUS_SUCCESS;
1171         }
1172     }
1173     else
1174     {
1175         DeleteNetQueues(pContext);
1176         status = NDIS_STATUS_RESOURCES;
1177     }
1178     return status;
1179 }
1180 
1181 static void VirtIODeviceRemoveStatus(VirtIODevice *vdev, u8 status)
1182 {
1183     virtio_set_status(
1184         vdev,
1185         virtio_get_status(vdev) & ~status);
1186 }
1187 
1188 /**********************************************************
1189 Finishes initialization of context structure, calling also version dependent part
1190 If this procedure failed, ParaNdis_CleanupContext must be called
1191 Parameters:
1192     context
1193 Return value:
1194     SUCCESS or some kind of failure
1195 ***********************************************************/
1196 NDIS_STATUS ParaNdis_FinishInitialization(PARANDIS_ADAPTER *pContext)
1197 {
1198     NDIS_STATUS status = NDIS_STATUS_SUCCESS;
1199     DEBUG_ENTRY(0);
1200 
1201     NdisAllocateSpinLock(&pContext->SendLock);
1202 #if !defined(UNIFY_LOCKS)
1203     NdisAllocateSpinLock(&pContext->ReceiveLock);
1204 #endif
1205 
1206     InitializeListHead(&pContext->NetReceiveBuffers);
1207     InitializeListHead(&pContext->NetReceiveBuffersWaiting);
1208     InitializeListHead(&pContext->NetSendBuffersInUse);
1209     InitializeListHead(&pContext->NetFreeSendBuffers);
1210 
1211     status = ParaNdis_FinishSpecificInitialization(pContext);
1212 
1213     if (status == NDIS_STATUS_SUCCESS)
1214     {
1215         status = ParaNdis_VirtIONetInit(pContext);
1216     }
1217 
1218     pContext->Limits.nReusedRxBuffers = pContext->NetMaxReceiveBuffers / 4 + 1;
1219 
1220     if (status == NDIS_STATUS_SUCCESS)
1221     {
1222         JustForCheckClearInterrupt(pContext, "start 3");
1223         pContext->bEnableInterruptHandlingDPC = TRUE;
1224         ParaNdis_SetPowerState(pContext, NdisDeviceStateD0);
1225         virtio_device_ready(&pContext->IODevice);
1226         JustForCheckClearInterrupt(pContext, "start 4");
1227         ParaNdis_UpdateDeviceFilters(pContext);
1228     }
1229     else
1230     {
1231         virtio_add_status(&pContext->IODevice, VIRTIO_CONFIG_S_FAILED);
1232     }
1233     DEBUG_EXIT_STATUS(0, status);
1234     return status;
1235 }
1236 
1237 /**********************************************************
1238 Releases VirtIO related resources - queues and buffers
1239 Parameters:
1240     context
1241 Return value:
1242 ***********************************************************/
1243 static void VirtIONetRelease(PARANDIS_ADAPTER *pContext)
1244 {
1245     BOOLEAN b;
1246     DEBUG_ENTRY(0);
1247 
1248     /* list NetReceiveBuffersWaiting must be free */
1249     do
1250     {
1251         NdisAcquireSpinLock(&pContext->ReceiveLock);
1252         b = !IsListEmpty(&pContext->NetReceiveBuffersWaiting);
1253         NdisReleaseSpinLock(&pContext->ReceiveLock);
1254         if (b)
1255         {
1256             DPrintf(0, ("[%s] There are waiting buffers", __FUNCTION__));
1257             PrintStatistics(pContext);
1258             NdisMSleep(5000000);
1259         }
1260     }while (b);
1261 
1262     DeleteNetQueues(pContext);
1263     virtio_device_shutdown(&pContext->IODevice);
1264     pContext->bIODeviceInitialized = FALSE;
1265 
1266     /* intentionally commented out
1267     FreeDescriptorsFromList(
1268         pContext,
1269         &pContext->NetReceiveBuffersWaiting,
1270         &pContext->ReceiveLock);
1271     */
1272 
1273     /* this can be freed, queue shut down */
1274     FreeDescriptorsFromList(
1275         pContext,
1276         &pContext->NetReceiveBuffers,
1277         &pContext->ReceiveLock);
1278 
1279     /* this can be freed, queue shut down */
1280     FreeDescriptorsFromList(
1281         pContext,
1282         &pContext->NetSendBuffersInUse,
1283         &pContext->SendLock);
1284 
1285     /* this can be freed, send disabled */
1286     FreeDescriptorsFromList(
1287         pContext,
1288         &pContext->NetFreeSendBuffers,
1289         &pContext->SendLock);
1290 
1291     if (pContext->ControlData.Virtual)
1292         ParaNdis_FreePhysicalMemory(pContext, &pContext->ControlData);
1293 
1294     PrintStatistics(pContext);
1295     if (pContext->sgTxGatherTable)
1296     {
1297         NdisFreeMemory(pContext->sgTxGatherTable, 0, 0);
1298     }
1299 }
1300 
1301 static void PreventDPCServicing(PARANDIS_ADAPTER *pContext)
1302 {
1303     LONG inside;;
1304     pContext->bEnableInterruptHandlingDPC = FALSE;
1305     do
1306     {
1307         inside = InterlockedIncrement(&pContext->counterDPCInside);
1308         InterlockedDecrement(&pContext->counterDPCInside);
1309         if (inside > 1)
1310         {
1311             DPrintf(0, ("[%s] waiting!", __FUNCTION__));
1312             NdisMSleep(20000);
1313         }
1314     } while (inside > 1);
1315 }
1316 
1317 /**********************************************************
1318 Frees all the resources allocated when the context initialized,
1319     calling also version-dependent part
1320 Parameters:
1321     context
1322 ***********************************************************/
1323 VOID ParaNdis_CleanupContext(PARANDIS_ADAPTER *pContext)
1324 {
1325     UINT i;
1326 
1327     /* disable any interrupt generation */
1328     if (pContext->IODevice.addr)
1329     {
1330         //int nActive;
1331         //nActive = virtio_read_isr_status(&pContext->IODevice);
1332         /* back compat - remove the OK flag only in legacy mode */
1333         VirtIODeviceRemoveStatus(&pContext->IODevice, VIRTIO_CONFIG_S_DRIVER_OK);
1334         JustForCheckClearInterrupt(pContext, "exit 1");
1335         //nActive += virtio_read_isr_status(&pContext->IODevice);
1336         //nActive += virtio_read_isr_status(&pContext->IODevice);
1337         //DPrintf(0, ("cleanup %d", nActive));
1338     }
1339 
1340     PreventDPCServicing(pContext);
1341 
1342     /****************************************
1343     ensure all the incoming packets returned,
1344     free all the buffers and their descriptors
1345     *****************************************/
1346 
1347     if (pContext->bIODeviceInitialized)
1348     {
1349         JustForCheckClearInterrupt(pContext, "exit 2");
1350         ParaNdis_ResetVirtIONetDevice(pContext);
1351         JustForCheckClearInterrupt(pContext, "exit 3");
1352     }
1353 
1354     ParaNdis_SetPowerState(pContext, NdisDeviceStateD3);
1355     VirtIONetRelease(pContext);
1356 
1357     ParaNdis_FinalizeCleanup(pContext);
1358 
1359     if (pContext->SendLock.SpinLock)
1360     {
1361         NdisFreeSpinLock(&pContext->SendLock);
1362     }
1363 
1364 #if !defined(UNIFY_LOCKS)
1365     if (pContext->ReceiveLock.SpinLock)
1366     {
1367         NdisFreeSpinLock(&pContext->ReceiveLock);
1368     }
1369 #endif
1370 
1371     /* free queue shared memory */
1372     for (i = 0; i < MAX_NUM_OF_QUEUES; i++) {
1373         if (pContext->SharedMemoryRanges[i].pBase != NULL) {
1374             NdisMFreeSharedMemory(
1375                 pContext->MiniportHandle,
1376                 pContext->SharedMemoryRanges[i].uLength,
1377                 TRUE /* Cached */,
1378                 pContext->SharedMemoryRanges[i].pBase,
1379                 pContext->SharedMemoryRanges[i].BasePA);
1380             pContext->SharedMemoryRanges[i].pBase = NULL;
1381         }
1382     }
1383 
1384     /* unmap our port and memory IO resources */
1385     for (i = 0; i < PCI_TYPE0_ADDRESSES; i++)
1386     {
1387        tBusResource *pRes = &pContext->AdapterResources.PciBars[i];
1388        if (pRes->pBase != NULL)
1389        {
1390           if (pRes->bPortSpace)
1391           {
1392              NdisMDeregisterIoPortRange(
1393                 pContext->MiniportHandle,
1394                 pRes->BasePA.LowPart,
1395                 pRes->uLength,
1396                 pRes->pBase);
1397           }
1398           else
1399           {
1400              NdisMUnmapIoSpace(
1401                 pContext->MiniportHandle,
1402                 pRes->pBase,
1403                 pRes->uLength);
1404           }
1405        }
1406     }
1407 }
1408 
1409 
1410 /**********************************************************
1411 System shutdown handler (shutdown, restart, bugcheck)
1412 Parameters:
1413     context
1414 ***********************************************************/
1415 VOID ParaNdis_OnShutdown(PARANDIS_ADAPTER *pContext)
1416 {
1417     DEBUG_ENTRY(0); // this is only for kdbg :)
1418     ParaNdis_ResetVirtIONetDevice(pContext);
1419 }
1420 
1421 /**********************************************************
1422 Handles hardware interrupt
1423 Parameters:
1424     context
1425     ULONG knownInterruptSources - bitmask of
1426 Return value:
1427     TRUE, if it is our interrupt
1428     sets *pRunDpc to TRUE if the DPC should be fired
1429 ***********************************************************/
1430 BOOLEAN ParaNdis_OnLegacyInterrupt(
1431     PARANDIS_ADAPTER *pContext,
1432     OUT BOOLEAN *pRunDpc)
1433 {
1434     ULONG status = virtio_read_isr_status(&pContext->IODevice);
1435 
1436     if((status == 0)                                   ||
1437        (status == VIRTIO_NET_INVALID_INTERRUPT_STATUS) ||
1438        (pContext->powerState != NdisDeviceStateD0))
1439     {
1440         *pRunDpc = FALSE;
1441         return FALSE;
1442     }
1443 
1444     PARANDIS_STORE_LAST_INTERRUPT_TIMESTAMP(pContext);
1445     ParaNdis_VirtIODisableIrqSynchronized(pContext, isAny);
1446     InterlockedOr(&pContext->InterruptStatus, (LONG) ((status & isControl) | isReceive | isTransmit));
1447     *pRunDpc = TRUE;
1448     return TRUE;
1449 }
1450 
1451 BOOLEAN ParaNdis_OnQueuedInterrupt(
1452     PARANDIS_ADAPTER *pContext,
1453     OUT BOOLEAN *pRunDpc,
1454     ULONG knownInterruptSources)
1455 {
1456     struct virtqueue* _vq = ParaNdis_GetQueueForInterrupt(pContext, knownInterruptSources);
1457 
1458     /* If interrupts for this queue disabled do nothing */
1459     if((_vq != NULL) && !ParaNDIS_IsQueueInterruptEnabled(_vq))
1460     {
1461         *pRunDpc = FALSE;
1462     }
1463     else
1464     {
1465         PARANDIS_STORE_LAST_INTERRUPT_TIMESTAMP(pContext);
1466         InterlockedOr(&pContext->InterruptStatus, (LONG)knownInterruptSources);
1467         ParaNdis_VirtIODisableIrqSynchronized(pContext, knownInterruptSources);
1468         *pRunDpc = TRUE;
1469     }
1470 
1471     return *pRunDpc;
1472 }
1473 
1474 
1475 /**********************************************************
1476 It is called from Rx processing routines in regular mode of operation.
1477 Returns received buffer back to VirtIO queue, inserting it to NetReceiveBuffers.
1478 If needed, signals end of RX pause operation
1479 
1480 Must be called with &pContext->ReceiveLock acquired
1481 
1482 Parameters:
1483     context
1484     void *pDescriptor - pIONetDescriptor to return
1485 ***********************************************************/
1486 void ReuseReceiveBufferRegular(PARANDIS_ADAPTER *pContext, pIONetDescriptor pBuffersDescriptor)
1487 {
1488     DEBUG_ENTRY(4);
1489 
1490     if(!pBuffersDescriptor)
1491         return;
1492 
1493     RemoveEntryList(&pBuffersDescriptor->listEntry);
1494 
1495     if(AddRxBufferToQueue(pContext, pBuffersDescriptor))
1496     {
1497         InsertTailList(&pContext->NetReceiveBuffers, &pBuffersDescriptor->listEntry);
1498 
1499         pContext->NetNofReceiveBuffers++;
1500 
1501         if (pContext->NetNofReceiveBuffers > pContext->NetMaxReceiveBuffers)
1502         {
1503             DPrintf(0, (" Error: NetNofReceiveBuffers > NetMaxReceiveBuffers(%d>%d)",
1504                 pContext->NetNofReceiveBuffers, pContext->NetMaxReceiveBuffers));
1505         }
1506 
1507         if (++pContext->Counters.nReusedRxBuffers >= pContext->Limits.nReusedRxBuffers)
1508         {
1509             pContext->Counters.nReusedRxBuffers = 0;
1510             virtqueue_kick_always(pContext->NetReceiveQueue);
1511         }
1512 
1513         if (IsListEmpty(&pContext->NetReceiveBuffersWaiting))
1514         {
1515             if (pContext->ReceiveState == srsPausing || pContext->ReceivePauseCompletionProc)
1516             {
1517                 ONPAUSECOMPLETEPROC callback = pContext->ReceivePauseCompletionProc;
1518                 pContext->ReceiveState = srsDisabled;
1519                 pContext->ReceivePauseCompletionProc = NULL;
1520                 ParaNdis_DebugHistory(pContext, hopInternalReceivePause, NULL, 0, 0, 0);
1521                 if (callback) callback(pContext);
1522             }
1523         }
1524     }
1525     else
1526     {
1527         DPrintf(0, ("FAILED TO REUSE THE BUFFER!!!!"));
1528         VirtIONetFreeBufferDescriptor(pContext, pBuffersDescriptor);
1529         pContext->NetMaxReceiveBuffers--;
1530     }
1531 }
1532 
1533 /**********************************************************
1534 It is called from Rx processing routines between power off and power on in non-paused mode (Win8).
1535 Returns received buffer to NetReceiveBuffers.
1536 All the buffers will be placed into Virtio queue during power-on procedure
1537 
1538 Must be called with &pContext->ReceiveLock acquired
1539 
1540 Parameters:
1541     context
1542     void *pDescriptor - pIONetDescriptor to return
1543 ***********************************************************/
1544 static void ReuseReceiveBufferPowerOff(PARANDIS_ADAPTER *pContext, pIONetDescriptor pBuffersDescriptor)
1545 {
1546     RemoveEntryList(&pBuffersDescriptor->listEntry);
1547     InsertTailList(&pContext->NetReceiveBuffers, &pBuffersDescriptor->listEntry);
1548 }
1549 
1550 /**********************************************************
1551 It is called from Tx processing routines
1552 Gets all the finished buffer from VirtIO TX path and
1553 returns them to NetFreeSendBuffers
1554 
1555 Must be called with &pContext->SendLock acquired
1556 
1557 Parameters:
1558     context
1559 Return value:
1560     (for reference) number of TX buffers returned
1561 ***********************************************************/
1562 UINT ParaNdis_VirtIONetReleaseTransmitBuffers(
1563     PARANDIS_ADAPTER *pContext)
1564 {
1565     UINT len, i = 0;
1566     pIONetDescriptor pBufferDescriptor;
1567 
1568     DEBUG_ENTRY(4);
1569 
1570     while(NULL != (pBufferDescriptor = virtqueue_get_buf(pContext->NetSendQueue, &len)))
1571     {
1572         RemoveEntryList(&pBufferDescriptor->listEntry);
1573         pContext->nofFreeTxDescriptors++;
1574         if (!pBufferDescriptor->nofUsedBuffers)
1575         {
1576             DPrintf(0, ("[%s] ERROR: nofUsedBuffers not set!", __FUNCTION__));
1577         }
1578         pContext->nofFreeHardwareBuffers += pBufferDescriptor->nofUsedBuffers;
1579         ParaNdis_OnTransmitBufferReleased(pContext, pBufferDescriptor);
1580         InsertTailList(&pContext->NetFreeSendBuffers, &pBufferDescriptor->listEntry);
1581         DPrintf(3, ("[%s] Free Tx: desc %d, buff %d", __FUNCTION__, pContext->nofFreeTxDescriptors, pContext->nofFreeHardwareBuffers));
1582         pBufferDescriptor->nofUsedBuffers = 0;
1583         ++i;
1584     }
1585     if (i)
1586     {
1587         NdisGetCurrentSystemTime(&pContext->LastTxCompletionTimeStamp);
1588         pContext->bDoKickOnNoBuffer = TRUE;
1589         pContext->nDetectedStoppedTx = 0;
1590     }
1591     DEBUG_EXIT_STATUS((i ? 3 : 5), i);
1592     return i;
1593 }
1594 
1595 static ULONG FORCEINLINE QueryTcpHeaderOffset(PVOID packetData, ULONG ipHeaderOffset, ULONG ipPacketLength)
1596 {
1597     ULONG res;
1598     tTcpIpPacketParsingResult ppr = ParaNdis_ReviewIPPacket(
1599         (PUCHAR)packetData + ipHeaderOffset,
1600         ipPacketLength,
1601         __FUNCTION__);
1602     if (ppr.xxpStatus == ppresXxpKnown)
1603     {
1604         res = ipHeaderOffset + ppr.ipHeaderSize;
1605     }
1606     else
1607     {
1608         DPrintf(0, ("[%s] ERROR: NOT a TCP or UDP packet - expected troubles!", __FUNCTION__));
1609         res = 0;
1610     }
1611     return res;
1612 }
1613 
1614 
1615 /*********************************************************
1616 Called with from ProcessTx routine with TxLock held
1617 Uses pContext->sgTxGatherTable
1618 ***********************************************************/
1619 tCopyPacketResult ParaNdis_DoSubmitPacket(PARANDIS_ADAPTER *pContext, tTxOperationParameters *Params)
1620 {
1621     tCopyPacketResult result;
1622     tMapperResult mapResult = {0,0,0};
1623     // populating priority tag or LSO MAY require additional SG element
1624     UINT nRequiredBuffers;
1625     BOOLEAN bUseCopy = FALSE;
1626     struct VirtIOBufferDescriptor *sg = pContext->sgTxGatherTable;
1627 
1628     nRequiredBuffers = Params->nofSGFragments + 1 + ((Params->flags & (pcrPriorityTag | pcrLSO)) ? 1 : 0);
1629 
1630     result.size = 0;
1631     result.error = cpeOK;
1632     if (!pContext->bUseScatterGather ||         // only copy available
1633         Params->nofSGFragments == 0 ||          // theoretical case
1634         !sg ||                                  // only copy available
1635         ((~Params->flags & pcrLSO) && nRequiredBuffers > pContext->maxFreeHardwareBuffers) // to many fragments and normal size of packet
1636         )
1637     {
1638         nRequiredBuffers = 2;
1639         bUseCopy = TRUE;
1640     }
1641     else if (pContext->bUseIndirect && !(Params->flags & pcrNoIndirect))
1642     {
1643         nRequiredBuffers = 1;
1644     }
1645 
1646     // I do not think this will help, but at least we can try freeing some buffers right now
1647     if (pContext->nofFreeHardwareBuffers < nRequiredBuffers || !pContext->nofFreeTxDescriptors)
1648     {
1649         ParaNdis_VirtIONetReleaseTransmitBuffers(pContext);
1650     }
1651 
1652     if (nRequiredBuffers > pContext->maxFreeHardwareBuffers)
1653     {
1654         // LSO and too many buffers, impossible to send
1655         result.error = cpeTooLarge;
1656         DPrintf(0, ("[%s] ERROR: too many fragments(%d required, %d max.avail)!", __FUNCTION__,
1657             nRequiredBuffers, pContext->maxFreeHardwareBuffers));
1658     }
1659     else if (pContext->nofFreeHardwareBuffers < nRequiredBuffers || !pContext->nofFreeTxDescriptors)
1660     {
1661         virtqueue_enable_cb_delayed(pContext->NetSendQueue);
1662         result.error = cpeNoBuffer;
1663     }
1664     else if (Params->offloadMss && bUseCopy)
1665     {
1666         result.error = cpeInternalError;
1667         DPrintf(0, ("[%s] ERROR: expecting SG for TSO! (%d buffers, %d bytes)", __FUNCTION__,
1668             Params->nofSGFragments, Params->ulDataSize));
1669     }
1670     else if (bUseCopy)
1671     {
1672         result = ParaNdis_DoCopyPacketData(pContext, Params);
1673     }
1674     else
1675     {
1676         UINT nMappedBuffers;
1677         ULONGLONG paOfIndirectArea = 0;
1678         PVOID vaOfIndirectArea = NULL;
1679         pIONetDescriptor pBuffersDescriptor = (pIONetDescriptor)RemoveHeadList(&pContext->NetFreeSendBuffers);
1680         pContext->nofFreeTxDescriptors--;
1681         NdisZeroMemory(pBuffersDescriptor->HeaderInfo.Virtual, pBuffersDescriptor->HeaderInfo.size);
1682         sg[0].physAddr = pBuffersDescriptor->HeaderInfo.Physical;
1683         sg[0].length = pBuffersDescriptor->HeaderInfo.size;
1684         ParaNdis_PacketMapper(
1685             pContext,
1686             Params->packet,
1687             Params->ReferenceValue,
1688             sg + 1,
1689             pBuffersDescriptor,
1690             &mapResult);
1691         nMappedBuffers = mapResult.usBuffersMapped;
1692         if (nMappedBuffers)
1693         {
1694             nMappedBuffers++;
1695             if (pContext->bUseIndirect && !(Params->flags & pcrNoIndirect))
1696             {
1697                 ULONG space1 = (mapResult.usBufferSpaceUsed + 7) & ~7;
1698                 ULONG space2 = nMappedBuffers * SIZE_OF_SINGLE_INDIRECT_DESC;
1699                 if (pBuffersDescriptor->DataInfo.size >= (space1 + space2))
1700                 {
1701                     vaOfIndirectArea = RtlOffsetToPointer(pBuffersDescriptor->DataInfo.Virtual, space1);
1702                     paOfIndirectArea = pBuffersDescriptor->DataInfo.Physical.QuadPart + space1;
1703                     pContext->extraStatistics.framesIndirect++;
1704                 }
1705                 else if (nMappedBuffers <= pContext->nofFreeHardwareBuffers)
1706                 {
1707                     // send as is, no indirect
1708                 }
1709                 else
1710                 {
1711                     result.error = cpeNoIndirect;
1712                     DPrintf(0, ("[%s] Unexpected ERROR of placement!", __FUNCTION__));
1713                 }
1714             }
1715             if (result.error == cpeOK)
1716             {
1717                 if (Params->flags & (pcrTcpChecksum | pcrUdpChecksum))
1718                 {
1719                     unsigned short addPriorityLen = (Params->flags & pcrPriorityTag) ? ETH_PRIORITY_HEADER_SIZE : 0;
1720                     if (pContext->bDoHardwareChecksum)
1721                     {
1722                         virtio_net_hdr_basic *pheader = pBuffersDescriptor->HeaderInfo.Virtual;
1723                         pheader->flags = VIRTIO_NET_HDR_F_NEEDS_CSUM;
1724                         if (!Params->tcpHeaderOffset)
1725                         {
1726                             Params->tcpHeaderOffset = QueryTcpHeaderOffset(
1727                                 pBuffersDescriptor->DataInfo.Virtual,
1728                                 pContext->Offload.ipHeaderOffset + addPriorityLen,
1729                                 mapResult.usBufferSpaceUsed - pContext->Offload.ipHeaderOffset - addPriorityLen);
1730                         }
1731                         else
1732                         {
1733                             Params->tcpHeaderOffset += addPriorityLen;
1734                         }
1735                         pheader->csum_start = (USHORT)Params->tcpHeaderOffset;
1736                         pheader->csum_offset = (Params->flags & pcrTcpChecksum) ? TCP_CHECKSUM_OFFSET : UDP_CHECKSUM_OFFSET;
1737                     }
1738                     else
1739                     {
1740                         // emulation mode - it is slow and intended only for test of flows
1741                         // and debugging of WLK test cases
1742                         PVOID pCopy = ParaNdis_AllocateMemory(pContext, Params->ulDataSize);
1743                         if (pCopy)
1744                         {
1745                             tTcpIpPacketParsingResult ppr;
1746                             // duplicate entire packet
1747                             ParaNdis_PacketCopier(Params->packet, pCopy, Params->ulDataSize, Params->ReferenceValue, FALSE);
1748                             // calculate complete TCP/UDP checksum
1749                             ppr = ParaNdis_CheckSumVerify(
1750                                 RtlOffsetToPointer(pCopy, pContext->Offload.ipHeaderOffset + addPriorityLen),
1751                                 Params->ulDataSize - pContext->Offload.ipHeaderOffset - addPriorityLen,
1752                                 pcrAnyChecksum | pcrFixXxpChecksum,
1753                                 __FUNCTION__);
1754                             // data portion in aside buffer contains complete IP+TCP header
1755                             // rewrite copy of original buffer by one new with calculated data
1756                             NdisMoveMemory(
1757                                 pBuffersDescriptor->DataInfo.Virtual,
1758                                 pCopy,
1759                                 mapResult.usBufferSpaceUsed);
1760                             NdisFreeMemory(pCopy, 0, 0);
1761                         }
1762                     }
1763                 }
1764 
1765                 if (0 <= virtqueue_add_buf(
1766                     pContext->NetSendQueue,
1767                     sg,
1768                     nMappedBuffers,
1769                     0,
1770                     pBuffersDescriptor,
1771                     vaOfIndirectArea,
1772                     paOfIndirectArea))
1773                 {
1774                     pBuffersDescriptor->nofUsedBuffers = nMappedBuffers;
1775                     pContext->nofFreeHardwareBuffers -= nMappedBuffers;
1776                     if (pContext->minFreeHardwareBuffers > pContext->nofFreeHardwareBuffers)
1777                         pContext->minFreeHardwareBuffers = pContext->nofFreeHardwareBuffers;
1778                     pBuffersDescriptor->ReferenceValue = Params->ReferenceValue;
1779                     result.size = Params->ulDataSize;
1780                     DPrintf(2, ("[%s] Submitted %d buffers (%d bytes), avail %d desc, %d bufs",
1781                         __FUNCTION__, nMappedBuffers, result.size,
1782                         pContext->nofFreeTxDescriptors, pContext->nofFreeHardwareBuffers
1783                     ));
1784                 }
1785                 else
1786                 {
1787                     result.error = cpeInternalError;
1788                     DPrintf(0, ("[%s] Unexpected ERROR adding buffer to TX engine!..", __FUNCTION__));
1789                 }
1790             }
1791         }
1792         else
1793         {
1794             DPrintf(0, ("[%s] Unexpected ERROR: packet not mapped!", __FUNCTION__));
1795             result.error = cpeInternalError;
1796         }
1797 
1798         if (result.error == cpeOK)
1799         {
1800             UCHAR ethernetHeader[sizeof(ETH_HEADER)];
1801             eInspectedPacketType packetType;
1802             /* get the ethernet header for review */
1803             ParaNdis_PacketCopier(Params->packet, ethernetHeader, sizeof(ethernetHeader), Params->ReferenceValue, TRUE);
1804             packetType = QueryPacketType(ethernetHeader);
1805             DebugDumpPacket("sending", ethernetHeader, 3);
1806             InsertTailList(&pContext->NetSendBuffersInUse, &pBuffersDescriptor->listEntry);
1807             pContext->Statistics.ifHCOutOctets += result.size;
1808             switch (packetType)
1809             {
1810                 case iptBroadcast:
1811                     pContext->Statistics.ifHCOutBroadcastOctets += result.size;
1812                     pContext->Statistics.ifHCOutBroadcastPkts++;
1813                     break;
1814                 case iptMulticast:
1815                     pContext->Statistics.ifHCOutMulticastOctets += result.size;
1816                     pContext->Statistics.ifHCOutMulticastPkts++;
1817                     break;
1818                 default:
1819                     pContext->Statistics.ifHCOutUcastOctets += result.size;
1820                     pContext->Statistics.ifHCOutUcastPkts++;
1821                     break;
1822             }
1823 
1824             if (Params->flags & pcrLSO)
1825                 pContext->extraStatistics.framesLSO++;
1826         }
1827         else
1828         {
1829             pContext->nofFreeTxDescriptors++;
1830             InsertHeadList(&pContext->NetFreeSendBuffers, &pBuffersDescriptor->listEntry);
1831         }
1832     }
1833     if (result.error == cpeNoBuffer && pContext->bDoKickOnNoBuffer)
1834     {
1835         virtqueue_kick_always(pContext->NetSendQueue);
1836         pContext->bDoKickOnNoBuffer = FALSE;
1837     }
1838     if (result.error == cpeOK)
1839     {
1840         if (Params->flags & (pcrTcpChecksum | pcrUdpChecksum))
1841             pContext->extraStatistics.framesCSOffload++;
1842     }
1843     return result;
1844 }
1845 
1846 
1847 /**********************************************************
1848 It is called from Tx processing routines
1849 Prepares the VirtIO buffer and copies to it the data from provided packet
1850 
1851 Must be called with &pContext->SendLock acquired
1852 
1853 Parameters:
1854     context
1855     tPacketType packet          specific type is NDIS dependent
1856     tCopyPacketDataFunction     PacketCopier procedure for NDIS-specific type of packet
1857 Return value:
1858     (for reference) number of TX buffers returned
1859 ***********************************************************/
1860 tCopyPacketResult ParaNdis_DoCopyPacketData(
1861     PARANDIS_ADAPTER *pContext,
1862     tTxOperationParameters *pParams)
1863 {
1864     tCopyPacketResult result;
1865     tCopyPacketResult CopierResult;
1866     struct VirtIOBufferDescriptor sg[2];
1867     pIONetDescriptor pBuffersDescriptor = NULL;
1868     ULONG flags = pParams->flags;
1869     UINT nRequiredHardwareBuffers = 2;
1870     result.size  = 0;
1871     result.error = cpeOK;
1872     if (pContext->nofFreeHardwareBuffers < nRequiredHardwareBuffers ||
1873         IsListEmpty(&pContext->NetFreeSendBuffers))
1874     {
1875         result.error = cpeNoBuffer;
1876     }
1877     if(result.error == cpeOK)
1878     {
1879         pBuffersDescriptor = (pIONetDescriptor)RemoveHeadList(&pContext->NetFreeSendBuffers);
1880         NdisZeroMemory(pBuffersDescriptor->HeaderInfo.Virtual, pBuffersDescriptor->HeaderInfo.size);
1881         sg[0].physAddr = pBuffersDescriptor->HeaderInfo.Physical;
1882         sg[0].length = pBuffersDescriptor->HeaderInfo.size;
1883         sg[1].physAddr = pBuffersDescriptor->DataInfo.Physical;
1884         CopierResult = ParaNdis_PacketCopier(
1885             pParams->packet,
1886             pBuffersDescriptor->DataInfo.Virtual,
1887             pBuffersDescriptor->DataInfo.size,
1888             pParams->ReferenceValue,
1889             FALSE);
1890         sg[1].length = result.size = CopierResult.size;
1891         // did NDIS ask us to compute CS?
1892         if ((flags & (pcrTcpChecksum | pcrUdpChecksum | pcrIpChecksum)) != 0)
1893         {
1894             // we asked
1895             unsigned short addPriorityLen = (pParams->flags & pcrPriorityTag) ? ETH_PRIORITY_HEADER_SIZE : 0;
1896             PVOID ipPacket = RtlOffsetToPointer(
1897                 pBuffersDescriptor->DataInfo.Virtual, pContext->Offload.ipHeaderOffset + addPriorityLen);
1898             ULONG ipPacketLength = CopierResult.size - pContext->Offload.ipHeaderOffset - addPriorityLen;
1899             if (!pParams->tcpHeaderOffset &&
1900                 (flags & (pcrTcpChecksum | pcrUdpChecksum)) )
1901             {
1902                 pParams->tcpHeaderOffset = QueryTcpHeaderOffset(
1903                     pBuffersDescriptor->DataInfo.Virtual,
1904                     pContext->Offload.ipHeaderOffset + addPriorityLen,
1905                     ipPacketLength);
1906             }
1907             else
1908             {
1909                 pParams->tcpHeaderOffset += addPriorityLen;
1910             }
1911 
1912             if (pContext->bDoHardwareChecksum)
1913             {
1914                 if (flags & (pcrTcpChecksum | pcrUdpChecksum))
1915                 {
1916                     // hardware offload
1917                     virtio_net_hdr_basic *pvnh = (virtio_net_hdr_basic *)pBuffersDescriptor->HeaderInfo.Virtual;
1918                     pvnh->csum_start = (USHORT)pParams->tcpHeaderOffset;
1919                     pvnh->csum_offset = (flags & pcrTcpChecksum) ? TCP_CHECKSUM_OFFSET : UDP_CHECKSUM_OFFSET;
1920                     pvnh->flags |= VIRTIO_NET_HDR_F_NEEDS_CSUM;
1921                 }
1922                 if (flags & (pcrIpChecksum))
1923                 {
1924                     ParaNdis_CheckSumVerify(
1925                         ipPacket,
1926                         ipPacketLength,
1927                         pcrIpChecksum | pcrFixIPChecksum,
1928                         __FUNCTION__);
1929                 }
1930             }
1931             else if (CopierResult.size > pContext->Offload.ipHeaderOffset)
1932             {
1933                 ULONG csFlags = 0;
1934                 if (flags & pcrIpChecksum) csFlags |= pcrIpChecksum | pcrFixIPChecksum;
1935                 if (flags & (pcrTcpChecksum | pcrUdpChecksum)) csFlags |= pcrTcpChecksum | pcrUdpChecksum| pcrFixXxpChecksum;
1936                 // software offload
1937                 ParaNdis_CheckSumVerify(
1938                     ipPacket,
1939                     ipPacketLength,
1940                     csFlags,
1941                     __FUNCTION__);
1942             }
1943             else
1944             {
1945                 DPrintf(0, ("[%s] ERROR: Invalid buffer size for offload!", __FUNCTION__));
1946                 result.size = 0;
1947                 result.error = cpeInternalError;
1948             }
1949         }
1950         pContext->nofFreeTxDescriptors--;
1951         if (result.size)
1952         {
1953             eInspectedPacketType packetType;
1954             packetType = QueryPacketType(pBuffersDescriptor->DataInfo.Virtual);
1955             DebugDumpPacket("sending", pBuffersDescriptor->DataInfo.Virtual, 3);
1956 
1957             pBuffersDescriptor->nofUsedBuffers = nRequiredHardwareBuffers;
1958             pContext->nofFreeHardwareBuffers -= nRequiredHardwareBuffers;
1959             if (pContext->minFreeHardwareBuffers > pContext->nofFreeHardwareBuffers)
1960                 pContext->minFreeHardwareBuffers = pContext->nofFreeHardwareBuffers;
1961             if (0 > virtqueue_add_buf(
1962                 pContext->NetSendQueue,
1963                 sg,
1964                 2,
1965                 0,
1966                 pBuffersDescriptor,
1967                 NULL,
1968                 0
1969                 ))
1970             {
1971                 pBuffersDescriptor->nofUsedBuffers = 0;
1972                 pContext->nofFreeHardwareBuffers += nRequiredHardwareBuffers;
1973                 result.error = cpeInternalError;
1974                 result.size  = 0;
1975                 DPrintf(0, ("[%s] Unexpected ERROR adding buffer to TX engine!..", __FUNCTION__));
1976             }
1977             else
1978             {
1979                 DPrintf(2, ("[%s] Submitted %d buffers (%d bytes), avail %d desc, %d bufs",
1980                     __FUNCTION__, nRequiredHardwareBuffers, result.size,
1981                     pContext->nofFreeTxDescriptors, pContext->nofFreeHardwareBuffers
1982                 ));
1983             }
1984             if (result.error != cpeOK)
1985             {
1986                 InsertTailList(&pContext->NetFreeSendBuffers, &pBuffersDescriptor->listEntry);
1987                 pContext->nofFreeTxDescriptors++;
1988             }
1989             else
1990             {
1991                 ULONG reportedSize = pParams->ulDataSize;
1992                 pBuffersDescriptor->ReferenceValue = pParams->ReferenceValue;
1993                 InsertTailList(&pContext->NetSendBuffersInUse, &pBuffersDescriptor->listEntry);
1994                 pContext->Statistics.ifHCOutOctets += reportedSize;
1995                 switch (packetType)
1996                 {
1997                     case iptBroadcast:
1998                         pContext->Statistics.ifHCOutBroadcastOctets += reportedSize;
1999                         pContext->Statistics.ifHCOutBroadcastPkts++;
2000                         break;
2001                     case iptMulticast:
2002                         pContext->Statistics.ifHCOutMulticastOctets += reportedSize;
2003                         pContext->Statistics.ifHCOutMulticastPkts++;
2004                         break;
2005                     default:
2006                         pContext->Statistics.ifHCOutUcastOctets += reportedSize;
2007                         pContext->Statistics.ifHCOutUcastPkts++;
2008                         break;
2009                 }
2010             }
2011         }
2012         else
2013         {
2014             DPrintf(0, ("[%s] Unexpected ERROR in copying packet data! Continue...", __FUNCTION__));
2015             InsertTailList(&pContext->NetFreeSendBuffers, &pBuffersDescriptor->listEntry);
2016             pContext->nofFreeTxDescriptors++;
2017             // the buffer is not copied and the callback will not be called
2018             result.error = cpeInternalError;
2019         }
2020     }
2021 
2022     return result;
2023 }
2024 
2025 static ULONG ShallPassPacket(PARANDIS_ADAPTER *pContext, PVOID address, UINT len, eInspectedPacketType *pType)
2026 {
2027     ULONG b;
2028     if (len <= sizeof(ETH_HEADER)) return FALSE;
2029     if (len > pContext->MaxPacketSize.nMaxFullSizeHwRx) return FALSE;
2030     if (len > pContext->MaxPacketSize.nMaxFullSizeOS && !ETH_HAS_PRIO_HEADER(address)) return FALSE;
2031     *pType = QueryPacketType(address);
2032     if (pContext->PacketFilter & NDIS_PACKET_TYPE_PROMISCUOUS)  return TRUE;
2033 
2034     switch(*pType)
2035     {
2036         case iptBroadcast:
2037             b = pContext->PacketFilter & NDIS_PACKET_TYPE_BROADCAST;
2038             break;
2039         case iptMulticast:
2040             b = pContext->PacketFilter & NDIS_PACKET_TYPE_ALL_MULTICAST;
2041             if (!b && (pContext->PacketFilter & NDIS_PACKET_TYPE_MULTICAST))
2042             {
2043                 UINT i, n = pContext->MulticastData.nofMulticastEntries * ETH_LENGTH_OF_ADDRESS;
2044                 b = 1;
2045                 for (i = 0; b && i < n; i += ETH_LENGTH_OF_ADDRESS)
2046                 {
2047                     ETH_COMPARE_NETWORK_ADDRESSES((PUCHAR)address, &pContext->MulticastData.MulticastList[i], &b)
2048                 }
2049                 b = !b;
2050             }
2051             break;
2052         default:
2053             ETH_COMPARE_NETWORK_ADDRESSES((PUCHAR)address, pContext->CurrentMacAddress, &b);
2054             b = !b && (pContext->PacketFilter & NDIS_PACKET_TYPE_DIRECTED);
2055             break;
2056     }
2057     if (!b)
2058     {
2059         pContext->extraStatistics.framesFilteredOut++;
2060     }
2061     return b;
2062 }
2063 
2064 void
2065 ParaNdis_PadPacketReceived(PVOID pDataBuffer, PULONG pLength)
2066 {
2067     // Ethernet standard declares minimal possible packet size
2068     // Packets smaller than that must be padded before transfer
2069     // Ethernet HW pads packets on transmit, however in our case
2070     // some packets do not travel over Ethernet but being routed
2071     // guest-to-guest by virtual switch.
2072     // In this case padding is not performed and we may
2073     // receive packet smaller than minimal allowed size. This is not
2074     // a problem for real life scenarios however WHQL/HCK contains
2075     // tests that check padding of received packets.
2076     // To make these tests happy we have to pad small packets on receive
2077 
2078     //NOTE: This function assumes that VLAN header has been already stripped out
2079 
2080     if(*pLength < ETH_MIN_PACKET_SIZE)
2081     {
2082         RtlZeroMemory(RtlOffsetToPointer(pDataBuffer, *pLength), ETH_MIN_PACKET_SIZE - *pLength);
2083         *pLength = ETH_MIN_PACKET_SIZE;
2084     }
2085 }
2086 
2087 /**********************************************************
2088 Manages RX path, calling NDIS-specific procedure for packet indication
2089 Parameters:
2090     context
2091 ***********************************************************/
2092 static UINT ParaNdis_ProcessRxPath(PARANDIS_ADAPTER *pContext, ULONG ulMaxPacketsToIndicate)
2093 {
2094     pIONetDescriptor pBuffersDescriptor;
2095     UINT len, headerSize = pContext->nVirtioHeaderSize;
2096     eInspectedPacketType packetType = iptInvalid;
2097     UINT nReceived = 0, nRetrieved = 0, nReported = 0;
2098     tPacketIndicationType   *pBatchOfPackets;
2099     UINT                    maxPacketsInBatch = pContext->NetMaxReceiveBuffers;
2100     pBatchOfPackets = pContext->bBatchReceive ?
2101         ParaNdis_AllocateMemory(pContext, maxPacketsInBatch * sizeof(tPacketIndicationType)) : NULL;
2102     NdisAcquireSpinLock(&pContext->ReceiveLock);
2103     while ((nReported < ulMaxPacketsToIndicate) && NULL != (pBuffersDescriptor = virtqueue_get_buf(pContext->NetReceiveQueue, &len)))
2104     {
2105         PVOID pDataBuffer = RtlOffsetToPointer(pBuffersDescriptor->DataInfo.Virtual, pContext->bUseMergedBuffers ? pContext->nVirtioHeaderSize : 0);
2106         RemoveEntryList(&pBuffersDescriptor->listEntry);
2107         InsertTailList(&pContext->NetReceiveBuffersWaiting, &pBuffersDescriptor->listEntry);
2108         pContext->NetNofReceiveBuffers--;
2109         nRetrieved++;
2110         DPrintf(2, ("[%s] retrieved header+%d b.", __FUNCTION__, len - headerSize));
2111         DebugDumpPacket("receive", pDataBuffer, 3);
2112 
2113         if( !pContext->bSurprizeRemoved &&
2114             ShallPassPacket(pContext, pDataBuffer, len - headerSize, &packetType) &&
2115             pContext->ReceiveState == srsEnabled &&
2116             pContext->bConnected)
2117         {
2118             BOOLEAN b = FALSE;
2119             ULONG length = len - headerSize;
2120             if (!pBatchOfPackets)
2121             {
2122                 NdisReleaseSpinLock(&pContext->ReceiveLock);
2123                 b = NULL != ParaNdis_IndicateReceivedPacket(
2124                     pContext,
2125                     pDataBuffer,
2126                     &length,
2127                     FALSE,
2128                     pBuffersDescriptor);
2129                 NdisAcquireSpinLock(&pContext->ReceiveLock);
2130             }
2131             else
2132             {
2133                 tPacketIndicationType packet;
2134                 packet = ParaNdis_IndicateReceivedPacket(
2135                     pContext,
2136                     pDataBuffer,
2137                     &length,
2138                     TRUE,
2139                     pBuffersDescriptor);
2140                 b = packet != NULL;
2141                 if (b) pBatchOfPackets[nReceived] = packet;
2142             }
2143             if (!b)
2144             {
2145                 pContext->ReuseBufferProc(pContext, pBuffersDescriptor);
2146                 //only possible reason for that is unexpected Vlan tag
2147                 //shall I count it as error?
2148                 pContext->Statistics.ifInErrors++;
2149                 pContext->Statistics.ifInDiscards++;
2150             }
2151             else
2152             {
2153                 nReceived++;
2154                 nReported++;
2155                 pContext->Statistics.ifHCInOctets += length;
2156                 switch(packetType)
2157                 {
2158                     case iptBroadcast:
2159                         pContext->Statistics.ifHCInBroadcastPkts++;
2160                         pContext->Statistics.ifHCInBroadcastOctets += length;
2161                         break;
2162                     case iptMulticast:
2163                         pContext->Statistics.ifHCInMulticastPkts++;
2164                         pContext->Statistics.ifHCInMulticastOctets += length;
2165                         break;
2166                     default:
2167                         pContext->Statistics.ifHCInUcastPkts++;
2168                         pContext->Statistics.ifHCInUcastOctets += length;
2169                         break;
2170                 }
2171                 if (pBatchOfPackets && nReceived == maxPacketsInBatch)
2172                 {
2173                     DPrintf(1, ("[%s] received %d buffers of max %d", __FUNCTION__, nReceived, ulMaxPacketsToIndicate));
2174                     NdisReleaseSpinLock(&pContext->ReceiveLock);
2175                     ParaNdis_IndicateReceivedBatch(pContext, pBatchOfPackets, nReceived);
2176                     NdisAcquireSpinLock(&pContext->ReceiveLock);
2177                     nReceived = 0;
2178                 }
2179             }
2180         }
2181         else
2182         {
2183             // reuse packet, there is no data or the RX is suppressed
2184             pContext->ReuseBufferProc(pContext, pBuffersDescriptor);
2185         }
2186     }
2187     ParaNdis_DebugHistory(pContext, hopReceiveStat, NULL, nRetrieved, nReported, pContext->NetNofReceiveBuffers);
2188     NdisReleaseSpinLock(&pContext->ReceiveLock);
2189     if (nReceived && pBatchOfPackets)
2190     {
2191         DPrintf(1, ("[%s]%d: received %d buffers of max %d", __FUNCTION__, KeGetCurrentProcessorNumber(), nReceived, ulMaxPacketsToIndicate));
2192         ParaNdis_IndicateReceivedBatch(pContext, pBatchOfPackets, nReceived);
2193     }
2194     if (pBatchOfPackets) NdisFreeMemory(pBatchOfPackets, 0, 0);
2195     return nReported;
2196 }
2197 
2198 void ParaNdis_ReportLinkStatus(PARANDIS_ADAPTER *pContext, BOOLEAN bForce)
2199 {
2200     BOOLEAN bConnected = TRUE;
2201     if (pContext->bLinkDetectSupported)
2202     {
2203         USHORT linkStatus = 0;
2204         USHORT offset = sizeof(pContext->CurrentMacAddress);
2205         // link changed
2206         virtio_get_config(&pContext->IODevice, offset, &linkStatus, sizeof(linkStatus));
2207         bConnected = (linkStatus & VIRTIO_NET_S_LINK_UP) != 0;
2208     }
2209     ParaNdis_IndicateConnect(pContext, bConnected, bForce);
2210 }
2211 
2212 static BOOLEAN NTAPI RestartQueueSynchronously(tSynchronizedContext *SyncContext)
2213 {
2214     struct virtqueue * _vq = (struct virtqueue *) SyncContext->Parameter;
2215     bool res = true;
2216     if (!virtqueue_enable_cb(_vq))
2217     {
2218         virtqueue_disable_cb(_vq);
2219         res = false;
2220     }
2221 
2222     ParaNdis_DebugHistory(SyncContext->pContext, hopDPC, (PVOID)SyncContext->Parameter, 0x20, res, 0);
2223     return !res;
2224 }
2225 /**********************************************************
2226 DPC implementation, common for both NDIS
2227 Parameters:
2228     context
2229 ***********************************************************/
2230 ULONG ParaNdis_DPCWorkBody(PARANDIS_ADAPTER *pContext, ULONG ulMaxPacketsToIndicate)
2231 {
2232     ULONG stillRequiresProcessing = 0;
2233     ULONG interruptSources;
2234     UINT uIndicatedRXPackets = 0;
2235     UINT numOfPacketsToIndicate = min(ulMaxPacketsToIndicate, pContext->uNumberOfHandledRXPacketsInDPC);
2236 
2237     DEBUG_ENTRY(5);
2238     if (pContext->bEnableInterruptHandlingDPC)
2239     {
2240         InterlockedIncrement(&pContext->counterDPCInside);
2241         if (pContext->bEnableInterruptHandlingDPC)
2242         {
2243             BOOLEAN bDoKick = FALSE;
2244 
2245             InterlockedExchange(&pContext->bDPCInactive, 0);
2246             interruptSources = InterlockedExchange(&pContext->InterruptStatus, 0);
2247             ParaNdis_DebugHistory(pContext, hopDPC, (PVOID)1, interruptSources, 0, 0);
2248             if ((interruptSources & isControl) && pContext->bLinkDetectSupported)
2249             {
2250                 ParaNdis_ReportLinkStatus(pContext, FALSE);
2251             }
2252             if (interruptSources & isTransmit)
2253             {
2254                 bDoKick = ParaNdis_ProcessTx(pContext, TRUE, TRUE);
2255             }
2256             if (interruptSources & isReceive)
2257             {
2258                 int nRestartResult = 0;
2259 
2260                 do
2261                 {
2262                     LONG rxActive = InterlockedIncrement(&pContext->dpcReceiveActive);
2263                     if (rxActive == 1)
2264                     {
2265                         uIndicatedRXPackets += ParaNdis_ProcessRxPath(pContext, numOfPacketsToIndicate - uIndicatedRXPackets);
2266                         InterlockedDecrement(&pContext->dpcReceiveActive);
2267                         NdisAcquireSpinLock(&pContext->ReceiveLock);
2268                         nRestartResult = ParaNdis_SynchronizeWithInterrupt(
2269                             pContext, pContext->ulRxMessage, RestartQueueSynchronously, pContext->NetReceiveQueue);
2270                         ParaNdis_DebugHistory(pContext, hopDPC, (PVOID)3, nRestartResult, 0, 0);
2271                         NdisReleaseSpinLock(&pContext->ReceiveLock);
2272                         DPrintf(nRestartResult ? 2 : 6, ("[%s] queue restarted%s", __FUNCTION__, nRestartResult ? "(Rerun)" : "(Done)"));
2273 
2274                         if (uIndicatedRXPackets < numOfPacketsToIndicate)
2275                         {
2276 
2277                         }
2278                         else if (uIndicatedRXPackets == numOfPacketsToIndicate)
2279                         {
2280                             DPrintf(1, ("[%s] Breaking Rx loop after %d indications", __FUNCTION__, uIndicatedRXPackets));
2281                             ParaNdis_DebugHistory(pContext, hopDPC, (PVOID)4, nRestartResult, 0, 0);
2282                             break;
2283                         }
2284                         else
2285                         {
2286                             DPrintf(0, ("[%s] Glitch found: %d allowed, %d indicated", __FUNCTION__, numOfPacketsToIndicate, uIndicatedRXPackets));
2287                             ParaNdis_DebugHistory(pContext, hopDPC, (PVOID)6, nRestartResult, 0, 0);
2288                         }
2289                     }
2290                     else
2291                     {
2292                         InterlockedDecrement(&pContext->dpcReceiveActive);
2293                         if (!nRestartResult)
2294                         {
2295                             NdisAcquireSpinLock(&pContext->ReceiveLock);
2296                             nRestartResult = ParaNdis_SynchronizeWithInterrupt(
2297                                 pContext, pContext->ulRxMessage, RestartQueueSynchronously, pContext->NetReceiveQueue);
2298                             ParaNdis_DebugHistory(pContext, hopDPC, (PVOID)5, nRestartResult, 0, 0);
2299                             NdisReleaseSpinLock(&pContext->ReceiveLock);
2300                         }
2301                         DPrintf(1, ("[%s] Skip Rx processing no.%d", __FUNCTION__, rxActive));
2302                         break;
2303                     }
2304                 } while (nRestartResult);
2305 
2306                 if (nRestartResult) stillRequiresProcessing |= isReceive;
2307             }
2308 
2309             if (interruptSources & isTransmit)
2310             {
2311                 NdisAcquireSpinLock(&pContext->SendLock);
2312                 if (ParaNdis_SynchronizeWithInterrupt(pContext, pContext->ulTxMessage, RestartQueueSynchronously, pContext->NetSendQueue))
2313                     stillRequiresProcessing |= isTransmit;
2314                 if(bDoKick)
2315                 {
2316 #ifdef PARANDIS_TEST_TX_KICK_ALWAYS
2317                     virtqueue_kick_always(pContext->NetSendQueue);
2318 #else
2319                     virtqueue_kick(pContext->NetSendQueue);
2320 #endif
2321                 }
2322                 NdisReleaseSpinLock(&pContext->SendLock);
2323             }
2324         }
2325         InterlockedDecrement(&pContext->counterDPCInside);
2326         ParaNdis_DebugHistory(pContext, hopDPC, NULL, stillRequiresProcessing, pContext->nofFreeHardwareBuffers, pContext->nofFreeTxDescriptors);
2327     }
2328     return stillRequiresProcessing;
2329 }
2330 
2331 /**********************************************************
2332 Periodically called procedure, checking dpc activity
2333 If DPC are not running, it does exactly the same that the DPC
2334 Parameters:
2335     context
2336 ***********************************************************/
2337 static BOOLEAN CheckRunningDpc(PARANDIS_ADAPTER *pContext)
2338 {
2339     BOOLEAN bStopped;
2340     BOOLEAN bReportHang = FALSE;
2341     bStopped = 0 != InterlockedExchange(&pContext->bDPCInactive, TRUE);
2342 
2343     if (bStopped)
2344     {
2345         pContext->nDetectedInactivity++;
2346         if (pContext->nEnableDPCChecker)
2347         {
2348             if (pContext->NetTxPacketsToReturn)
2349             {
2350                 DPrintf(0, ("[%s] - NO ACTIVITY!", __FUNCTION__));
2351                 if (!pContext->Limits.nPrintDiagnostic) PrintStatistics(pContext);
2352                 if (pContext->nEnableDPCChecker > 1)
2353                 {
2354                     int isrStatus1, isrStatus2;
2355                     isrStatus1 = virtio_read_isr_status(&pContext->IODevice);
2356                     isrStatus2 = virtio_read_isr_status(&pContext->IODevice);
2357                     if (isrStatus1 || isrStatus2)
2358                     {
2359                         DPrintf(0, ("WARNING: Interrupt status %d=>%d", isrStatus1, isrStatus2));
2360                     }
2361                 }
2362                 // simulateDPC
2363                 InterlockedOr(&pContext->InterruptStatus, isAny);
2364                 ParaNdis_DPCWorkBody(pContext, PARANDIS_UNLIMITED_PACKETS_TO_INDICATE);
2365             }
2366         }
2367     }
2368     else
2369     {
2370         pContext->nDetectedInactivity = 0;
2371     }
2372 
2373     NdisAcquireSpinLock(&pContext->SendLock);
2374     if (pContext->nofFreeHardwareBuffers != pContext->maxFreeHardwareBuffers)
2375     {
2376         if (pContext->nDetectedStoppedTx++ > 1)
2377         {
2378             DPrintf(0, ("[%s] - Suspicious Tx inactivity (%d)!", __FUNCTION__, pContext->nofFreeHardwareBuffers));
2379             //bReportHang = TRUE;
2380 #ifdef DBG_USE_VIRTIO_PCI_ISR_FOR_HOST_REPORT
2381             WriteVirtIODeviceByte(pContext->IODevice.isr, 0);
2382 #endif
2383         }
2384     }
2385     NdisReleaseSpinLock(&pContext->SendLock);
2386 
2387 
2388     if (pContext->Limits.nPrintDiagnostic &&
2389         ++pContext->Counters.nPrintDiagnostic >= pContext->Limits.nPrintDiagnostic)
2390     {
2391         pContext->Counters.nPrintDiagnostic = 0;
2392         // todo - collect more and put out optionally
2393         PrintStatistics(pContext);
2394     }
2395 
2396     if (pContext->Statistics.ifHCInOctets == pContext->Counters.prevIn)
2397     {
2398         pContext->Counters.nRxInactivity++;
2399         if (pContext->Counters.nRxInactivity >= 10)
2400         {
2401 //#define CRASH_ON_NO_RX
2402 #if defined(CRASH_ON_NO_RX)
2403             ONPAUSECOMPLETEPROC proc = (ONPAUSECOMPLETEPROC)(PVOID)1;
2404             proc(pContext);
2405 #endif
2406         }
2407     }
2408     else
2409     {
2410         pContext->Counters.nRxInactivity = 0;
2411         pContext->Counters.prevIn = pContext->Statistics.ifHCInOctets;
2412     }
2413     return bReportHang;
2414 }
2415 
2416 /**********************************************************
2417 Common implementation of periodic poll
2418 Parameters:
2419     context
2420 Return:
2421     TRUE, if reset required
2422 ***********************************************************/
2423 BOOLEAN ParaNdis_CheckForHang(PARANDIS_ADAPTER *pContext)
2424 {
2425     static int nHangOn = 0;
2426     BOOLEAN b = nHangOn >= 3 && nHangOn < 6;
2427     DEBUG_ENTRY(3);
2428     b |= CheckRunningDpc(pContext);
2429     //uncomment to cause 3 consecutive resets
2430     //nHangOn++;
2431     DEBUG_EXIT_STATUS(b ? 0 : 6, b);
2432     return b;
2433 }
2434 
2435 /**********************************************************
2436 Common handler of multicast address configuration
2437 Parameters:
2438     PVOID Buffer            array of addresses from NDIS
2439     ULONG BufferSize        size of incoming buffer
2440     PUINT pBytesRead        update on success
2441     PUINT pBytesNeeded      update on wrong buffer size
2442 Return value:
2443     SUCCESS or kind of failure
2444 ***********************************************************/
2445 NDIS_STATUS ParaNdis_SetMulticastList(
2446     PARANDIS_ADAPTER *pContext,
2447     PVOID Buffer,
2448     ULONG BufferSize,
2449     PUINT pBytesRead,
2450     PUINT pBytesNeeded)
2451 {
2452     NDIS_STATUS status;
2453     ULONG length = BufferSize;
2454     if (length > sizeof(pContext->MulticastData.MulticastList))
2455     {
2456         status = NDIS_STATUS_MULTICAST_FULL;
2457         *pBytesNeeded = sizeof(pContext->MulticastData.MulticastList);
2458     }
2459     else if (length % ETH_LENGTH_OF_ADDRESS)
2460     {
2461         status = NDIS_STATUS_INVALID_LENGTH;
2462         *pBytesNeeded = (length / ETH_LENGTH_OF_ADDRESS) * ETH_LENGTH_OF_ADDRESS;
2463     }
2464     else
2465     {
2466         NdisZeroMemory(pContext->MulticastData.MulticastList, sizeof(pContext->MulticastData.MulticastList));
2467         if (length)
2468             NdisMoveMemory(pContext->MulticastData.MulticastList, Buffer, length);
2469         pContext->MulticastData.nofMulticastEntries = length / ETH_LENGTH_OF_ADDRESS;
2470         DPrintf(1, ("[%s] New multicast list of %d bytes", __FUNCTION__, length));
2471         *pBytesRead = length;
2472         status = NDIS_STATUS_SUCCESS;
2473     }
2474     return status;
2475 }
2476 
2477 /**********************************************************
2478 Callable from synchronized routine or interrupt handler
2479 to enable or disable Rx and/or Tx interrupt generation
2480 Parameters:
2481     context
2482     interruptSource - isReceive, isTransmit
2483     b - 1/0 enable/disable
2484 ***********************************************************/
2485 VOID ParaNdis_VirtIOEnableIrqSynchronized(PARANDIS_ADAPTER *pContext, ULONG interruptSource)
2486 {
2487     if (interruptSource & isTransmit)
2488         virtqueue_enable_cb(pContext->NetSendQueue);
2489     if (interruptSource & isReceive)
2490         virtqueue_enable_cb(pContext->NetReceiveQueue);
2491     ParaNdis_DebugHistory(pContext, hopDPC, (PVOID)0x10, interruptSource, TRUE, 0);
2492 }
2493 
2494 VOID ParaNdis_VirtIODisableIrqSynchronized(PARANDIS_ADAPTER *pContext, ULONG interruptSource)
2495 {
2496     if (interruptSource & isTransmit)
2497         virtqueue_disable_cb(pContext->NetSendQueue);
2498     if (interruptSource & isReceive)
2499         virtqueue_disable_cb(pContext->NetReceiveQueue);
2500     ParaNdis_DebugHistory(pContext, hopDPC, (PVOID)0x10, interruptSource, FALSE, 0);
2501 }
2502 
2503 /**********************************************************
2504 Common handler of PnP events
2505 Parameters:
2506 Return value:
2507 ***********************************************************/
2508 VOID ParaNdis_OnPnPEvent(
2509     PARANDIS_ADAPTER *pContext,
2510     NDIS_DEVICE_PNP_EVENT pEvent,
2511     PVOID   pInfo,
2512     ULONG   ulSize)
2513 {
2514     const char *pName = "";
2515     DEBUG_ENTRY(0);
2516 #undef MAKECASE
2517 #define MAKECASE(x) case (x): pName = #x; break;
2518     switch (pEvent)
2519     {
2520         MAKECASE(NdisDevicePnPEventQueryRemoved)
2521         MAKECASE(NdisDevicePnPEventRemoved)
2522         MAKECASE(NdisDevicePnPEventSurpriseRemoved)
2523         MAKECASE(NdisDevicePnPEventQueryStopped)
2524         MAKECASE(NdisDevicePnPEventStopped)
2525         MAKECASE(NdisDevicePnPEventPowerProfileChanged)
2526         default:
2527             break;
2528     }
2529     ParaNdis_DebugHistory(pContext, hopPnpEvent, NULL, pEvent, 0, 0);
2530     DPrintf(0, ("[%s] (%s)", __FUNCTION__, pName));
2531     if (pEvent == NdisDevicePnPEventSurpriseRemoved)
2532     {
2533         // on simulated surprise removal (under PnpTest) we need to reset the device
2534         // to prevent any access of device queues to memory buffers
2535         pContext->bSurprizeRemoved = TRUE;
2536         ParaNdis_ResetVirtIONetDevice(pContext);
2537     }
2538     pContext->PnpEvents[pContext->nPnpEventIndex++] = pEvent;
2539     if (pContext->nPnpEventIndex > sizeof(pContext->PnpEvents)/sizeof(pContext->PnpEvents[0]))
2540         pContext->nPnpEventIndex = 0;
2541 }
2542 
2543 static BOOLEAN SendControlMessage(
2544     PARANDIS_ADAPTER *pContext,
2545     UCHAR cls,
2546     UCHAR cmd,
2547     PVOID buffer1,
2548     ULONG size1,
2549     PVOID buffer2,
2550     ULONG size2,
2551     int levelIfOK
2552     )
2553 {
2554     BOOLEAN bOK = FALSE;
2555     NdisAcquireSpinLock(&pContext->ReceiveLock);
2556     if (pContext->ControlData.Virtual && pContext->ControlData.size > (size1 + size2 + 16))
2557     {
2558         struct VirtIOBufferDescriptor sg[4];
2559         PUCHAR pBase = (PUCHAR)pContext->ControlData.Virtual;
2560         PHYSICAL_ADDRESS phBase = pContext->ControlData.Physical;
2561         ULONG offset = 0;
2562         UINT nOut = 1;
2563 
2564         ((virtio_net_ctrl_hdr *)pBase)->class_of_command = cls;
2565         ((virtio_net_ctrl_hdr *)pBase)->cmd = cmd;
2566         sg[0].physAddr = phBase;
2567         sg[0].length = sizeof(virtio_net_ctrl_hdr);
2568         offset += sg[0].length;
2569         offset = (offset + 3) & ~3;
2570         if (size1)
2571         {
2572             NdisMoveMemory(pBase + offset, buffer1, size1);
2573             sg[nOut].physAddr = phBase;
2574             sg[nOut].physAddr.QuadPart += offset;
2575             sg[nOut].length = size1;
2576             offset += size1;
2577             offset = (offset + 3) & ~3;
2578             nOut++;
2579         }
2580         if (size2)
2581         {
2582             NdisMoveMemory(pBase + offset, buffer2, size2);
2583             sg[nOut].physAddr = phBase;
2584             sg[nOut].physAddr.QuadPart += offset;
2585             sg[nOut].length = size2;
2586             offset += size2;
2587             offset = (offset + 3) & ~3;
2588             nOut++;
2589         }
2590         sg[nOut].physAddr = phBase;
2591         sg[nOut].physAddr.QuadPart += offset;
2592         sg[nOut].length = sizeof(virtio_net_ctrl_ack);
2593         *(virtio_net_ctrl_ack *)(pBase + offset) = VIRTIO_NET_ERR;
2594 
2595         if (0 <= virtqueue_add_buf(pContext->NetControlQueue, sg, nOut, 1, (PVOID)1, NULL, 0))
2596         {
2597             UINT len;
2598             void *p;
2599             virtqueue_kick_always(pContext->NetControlQueue);
2600             p = virtqueue_get_buf(pContext->NetControlQueue, &len);
2601             if (!p)
2602             {
2603                 DPrintf(0, ("%s - ERROR: get_buf failed", __FUNCTION__));
2604             }
2605             else if (len != sizeof(virtio_net_ctrl_ack))
2606             {
2607                 DPrintf(0, ("%s - ERROR: wrong len %d", __FUNCTION__, len));
2608             }
2609             else if (*(virtio_net_ctrl_ack *)(pBase + offset) != VIRTIO_NET_OK)
2610             {
2611                 DPrintf(0, ("%s - ERROR: error %d returned", __FUNCTION__, *(virtio_net_ctrl_ack *)(pBase + offset)));
2612             }
2613             else
2614             {
2615                 // everything is OK
2616                 DPrintf(levelIfOK, ("%s OK(%d.%d,buffers of %d and %d) ", __FUNCTION__, cls, cmd, size1, size2));
2617                 bOK = TRUE;
2618             }
2619         }
2620         else
2621         {
2622             DPrintf(0, ("%s - ERROR: add_buf failed", __FUNCTION__));
2623         }
2624     }
2625     else
2626     {
2627         DPrintf(0, ("%s (buffer %d,%d) - ERROR: message too LARGE", __FUNCTION__, size1, size2));
2628     }
2629     NdisReleaseSpinLock(&pContext->ReceiveLock);
2630     return bOK;
2631 }
2632 
2633 static VOID ParaNdis_DeviceFiltersUpdateRxMode(PARANDIS_ADAPTER *pContext)
2634 {
2635     u8 val;
2636     ULONG f = pContext->PacketFilter;
2637     val = (f & NDIS_PACKET_TYPE_ALL_MULTICAST) ? 1 : 0;
2638     SendControlMessage(pContext, VIRTIO_NET_CTRL_RX_MODE, VIRTIO_NET_CTRL_RX_MODE_ALLMULTI, &val, sizeof(val), NULL, 0, 2);
2639     //SendControlMessage(pContext, VIRTIO_NET_CTRL_RX_MODE, VIRTIO_NET_CTRL_RX_MODE_ALLUNI, &val, sizeof(val), NULL, 0, 2);
2640     val = (f & (NDIS_PACKET_TYPE_MULTICAST | NDIS_PACKET_TYPE_ALL_MULTICAST)) ? 0 : 1;
2641     SendControlMessage(pContext, VIRTIO_NET_CTRL_RX_MODE, VIRTIO_NET_CTRL_RX_MODE_NOMULTI, &val, sizeof(val), NULL, 0, 2);
2642     val = (f & NDIS_PACKET_TYPE_DIRECTED) ? 0 : 1;
2643     SendControlMessage(pContext, VIRTIO_NET_CTRL_RX_MODE, VIRTIO_NET_CTRL_RX_MODE_NOUNI, &val, sizeof(val), NULL, 0, 2);
2644     val = (f & NDIS_PACKET_TYPE_BROADCAST) ? 0 : 1;
2645     SendControlMessage(pContext, VIRTIO_NET_CTRL_RX_MODE, VIRTIO_NET_CTRL_RX_MODE_NOBCAST, &val, sizeof(val), NULL, 0, 2);
2646     val = (f & NDIS_PACKET_TYPE_PROMISCUOUS) ? 1 : 0;
2647     SendControlMessage(pContext, VIRTIO_NET_CTRL_RX_MODE, VIRTIO_NET_CTRL_RX_MODE_PROMISC, &val, sizeof(val), NULL, 0, 2);
2648 }
2649 
2650 static VOID ParaNdis_DeviceFiltersUpdateAddresses(PARANDIS_ADAPTER *pContext)
2651 {
2652     struct
2653     {
2654         struct virtio_net_ctrl_mac header;
2655         UCHAR addr[ETH_LENGTH_OF_ADDRESS];
2656     } uCast;
2657     uCast.header.entries = 1;
2658     NdisMoveMemory(uCast.addr, pContext->CurrentMacAddress, sizeof(uCast.addr));
2659     SendControlMessage(pContext, VIRTIO_NET_CTRL_MAC, VIRTIO_NET_CTRL_MAC_TABLE_SET,
2660         &uCast, sizeof(uCast), &pContext->MulticastData,sizeof(pContext->MulticastData.nofMulticastEntries) + pContext->MulticastData.nofMulticastEntries * ETH_ALEN, 2);
2661 }
2662 
2663 static VOID SetSingleVlanFilter(PARANDIS_ADAPTER *pContext, ULONG vlanId, BOOLEAN bOn, int levelIfOK)
2664 {
2665     u16 val = vlanId & 0xfff;
2666     UCHAR cmd = bOn ? VIRTIO_NET_CTRL_VLAN_ADD : VIRTIO_NET_CTRL_VLAN_DEL;
2667     SendControlMessage(pContext, VIRTIO_NET_CTRL_VLAN, cmd, &val, sizeof(val), NULL, 0, levelIfOK);
2668 }
2669 
2670 static VOID SetAllVlanFilters(PARANDIS_ADAPTER *pContext, BOOLEAN bOn)
2671 {
2672     ULONG i;
2673     for (i = 0; i <= MAX_VLAN_ID; ++i)
2674         SetSingleVlanFilter(pContext, i, bOn, 7);
2675 }
2676 
2677 /*
2678     possible values of filter set (pContext->ulCurrentVlansFilterSet):
2679     0 - all disabled
2680     1..4095 - one selected enabled
2681     4096 - all enabled
2682     Note that only 0th vlan can't be enabled
2683 */
2684 VOID ParaNdis_DeviceFiltersUpdateVlanId(PARANDIS_ADAPTER *pContext)
2685 {
2686     if (pContext->bHasHardwareFilters)
2687     {
2688         ULONG newFilterSet;
2689         if (IsVlanSupported(pContext))
2690             newFilterSet = pContext->VlanId ? pContext->VlanId : (MAX_VLAN_ID + 1);
2691         else
2692             newFilterSet = IsPrioritySupported(pContext) ? (MAX_VLAN_ID + 1) : 0;
2693         if (newFilterSet != pContext->ulCurrentVlansFilterSet)
2694         {
2695             if (pContext->ulCurrentVlansFilterSet > MAX_VLAN_ID)
2696                 SetAllVlanFilters(pContext, FALSE);
2697             else if (pContext->ulCurrentVlansFilterSet)
2698                 SetSingleVlanFilter(pContext, pContext->ulCurrentVlansFilterSet, FALSE, 2);
2699 
2700             pContext->ulCurrentVlansFilterSet = newFilterSet;
2701 
2702             if (pContext->ulCurrentVlansFilterSet > MAX_VLAN_ID)
2703                 SetAllVlanFilters(pContext, TRUE);
2704             else if (pContext->ulCurrentVlansFilterSet)
2705                 SetSingleVlanFilter(pContext, pContext->ulCurrentVlansFilterSet, TRUE, 2);
2706         }
2707     }
2708 }
2709 
2710 VOID ParaNdis_UpdateDeviceFilters(PARANDIS_ADAPTER *pContext)
2711 {
2712     if (pContext->bHasHardwareFilters)
2713     {
2714         ParaNdis_DeviceFiltersUpdateRxMode(pContext);
2715         ParaNdis_DeviceFiltersUpdateAddresses(pContext);
2716         ParaNdis_DeviceFiltersUpdateVlanId(pContext);
2717     }
2718 }
2719 
2720 NDIS_STATUS ParaNdis_PowerOn(PARANDIS_ADAPTER *pContext)
2721 {
2722     LIST_ENTRY TempList;
2723     NDIS_STATUS status;
2724     DEBUG_ENTRY(0);
2725     ParaNdis_DebugHistory(pContext, hopPowerOn, NULL, 1, 0, 0);
2726     ParaNdis_ResetVirtIONetDevice(pContext);
2727     virtio_add_status(&pContext->IODevice, VIRTIO_CONFIG_S_ACKNOWLEDGE | VIRTIO_CONFIG_S_DRIVER);
2728     /* virtio_get_features must be called once upon device initialization:
2729      otherwise the device will not work properly */
2730     (void)virtio_get_features(&pContext->IODevice);
2731 
2732     if (pContext->bUseMergedBuffers)
2733         VirtIODeviceEnableGuestFeature(pContext, VIRTIO_NET_F_MRG_RXBUF);
2734     if (VirtIODeviceGetHostFeature(pContext, VIRTIO_RING_F_EVENT_IDX))
2735         VirtIODeviceEnableGuestFeature(pContext, VIRTIO_RING_F_EVENT_IDX);
2736     if (pContext->bDoGuestChecksumOnReceive)
2737         VirtIODeviceEnableGuestFeature(pContext, VIRTIO_NET_F_GUEST_CSUM);
2738     if (VirtIODeviceGetHostFeature(pContext, VIRTIO_F_VERSION_1))
2739         VirtIODeviceEnableGuestFeature(pContext, VIRTIO_F_VERSION_1);
2740     if (VirtIODeviceGetHostFeature(pContext, VIRTIO_F_ANY_LAYOUT))
2741         VirtIODeviceEnableGuestFeature(pContext, VIRTIO_F_ANY_LAYOUT);
2742 
2743     status = FinalizeFeatures(pContext);
2744     if (status == NDIS_STATUS_SUCCESS) {
2745         status = FindNetQueues(pContext);
2746     }
2747     if (status != NDIS_STATUS_SUCCESS) {
2748         virtio_add_status(&pContext->IODevice, VIRTIO_CONFIG_S_FAILED);
2749         return status;
2750     }
2751 
2752     ParaNdis_RestoreDeviceConfigurationAfterReset(pContext);
2753 
2754     ParaNdis_UpdateDeviceFilters(pContext);
2755 
2756     InitializeListHead(&TempList);
2757 
2758     /* submit all the receive buffers */
2759     NdisAcquireSpinLock(&pContext->ReceiveLock);
2760 
2761     pContext->ReuseBufferProc = (tReuseReceiveBufferProc)ReuseReceiveBufferRegular;
2762 
2763     while (!IsListEmpty(&pContext->NetReceiveBuffers))
2764     {
2765         pIONetDescriptor pBufferDescriptor =
2766             (pIONetDescriptor)RemoveHeadList(&pContext->NetReceiveBuffers);
2767         InsertTailList(&TempList, &pBufferDescriptor->listEntry);
2768     }
2769     pContext->NetNofReceiveBuffers = 0;
2770     while (!IsListEmpty(&TempList))
2771     {
2772         pIONetDescriptor pBufferDescriptor =
2773             (pIONetDescriptor)RemoveHeadList(&TempList);
2774         if (AddRxBufferToQueue(pContext, pBufferDescriptor))
2775         {
2776             InsertTailList(&pContext->NetReceiveBuffers, &pBufferDescriptor->listEntry);
2777             pContext->NetNofReceiveBuffers++;
2778         }
2779         else
2780         {
2781             DPrintf(0, ("FAILED TO REUSE THE BUFFER!!!!"));
2782             VirtIONetFreeBufferDescriptor(pContext, pBufferDescriptor);
2783             pContext->NetMaxReceiveBuffers--;
2784         }
2785     }
2786     virtqueue_kick(pContext->NetReceiveQueue);
2787     ParaNdis_SetPowerState(pContext, NdisDeviceStateD0);
2788     pContext->bEnableInterruptHandlingDPC = TRUE;
2789     virtio_device_ready(&pContext->IODevice);
2790 
2791     NdisReleaseSpinLock(&pContext->ReceiveLock);
2792 
2793     // if bFastSuspendInProcess is set by Win8 power-off procedure,
2794     // the ParaNdis_Resume enables Tx and RX
2795     // otherwise it does not do anything in Vista+ (Tx and RX are enabled after power-on by Restart)
2796     ParaNdis_Resume(pContext);
2797     pContext->bFastSuspendInProcess = FALSE;
2798 
2799     ParaNdis_ReportLinkStatus(pContext, TRUE);
2800     ParaNdis_DebugHistory(pContext, hopPowerOn, NULL, 0, 0, 0);
2801 
2802     return status;
2803 }
2804 
2805 VOID ParaNdis_PowerOff(PARANDIS_ADAPTER *pContext)
2806 {
2807     DEBUG_ENTRY(0);
2808     ParaNdis_DebugHistory(pContext, hopPowerOff, NULL, 1, 0, 0);
2809 
2810     ParaNdis_IndicateConnect(pContext, FALSE, FALSE);
2811 
2812     // if bFastSuspendInProcess is set by Win8 power-off procedure
2813     // the ParaNdis_Suspend does fast Rx stop without waiting (=>srsPausing, if there are some RX packets in Ndis)
2814     pContext->bFastSuspendInProcess = pContext->bNoPauseOnSuspend && pContext->ReceiveState == srsEnabled;
2815     ParaNdis_Suspend(pContext);
2816     if (pContext->IODevice.addr)
2817     {
2818         /* back compat - remove the OK flag only in legacy mode */
2819         VirtIODeviceRemoveStatus(&pContext->IODevice, VIRTIO_CONFIG_S_DRIVER_OK);
2820     }
2821 
2822     if (pContext->bFastSuspendInProcess)
2823     {
2824         NdisAcquireSpinLock(&pContext->ReceiveLock);
2825         pContext->ReuseBufferProc = (tReuseReceiveBufferProc)ReuseReceiveBufferPowerOff;
2826         NdisReleaseSpinLock(&pContext->ReceiveLock);
2827     }
2828 
2829     ParaNdis_SetPowerState(pContext, NdisDeviceStateD3);
2830 
2831     PreventDPCServicing(pContext);
2832 
2833     /*******************************************************************
2834         shutdown queues to have all the receive buffers under our control
2835         all the transmit buffers move to list of free buffers
2836     ********************************************************************/
2837 
2838     NdisAcquireSpinLock(&pContext->SendLock);
2839     virtqueue_shutdown(pContext->NetSendQueue);
2840     while (!IsListEmpty(&pContext->NetSendBuffersInUse))
2841     {
2842         pIONetDescriptor pBufferDescriptor =
2843             (pIONetDescriptor)RemoveHeadList(&pContext->NetSendBuffersInUse);
2844         InsertTailList(&pContext->NetFreeSendBuffers, &pBufferDescriptor->listEntry);
2845         pContext->nofFreeTxDescriptors++;
2846         pContext->nofFreeHardwareBuffers += pBufferDescriptor->nofUsedBuffers;
2847     }
2848     NdisReleaseSpinLock(&pContext->SendLock);
2849 
2850     NdisAcquireSpinLock(&pContext->ReceiveLock);
2851     virtqueue_shutdown(pContext->NetReceiveQueue);
2852     NdisReleaseSpinLock(&pContext->ReceiveLock);
2853     if (pContext->NetControlQueue) {
2854         virtqueue_shutdown(pContext->NetControlQueue);
2855     }
2856 
2857     DPrintf(0, ("WARNING: deleting queues!!!!!!!!!"));
2858     DeleteNetQueues(pContext);
2859     pContext->NetSendQueue = NULL;
2860     pContext->NetReceiveQueue = NULL;
2861     pContext->NetControlQueue = NULL;
2862 
2863     ParaNdis_ResetVirtIONetDevice(pContext);
2864     ParaNdis_DebugHistory(pContext, hopPowerOff, NULL, 0, 0, 0);
2865 }
2866 
2867 void ParaNdis_CallOnBugCheck(PARANDIS_ADAPTER *pContext)
2868 {
2869     if (pContext->IODevice.isr)
2870     {
2871 #ifdef DBG_USE_VIRTIO_PCI_ISR_FOR_HOST_REPORT
2872         WriteVirtIODeviceByte(pContext->IODevice.isr, 1);
2873 #endif
2874     }
2875 }
2876 
2877 tChecksumCheckResult ParaNdis_CheckRxChecksum(PARANDIS_ADAPTER *pContext, ULONG virtioFlags, PVOID pRxPacket, ULONG len)
2878 {
2879     tOffloadSettingsFlags f = pContext->Offload.flags;
2880     tChecksumCheckResult res, resIp;
2881     PVOID pIpHeader = RtlOffsetToPointer(pRxPacket, ETH_HEADER_SIZE);
2882     tTcpIpPacketParsingResult ppr;
2883     ULONG flagsToCalculate = 0;
2884     res.value = 0;
2885     resIp.value = 0;
2886 
2887     //VIRTIO_NET_HDR_F_NEEDS_CSUM - we need to calculate TCP/UDP CS
2888     //VIRTIO_NET_HDR_F_DATA_VALID - host tells us TCP/UDP CS is OK
2889 
2890     if (f.fRxIPChecksum) flagsToCalculate |= pcrIpChecksum; // check only
2891 
2892     if (!(virtioFlags & VIRTIO_NET_HDR_F_DATA_VALID))
2893     {
2894         if (virtioFlags & VIRTIO_NET_HDR_F_NEEDS_CSUM)
2895         {
2896             flagsToCalculate |= pcrFixXxpChecksum | pcrTcpChecksum | pcrUdpChecksum;
2897         }
2898         else
2899         {
2900             if (f.fRxTCPChecksum) flagsToCalculate |= pcrTcpV4Checksum;
2901             if (f.fRxUDPChecksum) flagsToCalculate |= pcrUdpV4Checksum;
2902             if (f.fRxTCPv6Checksum) flagsToCalculate |= pcrTcpV6Checksum;
2903             if (f.fRxUDPv6Checksum) flagsToCalculate |= pcrUdpV6Checksum;
2904         }
2905     }
2906 
2907     ppr = ParaNdis_CheckSumVerify(pIpHeader, len - ETH_HEADER_SIZE, flagsToCalculate, __FUNCTION__);
2908 
2909     if (virtioFlags & VIRTIO_NET_HDR_F_DATA_VALID)
2910     {
2911         pContext->extraStatistics.framesRxCSHwOK++;
2912         ppr.xxpCheckSum = ppresCSOK;
2913     }
2914 
2915     if (ppr.ipStatus == ppresIPV4 && !ppr.IsFragment)
2916     {
2917         if (f.fRxIPChecksum)
2918         {
2919             res.flags.IpOK =  ppr.ipCheckSum == ppresCSOK;
2920             res.flags.IpFailed = ppr.ipCheckSum == ppresCSBad;
2921         }
2922         if(ppr.xxpStatus == ppresXxpKnown)
2923         {
2924             if(ppr.TcpUdp == ppresIsTCP) /* TCP */
2925             {
2926                 if (f.fRxTCPChecksum)
2927                 {
2928                     res.flags.TcpOK = ppr.xxpCheckSum == ppresCSOK || ppr.fixedXxpCS;
2929                     res.flags.TcpFailed = !res.flags.TcpOK;
2930                 }
2931             }
2932             else /* UDP */
2933             {
2934                 if (f.fRxUDPChecksum)
2935                 {
2936                     res.flags.UdpOK = ppr.xxpCheckSum == ppresCSOK || ppr.fixedXxpCS;
2937                     res.flags.UdpFailed = !res.flags.UdpOK;
2938                 }
2939             }
2940         }
2941     }
2942     else if (ppr.ipStatus == ppresIPV6)
2943     {
2944         if(ppr.xxpStatus == ppresXxpKnown)
2945         {
2946             if(ppr.TcpUdp == ppresIsTCP) /* TCP */
2947             {
2948                 if (f.fRxTCPv6Checksum)
2949                 {
2950                     res.flags.TcpOK = ppr.xxpCheckSum == ppresCSOK || ppr.fixedXxpCS;
2951                     res.flags.TcpFailed = !res.flags.TcpOK;
2952                 }
2953             }
2954             else /* UDP */
2955             {
2956                 if (f.fRxUDPv6Checksum)
2957                 {
2958                     res.flags.UdpOK = ppr.xxpCheckSum == ppresCSOK || ppr.fixedXxpCS;
2959                     res.flags.UdpFailed = !res.flags.UdpOK;
2960                 }
2961             }
2962         }
2963     }
2964 
2965     if (pContext->bDoIPCheckRx &&
2966         (f.fRxIPChecksum || f.fRxTCPChecksum || f.fRxUDPChecksum || f.fRxTCPv6Checksum || f.fRxUDPv6Checksum))
2967     {
2968         ppr = ParaNdis_CheckSumVerify(pIpHeader, len - ETH_HEADER_SIZE, pcrAnyChecksum, __FUNCTION__);
2969         if (ppr.ipStatus == ppresIPV4 && !ppr.IsFragment)
2970         {
2971             resIp.flags.IpOK = !!f.fRxIPChecksum && ppr.ipCheckSum == ppresCSOK;
2972             resIp.flags.IpFailed = !!f.fRxIPChecksum && ppr.ipCheckSum == ppresCSBad;
2973             if (f.fRxTCPChecksum && ppr.xxpStatus == ppresXxpKnown && ppr.TcpUdp == ppresIsTCP)
2974             {
2975                 resIp.flags.TcpOK = ppr.xxpCheckSum == ppresCSOK;
2976                 resIp.flags.TcpFailed = ppr.xxpCheckSum == ppresCSBad;
2977             }
2978             if (f.fRxUDPChecksum && ppr.xxpStatus == ppresXxpKnown && ppr.TcpUdp == ppresIsUDP)
2979             {
2980                 resIp.flags.UdpOK = ppr.xxpCheckSum == ppresCSOK;
2981                 resIp.flags.UdpFailed = ppr.xxpCheckSum == ppresCSBad;
2982             }
2983         }
2984         else if (ppr.ipStatus == ppresIPV6)
2985         {
2986             if (f.fRxTCPv6Checksum && ppr.xxpStatus == ppresXxpKnown && ppr.TcpUdp == ppresIsTCP)
2987             {
2988                 resIp.flags.TcpOK = ppr.xxpCheckSum == ppresCSOK;
2989                 resIp.flags.TcpFailed = ppr.xxpCheckSum == ppresCSBad;
2990             }
2991             if (f.fRxUDPv6Checksum && ppr.xxpStatus == ppresXxpKnown && ppr.TcpUdp == ppresIsUDP)
2992             {
2993                 resIp.flags.UdpOK = ppr.xxpCheckSum == ppresCSOK;
2994                 resIp.flags.UdpFailed = ppr.xxpCheckSum == ppresCSBad;
2995             }
2996         }
2997 
2998         if (res.value != resIp.value)
2999         {
3000             // if HW did not set some bits that IP checker set, it is a mistake:
3001             // or GOOD CS is not labeled, or BAD checksum is not labeled
3002             tChecksumCheckResult diff;
3003             diff.value = resIp.value & ~res.value;
3004             if (diff.flags.IpFailed || diff.flags.TcpFailed || diff.flags.UdpFailed)
3005                 pContext->extraStatistics.framesRxCSHwMissedBad++;
3006             if (diff.flags.IpOK || diff.flags.TcpOK || diff.flags.UdpOK)
3007                 pContext->extraStatistics.framesRxCSHwMissedGood++;
3008             if (diff.value)
3009             {
3010                 DPrintf(0, ("[%s] real %X <> %X (virtio %X)", __FUNCTION__, resIp.value, res.value, virtioFlags));
3011             }
3012             res.value = resIp.value;
3013         }
3014     }
3015 
3016     return res;
3017 }
3018