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
DebugDumpPacket(LPCSTR prefix,PVOID header,int level)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 ***********************************************************/
ParaNdis_ValidateMacAddress(PUCHAR pcMacAddress,BOOLEAN bLocal)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
QueryPacketType(PVOID data)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
ParaNdis_ResetVirtIONetDevice(PARANDIS_ADAPTER * pContext)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 ***********************************************************/
GetConfigurationEntry(NDIS_HANDLE cfg,tConfigurationEntry * pEntry)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
DisableLSOv4Permanently(PARANDIS_ADAPTER * pContext,LPCSTR procname,LPCSTR reason)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
DisableLSOv6Permanently(PARANDIS_ADAPTER * pContext,LPCSTR procname,LPCSTR reason)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
DisableBothLSOPermanently(PARANDIS_ADAPTER * pContext,LPCSTR procname,LPCSTR reason)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 ***********************************************************/
ReadNicConfiguration(PARANDIS_ADAPTER * pContext,PUCHAR * ppNewMACAddress)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
ParaNdis_ResetOffloadSettings(PARANDIS_ADAPTER * pContext,tOffloadSettingsFlags * pDest,PULONG from)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 ***********************************************************/
GetAdapterResources(NDIS_HANDLE MiniportHandle,PNDIS_RESOURCE_LIST RList,tAdapterResources * pResources)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
DumpVirtIOFeatures(PARANDIS_ADAPTER * pContext)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 ***********************************************************/
JustForCheckClearInterrupt(PARANDIS_ADAPTER * pContext,const char * Label)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 ***********************************************************/
PrintStatistics(PARANDIS_ADAPTER * pContext)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
NTStatusToNdisStatus(NTSTATUS nt_status)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
FinalizeFeatures(PARANDIS_ADAPTER * pContext)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 ***********************************************************/
ParaNdis_InitializeContext(PARANDIS_ADAPTER * pContext,PNDIS_RESOURCE_LIST pResourceList)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 ***********************************************************/
VirtIONetFreeBufferDescriptor(PARANDIS_ADAPTER * pContext,pIONetDescriptor pBufferDescriptor)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 ***********************************************************/
FreeDescriptorsFromList(PARANDIS_ADAPTER * pContext,PLIST_ENTRY pListRoot,PNDIS_SPIN_LOCK pLock)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
AllocatePairOfBuffersOnInit(PARANDIS_ADAPTER * pContext,ULONG size1,ULONG size2,BOOLEAN bForTx)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 ***********************************************************/
PrepareTransmitBuffers(PARANDIS_ADAPTER * pContext)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
AddRxBufferToQueue(PARANDIS_ADAPTER * pContext,pIONetDescriptor pBufferDescriptor)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 ***********************************************************/
PrepareReceiveBuffers(PARANDIS_ADAPTER * pContext)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
FindNetQueues(PARANDIS_ADAPTER * pContext)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
DeleteNetQueues(PARANDIS_ADAPTER * pContext)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 ***********************************************************/
ParaNdis_VirtIONetInit(PARANDIS_ADAPTER * pContext)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
VirtIODeviceRemoveStatus(VirtIODevice * vdev,u8 status)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 ***********************************************************/
ParaNdis_FinishInitialization(PARANDIS_ADAPTER * pContext)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 ***********************************************************/
VirtIONetRelease(PARANDIS_ADAPTER * pContext)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
PreventDPCServicing(PARANDIS_ADAPTER * pContext)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 ***********************************************************/
ParaNdis_CleanupContext(PARANDIS_ADAPTER * pContext)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 ***********************************************************/
ParaNdis_OnShutdown(PARANDIS_ADAPTER * pContext)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 ***********************************************************/
ParaNdis_OnLegacyInterrupt(PARANDIS_ADAPTER * pContext,OUT BOOLEAN * pRunDpc)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
ParaNdis_OnQueuedInterrupt(PARANDIS_ADAPTER * pContext,OUT BOOLEAN * pRunDpc,ULONG knownInterruptSources)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 ***********************************************************/
ReuseReceiveBufferRegular(PARANDIS_ADAPTER * pContext,pIONetDescriptor pBuffersDescriptor)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 ***********************************************************/
ReuseReceiveBufferPowerOff(PARANDIS_ADAPTER * pContext,pIONetDescriptor pBuffersDescriptor)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 ***********************************************************/
ParaNdis_VirtIONetReleaseTransmitBuffers(PARANDIS_ADAPTER * pContext)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
QueryTcpHeaderOffset(PVOID packetData,ULONG ipHeaderOffset,ULONG ipPacketLength)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 ***********************************************************/
ParaNdis_DoSubmitPacket(PARANDIS_ADAPTER * pContext,tTxOperationParameters * Params)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 ***********************************************************/
ParaNdis_DoCopyPacketData(PARANDIS_ADAPTER * pContext,tTxOperationParameters * pParams)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
ShallPassPacket(PARANDIS_ADAPTER * pContext,PVOID address,UINT len,eInspectedPacketType * pType)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
ParaNdis_PadPacketReceived(PVOID pDataBuffer,PULONG pLength)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 ***********************************************************/
ParaNdis_ProcessRxPath(PARANDIS_ADAPTER * pContext,ULONG ulMaxPacketsToIndicate)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
ParaNdis_ReportLinkStatus(PARANDIS_ADAPTER * pContext,BOOLEAN bForce)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
RestartQueueSynchronously(tSynchronizedContext * SyncContext)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 ***********************************************************/
ParaNdis_DPCWorkBody(PARANDIS_ADAPTER * pContext,ULONG ulMaxPacketsToIndicate)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 ***********************************************************/
CheckRunningDpc(PARANDIS_ADAPTER * pContext)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 ***********************************************************/
ParaNdis_CheckForHang(PARANDIS_ADAPTER * pContext)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 ***********************************************************/
ParaNdis_SetMulticastList(PARANDIS_ADAPTER * pContext,PVOID Buffer,ULONG BufferSize,PUINT pBytesRead,PUINT pBytesNeeded)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 ***********************************************************/
ParaNdis_VirtIOEnableIrqSynchronized(PARANDIS_ADAPTER * pContext,ULONG interruptSource)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
ParaNdis_VirtIODisableIrqSynchronized(PARANDIS_ADAPTER * pContext,ULONG interruptSource)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 ***********************************************************/
ParaNdis_OnPnPEvent(PARANDIS_ADAPTER * pContext,NDIS_DEVICE_PNP_EVENT pEvent,PVOID pInfo,ULONG ulSize)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
SendControlMessage(PARANDIS_ADAPTER * pContext,UCHAR cls,UCHAR cmd,PVOID buffer1,ULONG size1,PVOID buffer2,ULONG size2,int levelIfOK)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
ParaNdis_DeviceFiltersUpdateRxMode(PARANDIS_ADAPTER * pContext)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
ParaNdis_DeviceFiltersUpdateAddresses(PARANDIS_ADAPTER * pContext)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
SetSingleVlanFilter(PARANDIS_ADAPTER * pContext,ULONG vlanId,BOOLEAN bOn,int levelIfOK)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
SetAllVlanFilters(PARANDIS_ADAPTER * pContext,BOOLEAN bOn)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 */
ParaNdis_DeviceFiltersUpdateVlanId(PARANDIS_ADAPTER * pContext)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
ParaNdis_UpdateDeviceFilters(PARANDIS_ADAPTER * pContext)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
ParaNdis_PowerOn(PARANDIS_ADAPTER * pContext)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
ParaNdis_PowerOff(PARANDIS_ADAPTER * pContext)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
ParaNdis_CallOnBugCheck(PARANDIS_ADAPTER * pContext)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
ParaNdis_CheckRxChecksum(PARANDIS_ADAPTER * pContext,ULONG virtioFlags,PVOID pRxPacket,ULONG len)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