https://git.reactos.org/?p=reactos.git;a=commitdiff;h=4c37757e81109128109fe…
commit 4c37757e81109128109feb0a0a9342452c6caf78
Author: Nguyen Trung Khanh <nguyentrungkhanh97(a)gmail.com>
AuthorDate: Thu Mar 5 10:58:56 2020 +0700
Commit: Victor Perevertkin <victor.perevertkin(a)reactos.org>
CommitDate: Thu Apr 23 16:33:09 2020 +0300
[NETKVM] Import NetKVM network adapter driver by Red Hat
CORE-15841
---
drivers/network/dd/CMakeLists.txt | 1 +
drivers/network/dd/netkvm/CMakeLists.txt | 37 +
drivers/network/dd/netkvm/Common/DebugData.h | 193 ++
drivers/network/dd/netkvm/Common/IONetDescriptor.h | 133 +
drivers/network/dd/netkvm/Common/ParaNdis-Common.c | 3017 ++++++++++++++++++++
drivers/network/dd/netkvm/Common/ParaNdis-Debug.c | 394 +++
drivers/network/dd/netkvm/Common/ParaNdis-Oid.c | 677 +++++
drivers/network/dd/netkvm/Common/ParaNdis-Oid.h | 104 +
drivers/network/dd/netkvm/Common/ParaNdis-VirtIO.c | 389 +++
drivers/network/dd/netkvm/Common/ethernetutils.h | 123 +
drivers/network/dd/netkvm/Common/kdebugprint.h | 108 +
drivers/network/dd/netkvm/Common/ndis56common.h | 892 ++++++
drivers/network/dd/netkvm/Common/quverp.h | 45 +
drivers/network/dd/netkvm/Common/rhel.ver | 64 +
drivers/network/dd/netkvm/Common/sw-offload.c | 619 ++++
drivers/network/dd/netkvm/Common/vendor.ver | 98 +
drivers/network/dd/netkvm/virtio/LICENSE | 30 +
drivers/network/dd/netkvm/virtio/VirtIO.h | 128 +
drivers/network/dd/netkvm/virtio/VirtIOPCICommon.c | 411 +++
drivers/network/dd/netkvm/virtio/VirtIOPCILegacy.c | 283 ++
drivers/network/dd/netkvm/virtio/VirtIOPCIModern.c | 597 ++++
.../network/dd/netkvm/virtio/VirtIORing-Packed.c | 651 +++++
drivers/network/dd/netkvm/virtio/VirtIORing.c | 562 ++++
drivers/network/dd/netkvm/virtio/kdebugprint.h | 11 +
drivers/network/dd/netkvm/virtio/linux/types.h | 19 +
.../network/dd/netkvm/virtio/linux/virtio_config.h | 73 +
.../network/dd/netkvm/virtio/linux/virtio_types.h | 47 +
drivers/network/dd/netkvm/virtio/osdep.h | 39 +
drivers/network/dd/netkvm/virtio/virtio_pci.h | 392 +++
.../network/dd/netkvm/virtio/virtio_pci_common.h | 88 +
drivers/network/dd/netkvm/virtio/virtio_ring.h | 50 +
.../netkvm/virtio/windows/virtio_ring_allocation.h | 24 +
drivers/network/dd/netkvm/wxp/ParaNdis5-Driver.c | 452 +++
drivers/network/dd/netkvm/wxp/ParaNdis5-Impl.c | 1474 ++++++++++
drivers/network/dd/netkvm/wxp/ParaNdis5-Oid.c | 785 +++++
drivers/network/dd/netkvm/wxp/ParaNdis5.h | 88 +
drivers/network/dd/netkvm/wxp/netkvm.inx | 331 +++
drivers/network/dd/netkvm/wxp/parandis.rc | 31 +
media/inf/CMakeLists.txt | 1 +
media/inf/netkvm.inf | 331 +++
sdk/include/ddk/ndis.h | 6 +-
sdk/include/xdk/kefuncs.h | 2 +-
42 files changed, 13796 insertions(+), 4 deletions(-)
diff --git a/drivers/network/dd/CMakeLists.txt b/drivers/network/dd/CMakeLists.txt
index 89b96df4897..39a20fce8e3 100644
--- a/drivers/network/dd/CMakeLists.txt
+++ b/drivers/network/dd/CMakeLists.txt
@@ -1,5 +1,6 @@
add_subdirectory(e1000)
add_subdirectory(ne2000)
+add_subdirectory(netkvm)
add_subdirectory(pcnet)
add_subdirectory(rtl8139)
diff --git a/drivers/network/dd/netkvm/CMakeLists.txt
b/drivers/network/dd/netkvm/CMakeLists.txt
new file mode 100644
index 00000000000..6906334f40e
--- /dev/null
+++ b/drivers/network/dd/netkvm/CMakeLists.txt
@@ -0,0 +1,37 @@
+
+include_directories(BEFORE common virtio)
+
+add_definitions(
+ -DNDIS_MINIPORT_DRIVER
+ -DNDIS51_MINIPORT=1)
+
+list(APPEND SOURCE
+ common/ParaNdis-Common.c
+ common/ParaNdis-Oid.c
+ common/ParaNdis-VirtIO.c
+ common/ParaNdis-Debug.c
+ common/sw-offload.c
+ virtio/VirtIOPCICommon.c
+ virtio/VirtIOPCILegacy.c
+ virtio/VirtIOPCIModern.c
+ virtio/VirtIORing.c
+ virtio/VirtIORing-Packed.c
+ wxp/ParaNdis5-Driver.c
+ wxp/ParaNdis5-Impl.c
+ wxp/ParaNdis5-Oid.c)
+
+add_library(netkvm MODULE ${SOURCE} wxp/parandis.rc)
+set_module_type(netkvm kernelmodedriver)
+add_importlibs(netkvm ndis ntoskrnl hal)
+add_cd_file(TARGET netkvm DESTINATION reactos/system32/drivers FOR all)
+
+if (NOT MSVC)
+ add_compile_flags("-Wno-unused-function")
+ add_compile_flags("-Wno-old-style-declaration")
+ add_compile_flags("-Wno-unknown-pragmas")
+ add_compile_flags("-Wno-unused-but-set-variable")
+ add_compile_flags("-Wno-pointer-sign")
+ add_compile_flags("-Wno-pointer-to-int-cast")
+ add_compile_flags("-Wno-int-to-pointer-cast")
+ add_compile_flags("-Wno-attributes")
+endif()
diff --git a/drivers/network/dd/netkvm/Common/DebugData.h
b/drivers/network/dd/netkvm/Common/DebugData.h
new file mode 100644
index 00000000000..cf0bf849f6a
--- /dev/null
+++ b/drivers/network/dd/netkvm/Common/DebugData.h
@@ -0,0 +1,193 @@
+/*
+ * This file contains definitions and data structures, common between
+ * NDIS driver and debugger helper unit, processing crash dump with built-in
+ * data provided by the driver.
+ *
+ * Included in NetKVM NDIS kernel driver for Windows.
+ * Included in NetKVMDumpParser application.
+ *
+ * Copyright (c) 2008-2017 Red Hat, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met :
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and / or other materials provided with the distribution.
+ * 3. Neither the names of the copyright holders nor the names of their contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS''
AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef PARANDIS_DEBUG_DATA_H
+#define PARANDIS_DEBUG_DATA_H
+
+typedef enum _etagHistoryLogOperation
+{
+ hopPowerOff, // common::PowerOff, 1/0 - entry/exit (none, entry, none,
none)
+ hopPowerOn, // common::PowerOn, 1/0 - entry/exit (none, entry, none,
none)
+ hopSysPause, // ndis6::Pause, 1/0 - entry/completion
+ hopSysResume, // ndis6::Restart, 1/0 - entry/completion
+ hopInternalSendPause, // implementation, 1/0 - entry/completion
+ hopInternalReceivePause, // implementation, 1/0 - entry/completion
+ hopInternalSendResume, // implementation
+ hopInternalReceiveResume, // implementation
+ hopSysReset, // implementation driver, 1/0 - entry/completion
+ hopHalt, // implementation driver, 1/0 - entry/completion
+ hopConnectIndication, // implementation
+ hopDPC, // common::DpcWorkBody (1, none, none, none) (0, left,
free buffers, free desc)
+ hopSend, // implementation, when Send requested (nbl, nof lists,
nof bufs, nof bytes) (packet, 1, nof packets, none)
+ hopSendNBLRequest, // ndis6 implementation (nbl, nof packets, none, none)
+ hopSendPacketRequest, // not used
+ hopSendPacketMapped, // implementation, before the packet inserted into queue
(nbl, which packet, nof frags, none)
+ hopSubmittedPacket, // implementation, when the packet submitted (nbl, which
packet, result, flags)
+ hopBufferSent, // implementation, when the packet returned from VirtIO
queue (nbl, packet no., free buf, free desc)
+ hopReceiveStat, // common: RX (none, retrieved, reported, ready rx
buffers)
+ hopBufferReturned, // not used
+ hopSendComplete, // implementation, when the packet completed
+ hopTxProcess,
+ hopPacketReceived, // implementation, when the packet prepared for
indication (nbl, length, prio tag, type)
+ hopOidRequest, // implementation, none, OID, on entry(type, 1), on exit
(status, 0), on complete (status, 2)
+ hopPnpEvent // common, none, event, 0, 0
+}eHistoryLogOperation;
+
+// {E51FCE18-B3E7-441e-B18C-D9E9B71616F3}
+static const GUID ParaNdis_CrashGuid =
+{ 0xe51fce18, 0xb3e7, 0x441e, { 0xb1, 0x8c, 0xd9, 0xe9, 0xb7, 0x16, 0x16, 0xf3 } };
+
+/* This structure is NOT changeable */
+typedef struct _tagBugCheckStaticDataHeader
+{
+ USHORT SizeOfPointer;
+ USHORT StaticDataVersion;
+ USHORT PerNicDataVersion;
+ USHORT ulMaxContexts;
+ LARGE_INTEGER qCrashTime;
+ UINT64 PerNicData;
+ UINT64 DataArea;
+ UINT64 DataAreaSize;
+}tBugCheckStaticDataHeader;
+
+/* This structure is NOT changeable */
+typedef struct _tagBugCheckDataLocation
+{
+ UINT64 Address;
+ UINT64 Size;
+}tBugCheckDataLocation;
+
+#define PARANDIS_DEBUG_STATIC_DATA_VERSION 0
+#define PARANDIS_DEBUG_PER_NIC_DATA_VERSION 0
+#define PARANDIS_DEBUG_HISTORY_DATA_VERSION 1
+/* This structure is NOT changeable */
+typedef struct _tagBugCheckStaticDataContent_V0
+{
+ ULONG SizeOfHistory;
+ ULONG SizeOfHistoryEntry;
+ LONG CurrentHistoryIndex;
+ ULONG HistoryDataVersion;
+ ULONG64 HistoryData;
+}tBugCheckStaticDataContent_V0;
+
+#define PARANDIS_DEBUG_INTERRUPTS
+
+#ifdef PARANDIS_DEBUG_INTERRUPTS
+# define PARANDIS_STORE_LAST_INTERRUPT_TIMESTAMP(p) \
+ NdisGetCurrentSystemTime(&(p)->LastInterruptTimeStamp)
+# define PARANDIS_GET_LAST_INTERRUPT_TIMESTAMP(p) \
+ (p)->LastInterruptTimeStamp.QuadPart
+#else
+# define PARANDIS_STORE_LAST_INTERRUPT_TIMESTAMP(p)
+# define PARANDIS_GET_LAST_INTERRUPT_TIMESTAMP(p) (0)
+#endif
+
+typedef struct _tagBugCheckPerNicDataContent_V0
+{
+ UINT64 Context;
+ LARGE_INTEGER LastInterruptTimeStamp;
+ LARGE_INTEGER LastTxCompletionTimeStamp;
+ ULONG nofPacketsToComplete;
+ ULONG nofReadyTxBuffers;
+}tBugCheckPerNicDataContent_V0;
+
+typedef struct _tagBugCheckHistoryDataEntry_V0
+{
+ LARGE_INTEGER TimeStamp;
+ UINT64 Context;
+ UINT64 pParam1;
+ ULONG operation;
+ ULONG lParam2;
+ ULONG lParam3;
+ ULONG lParam4;
+}tBugCheckHistoryDataEntry_V0;
+
+typedef struct _tagBugCheckHistoryDataEntry_V1
+{
+ LARGE_INTEGER TimeStamp;
+ UINT64 Context;
+ ULONG uIRQL;
+ ULONG uProcessor;
+ UINT64 pParam1;
+ ULONG operation;
+ ULONG lParam2;
+ ULONG lParam3;
+ ULONG lParam4;
+}tBugCheckHistoryDataEntry_V1;
+
+
+#if (PARANDIS_DEBUG_STATIC_DATA_VERSION == 0)
+typedef tBugCheckStaticDataContent_V0 tBugCheckStaticDataContent;
+#endif
+
+#if (PARANDIS_DEBUG_PER_NIC_DATA_VERSION == 0)
+typedef tBugCheckPerNicDataContent_V0 tBugCheckPerNicDataContent;
+#endif
+
+#if (PARANDIS_DEBUG_HISTORY_DATA_VERSION == 0)
+typedef tBugCheckHistoryDataEntry_V0 tBugCheckHistoryDataEntry;
+#elif (PARANDIS_DEBUG_HISTORY_DATA_VERSION == 1)
+typedef tBugCheckHistoryDataEntry_V1 tBugCheckHistoryDataEntry;
+#endif
+
+typedef struct _tagBugCheckStaticDataContent_V1
+{
+ UINT64 res1;
+ UINT64 res2;
+ UINT64 History;
+}tBugCheckStaticDataContent_V1;
+
+typedef struct _tagBugCheckPerNicDataContent_V1
+{
+ UINT64 Context;
+ LARGE_INTEGER LastInterruptTimeStamp;
+ LARGE_INTEGER LastTxCompletionTimeStamp;
+ ULONG nofPacketsToComplete;
+ ULONG nofReadyTxBuffers;
+}tBugCheckPerNicDataContent_V1;
+
+
+#if (PARANDIS_DEBUG_HEADER_VERSION == 1)
+typedef tBugCheckStaticDataContent_V1 tBugCheckStaticDataContent;
+#endif
+
+#if (PARANDIS_DEBUG_PER_NIC_DATA_VERSION == 1)
+typedef tBugCheckPerNicDataContent_V1 tBugCheckPerNicDataContent;
+#endif
+
+// etc
+
+
+
+#endif
diff --git a/drivers/network/dd/netkvm/Common/IONetDescriptor.h
b/drivers/network/dd/netkvm/Common/IONetDescriptor.h
new file mode 100644
index 00000000000..8e9a05b2110
--- /dev/null
+++ b/drivers/network/dd/netkvm/Common/IONetDescriptor.h
@@ -0,0 +1,133 @@
+/*
+ * This file contains common guest/host definition, related
+ * to VirtIO network adapter
+ *
+ * Copyright (c) 2008-2017 Red Hat, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met :
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and / or other materials provided with the distribution.
+ * 3. Neither the names of the copyright holders nor the names of their contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS''
AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+#ifndef IONETDESCRIPTOR_H
+#define IONETDESCRIPTOR_H
+
+#pragma pack (push)
+#pragma pack (1)
+/* This is the first element of the scatter-gather list. If you don't
+ * specify GSO or CSUM features, you can simply ignore the header. */
+typedef struct _tagvirtio_net_hdr
+{
+#define VIRTIO_NET_HDR_F_NEEDS_CSUM 1 // Use csum_start, csum_offset
+#define VIRTIO_NET_HDR_F_DATA_VALID 2 // Host checked checksum, no need to recheck
+ u8 flags;
+#define VIRTIO_NET_HDR_GSO_NONE 0 // Not a GSO frame
+#define VIRTIO_NET_HDR_GSO_TCPV4 1 // GSO frame, IPv4 TCP (TSO)
+#define VIRTIO_NET_HDR_GSO_UDP 3 // GSO frame, IPv4 UDP (UFO)
+#define VIRTIO_NET_HDR_GSO_TCPV6 4 // GSO frame, IPv6 TCP
+#define VIRTIO_NET_HDR_GSO_ECN 0x80 // TCP has ECN set
+ u8 gso_type;
+ u16 hdr_len; // Ethernet + IP + tcp/udp hdrs
+ u16 gso_size; // Bytes to append to gso_hdr_len per frame
+ u16 csum_start; // Position to start checksumming from
+ u16 csum_offset; // Offset after that to place checksum
+}virtio_net_hdr_basic;
+
+typedef struct _tagvirtio_net_hdr_ext
+{
+ virtio_net_hdr_basic BasicHeader;
+ u16 nBuffers;
+}virtio_net_hdr_ext;
+
+/*
+ * Control virtqueue data structures
+ *
+ * The control virtqueue expects a header in the first sg entry
+ * and an ack/status response in the last entry. Data for the
+ * command goes in between.
+ */
+typedef struct tag_virtio_net_ctrl_hdr {
+ u8 class_of_command;
+ u8 cmd;
+}virtio_net_ctrl_hdr;
+
+typedef u8 virtio_net_ctrl_ack;
+
+#define VIRTIO_NET_OK 0
+#define VIRTIO_NET_ERR 1
+
+/*
+ * Control the RX mode, ie. promiscuous, allmulti, etc...
+ * All commands require an "out" sg entry containing a 1 byte
+ * state value, zero = disable, non-zero = enable. Commands
+ * 0 and 1 are supported with the VIRTIO_NET_F_CTRL_RX feature.
+ * Commands 2-5 are added with VIRTIO_NET_F_CTRL_RX_EXTRA.
+ */
+#define VIRTIO_NET_CTRL_RX_MODE 0
+ #define VIRTIO_NET_CTRL_RX_MODE_PROMISC 0
+ #define VIRTIO_NET_CTRL_RX_MODE_ALLMULTI 1
+ #define VIRTIO_NET_CTRL_RX_MODE_ALLUNI 2
+ #define VIRTIO_NET_CTRL_RX_MODE_NOMULTI 3
+ #define VIRTIO_NET_CTRL_RX_MODE_NOUNI 4
+ #define VIRTIO_NET_CTRL_RX_MODE_NOBCAST 5
+
+/*
+ * Control the MAC filter table.
+ *
+ * The MAC filter table is managed by the hypervisor, the guest should
+ * assume the size is infinite. Filtering should be considered
+ * non-perfect, ie. based on hypervisor resources, the guest may
+ * received packets from sources not specified in the filter list.
+ *
+ * In addition to the class/cmd header, the TABLE_SET command requires
+ * two out scatterlists. Each contains a 4 byte count of entries followed
+ * by a concatenated byte stream of the ETH_ALEN MAC addresses. The
+ * first sg list contains unicast addresses, the second is for multicast.
+ * This functionality is present if the VIRTIO_NET_F_CTRL_RX feature
+ * is available.
+ */
+#define ETH_ALEN 6
+
+struct virtio_net_ctrl_mac {
+ u32 entries;
+ // follows
+ //u8 macs[][ETH_ALEN];
+};
+#define VIRTIO_NET_CTRL_MAC 1
+ #define VIRTIO_NET_CTRL_MAC_TABLE_SET 0
+
+/*
+ * Control VLAN filtering
+ *
+ * The VLAN filter table is controlled via a simple ADD/DEL interface.
+ * VLAN IDs not added may be filterd by the hypervisor. Del is the
+ * opposite of add. Both commands expect an out entry containing a 2
+ * byte VLAN ID. VLAN filtering is available with the
+ * VIRTIO_NET_F_CTRL_VLAN feature bit.
+ */
+#define VIRTIO_NET_CTRL_VLAN 2
+ #define VIRTIO_NET_CTRL_VLAN_ADD 0
+ #define VIRTIO_NET_CTRL_VLAN_DEL 1
+
+
+#pragma pack (pop)
+
+#endif
diff --git a/drivers/network/dd/netkvm/Common/ParaNdis-Common.c
b/drivers/network/dd/netkvm/Common/ParaNdis-Common.c
new file mode 100644
index 00000000000..ebb0c04b981
--- /dev/null
+++ b/drivers/network/dd/netkvm/Common/ParaNdis-Common.c
@@ -0,0 +1,3017 @@
+/*
+ * This file contains NDIS driver procedures, common for NDIS5 and NDIS6
+ *
+ * Copyright (c) 2008-2017 Red Hat, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met :
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and / or other materials provided with the distribution.
+ * 3. Neither the names of the copyright holders nor the names of their contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS''
AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+#include "ndis56common.h"
+
+#ifdef WPP_EVENT_TRACING
+#include "ParaNdis-Common.tmh"
+#endif
+
+static void ReuseReceiveBufferRegular(PARANDIS_ADAPTER *pContext, pIONetDescriptor
pBuffersDescriptor);
+static void ReuseReceiveBufferPowerOff(PARANDIS_ADAPTER *pContext, pIONetDescriptor
pBuffersDescriptor);
+
+//#define ROUNDSIZE(sz) ((sz + 15) & ~15)
+#define MAX_VLAN_ID 4095
+
+#if 0
+void FORCEINLINE DebugDumpPacket(LPCSTR prefix, PVOID header, int level)
+{
+ PUCHAR peth = (PUCHAR)header;
+ DPrintf(level, ("[%s] %02X%02X%02X%02X%02X%02X =>
%02X%02X%02X%02X%02X%02X", prefix,
+ peth[6], peth[7], peth[8], peth[9], peth[10], peth[11],
+ peth[0], peth[1], peth[2], peth[3], peth[4], peth[5]));
+}
+#else
+void FORCEINLINE DebugDumpPacket(LPCSTR prefix, PVOID header, int level)
+{
+}
+#endif
+
+
+
+/**********************************************************
+Validates MAC address
+Valid MAC address is not broadcast, not multicast, not empty
+if bLocal is set, it must be LOCAL
+if not, is must be non-local or local
+Parameters:
+ PUCHAR pcMacAddress - MAC address to validate
+ BOOLEAN bLocal - TRUE, if we validate locally administered address
+Return value:
+ TRUE if valid
+***********************************************************/
+BOOLEAN ParaNdis_ValidateMacAddress(PUCHAR pcMacAddress, BOOLEAN bLocal)
+{
+ BOOLEAN bLA = FALSE, bEmpty, bBroadcast, bMulticast = FALSE;
+ bBroadcast = ETH_IS_BROADCAST(pcMacAddress);
+ bLA = !bBroadcast && ETH_IS_LOCALLY_ADMINISTERED(pcMacAddress);
+ bMulticast = !bBroadcast && ETH_IS_MULTICAST(pcMacAddress);
+ bEmpty = ETH_IS_EMPTY(pcMacAddress);
+ return !bBroadcast && !bEmpty && !bMulticast && (!bLocal ||
bLA);
+}
+
+static eInspectedPacketType QueryPacketType(PVOID data)
+{
+ if (ETH_IS_BROADCAST(data))
+ return iptBroadcast;
+ if (ETH_IS_MULTICAST(data))
+ return iptMulticast;
+ return iptUnicast;
+}
+
+typedef struct _tagConfigurationEntry
+{
+ const char *Name;
+ ULONG ulValue;
+ ULONG ulMinimal;
+ ULONG ulMaximal;
+}tConfigurationEntry;
+
+typedef struct _tagConfigurationEntries
+{
+ tConfigurationEntry isPromiscuous;
+ tConfigurationEntry PrioritySupport;
+ tConfigurationEntry ConnectRate;
+ tConfigurationEntry isLogEnabled;
+ tConfigurationEntry debugLevel;
+ tConfigurationEntry connectTimer;
+ tConfigurationEntry dpcChecker;
+ tConfigurationEntry TxCapacity;
+ tConfigurationEntry RxCapacity;
+ tConfigurationEntry InterruptRecovery;
+ tConfigurationEntry LogStatistics;
+ tConfigurationEntry PacketFiltering;
+ tConfigurationEntry ScatterGather;
+ tConfigurationEntry BatchReceive;
+ tConfigurationEntry OffloadTxChecksum;
+ tConfigurationEntry OffloadTxLSO;
+ tConfigurationEntry OffloadRxCS;
+ tConfigurationEntry OffloadGuestCS;
+ tConfigurationEntry UseSwTxChecksum;
+ tConfigurationEntry IPPacketsCheck;
+ tConfigurationEntry stdIpcsV4;
+ tConfigurationEntry stdTcpcsV4;
+ tConfigurationEntry stdTcpcsV6;
+ tConfigurationEntry stdUdpcsV4;
+ tConfigurationEntry stdUdpcsV6;
+ tConfigurationEntry stdLsoV1;
+ tConfigurationEntry stdLsoV2ip4;
+ tConfigurationEntry stdLsoV2ip6;
+ tConfigurationEntry PriorityVlanTagging;
+ tConfigurationEntry VlanId;
+ tConfigurationEntry UseMergeableBuffers;
+ tConfigurationEntry MTU;
+ tConfigurationEntry NumberOfHandledRXPackersInDPC;
+ tConfigurationEntry Indirect;
+}tConfigurationEntries;
+
+static const tConfigurationEntries defaultConfiguration =
+{
+ { "Promiscuous", 0, 0, 1 },
+ { "Priority", 0, 0, 1 },
+ { "ConnectRate", 100,10,10000 },
+ { "DoLog", 1, 0, 1 },
+ { "DebugLevel", 2, 0, 8 },
+ { "ConnectTimer", 0, 0, 300000 },
+ { "DpcCheck", 0, 0, 2 },
+ { "TxCapacity", 1024, 16, 1024 },
+ { "RxCapacity", 256, 32, 1024 },
+ { "InterruptRecovery", 0, 0, 1},
+ { "LogStatistics", 0, 0, 10000},
+ { "PacketFilter", 1, 0, 1},
+ { "Gather", 1, 0, 1},
+ { "BatchReceive", 1, 0, 1},
+ { "Offload.TxChecksum", 0, 0, 31},
+ { "Offload.TxLSO", 0, 0, 2},
+ { "Offload.RxCS", 0, 0, 31},
+ { "Offload.GuestCS", 0, 0, 1},
+ { "UseSwTxChecksum", 0, 0, 1 },
+ { "IPPacketsCheck", 0, 0, 3 },
+ { "*IPChecksumOffloadIPv4", 3, 0, 3 },
+ { "*TCPChecksumOffloadIPv4",3, 0, 3 },
+ { "*TCPChecksumOffloadIPv6",3, 0, 3 },
+ { "*UDPChecksumOffloadIPv4",3, 0, 3 },
+ { "*UDPChecksumOffloadIPv6",3, 0, 3 },
+ { "*LsoV1IPv4", 1, 0, 1 },
+ { "*LsoV2IPv4", 1, 0, 1 },
+ { "*LsoV2IPv6", 1, 0, 1 },
+ { "*PriorityVLANTag", 3, 0, 3},
+ { "VlanId", 0, 0, MAX_VLAN_ID},
+ { "MergeableBuf", 1, 0, 1},
+ { "MTU", 1500, 500, 65500},
+ { "NumberOfHandledRXPackersInDPC", MAX_RX_LOOPS, 1, 10000},
+ { "Indirect", 0, 0, 2},
+};
+
+static void ParaNdis_ResetVirtIONetDevice(PARANDIS_ADAPTER *pContext)
+{
+ virtio_device_reset(&pContext->IODevice);
+ DPrintf(0, ("[%s] Done", __FUNCTION__));
+ /* reset all the features in the device */
+ pContext->ulCurrentVlansFilterSet = 0;
+ pContext->ullGuestFeatures = 0;
+#ifdef VIRTIO_RESET_VERIFY
+ if (1)
+ {
+ u8 devStatus;
+ devStatus = virtio_get_status(&pContext->IODevice);
+ if (devStatus)
+ {
+ DPrintf(0, ("[%s] Device status is still %02X", __FUNCTION__,
(ULONG)devStatus));
+ virtio_device_reset(&pContext->IODevice);
+ devStatus = virtio_get_status(&pContext->IODevice);
+ DPrintf(0, ("[%s] Device status on retry %02X", __FUNCTION__,
(ULONG)devStatus));
+ }
+ }
+#endif
+}
+
+/**********************************************************
+Gets integer value for specifies in pEntry->Name name
+Parameters:
+ NDIS_HANDLE cfg previously open configuration
+ tConfigurationEntry *pEntry - Entry to fill value in
+***********************************************************/
+static void GetConfigurationEntry(NDIS_HANDLE cfg, tConfigurationEntry *pEntry)
+{
+ NDIS_STATUS status;
+ const char *statusName;
+ NDIS_STRING name = {0};
+ PNDIS_CONFIGURATION_PARAMETER pParam = NULL;
+ NDIS_PARAMETER_TYPE ParameterType = NdisParameterInteger;
+ NdisInitializeString(&name, (PUCHAR)pEntry->Name);
+ NdisReadConfiguration(
+ &status,
+ &pParam,
+ cfg,
+ &name,
+ ParameterType);
+ if (status == NDIS_STATUS_SUCCESS)
+ {
+ ULONG ulValue = pParam->ParameterData.IntegerData;
+ if (ulValue >= pEntry->ulMinimal && ulValue <=
pEntry->ulMaximal)
+ {
+ pEntry->ulValue = ulValue;
+ statusName = "value";
+ }
+ else
+ {
+ statusName = "out of range";
+ }
+ }
+ else
+ {
+ statusName = "nothing";
+ }
+ DPrintf(2, ("[%s] %s read for %s - 0x%x",
+ __FUNCTION__,
+ statusName,
+ pEntry->Name,
+ pEntry->ulValue));
+ if (name.Buffer) NdisFreeString(name);
+}
+
+static void DisableLSOv4Permanently(PARANDIS_ADAPTER *pContext, LPCSTR procname, LPCSTR
reason)
+{
+ if (pContext->Offload.flagsValue & osbT4Lso)
+ {
+ DPrintf(0, ("[%s] Warning: %s", procname, reason));
+ pContext->Offload.flagsValue &= ~osbT4Lso;
+ ParaNdis_ResetOffloadSettings(pContext, NULL, NULL);
+ }
+}
+
+static void DisableLSOv6Permanently(PARANDIS_ADAPTER *pContext, LPCSTR procname, LPCSTR
reason)
+{
+ if (pContext->Offload.flagsValue & osbT6Lso)
+ {
+ DPrintf(0, ("[%s] Warning: %s", procname, reason));
+ pContext->Offload.flagsValue &= ~osbT6Lso;
+ ParaNdis_ResetOffloadSettings(pContext, NULL, NULL);
+ }
+}
+
+static void DisableBothLSOPermanently(PARANDIS_ADAPTER *pContext, LPCSTR procname, LPCSTR
reason)
+{
+ if (pContext->Offload.flagsValue & (osbT4Lso | osbT6Lso))
+ {
+ DPrintf(0, ("[%s] Warning: %s", procname, reason));
+ pContext->Offload.flagsValue &= ~(osbT6Lso | osbT4Lso);
+ ParaNdis_ResetOffloadSettings(pContext, NULL, NULL);
+ }
+}
+
+/**********************************************************
+Loads NIC parameters from adapter registry key
+Parameters:
+ context
+ PUCHAR *ppNewMACAddress - pointer to hold MAC address if configured from host
+***********************************************************/
+static void ReadNicConfiguration(PARANDIS_ADAPTER *pContext, PUCHAR *ppNewMACAddress)
+{
+ NDIS_HANDLE cfg;
+ tConfigurationEntries *pConfiguration = ParaNdis_AllocateMemory(pContext,
sizeof(tConfigurationEntries));
+ if (pConfiguration)
+ {
+ *pConfiguration = defaultConfiguration;
+ cfg = ParaNdis_OpenNICConfiguration(pContext);
+ if (cfg)
+ {
+ GetConfigurationEntry(cfg, &pConfiguration->isLogEnabled);
+ GetConfigurationEntry(cfg, &pConfiguration->debugLevel);
+ GetConfigurationEntry(cfg, &pConfiguration->ConnectRate);
+ GetConfigurationEntry(cfg, &pConfiguration->PrioritySupport);
+ GetConfigurationEntry(cfg, &pConfiguration->isPromiscuous);
+ GetConfigurationEntry(cfg, &pConfiguration->TxCapacity);
+ GetConfigurationEntry(cfg, &pConfiguration->RxCapacity);
+ GetConfigurationEntry(cfg, &pConfiguration->connectTimer);
+ GetConfigurationEntry(cfg, &pConfiguration->dpcChecker);
+ GetConfigurationEntry(cfg, &pConfiguration->InterruptRecovery);
+ GetConfigurationEntry(cfg, &pConfiguration->LogStatistics);
+ GetConfigurationEntry(cfg, &pConfiguration->PacketFiltering);
+ GetConfigurationEntry(cfg, &pConfiguration->ScatterGather);
+ GetConfigurationEntry(cfg, &pConfiguration->BatchReceive);
+ GetConfigurationEntry(cfg, &pConfiguration->OffloadTxChecksum);
+ GetConfigurationEntry(cfg, &pConfiguration->OffloadTxLSO);
+ GetConfigurationEntry(cfg, &pConfiguration->OffloadRxCS);
+ GetConfigurationEntry(cfg, &pConfiguration->OffloadGuestCS);
+ GetConfigurationEntry(cfg, &pConfiguration->UseSwTxChecksum);
+ GetConfigurationEntry(cfg, &pConfiguration->IPPacketsCheck);
+ GetConfigurationEntry(cfg, &pConfiguration->stdIpcsV4);
+ GetConfigurationEntry(cfg, &pConfiguration->stdTcpcsV4);
+ GetConfigurationEntry(cfg, &pConfiguration->stdTcpcsV6);
+ GetConfigurationEntry(cfg, &pConfiguration->stdUdpcsV4);
+ GetConfigurationEntry(cfg, &pConfiguration->stdUdpcsV6);
+ GetConfigurationEntry(cfg, &pConfiguration->stdLsoV1);
+ GetConfigurationEntry(cfg, &pConfiguration->stdLsoV2ip4);
+ GetConfigurationEntry(cfg, &pConfiguration->stdLsoV2ip6);
+ GetConfigurationEntry(cfg, &pConfiguration->PriorityVlanTagging);
+ GetConfigurationEntry(cfg, &pConfiguration->VlanId);
+ GetConfigurationEntry(cfg, &pConfiguration->UseMergeableBuffers);
+ GetConfigurationEntry(cfg, &pConfiguration->MTU);
+ GetConfigurationEntry(cfg,
&pConfiguration->NumberOfHandledRXPackersInDPC);
+ GetConfigurationEntry(cfg, &pConfiguration->Indirect);
+
+ #if !defined(WPP_EVENT_TRACING)
+ bDebugPrint = pConfiguration->isLogEnabled.ulValue;
+ nDebugLevel = pConfiguration->debugLevel.ulValue;
+ #endif
+ // ignoring promiscuous setting, nothing to do with it
+ pContext->maxFreeTxDescriptors = pConfiguration->TxCapacity.ulValue;
+ pContext->NetMaxReceiveBuffers = pConfiguration->RxCapacity.ulValue;
+ pContext->ulMilliesToConnect = pConfiguration->connectTimer.ulValue;
+ pContext->nEnableDPCChecker = pConfiguration->dpcChecker.ulValue;
+ pContext->bDoInterruptRecovery =
pConfiguration->InterruptRecovery.ulValue != 0;
+ pContext->Limits.nPrintDiagnostic =
pConfiguration->LogStatistics.ulValue;
+ pContext->uNumberOfHandledRXPacketsInDPC =
pConfiguration->NumberOfHandledRXPackersInDPC.ulValue;
+ pContext->bDoSupportPriority = pConfiguration->PrioritySupport.ulValue
!= 0;
+ pContext->ulFormalLinkSpeed = pConfiguration->ConnectRate.ulValue;
+ pContext->ulFormalLinkSpeed *= 1000000;
+ pContext->bDoHwPacketFiltering =
pConfiguration->PacketFiltering.ulValue != 0;
+ pContext->bUseScatterGather = pConfiguration->ScatterGather.ulValue !=
0;
+ pContext->bBatchReceive = pConfiguration->BatchReceive.ulValue !=
0;
+ pContext->bDoHardwareChecksum = pConfiguration->UseSwTxChecksum.ulValue
== 0;
+ pContext->bDoGuestChecksumOnReceive =
pConfiguration->OffloadGuestCS.ulValue != 0;
+ pContext->bDoIPCheckTx = pConfiguration->IPPacketsCheck.ulValue &
1;
+ pContext->bDoIPCheckRx = pConfiguration->IPPacketsCheck.ulValue &
2;
+ pContext->Offload.flagsValue = 0;
+ // TX caps: 1 - TCP, 2 - UDP, 4 - IP, 8 - TCPv6, 16 - UDPv6
+ if (pConfiguration->OffloadTxChecksum.ulValue & 1)
pContext->Offload.flagsValue |= osbT4TcpChecksum | osbT4TcpOptionsChecksum;
+ if (pConfiguration->OffloadTxChecksum.ulValue & 2)
pContext->Offload.flagsValue |= osbT4UdpChecksum;
+ if (pConfiguration->OffloadTxChecksum.ulValue & 4)
pContext->Offload.flagsValue |= osbT4IpChecksum | osbT4IpOptionsChecksum;
+ if (pConfiguration->OffloadTxChecksum.ulValue & 8)
pContext->Offload.flagsValue |= osbT6TcpChecksum | osbT6TcpOptionsChecksum;
+ if (pConfiguration->OffloadTxChecksum.ulValue & 16)
pContext->Offload.flagsValue |= osbT6UdpChecksum;
+ if (pConfiguration->OffloadTxLSO.ulValue) pContext->Offload.flagsValue
|= osbT4Lso | osbT4LsoIp | osbT4LsoTcp;
+ if (pConfiguration->OffloadTxLSO.ulValue > 1)
pContext->Offload.flagsValue |= osbT6Lso | osbT6LsoTcpOptions;
+ // RX caps: 1 - TCP, 2 - UDP, 4 - IP, 8 - TCPv6, 16 - UDPv6
+ if (pConfiguration->OffloadRxCS.ulValue & 1)
pContext->Offload.flagsValue |= osbT4RxTCPChecksum | osbT4RxTCPOptionsChecksum;
+ if (pConfiguration->OffloadRxCS.ulValue & 2)
pContext->Offload.flagsValue |= osbT4RxUDPChecksum;
+ if (pConfiguration->OffloadRxCS.ulValue & 4)
pContext->Offload.flagsValue |= osbT4RxIPChecksum | osbT4RxIPOptionsChecksum;
+ if (pConfiguration->OffloadRxCS.ulValue & 8)
pContext->Offload.flagsValue |= osbT6RxTCPChecksum | osbT6RxTCPOptionsChecksum;
+ if (pConfiguration->OffloadRxCS.ulValue & 16)
pContext->Offload.flagsValue |= osbT6RxUDPChecksum;
+ /* full packet size that can be configured as GSO for VIRTIO is short */
+ /* NDIS test fails sometimes fails on segments 50-60K */
+ pContext->Offload.maxPacketSize = PARANDIS_MAX_LSO_SIZE;
+ pContext->InitialOffloadParameters.IPv4Checksum =
(UCHAR)pConfiguration->stdIpcsV4.ulValue;
+ pContext->InitialOffloadParameters.TCPIPv4Checksum =
(UCHAR)pConfiguration->stdTcpcsV4.ulValue;
+ pContext->InitialOffloadParameters.TCPIPv6Checksum =
(UCHAR)pConfiguration->stdTcpcsV6.ulValue;
+ pContext->InitialOffloadParameters.UDPIPv4Checksum =
(UCHAR)pConfiguration->stdUdpcsV4.ulValue;
+ pContext->InitialOffloadParameters.UDPIPv6Checksum =
(UCHAR)pConfiguration->stdUdpcsV6.ulValue;
+ pContext->InitialOffloadParameters.LsoV1 =
(UCHAR)pConfiguration->stdLsoV1.ulValue;
+ pContext->InitialOffloadParameters.LsoV2IPv4 =
(UCHAR)pConfiguration->stdLsoV2ip4.ulValue;
+ pContext->InitialOffloadParameters.LsoV2IPv6 =
(UCHAR)pConfiguration->stdLsoV2ip6.ulValue;
+ pContext->ulPriorityVlanSetting =
pConfiguration->PriorityVlanTagging.ulValue;
+ pContext->VlanId = pConfiguration->VlanId.ulValue & 0xfff;
+ pContext->bUseMergedBuffers =
pConfiguration->UseMergeableBuffers.ulValue != 0;
+ pContext->MaxPacketSize.nMaxDataSize = pConfiguration->MTU.ulValue;
+ pContext->bUseIndirect = pConfiguration->Indirect.ulValue != 0;
+ if (!pContext->bDoSupportPriority)
+ pContext->ulPriorityVlanSetting = 0;
+ // if Vlan not supported
+ if (!IsVlanSupported(pContext))
+ pContext->VlanId = 0;
+ if (1)
+ {
+ NDIS_STATUS status;
+ PVOID p;
+ UINT len = 0;
+ NdisReadNetworkAddress(&status, &p, &len, cfg);
+ if (status == NDIS_STATUS_SUCCESS && len ==
sizeof(pContext->CurrentMacAddress))
+ {
+ *ppNewMACAddress = ParaNdis_AllocateMemory(pContext,
sizeof(pContext->CurrentMacAddress));
+ if (*ppNewMACAddress)
+ {
+ NdisMoveMemory(*ppNewMACAddress, p, len);
+ }
+ else
+ {
+ DPrintf(0, ("[%s] MAC address present, but some problem
also...", __FUNCTION__));
+ }
+ }
+ else if (len && len != sizeof(pContext->CurrentMacAddress))
+ {
+ DPrintf(0, ("[%s] MAC address has wrong length of %d",
__FUNCTION__, len));
+ }
+ else
+ {
+ DPrintf(4, ("[%s] Nothing read for MAC, error %X",
__FUNCTION__, status));
+ }
+ }
+ NdisCloseConfiguration(cfg);
+ }
+ NdisFreeMemory(pConfiguration, 0, 0);
+ }
+}
+
+void ParaNdis_ResetOffloadSettings(PARANDIS_ADAPTER *pContext, tOffloadSettingsFlags
*pDest, PULONG from)
+{
+ if (!pDest) pDest = &pContext->Offload.flags;
+ if (!from) from = &pContext->Offload.flagsValue;
+
+ pDest->fTxIPChecksum = !!(*from & osbT4IpChecksum);
+ pDest->fTxTCPChecksum = !!(*from & osbT4TcpChecksum);
+ pDest->fTxUDPChecksum = !!(*from & osbT4UdpChecksum);
+ pDest->fTxTCPOptions = !!(*from & osbT4TcpOptionsChecksum);
+ pDest->fTxIPOptions = !!(*from & osbT4IpOptionsChecksum);
+
+ pDest->fTxLso = !!(*from & osbT4Lso);
+ pDest->fTxLsoIP = !!(*from & osbT4LsoIp);
+ pDest->fTxLsoTCP = !!(*from & osbT4LsoTcp);
+
+ pDest->fRxIPChecksum = !!(*from & osbT4RxIPChecksum);
+ pDest->fRxIPOptions = !!(*from & osbT4RxIPOptionsChecksum);
+ pDest->fRxTCPChecksum = !!(*from & osbT4RxTCPChecksum);
+ pDest->fRxTCPOptions = !!(*from & osbT4RxTCPOptionsChecksum);
+ pDest->fRxUDPChecksum = !!(*from & osbT4RxUDPChecksum);
+
+ pDest->fTxTCPv6Checksum = !!(*from & osbT6TcpChecksum);
+ pDest->fTxTCPv6Options = !!(*from & osbT6TcpOptionsChecksum);
+ pDest->fTxUDPv6Checksum = !!(*from & osbT6UdpChecksum);
+ pDest->fTxIPv6Ext = !!(*from & osbT6IpExtChecksum);
+
+ pDest->fTxLsov6 = !!(*from & osbT6Lso);
+ pDest->fTxLsov6IP = !!(*from & osbT6LsoIpExt);
+ pDest->fTxLsov6TCP = !!(*from & osbT6LsoTcpOptions);
+
+ pDest->fRxTCPv6Checksum = !!(*from & osbT6RxTCPChecksum);
+ pDest->fRxTCPv6Options = !!(*from & osbT6RxTCPOptionsChecksum);
+ pDest->fRxUDPv6Checksum = !!(*from & osbT6RxUDPChecksum);
+ pDest->fRxIPv6Ext = !!(*from & osbT6RxIpExtChecksum);
+}
+
+/**********************************************************
+Enumerates adapter resources and fills the structure holding them
+Verifies that IO assigned and has correct size
+Verifies that interrupt assigned
+Parameters:
+ PNDIS_RESOURCE_LIST RList - list of resources, received from NDIS
+ tAdapterResources *pResources - structure to fill
+Return value:
+ TRUE if everything is OK
+***********************************************************/
+static BOOLEAN GetAdapterResources(NDIS_HANDLE MiniportHandle, PNDIS_RESOURCE_LIST RList,
tAdapterResources *pResources)
+{
+ UINT i;
+ int read, bar = -1;
+ PCI_COMMON_HEADER pci_config;
+ NdisZeroMemory(pResources, sizeof(*pResources));
+
+ // read the PCI config space header
+ read = NdisReadPciSlotInformation(
+ MiniportHandle,
+ 0 /* SlotNumber, reserved */,
+ 0 /* Offset */,
+ &pci_config,
+ sizeof(pci_config));
+ if (read != sizeof(pci_config)) {
+ return FALSE;
+ }
+
+ for (i = 0; i < RList->Count; ++i)
+ {
+ ULONG type = RList->PartialDescriptors[i].Type;
+ if (type == CmResourceTypePort)
+ {
+ PHYSICAL_ADDRESS Start = RList->PartialDescriptors[i].u.Port.Start;
+ ULONG len = RList->PartialDescriptors[i].u.Port.Length;
+ DPrintf(0, ("Found IO ports at %08lX(%d)", Start.LowPart, len));
+ bar = virtio_get_bar_index(&pci_config, Start);
+ if (bar < 0) {
+ break;
+ }
+ pResources->PciBars[bar].BasePA = Start;
+ pResources->PciBars[bar].uLength = len;
+ pResources->PciBars[bar].bPortSpace = TRUE;
+ }
+ else if (type == CmResourceTypeMemory)
+ {
+ PHYSICAL_ADDRESS Start = RList->PartialDescriptors[i].u.Memory.Start;
+ ULONG len = RList->PartialDescriptors[i].u.Memory.Length;
+ DPrintf(0, ("Found IO memory at %08I64X(%d)", Start.QuadPart,
len));
+ bar = virtio_get_bar_index(&pci_config, Start);
+ if (bar < 0) {
+ break;
+ }
+ pResources->PciBars[bar].BasePA = Start;
+ pResources->PciBars[bar].uLength = len;
+ pResources->PciBars[bar].bPortSpace = FALSE;
+ }
+ else if (type == CmResourceTypeInterrupt)
+ {
+ pResources->Vector = RList->PartialDescriptors[i].u.Interrupt.Vector;
+ pResources->Level = RList->PartialDescriptors[i].u.Interrupt.Level;
+ pResources->Affinity =
RList->PartialDescriptors[i].u.Interrupt.Affinity;
+ pResources->InterruptFlags = RList->PartialDescriptors[i].Flags;
+ DPrintf(0, ("Found Interrupt vector %d, level %d, affinity %X, flags
%X",
+ pResources->Vector, pResources->Level,
(ULONG)pResources->Affinity, pResources->InterruptFlags));
+ }
+ }
+ return bar >= 0 && pResources->Vector;
+}
+
+static void DumpVirtIOFeatures(PARANDIS_ADAPTER *pContext)
+{
+ const struct { ULONG bitmask; const PCHAR Name; } Features[] =
+ {
+
+ {VIRTIO_NET_F_CSUM, "VIRTIO_NET_F_CSUM" },
+ {VIRTIO_NET_F_GUEST_CSUM, "VIRTIO_NET_F_GUEST_CSUM" },
+ {VIRTIO_NET_F_MAC, "VIRTIO_NET_F_MAC" },
+ {VIRTIO_NET_F_GSO, "VIRTIO_NET_F_GSO" },
+ {VIRTIO_NET_F_GUEST_TSO4, "VIRTIO_NET_F_GUEST_TSO4"},
+ {VIRTIO_NET_F_GUEST_TSO6, "VIRTIO_NET_F_GUEST_TSO6"},
+ {VIRTIO_NET_F_GUEST_ECN, "VIRTIO_NET_F_GUEST_ECN"},
+ {VIRTIO_NET_F_GUEST_UFO, "VIRTIO_NET_F_GUEST_UFO"},
+ {VIRTIO_NET_F_HOST_TSO4, "VIRTIO_NET_F_HOST_TSO4"},
+ {VIRTIO_NET_F_HOST_TSO6, "VIRTIO_NET_F_HOST_TSO6"},
+ {VIRTIO_NET_F_HOST_ECN, "VIRTIO_NET_F_HOST_ECN"},
+ {VIRTIO_NET_F_HOST_UFO, "VIRTIO_NET_F_HOST_UFO"},
+ {VIRTIO_NET_F_MRG_RXBUF, "VIRTIO_NET_F_MRG_RXBUF"},
+ {VIRTIO_NET_F_STATUS, "VIRTIO_NET_F_STATUS"},
+ {VIRTIO_NET_F_CTRL_VQ, "VIRTIO_NET_F_CTRL_VQ"},
+ {VIRTIO_NET_F_CTRL_RX, "VIRTIO_NET_F_CTRL_RX"},
+ {VIRTIO_NET_F_CTRL_VLAN, "VIRTIO_NET_F_CTRL_VLAN"},
+ {VIRTIO_NET_F_CTRL_RX_EXTRA, "VIRTIO_NET_F_CTRL_RX_EXTRA"},
+ {VIRTIO_RING_F_INDIRECT_DESC, "VIRTIO_RING_F_INDIRECT_DESC"},
+ {VIRTIO_F_VERSION_1, "VIRTIO_F_VERSION_1" },
+ {VIRTIO_F_ANY_LAYOUT, "VIRTIO_F_ANY_LAYOUT" },
+ };
+ UINT i;
+ for (i = 0; i < sizeof(Features)/sizeof(Features[0]); ++i)
+ {
+ if (VirtIODeviceGetHostFeature(pContext, Features[i].bitmask))
+ {
+ DPrintf(0, ("VirtIO Host Feature %s", Features[i].Name));
+ }
+ }
+}
+
+/**********************************************************
+ Only for test. Prints out if the interrupt line is ON
+Parameters:
+Return value:
+***********************************************************/
+static void JustForCheckClearInterrupt(PARANDIS_ADAPTER *pContext, const char *Label)
+{
+ if (pContext->bEnableInterruptChecking)
+ {
+ ULONG ulActive;
+ ulActive = virtio_read_isr_status(&pContext->IODevice);
+ if (ulActive)
+ {
+ DPrintf(0,("WARNING: Interrupt Line %d(%s)!", ulActive, Label));
+ }
+ }
+}
+
+/**********************************************************
+Prints out statistics
+***********************************************************/
+static void PrintStatistics(PARANDIS_ADAPTER *pContext)
+{
+ ULONG64 totalTxFrames =
+ pContext->Statistics.ifHCOutBroadcastPkts +
+ pContext->Statistics.ifHCOutMulticastPkts +
+ pContext->Statistics.ifHCOutUcastPkts;
+ ULONG64 totalRxFrames =
+ pContext->Statistics.ifHCInBroadcastPkts +
+ pContext->Statistics.ifHCInMulticastPkts +
+ pContext->Statistics.ifHCInUcastPkts;
+
+ DPrintf(0, ("[Diag!%X] RX buffers at VIRTIO %d of %d",
+ pContext->CurrentMacAddress[5],
+ pContext->NetNofReceiveBuffers,
+ pContext->NetMaxReceiveBuffers));
+ DPrintf(0, ("[Diag!] TX desc available %d/%d, buf %d/min. %d",
+ pContext->nofFreeTxDescriptors,
+ pContext->maxFreeTxDescriptors,
+ pContext->nofFreeHardwareBuffers,
+ pContext->minFreeHardwareBuffers));
+ pContext->minFreeHardwareBuffers = pContext->nofFreeHardwareBuffers;
+ if (pContext->NetTxPacketsToReturn)
+ {
+ DPrintf(0, ("[Diag!] TX packets to return %d",
pContext->NetTxPacketsToReturn));
+ }
+ DPrintf(0, ("[Diag!] Bytes transmitted %I64u, received %I64u",
+ pContext->Statistics.ifHCOutOctets,
+ pContext->Statistics.ifHCInOctets));
+ DPrintf(0, ("[Diag!] Tx frames %I64u, CSO %d, LSO %d, indirect %d",
+ totalTxFrames,
+ pContext->extraStatistics.framesCSOffload,
+ pContext->extraStatistics.framesLSO,
+ pContext->extraStatistics.framesIndirect));
+ DPrintf(0, ("[Diag!] Rx frames %I64u, Rx.Pri %d, RxHwCS.OK %d, FiltOut
%d",
+ totalRxFrames, pContext->extraStatistics.framesRxPriority,
+ pContext->extraStatistics.framesRxCSHwOK,
pContext->extraStatistics.framesFilteredOut));
+ if (pContext->extraStatistics.framesRxCSHwMissedBad ||
pContext->extraStatistics.framesRxCSHwMissedGood)
+ {
+ DPrintf(0, ("[Diag!] RxHwCS mistakes: missed bad %d, missed good %d",
+ pContext->extraStatistics.framesRxCSHwMissedBad,
pContext->extraStatistics.framesRxCSHwMissedGood));
+ }
+}
+
+static NDIS_STATUS NTStatusToNdisStatus(NTSTATUS nt_status) {
+ switch (nt_status) {
+ case STATUS_SUCCESS:
+ return NDIS_STATUS_SUCCESS;
+ case STATUS_NOT_FOUND:
+ case STATUS_DEVICE_NOT_CONNECTED:
+ return NDIS_STATUS_ADAPTER_NOT_FOUND;
+ case STATUS_INSUFFICIENT_RESOURCES:
+ return NDIS_STATUS_RESOURCES;
+ case STATUS_INVALID_PARAMETER:
+ return NDIS_STATUS_INVALID_DEVICE_REQUEST;
+ default:
+ return NDIS_STATUS_FAILURE;
+ }
+}
+
+static NDIS_STATUS FinalizeFeatures(PARANDIS_ADAPTER *pContext)
+{
+ NTSTATUS nt_status = virtio_set_features(&pContext->IODevice,
pContext->ullGuestFeatures);
+ if (!NT_SUCCESS(nt_status)) {
+ DPrintf(0, ("[%s] virtio_set_features failed with %x\n", __FUNCTION__,
nt_status));
+ }
+ return NTStatusToNdisStatus(nt_status);
+}
+
+/**********************************************************
+Initializes the context structure
+Major variables, received from NDIS on initialization, must be be set before this call
+(for ex. pContext->MiniportHandle)
+
+If this procedure fails, no need to call
+ ParaNdis_CleanupContext
+
+
+Parameters:
+Return value:
+ SUCCESS, if resources are OK
+ NDIS_STATUS_RESOURCE_CONFLICT if not
+***********************************************************/
+NDIS_STATUS ParaNdis_InitializeContext(
+ PARANDIS_ADAPTER *pContext,
+ PNDIS_RESOURCE_LIST pResourceList)
+{
+ NDIS_STATUS status = NDIS_STATUS_SUCCESS;
+ PUCHAR pNewMacAddress = NULL;
+ USHORT linkStatus = 0;
+ NTSTATUS nt_status;
+
+ DEBUG_ENTRY(0);
+ /* read first PCI IO bar*/
+ //ulIOAddress = ReadPCIConfiguration(miniportAdapterHandle, 0x10);
+ /* check this is IO and assigned */
+ ReadNicConfiguration(pContext, &pNewMacAddress);
+ if (pNewMacAddress)
+ {
+ if (ParaNdis_ValidateMacAddress(pNewMacAddress, TRUE))
+ {
+ DPrintf(0, ("[%s] WARNING: MAC address reloaded", __FUNCTION__));
+ NdisMoveMemory(pContext->CurrentMacAddress, pNewMacAddress,
sizeof(pContext->CurrentMacAddress));
+ }
+ else
+ {
+ DPrintf(0, ("[%s] WARNING: Invalid MAC address ignored",
__FUNCTION__));
+ }
+ NdisFreeMemory(pNewMacAddress, 0, 0);
+ }
+
+ pContext->MaxPacketSize.nMaxFullSizeOS = pContext->MaxPacketSize.nMaxDataSize +
ETH_HEADER_SIZE;
+ pContext->MaxPacketSize.nMaxFullSizeHwTx =
pContext->MaxPacketSize.nMaxFullSizeOS;
+ pContext->MaxPacketSize.nMaxFullSizeHwRx =
pContext->MaxPacketSize.nMaxFullSizeOS + ETH_PRIORITY_HEADER_SIZE;
+ if (pContext->ulPriorityVlanSetting)
+ pContext->MaxPacketSize.nMaxFullSizeHwTx =
pContext->MaxPacketSize.nMaxFullSizeHwRx;
+
+ if (GetAdapterResources(pContext->MiniportHandle, pResourceList,
&pContext->AdapterResources))
+ {
+ if (pContext->AdapterResources.InterruptFlags &
CM_RESOURCE_INTERRUPT_MESSAGE)
+ {
+ DPrintf(0, ("[%s] Message interrupt assigned", __FUNCTION__));
+ pContext->bUsingMSIX = TRUE;
+ }
+
+ nt_status = virtio_device_initialize(
+ &pContext->IODevice,
+ &ParaNdisSystemOps,
+ pContext,
+ pContext->bUsingMSIX);
+ if (!NT_SUCCESS(nt_status)) {
+ DPrintf(0, ("[%s] virtio_device_initialize failed with %x\n",
__FUNCTION__, nt_status));
+ status = NTStatusToNdisStatus(nt_status);
+ DEBUG_EXIT_STATUS(0, status);
+ return status;
+ }
+
+ pContext->bIODeviceInitialized = TRUE;
+ JustForCheckClearInterrupt(pContext, "init 0");
+ ParaNdis_ResetVirtIONetDevice(pContext);
+ JustForCheckClearInterrupt(pContext, "init 1");
+ virtio_add_status(&pContext->IODevice, VIRTIO_CONFIG_S_ACKNOWLEDGE);
+ JustForCheckClearInterrupt(pContext, "init 2");
+ virtio_add_status(&pContext->IODevice, VIRTIO_CONFIG_S_DRIVER);
+ pContext->ullHostFeatures = virtio_get_features(&pContext->IODevice);
+ DumpVirtIOFeatures(pContext);
+ JustForCheckClearInterrupt(pContext, "init 3");
+ pContext->bLinkDetectSupported = 0 != VirtIODeviceGetHostFeature(pContext,
VIRTIO_NET_F_STATUS);
+
+ if(pContext->bLinkDetectSupported) {
+ virtio_get_config(&pContext->IODevice,
sizeof(pContext->CurrentMacAddress), &linkStatus, sizeof(linkStatus));
+ pContext->bConnected = (linkStatus & VIRTIO_NET_S_LINK_UP) != 0;
+ DPrintf(0, ("[%s] Link status on driver startup: %d", __FUNCTION__,
pContext->bConnected));
+ }
+
+ if (VirtIODeviceGetHostFeature(pContext, VIRTIO_F_VERSION_1))
+ {
+ // virtio 1.0 always uses the extended header
+ pContext->nVirtioHeaderSize = sizeof(virtio_net_hdr_ext);
+ VirtIODeviceEnableGuestFeature(pContext, VIRTIO_F_VERSION_1);
+ }
+ else
+ {
+ pContext->nVirtioHeaderSize = sizeof(virtio_net_hdr_basic);
+ }
+
+ if (VirtIODeviceGetHostFeature(pContext, VIRTIO_F_ANY_LAYOUT))
+ {
+ VirtIODeviceEnableGuestFeature(pContext, VIRTIO_F_ANY_LAYOUT);
+ }
+ if (VirtIODeviceGetHostFeature(pContext, VIRTIO_RING_F_EVENT_IDX))
+ {
+ VirtIODeviceEnableGuestFeature(pContext, VIRTIO_RING_F_EVENT_IDX);
+ }
+
+ if (!pContext->bUseMergedBuffers &&
VirtIODeviceGetHostFeature(pContext, VIRTIO_NET_F_MRG_RXBUF))
+ {
+ DPrintf(0, ("[%s] Not using mergeable buffers", __FUNCTION__));
+ }
+ else
+ {
+ pContext->bUseMergedBuffers = VirtIODeviceGetHostFeature(pContext,
VIRTIO_NET_F_MRG_RXBUF) != 0;
+ if (pContext->bUseMergedBuffers)
+ {
+ pContext->nVirtioHeaderSize = sizeof(virtio_net_hdr_ext);
+ VirtIODeviceEnableGuestFeature(pContext, VIRTIO_NET_F_MRG_RXBUF);
+ }
+ }
+ if (VirtIODeviceGetHostFeature(pContext, VIRTIO_NET_F_MAC))
+ {
+ virtio_get_config(
+ &pContext->IODevice,
+ 0, // + offsetof(struct virtio_net_config, mac)
+ &pContext->PermanentMacAddress,
+ ETH_LENGTH_OF_ADDRESS);
+ if (!ParaNdis_ValidateMacAddress(pContext->PermanentMacAddress, FALSE))
+ {
+ DPrintf(0,("Invalid device MAC
ignored(%02x-%02x-%02x-%02x-%02x-%02x)",
+ pContext->PermanentMacAddress[0],
+ pContext->PermanentMacAddress[1],
+ pContext->PermanentMacAddress[2],
+ pContext->PermanentMacAddress[3],
+ pContext->PermanentMacAddress[4],
+ pContext->PermanentMacAddress[5]));
+ NdisZeroMemory(pContext->PermanentMacAddress,
sizeof(pContext->PermanentMacAddress));
+ }
+ }
+
+ if (ETH_IS_EMPTY(pContext->PermanentMacAddress))
+ {
+ DPrintf(0, ("No device MAC present, use default"));
+ pContext->PermanentMacAddress[0] = 0x02;
+ pContext->PermanentMacAddress[1] = 0x50;
+ pContext->PermanentMacAddress[2] = 0xF2;
+ pContext->PermanentMacAddress[3] = 0x00;
+ pContext->PermanentMacAddress[4] = 0x01;
+ pContext->PermanentMacAddress[5] = 0x80 | (UCHAR)(pContext->ulUniqueID
& 0xFF);
+ }
+ DPrintf(0,("Device MAC = %02x-%02x-%02x-%02x-%02x-%02x",
+ pContext->PermanentMacAddress[0],
+ pContext->PermanentMacAddress[1],
+ pContext->PermanentMacAddress[2],
+ pContext->PermanentMacAddress[3],
+ pContext->PermanentMacAddress[4],
+ pContext->PermanentMacAddress[5]));
+
+ if (ETH_IS_EMPTY(pContext->CurrentMacAddress))
+ {
+ NdisMoveMemory(
+ &pContext->CurrentMacAddress,
+ &pContext->PermanentMacAddress,
+ ETH_LENGTH_OF_ADDRESS);
+ }
+ else
+ {
+ DPrintf(0,("Current MAC = %02x-%02x-%02x-%02x-%02x-%02x",
+ pContext->CurrentMacAddress[0],
+ pContext->CurrentMacAddress[1],
+ pContext->CurrentMacAddress[2],
+ pContext->CurrentMacAddress[3],
+ pContext->CurrentMacAddress[4],
+ pContext->CurrentMacAddress[5]));
+ }
+ if (VirtIODeviceGetHostFeature(pContext, VIRTIO_NET_F_CTRL_VQ)) {
+ pContext->bHasControlQueue = TRUE;
+ VirtIODeviceEnableGuestFeature(pContext, VIRTIO_NET_F_CTRL_VQ);
+ }
+ }
+ else
+ {
+ DPrintf(0, ("[%s] Error: Incomplete resources", __FUNCTION__));
+ status = NDIS_STATUS_RESOURCE_CONFLICT;
+ }
+
+
+ if (pContext->bDoHardwareChecksum)
+ {
+ ULONG dependentOptions;
+ dependentOptions = osbT4TcpChecksum | osbT4UdpChecksum |
osbT4TcpOptionsChecksum;
+ if (!VirtIODeviceGetHostFeature(pContext, VIRTIO_NET_F_CSUM) &&
+ (pContext->Offload.flagsValue & dependentOptions))
+ {
+ DPrintf(0, ("[%s] Host does not support CSUM, disabling CS
offload", __FUNCTION__) );
+ pContext->Offload.flagsValue &= ~dependentOptions;
+ }
+ }
+
+ if (pContext->bDoGuestChecksumOnReceive &&
VirtIODeviceGetHostFeature(pContext, VIRTIO_NET_F_GUEST_CSUM))
+ {
+ DPrintf(0, ("[%s] Enabling guest checksum", __FUNCTION__) );
+ VirtIODeviceEnableGuestFeature(pContext, VIRTIO_NET_F_GUEST_CSUM);
+ }
+ else
+ {
+ pContext->bDoGuestChecksumOnReceive = FALSE;
+ }
+
+ // now, after we checked the capabilities, we can initialize current
+ // configuration of offload tasks
+ ParaNdis_ResetOffloadSettings(pContext, NULL, NULL);
+ if (pContext->Offload.flags.fTxLso && !pContext->bUseScatterGather)
+ {
+ DisableBothLSOPermanently(pContext, __FUNCTION__, "SG is not active");
+ }
+ if (pContext->Offload.flags.fTxLso &&
+ !VirtIODeviceGetHostFeature(pContext, VIRTIO_NET_F_HOST_TSO4))
+ {
+ DisableLSOv4Permanently(pContext, __FUNCTION__, "Host does not support
TSOv4");
+ }
+ if (pContext->Offload.flags.fTxLsov6 &&
+ !VirtIODeviceGetHostFeature(pContext, VIRTIO_NET_F_HOST_TSO6))
+ {
+ DisableLSOv6Permanently(pContext, __FUNCTION__, "Host does not support
TSOv6");
+ }
+ if (pContext->bUseIndirect)
+ {
+ const char *reason = "";
+ if (!VirtIODeviceGetHostFeature(pContext, VIRTIO_RING_F_INDIRECT_DESC))
+ {
+ pContext->bUseIndirect = FALSE;
+ reason = "Host support";
+ }
+ else if (!pContext->bUseScatterGather)
+ {
+ pContext->bUseIndirect = FALSE;
+ reason = "SG";
+ }
+ DPrintf(0, ("[%s] %sable indirect Tx(!%s)", __FUNCTION__,
pContext->bUseIndirect ? "En" : "Dis", reason) );
+ }
+
+ if (VirtIODeviceGetHostFeature(pContext, VIRTIO_NET_F_CTRL_RX_EXTRA) &&
+ pContext->bDoHwPacketFiltering)
+ {
+ DPrintf(0, ("[%s] Using hardware packet filtering", __FUNCTION__));
+ pContext->bHasHardwareFilters = TRUE;
+ }
+
+ status = FinalizeFeatures(pContext);
+
+ pContext->ReuseBufferProc = (tReuseReceiveBufferProc)ReuseReceiveBufferRegular;
+
+ NdisInitializeEvent(&pContext->ResetEvent);
+ DEBUG_EXIT_STATUS(0, status);
+ return status;
+}
+
+/**********************************************************
+Free the resources allocated for VirtIO buffer descriptor
+Parameters:
+ PVOID pParam pIONetDescriptor to free
+ BOOLEAN bRemoveFromList TRUE, if also remove it from list
+***********************************************************/
+static void VirtIONetFreeBufferDescriptor(PARANDIS_ADAPTER *pContext, pIONetDescriptor
pBufferDescriptor)
+{
+ if(pBufferDescriptor)
+ {
+ if (pBufferDescriptor->pHolder)
+ ParaNdis_UnbindBufferFromPacket(pContext, pBufferDescriptor);
+ if (pBufferDescriptor->DataInfo.Virtual)
+ ParaNdis_FreePhysicalMemory(pContext, &pBufferDescriptor->DataInfo);
+ if (pBufferDescriptor->HeaderInfo.Virtual)
+ ParaNdis_FreePhysicalMemory(pContext,
&pBufferDescriptor->HeaderInfo);
+ NdisFreeMemory(pBufferDescriptor, 0, 0);
+ }
+}
+
+/**********************************************************
+Free all the buffer descriptors from specified list
+Parameters:
+ PLIST_ENTRY pListRoot list containing pIONetDescriptor structures
+ PNDIS_SPIN_LOCK pLock lock to protest this list
+Return value:
+***********************************************************/
+static void FreeDescriptorsFromList(PARANDIS_ADAPTER *pContext, PLIST_ENTRY pListRoot,
PNDIS_SPIN_LOCK pLock)
+{
+ pIONetDescriptor pBufferDescriptor;
+ LIST_ENTRY TempList;
+ InitializeListHead(&TempList);
+ NdisAcquireSpinLock(pLock);
+ while(!IsListEmpty(pListRoot))
+ {
+ pBufferDescriptor = (pIONetDescriptor)RemoveHeadList(pListRoot);
+ InsertTailList(&TempList, &pBufferDescriptor->listEntry);
+ }
+ NdisReleaseSpinLock(pLock);
+ while(!IsListEmpty(&TempList))
+ {
+ pBufferDescriptor = (pIONetDescriptor)RemoveHeadList(&TempList);
+ VirtIONetFreeBufferDescriptor(pContext, pBufferDescriptor);
+ }
+}
+
+static pIONetDescriptor AllocatePairOfBuffersOnInit(
+ PARANDIS_ADAPTER *pContext,
+ ULONG size1,
+ ULONG size2,
+ BOOLEAN bForTx)
+{
+ pIONetDescriptor p;
+ p = (pIONetDescriptor)ParaNdis_AllocateMemory(pContext, sizeof(*p));
+ if (p)
+ {
+ BOOLEAN b1 = FALSE, b2 = FALSE;
+ NdisZeroMemory(p, sizeof(*p));
+ p->HeaderInfo.size = size1;
+ p->DataInfo.size = size2;
+ p->HeaderInfo.IsCached = p->DataInfo.IsCached = 1;
+ p->HeaderInfo.IsTX = p->DataInfo.IsTX = bForTx;
+ p->nofUsedBuffers = 0;
+ b1 = ParaNdis_InitialAllocatePhysicalMemory(pContext, &p->HeaderInfo);
+ if (b1) b2 = ParaNdis_InitialAllocatePhysicalMemory(pContext,
&p->DataInfo);
+ if (b1 && b2)
+ {
+ BOOLEAN b = bForTx || ParaNdis_BindBufferToPacket(pContext, p);
+ if (!b)
+ {
+ DPrintf(0, ("[INITPHYS](%s) Failed to bind memory to net
packet", bForTx ? "TX" : "RX"));
+ VirtIONetFreeBufferDescriptor(pContext, p);
+ p = NULL;
+ }
+ }
+ else
+ {
+ if (b1) ParaNdis_FreePhysicalMemory(pContext, &p->HeaderInfo);
+ if (b2) ParaNdis_FreePhysicalMemory(pContext, &p->DataInfo);
+ NdisFreeMemory(p, 0, 0);
+ p = NULL;
+ DPrintf(0, ("[INITPHYS](%s) Failed to allocate memory block",
bForTx ? "TX" : "RX"));
+ }
+ }
+ if (p)
+ {
+ DPrintf(3, ("[INITPHYS](%s) Header v%p(p%08lX), Data v%p(p%08lX)",
bForTx ? "TX" : "RX",
+ p->HeaderInfo.Virtual, p->HeaderInfo.Physical.LowPart,
+ p->DataInfo.Virtual, p->DataInfo.Physical.LowPart));
+ }
+ return p;
+}
+
+/**********************************************************
+Allocates TX buffers according to startup setting (pContext->maxFreeTxDescriptors as
got from registry)
+Buffers are chained in NetFreeSendBuffers
+Parameters:
+ context
+***********************************************************/
+static void PrepareTransmitBuffers(PARANDIS_ADAPTER *pContext)
+{
+ UINT nBuffers, nMaxBuffers;
+ DEBUG_ENTRY(4);
+ nMaxBuffers = virtio_get_queue_size(pContext->NetSendQueue) / 2;
+ if (nMaxBuffers > pContext->maxFreeTxDescriptors) nMaxBuffers =
pContext->maxFreeTxDescriptors;
+
+ for (nBuffers = 0; nBuffers < nMaxBuffers; ++nBuffers)
+ {
+ pIONetDescriptor pBuffersDescriptor =
+ AllocatePairOfBuffersOnInit(
+ pContext,
+ pContext->nVirtioHeaderSize,
+ pContext->MaxPacketSize.nMaxFullSizeHwTx,
+ TRUE);
+ if (!pBuffersDescriptor) break;
+
+ NdisZeroMemory(pBuffersDescriptor->HeaderInfo.Virtual,
pBuffersDescriptor->HeaderInfo.size);
+ InsertTailList(&pContext->NetFreeSendBuffers,
&pBuffersDescriptor->listEntry);
+ pContext->nofFreeTxDescriptors++;
+ }
+
+ pContext->maxFreeTxDescriptors = pContext->nofFreeTxDescriptors;
+ pContext->nofFreeHardwareBuffers = pContext->nofFreeTxDescriptors * 2;
+ pContext->maxFreeHardwareBuffers = pContext->minFreeHardwareBuffers =
pContext->nofFreeHardwareBuffers;
+ DPrintf(0, ("[%s] available %d Tx descriptors, %d hw buffers",
+ __FUNCTION__, pContext->nofFreeTxDescriptors,
pContext->nofFreeHardwareBuffers));
+}
+
+static BOOLEAN AddRxBufferToQueue(PARANDIS_ADAPTER *pContext, pIONetDescriptor
pBufferDescriptor)
+{
+ UINT nBuffersToSubmit = 2;
+ struct VirtIOBufferDescriptor sg[2];
+ if (!pContext->bUseMergedBuffers)
+ {
+ sg[0].physAddr = pBufferDescriptor->HeaderInfo.Physical;
+ sg[0].length = pBufferDescriptor->HeaderInfo.size;
+ sg[1].physAddr = pBufferDescriptor->DataInfo.Physical;
+ sg[1].length = pBufferDescriptor->DataInfo.size;
+ }
+ else
+ {
+ sg[0].physAddr = pBufferDescriptor->DataInfo.Physical;
+ sg[0].length = pBufferDescriptor->DataInfo.size;
+ nBuffersToSubmit = 1;
+ }
+ return 0 <= virtqueue_add_buf(
+ pContext->NetReceiveQueue,
+ sg,
+ 0,
+ nBuffersToSubmit,
+ pBufferDescriptor,
+ NULL,
+ 0);
+}
+
+
+/**********************************************************
+Allocates maximum RX buffers for incoming packets
+Buffers are chained in NetReceiveBuffers
+Parameters:
+ context
+***********************************************************/
+static int PrepareReceiveBuffers(PARANDIS_ADAPTER *pContext)
+{
+ int nRet = 0;
+ UINT i;
+ DEBUG_ENTRY(4);
+
+ for (i = 0; i < pContext->NetMaxReceiveBuffers; ++i)
+ {
+ ULONG size1 = pContext->bUseMergedBuffers ? 4 :
pContext->nVirtioHeaderSize;
+ ULONG size2 = pContext->MaxPacketSize.nMaxFullSizeHwRx +
+ (pContext->bUseMergedBuffers ? pContext->nVirtioHeaderSize : 0);
+ pIONetDescriptor pBuffersDescriptor =
+ AllocatePairOfBuffersOnInit(pContext, size1, size2, FALSE);
+ if (!pBuffersDescriptor) break;
+
+ if (!AddRxBufferToQueue(pContext, pBuffersDescriptor))
+ {
+ VirtIONetFreeBufferDescriptor(pContext, pBuffersDescriptor);
+ break;
+ }
+
+ InsertTailList(&pContext->NetReceiveBuffers,
&pBuffersDescriptor->listEntry);
+
+ pContext->NetNofReceiveBuffers++;
+ }
+
+ pContext->NetMaxReceiveBuffers = pContext->NetNofReceiveBuffers;
+ DPrintf(0, ("[%s] MaxReceiveBuffers %d\n", __FUNCTION__,
pContext->NetMaxReceiveBuffers) );
+
+ virtqueue_kick(pContext->NetReceiveQueue);
+
+ return nRet;
+}
+
+static NDIS_STATUS FindNetQueues(PARANDIS_ADAPTER *pContext)
+{
+ struct virtqueue *queues[3];
+ unsigned nvqs = pContext->bHasControlQueue ? 3 : 2;
+ NTSTATUS status;
+
+ // We work with two or three virtqueues, 0 - receive, 1 - send, 2 - control
+ status = virtio_find_queues(
+ &pContext->IODevice,
+ nvqs,
+ queues);
+ if (!NT_SUCCESS(status)) {
+ DPrintf(0, ("[%s] virtio_find_queues failed with %x\n", __FUNCTION__,
status));
+ return NTStatusToNdisStatus(status);
+ }
+
+ pContext->NetReceiveQueue = queues[0];
+ pContext->NetSendQueue = queues[1];
+ if (pContext->bHasControlQueue) {
+ pContext->NetControlQueue = queues[2];
+ }
+
+ return NDIS_STATUS_SUCCESS;
+}
+
+// called on PASSIVE upon unsuccessful Init or upon Halt
+static void DeleteNetQueues(PARANDIS_ADAPTER *pContext)
+{
+ virtio_delete_queues(&pContext->IODevice);
+}
+
+/**********************************************************
+Initializes VirtIO buffering and related stuff:
+Allocates RX and TX queues and buffers
+Parameters:
+ context
+Return value:
+ TRUE if both queues are allocated
+***********************************************************/
+static NDIS_STATUS ParaNdis_VirtIONetInit(PARANDIS_ADAPTER *pContext)
+{
+ NDIS_STATUS status;
+ DEBUG_ENTRY(0);
+
+ pContext->ControlData.IsCached = 1;
+ pContext->ControlData.size = 512;
+
+ status = FindNetQueues(pContext);
+ if (status != NDIS_STATUS_SUCCESS) {
+ return status;
+ }
+
+ if (pContext->NetReceiveQueue && pContext->NetSendQueue)
+ {
+ PrepareTransmitBuffers(pContext);
+ PrepareReceiveBuffers(pContext);
+
+ if (pContext->NetControlQueue)
+ ParaNdis_InitialAllocatePhysicalMemory(pContext,
&pContext->ControlData);
+ if (!pContext->NetControlQueue || !pContext->ControlData.Virtual)
+ {
+ DPrintf(0, ("[%s] The Control vQueue does not work!\n",
__FUNCTION__) );
+ pContext->bHasHardwareFilters = FALSE;
+ }
+ if (pContext->nofFreeTxDescriptors &&
+ pContext->NetMaxReceiveBuffers &&
+ pContext->maxFreeHardwareBuffers)
+ {
+ pContext->sgTxGatherTable = ParaNdis_AllocateMemory(pContext,
+ pContext->maxFreeHardwareBuffers *
sizeof(pContext->sgTxGatherTable[0]));
+ if (!pContext->sgTxGatherTable)
+ {
+ DisableBothLSOPermanently(pContext, __FUNCTION__, "Can not allocate
SG table");
+ }
+ status = NDIS_STATUS_SUCCESS;
+ }
+ }
+ else
+ {
+ DeleteNetQueues(pContext);
+ status = NDIS_STATUS_RESOURCES;
+ }
+ return status;
+}
+
+static void VirtIODeviceRemoveStatus(VirtIODevice *vdev, u8 status)
+{
+ virtio_set_status(
+ vdev,
+ virtio_get_status(vdev) & ~status);
+}
+
+/**********************************************************
+Finishes initialization of context structure, calling also version dependent part
+If this procedure failed, ParaNdis_CleanupContext must be called
+Parameters:
+ context
+Return value:
+ SUCCESS or some kind of failure
+***********************************************************/
+NDIS_STATUS ParaNdis_FinishInitialization(PARANDIS_ADAPTER *pContext)
+{
+ NDIS_STATUS status = NDIS_STATUS_SUCCESS;
+ DEBUG_ENTRY(0);
+
+ NdisAllocateSpinLock(&pContext->SendLock);
+#if !defined(UNIFY_LOCKS)
+ NdisAllocateSpinLock(&pContext->ReceiveLock);
+#endif
+
+ InitializeListHead(&pContext->NetReceiveBuffers);
+ InitializeListHead(&pContext->NetReceiveBuffersWaiting);
+ InitializeListHead(&pContext->NetSendBuffersInUse);
+ InitializeListHead(&pContext->NetFreeSendBuffers);
+
+ status = ParaNdis_FinishSpecificInitialization(pContext);
+
+ if (status == NDIS_STATUS_SUCCESS)
+ {
+ status = ParaNdis_VirtIONetInit(pContext);
+ }
+
+ pContext->Limits.nReusedRxBuffers = pContext->NetMaxReceiveBuffers / 4 + 1;
+
+ if (status == NDIS_STATUS_SUCCESS)
+ {
+ JustForCheckClearInterrupt(pContext, "start 3");
+ pContext->bEnableInterruptHandlingDPC = TRUE;
+ ParaNdis_SetPowerState(pContext, NdisDeviceStateD0);
+ virtio_device_ready(&pContext->IODevice);
+ JustForCheckClearInterrupt(pContext, "start 4");
+ ParaNdis_UpdateDeviceFilters(pContext);
+ }
+ else
+ {
+ virtio_add_status(&pContext->IODevice, VIRTIO_CONFIG_S_FAILED);
+ }
+ DEBUG_EXIT_STATUS(0, status);
+ return status;
+}
+
+/**********************************************************
+Releases VirtIO related resources - queues and buffers
+Parameters:
+ context
+Return value:
+***********************************************************/
+static void VirtIONetRelease(PARANDIS_ADAPTER *pContext)
+{
+ BOOLEAN b;
+ DEBUG_ENTRY(0);
+
+ /* list NetReceiveBuffersWaiting must be free */
+ do
+ {
+ NdisAcquireSpinLock(&pContext->ReceiveLock);
+ b = !IsListEmpty(&pContext->NetReceiveBuffersWaiting);
+ NdisReleaseSpinLock(&pContext->ReceiveLock);
+ if (b)
+ {
+ DPrintf(0, ("[%s] There are waiting buffers", __FUNCTION__));
+ PrintStatistics(pContext);
+ NdisMSleep(5000000);
+ }
+ }while (b);
+
+ DeleteNetQueues(pContext);
+ virtio_device_shutdown(&pContext->IODevice);
+ pContext->bIODeviceInitialized = FALSE;
+
+ /* intentionally commented out
+ FreeDescriptorsFromList(
+ pContext,
+ &pContext->NetReceiveBuffersWaiting,
+ &pContext->ReceiveLock);
+ */
+
+ /* this can be freed, queue shut down */
+ FreeDescriptorsFromList(
+ pContext,
+ &pContext->NetReceiveBuffers,
+ &pContext->ReceiveLock);
+
+ /* this can be freed, queue shut down */
+ FreeDescriptorsFromList(
+ pContext,
+ &pContext->NetSendBuffersInUse,
+ &pContext->SendLock);
+
+ /* this can be freed, send disabled */
+ FreeDescriptorsFromList(
+ pContext,
+ &pContext->NetFreeSendBuffers,
+ &pContext->SendLock);
+
+ if (pContext->ControlData.Virtual)
+ ParaNdis_FreePhysicalMemory(pContext, &pContext->ControlData);
+
+ PrintStatistics(pContext);
+ if (pContext->sgTxGatherTable)
+ {
+ NdisFreeMemory(pContext->sgTxGatherTable, 0, 0);
+ }
+}
+
+static void PreventDPCServicing(PARANDIS_ADAPTER *pContext)
+{
+ LONG inside;;
+ pContext->bEnableInterruptHandlingDPC = FALSE;
+ do
+ {
+ inside = InterlockedIncrement(&pContext->counterDPCInside);
+ InterlockedDecrement(&pContext->counterDPCInside);
+ if (inside > 1)
+ {
+ DPrintf(0, ("[%s] waiting!", __FUNCTION__));
+ NdisMSleep(20000);
+ }
+ } while (inside > 1);
+}
+
+/**********************************************************
+Frees all the resources allocated when the context initialized,
+ calling also version-dependent part
+Parameters:
+ context
+***********************************************************/
+VOID ParaNdis_CleanupContext(PARANDIS_ADAPTER *pContext)
+{
+ UINT i;
+
+ /* disable any interrupt generation */
+ if (pContext->IODevice.addr)
+ {
+ //int nActive;
+ //nActive = virtio_read_isr_status(&pContext->IODevice);
+ /* back compat - remove the OK flag only in legacy mode */
+ VirtIODeviceRemoveStatus(&pContext->IODevice, VIRTIO_CONFIG_S_DRIVER_OK);
+ JustForCheckClearInterrupt(pContext, "exit 1");
+ //nActive += virtio_read_isr_status(&pContext->IODevice);
+ //nActive += virtio_read_isr_status(&pContext->IODevice);
+ //DPrintf(0, ("cleanup %d", nActive));
+ }
+
+ PreventDPCServicing(pContext);
+
+ /****************************************
+ ensure all the incoming packets returned,
+ free all the buffers and their descriptors
+ *****************************************/
+
+ if (pContext->bIODeviceInitialized)
+ {
+ JustForCheckClearInterrupt(pContext, "exit 2");
+ ParaNdis_ResetVirtIONetDevice(pContext);
+ JustForCheckClearInterrupt(pContext, "exit 3");
+ }
+
+ ParaNdis_SetPowerState(pContext, NdisDeviceStateD3);
+ VirtIONetRelease(pContext);
+
+ ParaNdis_FinalizeCleanup(pContext);
+
+ if (pContext->SendLock.SpinLock)
+ {
+ NdisFreeSpinLock(&pContext->SendLock);
+ }
+
+#if !defined(UNIFY_LOCKS)
+ if (pContext->ReceiveLock.SpinLock)
+ {
+ NdisFreeSpinLock(&pContext->ReceiveLock);
+ }
+#endif
+
+ /* free queue shared memory */
+ for (i = 0; i < MAX_NUM_OF_QUEUES; i++) {
+ if (pContext->SharedMemoryRanges[i].pBase != NULL) {
+ NdisMFreeSharedMemory(
+ pContext->MiniportHandle,
+ pContext->SharedMemoryRanges[i].uLength,
+ TRUE /* Cached */,
+ pContext->SharedMemoryRanges[i].pBase,
+ pContext->SharedMemoryRanges[i].BasePA);
+ pContext->SharedMemoryRanges[i].pBase = NULL;
+ }
+ }
+
+ /* unmap our port and memory IO resources */
+ for (i = 0; i < PCI_TYPE0_ADDRESSES; i++)
+ {
+ tBusResource *pRes = &pContext->AdapterResources.PciBars[i];
+ if (pRes->pBase != NULL)
+ {
+ if (pRes->bPortSpace)
+ {
+ NdisMDeregisterIoPortRange(
+ pContext->MiniportHandle,
+ pRes->BasePA.LowPart,
+ pRes->uLength,
+ pRes->pBase);
+ }
+ else
+ {
+ NdisMUnmapIoSpace(
+ pContext->MiniportHandle,
+ pRes->pBase,
+ pRes->uLength);
+ }
+ }
+ }
+}
+
+
+/**********************************************************
+System shutdown handler (shutdown, restart, bugcheck)
+Parameters:
+ context
+***********************************************************/
+VOID ParaNdis_OnShutdown(PARANDIS_ADAPTER *pContext)
+{
+ DEBUG_ENTRY(0); // this is only for kdbg :)
+ ParaNdis_ResetVirtIONetDevice(pContext);
+}
+
+/**********************************************************
+Handles hardware interrupt
+Parameters:
+ context
+ ULONG knownInterruptSources - bitmask of
+Return value:
+ TRUE, if it is our interrupt
+ sets *pRunDpc to TRUE if the DPC should be fired
+***********************************************************/
+BOOLEAN ParaNdis_OnLegacyInterrupt(
+ PARANDIS_ADAPTER *pContext,
+ OUT BOOLEAN *pRunDpc)
+{
+ ULONG status = virtio_read_isr_status(&pContext->IODevice);
+
+ if((status == 0) ||
+ (status == VIRTIO_NET_INVALID_INTERRUPT_STATUS) ||
+ (pContext->powerState != NdisDeviceStateD0))
+ {
+ *pRunDpc = FALSE;
+ return FALSE;
+ }
+
+ PARANDIS_STORE_LAST_INTERRUPT_TIMESTAMP(pContext);
+ ParaNdis_VirtIODisableIrqSynchronized(pContext, isAny);
+ InterlockedOr(&pContext->InterruptStatus, (LONG) ((status & isControl) |
isReceive | isTransmit));
+ *pRunDpc = TRUE;
+ return TRUE;
+}
+
+BOOLEAN ParaNdis_OnQueuedInterrupt(
+ PARANDIS_ADAPTER *pContext,
+ OUT BOOLEAN *pRunDpc,
+ ULONG knownInterruptSources)
+{
+ struct virtqueue* _vq = ParaNdis_GetQueueForInterrupt(pContext,
knownInterruptSources);
+
+ /* If interrupts for this queue disabled do nothing */
+ if((_vq != NULL) && !ParaNDIS_IsQueueInterruptEnabled(_vq))
+ {
+ *pRunDpc = FALSE;
+ }
+ else
+ {
+ PARANDIS_STORE_LAST_INTERRUPT_TIMESTAMP(pContext);
+ InterlockedOr(&pContext->InterruptStatus, (LONG)knownInterruptSources);
+ ParaNdis_VirtIODisableIrqSynchronized(pContext, knownInterruptSources);
+ *pRunDpc = TRUE;
+ }
+
+ return *pRunDpc;
+}
+
+
+/**********************************************************
+It is called from Rx processing routines in regular mode of operation.
+Returns received buffer back to VirtIO queue, inserting it to NetReceiveBuffers.
+If needed, signals end of RX pause operation
+
+Must be called with &pContext->ReceiveLock acquired
+
+Parameters:
+ context
+ void *pDescriptor - pIONetDescriptor to return
+***********************************************************/
+void ReuseReceiveBufferRegular(PARANDIS_ADAPTER *pContext, pIONetDescriptor
pBuffersDescriptor)
+{
+ DEBUG_ENTRY(4);
+
+ if(!pBuffersDescriptor)
+ return;
+
+ RemoveEntryList(&pBuffersDescriptor->listEntry);
+
+ if(AddRxBufferToQueue(pContext, pBuffersDescriptor))
+ {
+ InsertTailList(&pContext->NetReceiveBuffers,
&pBuffersDescriptor->listEntry);
+
+ pContext->NetNofReceiveBuffers++;
+
+ if (pContext->NetNofReceiveBuffers > pContext->NetMaxReceiveBuffers)
+ {
+ DPrintf(0, (" Error: NetNofReceiveBuffers >
NetMaxReceiveBuffers(%d>%d)",
+ pContext->NetNofReceiveBuffers, pContext->NetMaxReceiveBuffers));
+ }
+
+ if (++pContext->Counters.nReusedRxBuffers >=
pContext->Limits.nReusedRxBuffers)
+ {
+ pContext->Counters.nReusedRxBuffers = 0;
+ virtqueue_kick_always(pContext->NetReceiveQueue);
+ }
+
+ if (IsListEmpty(&pContext->NetReceiveBuffersWaiting))
+ {
+ if (pContext->ReceiveState == srsPausing ||
pContext->ReceivePauseCompletionProc)
+ {
+ ONPAUSECOMPLETEPROC callback = pContext->ReceivePauseCompletionProc;
+ pContext->ReceiveState = srsDisabled;
+ pContext->ReceivePauseCompletionProc = NULL;
+ ParaNdis_DebugHistory(pContext, hopInternalReceivePause, NULL, 0, 0, 0);
+ if (callback) callback(pContext);
+ }
+ }
+ }
+ else
+ {
+ DPrintf(0, ("FAILED TO REUSE THE BUFFER!!!!"));
+ VirtIONetFreeBufferDescriptor(pContext, pBuffersDescriptor);
+ pContext->NetMaxReceiveBuffers--;
+ }
+}
+
+/**********************************************************
+It is called from Rx processing routines between power off and power on in non-paused
mode (Win8).
+Returns received buffer to NetReceiveBuffers.
+All the buffers will be placed into Virtio queue during power-on procedure
+
+Must be called with &pContext->ReceiveLock acquired
+
+Parameters:
+ context
+ void *pDescriptor - pIONetDescriptor to return
+***********************************************************/
+static void ReuseReceiveBufferPowerOff(PARANDIS_ADAPTER *pContext, pIONetDescriptor
pBuffersDescriptor)
+{
+ RemoveEntryList(&pBuffersDescriptor->listEntry);
+ InsertTailList(&pContext->NetReceiveBuffers,
&pBuffersDescriptor->listEntry);
+}
+
+/**********************************************************
+It is called from Tx processing routines
+Gets all the finished buffer from VirtIO TX path and
+returns them to NetFreeSendBuffers
+
+Must be called with &pContext->SendLock acquired
+
+Parameters:
+ context
+Return value:
+ (for reference) number of TX buffers returned
+***********************************************************/
+UINT ParaNdis_VirtIONetReleaseTransmitBuffers(
+ PARANDIS_ADAPTER *pContext)
+{
+ UINT len, i = 0;
+ pIONetDescriptor pBufferDescriptor;
+
+ DEBUG_ENTRY(4);
+
+ while(NULL != (pBufferDescriptor = virtqueue_get_buf(pContext->NetSendQueue,
&len)))
+ {
+ RemoveEntryList(&pBufferDescriptor->listEntry);
+ pContext->nofFreeTxDescriptors++;
+ if (!pBufferDescriptor->nofUsedBuffers)
+ {
+ DPrintf(0, ("[%s] ERROR: nofUsedBuffers not set!", __FUNCTION__));
+ }
+ pContext->nofFreeHardwareBuffers += pBufferDescriptor->nofUsedBuffers;
+ ParaNdis_OnTransmitBufferReleased(pContext, pBufferDescriptor);
+ InsertTailList(&pContext->NetFreeSendBuffers,
&pBufferDescriptor->listEntry);
+ DPrintf(3, ("[%s] Free Tx: desc %d, buff %d", __FUNCTION__,
pContext->nofFreeTxDescriptors, pContext->nofFreeHardwareBuffers));
+ pBufferDescriptor->nofUsedBuffers = 0;
+ ++i;
+ }
+ if (i)
+ {
+ NdisGetCurrentSystemTime(&pContext->LastTxCompletionTimeStamp);
+ pContext->bDoKickOnNoBuffer = TRUE;
+ pContext->nDetectedStoppedTx = 0;
+ }
+ DEBUG_EXIT_STATUS((i ? 3 : 5), i);
+ return i;
+}
+
+static ULONG FORCEINLINE QueryTcpHeaderOffset(PVOID packetData, ULONG ipHeaderOffset,
ULONG ipPacketLength)
+{
+ ULONG res;
+ tTcpIpPacketParsingResult ppr = ParaNdis_ReviewIPPacket(
+ (PUCHAR)packetData + ipHeaderOffset,
+ ipPacketLength,
+ __FUNCTION__);
+ if (ppr.xxpStatus == ppresXxpKnown)
+ {
+ res = ipHeaderOffset + ppr.ipHeaderSize;
+ }
+ else
+ {
+ DPrintf(0, ("[%s] ERROR: NOT a TCP or UDP packet - expected troubles!",
__FUNCTION__));
+ res = 0;
+ }
+ return res;
+}
+
+
+/*********************************************************
+Called with from ProcessTx routine with TxLock held
+Uses pContext->sgTxGatherTable
+***********************************************************/
+tCopyPacketResult ParaNdis_DoSubmitPacket(PARANDIS_ADAPTER *pContext,
tTxOperationParameters *Params)
+{
+ tCopyPacketResult result;
+ tMapperResult mapResult = {0,0,0};
+ // populating priority tag or LSO MAY require additional SG element
+ UINT nRequiredBuffers;
+ BOOLEAN bUseCopy = FALSE;
+ struct VirtIOBufferDescriptor *sg = pContext->sgTxGatherTable;
+
+ nRequiredBuffers = Params->nofSGFragments + 1 + ((Params->flags &
(pcrPriorityTag | pcrLSO)) ? 1 : 0);
+
+ result.size = 0;
+ result.error = cpeOK;
+ if (!pContext->bUseScatterGather || // only copy available
+ Params->nofSGFragments == 0 || // theoretical case
+ !sg || // only copy available
+ ((~Params->flags & pcrLSO) && nRequiredBuffers >
pContext->maxFreeHardwareBuffers) // to many fragments and normal size of packet
+ )
+ {
+ nRequiredBuffers = 2;
+ bUseCopy = TRUE;
+ }
+ else if (pContext->bUseIndirect && !(Params->flags &
pcrNoIndirect))
+ {
+ nRequiredBuffers = 1;
+ }
+
+ // I do not think this will help, but at least we can try freeing some buffers right
now
+ if (pContext->nofFreeHardwareBuffers < nRequiredBuffers ||
!pContext->nofFreeTxDescriptors)
+ {
+ ParaNdis_VirtIONetReleaseTransmitBuffers(pContext);
+ }
+
+ if (nRequiredBuffers > pContext->maxFreeHardwareBuffers)
+ {
+ // LSO and too many buffers, impossible to send
+ result.error = cpeTooLarge;
+ DPrintf(0, ("[%s] ERROR: too many fragments(%d required, %d
max.avail)!", __FUNCTION__,
+ nRequiredBuffers, pContext->maxFreeHardwareBuffers));
+ }
+ else if (pContext->nofFreeHardwareBuffers < nRequiredBuffers ||
!pContext->nofFreeTxDescriptors)
+ {
+ virtqueue_enable_cb_delayed(pContext->NetSendQueue);
+ result.error = cpeNoBuffer;
+ }
+ else if (Params->offloadMss && bUseCopy)
+ {
+ result.error = cpeInternalError;
+ DPrintf(0, ("[%s] ERROR: expecting SG for TSO! (%d buffers, %d bytes)",
__FUNCTION__,
+ Params->nofSGFragments, Params->ulDataSize));
+ }
+ else if (bUseCopy)
+ {
+ result = ParaNdis_DoCopyPacketData(pContext, Params);
+ }
+ else
+ {
+ UINT nMappedBuffers;
+ ULONGLONG paOfIndirectArea = 0;
+ PVOID vaOfIndirectArea = NULL;
+ pIONetDescriptor pBuffersDescriptor =
(pIONetDescriptor)RemoveHeadList(&pContext->NetFreeSendBuffers);
+ pContext->nofFreeTxDescriptors--;
+ NdisZeroMemory(pBuffersDescriptor->HeaderInfo.Virtual,
pBuffersDescriptor->HeaderInfo.size);
+ sg[0].physAddr = pBuffersDescriptor->HeaderInfo.Physical;
+ sg[0].length = pBuffersDescriptor->HeaderInfo.size;
+ ParaNdis_PacketMapper(
+ pContext,
+ Params->packet,
+ Params->ReferenceValue,
+ sg + 1,
+ pBuffersDescriptor,
+ &mapResult);
+ nMappedBuffers = mapResult.usBuffersMapped;
+ if (nMappedBuffers)
+ {
+ nMappedBuffers++;
+ if (pContext->bUseIndirect && !(Params->flags &
pcrNoIndirect))
+ {
+ ULONG space1 = (mapResult.usBufferSpaceUsed + 7) & ~7;
+ ULONG space2 = nMappedBuffers * SIZE_OF_SINGLE_INDIRECT_DESC;
+ if (pBuffersDescriptor->DataInfo.size >= (space1 + space2))
+ {
+ vaOfIndirectArea =
RtlOffsetToPointer(pBuffersDescriptor->DataInfo.Virtual, space1);
+ paOfIndirectArea = pBuffersDescriptor->DataInfo.Physical.QuadPart
+ space1;
+ pContext->extraStatistics.framesIndirect++;
+ }
+ else if (nMappedBuffers <= pContext->nofFreeHardwareBuffers)
+ {
+ // send as is, no indirect
+ }
+ else
+ {
+ result.error = cpeNoIndirect;
+ DPrintf(0, ("[%s] Unexpected ERROR of placement!",
__FUNCTION__));
+ }
+ }
+ if (result.error == cpeOK)
+ {
+ if (Params->flags & (pcrTcpChecksum | pcrUdpChecksum))
+ {
+ unsigned short addPriorityLen = (Params->flags &
pcrPriorityTag) ? ETH_PRIORITY_HEADER_SIZE : 0;
+ if (pContext->bDoHardwareChecksum)
+ {
+ virtio_net_hdr_basic *pheader =
pBuffersDescriptor->HeaderInfo.Virtual;
+ pheader->flags = VIRTIO_NET_HDR_F_NEEDS_CSUM;
+ if (!Params->tcpHeaderOffset)
+ {
+ Params->tcpHeaderOffset = QueryTcpHeaderOffset(
+ pBuffersDescriptor->DataInfo.Virtual,
+ pContext->Offload.ipHeaderOffset + addPriorityLen,
+ mapResult.usBufferSpaceUsed -
pContext->Offload.ipHeaderOffset - addPriorityLen);
+ }
+ else
+ {
+ Params->tcpHeaderOffset += addPriorityLen;
+ }
+ pheader->csum_start = (USHORT)Params->tcpHeaderOffset;
+ pheader->csum_offset = (Params->flags & pcrTcpChecksum)
? TCP_CHECKSUM_OFFSET : UDP_CHECKSUM_OFFSET;
+ }
+ else
+ {
+ // emulation mode - it is slow and intended only for test of
flows
+ // and debugging of WLK test cases
+ PVOID pCopy = ParaNdis_AllocateMemory(pContext,
Params->ulDataSize);
+ if (pCopy)
+ {
+ tTcpIpPacketParsingResult ppr;
+ // duplicate entire packet
+ ParaNdis_PacketCopier(Params->packet, pCopy,
Params->ulDataSize, Params->ReferenceValue, FALSE);
+ // calculate complete TCP/UDP checksum
+ ppr = ParaNdis_CheckSumVerify(
+ RtlOffsetToPointer(pCopy,
pContext->Offload.ipHeaderOffset + addPriorityLen),
+ Params->ulDataSize -
pContext->Offload.ipHeaderOffset - addPriorityLen,
+ pcrAnyChecksum | pcrFixXxpChecksum,
+ __FUNCTION__);
+ // data portion in aside buffer contains complete IP+TCP
header
+ // rewrite copy of original buffer by one new with calculated
data
+ NdisMoveMemory(
+ pBuffersDescriptor->DataInfo.Virtual,
+ pCopy,
+ mapResult.usBufferSpaceUsed);
+ NdisFreeMemory(pCopy, 0, 0);
+ }
+ }
+ }
+
+ if (0 <= virtqueue_add_buf(
+ pContext->NetSendQueue,
+ sg,
+ nMappedBuffers,
+ 0,
+ pBuffersDescriptor,
+ vaOfIndirectArea,
+ paOfIndirectArea))
+ {
+ pBuffersDescriptor->nofUsedBuffers = nMappedBuffers;
+ pContext->nofFreeHardwareBuffers -= nMappedBuffers;
+ if (pContext->minFreeHardwareBuffers >
pContext->nofFreeHardwareBuffers)
+ pContext->minFreeHardwareBuffers =
pContext->nofFreeHardwareBuffers;
+ pBuffersDescriptor->ReferenceValue = Params->ReferenceValue;
+ result.size = Params->ulDataSize;
+ DPrintf(2, ("[%s] Submitted %d buffers (%d bytes), avail %d
desc, %d bufs",
+ __FUNCTION__, nMappedBuffers, result.size,
+ pContext->nofFreeTxDescriptors,
pContext->nofFreeHardwareBuffers
+ ));
+ }
+ else
+ {
+ result.error = cpeInternalError;
+ DPrintf(0, ("[%s] Unexpected ERROR adding buffer to TX
engine!..", __FUNCTION__));
+ }
+ }
+ }
+ else
+ {
+ DPrintf(0, ("[%s] Unexpected ERROR: packet not mapped!",
__FUNCTION__));
+ result.error = cpeInternalError;
+ }
+
+ if (result.error == cpeOK)
+ {
+ UCHAR ethernetHeader[sizeof(ETH_HEADER)];
+ eInspectedPacketType packetType;
+ /* get the ethernet header for review */
+ ParaNdis_PacketCopier(Params->packet, ethernetHeader,
sizeof(ethernetHeader), Params->ReferenceValue, TRUE);
+ packetType = QueryPacketType(ethernetHeader);
+ DebugDumpPacket("sending", ethernetHeader, 3);
+ InsertTailList(&pContext->NetSendBuffersInUse,
&pBuffersDescriptor->listEntry);
+ pContext->Statistics.ifHCOutOctets += result.size;
+ switch (packetType)
+ {
+ case iptBroadcast:
+ pContext->Statistics.ifHCOutBroadcastOctets += result.size;
+ pContext->Statistics.ifHCOutBroadcastPkts++;
+ break;
+ case iptMulticast:
+ pContext->Statistics.ifHCOutMulticastOctets += result.size;
+ pContext->Statistics.ifHCOutMulticastPkts++;
+ break;
+ default:
+ pContext->Statistics.ifHCOutUcastOctets += result.size;
+ pContext->Statistics.ifHCOutUcastPkts++;
+ break;
+ }
+
+ if (Params->flags & pcrLSO)
+ pContext->extraStatistics.framesLSO++;
+ }
+ else
+ {
+ pContext->nofFreeTxDescriptors++;
+ InsertHeadList(&pContext->NetFreeSendBuffers,
&pBuffersDescriptor->listEntry);
+ }
+ }
+ if (result.error == cpeNoBuffer && pContext->bDoKickOnNoBuffer)
+ {
+ virtqueue_kick_always(pContext->NetSendQueue);
+ pContext->bDoKickOnNoBuffer = FALSE;
+ }
+ if (result.error == cpeOK)
+ {
+ if (Params->flags & (pcrTcpChecksum | pcrUdpChecksum))
+ pContext->extraStatistics.framesCSOffload++;
+ }
+ return result;
+}
+
+
+/**********************************************************
+It is called from Tx processing routines
+Prepares the VirtIO buffer and copies to it the data from provided packet
+
+Must be called with &pContext->SendLock acquired
+
+Parameters:
+ context
+ tPacketType packet specific type is NDIS dependent
+ tCopyPacketDataFunction PacketCopier procedure for NDIS-specific type of packet
+Return value:
+ (for reference) number of TX buffers returned
+***********************************************************/
+tCopyPacketResult ParaNdis_DoCopyPacketData(
+ PARANDIS_ADAPTER *pContext,
+ tTxOperationParameters *pParams)
+{
+ tCopyPacketResult result;
+ tCopyPacketResult CopierResult;
+ struct VirtIOBufferDescriptor sg[2];
+ pIONetDescriptor pBuffersDescriptor = NULL;
+ ULONG flags = pParams->flags;
+ UINT nRequiredHardwareBuffers = 2;
+ result.size = 0;
+ result.error = cpeOK;
+ if (pContext->nofFreeHardwareBuffers < nRequiredHardwareBuffers ||
+ IsListEmpty(&pContext->NetFreeSendBuffers))
+ {
+ result.error = cpeNoBuffer;
+ }
+ if(result.error == cpeOK)
+ {
+ pBuffersDescriptor =
(pIONetDescriptor)RemoveHeadList(&pContext->NetFreeSendBuffers);
+ NdisZeroMemory(pBuffersDescriptor->HeaderInfo.Virtual,
pBuffersDescriptor->HeaderInfo.size);
+ sg[0].physAddr = pBuffersDescriptor->HeaderInfo.Physical;
+ sg[0].length = pBuffersDescriptor->HeaderInfo.size;
+ sg[1].physAddr = pBuffersDescriptor->DataInfo.Physical;
+ CopierResult = ParaNdis_PacketCopier(
+ pParams->packet,
+ pBuffersDescriptor->DataInfo.Virtual,
+ pBuffersDescriptor->DataInfo.size,
+ pParams->ReferenceValue,
+ FALSE);
+ sg[1].length = result.size = CopierResult.size;
+ // did NDIS ask us to compute CS?
+ if ((flags & (pcrTcpChecksum | pcrUdpChecksum | pcrIpChecksum)) != 0)
+ {
+ // we asked
+ unsigned short addPriorityLen = (pParams->flags & pcrPriorityTag) ?
ETH_PRIORITY_HEADER_SIZE : 0;
+ PVOID ipPacket = RtlOffsetToPointer(
+ pBuffersDescriptor->DataInfo.Virtual,
pContext->Offload.ipHeaderOffset + addPriorityLen);
+ ULONG ipPacketLength = CopierResult.size -
pContext->Offload.ipHeaderOffset - addPriorityLen;
+ if (!pParams->tcpHeaderOffset &&
+ (flags & (pcrTcpChecksum | pcrUdpChecksum)) )
+ {
+ pParams->tcpHeaderOffset = QueryTcpHeaderOffset(
+ pBuffersDescriptor->DataInfo.Virtual,
+ pContext->Offload.ipHeaderOffset + addPriorityLen,
+ ipPacketLength);
+ }
+ else
+ {
+ pParams->tcpHeaderOffset += addPriorityLen;
+ }
+
+ if (pContext->bDoHardwareChecksum)
+ {
+ if (flags & (pcrTcpChecksum | pcrUdpChecksum))
+ {
+ // hardware offload
+ virtio_net_hdr_basic *pvnh = (virtio_net_hdr_basic
*)pBuffersDescriptor->HeaderInfo.Virtual;
+ pvnh->csum_start = (USHORT)pParams->tcpHeaderOffset;
+ pvnh->csum_offset = (flags & pcrTcpChecksum) ?
TCP_CHECKSUM_OFFSET : UDP_CHECKSUM_OFFSET;
+ pvnh->flags |= VIRTIO_NET_HDR_F_NEEDS_CSUM;
+ }
+ if (flags & (pcrIpChecksum))
+ {
+ ParaNdis_CheckSumVerify(
+ ipPacket,
+ ipPacketLength,
+ pcrIpChecksum | pcrFixIPChecksum,
+ __FUNCTION__);
+ }
+ }
+ else if (CopierResult.size > pContext->Offload.ipHeaderOffset)
+ {
+ ULONG csFlags = 0;
+ if (flags & pcrIpChecksum) csFlags |= pcrIpChecksum |
pcrFixIPChecksum;
+ if (flags & (pcrTcpChecksum | pcrUdpChecksum)) csFlags |=
pcrTcpChecksum | pcrUdpChecksum| pcrFixXxpChecksum;
+ // software offload
+ ParaNdis_CheckSumVerify(
+ ipPacket,
+ ipPacketLength,
+ csFlags,
+ __FUNCTION__);
+ }
+ else
+ {
+ DPrintf(0, ("[%s] ERROR: Invalid buffer size for offload!",
__FUNCTION__));
+ result.size = 0;
+ result.error = cpeInternalError;
+ }
+ }
+ pContext->nofFreeTxDescriptors--;
+ if (result.size)
+ {
+ eInspectedPacketType packetType;
+ packetType = QueryPacketType(pBuffersDescriptor->DataInfo.Virtual);
+ DebugDumpPacket("sending", pBuffersDescriptor->DataInfo.Virtual,
3);
+
+ pBuffersDescriptor->nofUsedBuffers = nRequiredHardwareBuffers;
+ pContext->nofFreeHardwareBuffers -= nRequiredHardwareBuffers;
+ if (pContext->minFreeHardwareBuffers >
pContext->nofFreeHardwareBuffers)
+ pContext->minFreeHardwareBuffers =
pContext->nofFreeHardwareBuffers;
+ if (0 > virtqueue_add_buf(
+ pContext->NetSendQueue,
+ sg,
+ 2,
+ 0,
+ pBuffersDescriptor,
+ NULL,
+ 0
+ ))
+ {
+ pBuffersDescriptor->nofUsedBuffers = 0;
+ pContext->nofFreeHardwareBuffers += nRequiredHardwareBuffers;
+ result.error = cpeInternalError;
+ result.size = 0;
+ DPrintf(0, ("[%s] Unexpected ERROR adding buffer to TX
engine!..", __FUNCTION__));
+ }
+ else
+ {
+ DPrintf(2, ("[%s] Submitted %d buffers (%d bytes), avail %d desc, %d
bufs",
+ __FUNCTION__, nRequiredHardwareBuffers, result.size,
+ pContext->nofFreeTxDescriptors,
pContext->nofFreeHardwareBuffers
+ ));
+ }
+ if (result.error != cpeOK)
+ {
+ InsertTailList(&pContext->NetFreeSendBuffers,
&pBuffersDescriptor->listEntry);
+ pContext->nofFreeTxDescriptors++;
+ }
+ else
+ {
+ ULONG reportedSize = pParams->ulDataSize;
+ pBuffersDescriptor->ReferenceValue = pParams->ReferenceValue;
+ InsertTailList(&pContext->NetSendBuffersInUse,
&pBuffersDescriptor->listEntry);
+ pContext->Statistics.ifHCOutOctets += reportedSize;
+ switch (packetType)
+ {
+ case iptBroadcast:
+ pContext->Statistics.ifHCOutBroadcastOctets += reportedSize;
+ pContext->Statistics.ifHCOutBroadcastPkts++;
+ break;
+ case iptMulticast:
+ pContext->Statistics.ifHCOutMulticastOctets += reportedSize;
+ pContext->Statistics.ifHCOutMulticastPkts++;
+ break;
+ default:
+ pContext->Statistics.ifHCOutUcastOctets += reportedSize;
+ pContext->Statistics.ifHCOutUcastPkts++;
+ break;
+ }
+ }
+ }
+ else
+ {
+ DPrintf(0, ("[%s] Unexpected ERROR in copying packet data!
Continue...", __FUNCTION__));
+ InsertTailList(&pContext->NetFreeSendBuffers,
&pBuffersDescriptor->listEntry);
+ pContext->nofFreeTxDescriptors++;
+ // the buffer is not copied and the callback will not be called
+ result.error = cpeInternalError;
+ }
+ }
+
+ return result;
+}
+
+static ULONG ShallPassPacket(PARANDIS_ADAPTER *pContext, PVOID address, UINT len,
eInspectedPacketType *pType)
+{
+ ULONG b;
+ if (len <= sizeof(ETH_HEADER)) return FALSE;
+ if (len > pContext->MaxPacketSize.nMaxFullSizeHwRx) return FALSE;
+ if (len > pContext->MaxPacketSize.nMaxFullSizeOS &&
!ETH_HAS_PRIO_HEADER(address)) return FALSE;
+ *pType = QueryPacketType(address);
+ if (pContext->PacketFilter & NDIS_PACKET_TYPE_PROMISCUOUS) return TRUE;
+
+ switch(*pType)
+ {
+ case iptBroadcast:
+ b = pContext->PacketFilter & NDIS_PACKET_TYPE_BROADCAST;
+ break;
+ case iptMulticast:
+ b = pContext->PacketFilter & NDIS_PACKET_TYPE_ALL_MULTICAST;
+ if (!b && (pContext->PacketFilter &
NDIS_PACKET_TYPE_MULTICAST))
+ {
+ UINT i, n = pContext->MulticastData.nofMulticastEntries *
ETH_LENGTH_OF_ADDRESS;
+ b = 1;
+ for (i = 0; b && i < n; i += ETH_LENGTH_OF_ADDRESS)
+ {
+ ETH_COMPARE_NETWORK_ADDRESSES((PUCHAR)address,
&pContext->MulticastData.MulticastList[i], &b)
+ }
+ b = !b;
+ }
+ break;
+ default:
+ ETH_COMPARE_NETWORK_ADDRESSES((PUCHAR)address,
pContext->CurrentMacAddress, &b);
+ b = !b && (pContext->PacketFilter &
NDIS_PACKET_TYPE_DIRECTED);
+ break;
+ }
+ if (!b)
+ {
+ pContext->extraStatistics.framesFilteredOut++;
+ }
+ return b;
+}
+
+void
+ParaNdis_PadPacketReceived(PVOID pDataBuffer, PULONG pLength)
+{
+ // Ethernet standard declares minimal possible packet size
+ // Packets smaller than that must be padded before transfer
+ // Ethernet HW pads packets on transmit, however in our case
+ // some packets do not travel over Ethernet but being routed
+ // guest-to-guest by virtual switch.
+ // In this case padding is not performed and we may
+ // receive packet smaller than minimal allowed size. This is not
+ // a problem for real life scenarios however WHQL/HCK contains
+ // tests that check padding of received packets.
+ // To make these tests happy we have to pad small packets on receive
+
+ //NOTE: This function assumes that VLAN header has been already stripped out
+
+ if(*pLength < ETH_MIN_PACKET_SIZE)
+ {
+ RtlZeroMemory(RtlOffsetToPointer(pDataBuffer, *pLength), ETH_MIN_PACKET_SIZE -
*pLength);
+ *pLength = ETH_MIN_PACKET_SIZE;
+ }
+}
+
+/**********************************************************
+Manages RX path, calling NDIS-specific procedure for packet indication
+Parameters:
+ context
+***********************************************************/
+static UINT ParaNdis_ProcessRxPath(PARANDIS_ADAPTER *pContext, ULONG
ulMaxPacketsToIndicate)
+{
+ pIONetDescriptor pBuffersDescriptor;
+ UINT len, headerSize = pContext->nVirtioHeaderSize;
+ eInspectedPacketType packetType = iptInvalid;
+ UINT nReceived = 0, nRetrieved = 0, nReported = 0;
+ tPacketIndicationType *pBatchOfPackets;
+ UINT maxPacketsInBatch = pContext->NetMaxReceiveBuffers;
+ pBatchOfPackets = pContext->bBatchReceive ?
+ ParaNdis_AllocateMemory(pContext, maxPacketsInBatch *
sizeof(tPacketIndicationType)) : NULL;
+ NdisAcquireSpinLock(&pContext->ReceiveLock);
+ while ((nReported < ulMaxPacketsToIndicate) && NULL != (pBuffersDescriptor
= virtqueue_get_buf(pContext->NetReceiveQueue, &len)))
+ {
+ PVOID pDataBuffer = RtlOffsetToPointer(pBuffersDescriptor->DataInfo.Virtual,
pContext->bUseMergedBuffers ? pContext->nVirtioHeaderSize : 0);
+ RemoveEntryList(&pBuffersDescriptor->listEntry);
+ InsertTailList(&pContext->NetReceiveBuffersWaiting,
&pBuffersDescriptor->listEntry);
+ pContext->NetNofReceiveBuffers--;
+ nRetrieved++;
+ DPrintf(2, ("[%s] retrieved header+%d b.", __FUNCTION__, len -
headerSize));
+ DebugDumpPacket("receive", pDataBuffer, 3);
+
+ if( !pContext->bSurprizeRemoved &&
+ ShallPassPacket(pContext, pDataBuffer, len - headerSize, &packetType)
&&
+ pContext->ReceiveState == srsEnabled &&
+ pContext->bConnected)
+ {
+ BOOLEAN b = FALSE;
+ ULONG length = len - headerSize;
+ if (!pBatchOfPackets)
+ {
+ NdisReleaseSpinLock(&pContext->ReceiveLock);
+ b = NULL != ParaNdis_IndicateReceivedPacket(
+ pContext,
+ pDataBuffer,
+ &length,
+ FALSE,
+ pBuffersDescriptor);
+ NdisAcquireSpinLock(&pContext->ReceiveLock);
+ }
+ else
+ {
+ tPacketIndicationType packet;
+ packet = ParaNdis_IndicateReceivedPacket(
+ pContext,
+ pDataBuffer,
+ &length,
+ TRUE,
+ pBuffersDescriptor);
+ b = packet != NULL;
+ if (b) pBatchOfPackets[nReceived] = packet;
+ }
+ if (!b)
+ {
+ pContext->ReuseBufferProc(pContext, pBuffersDescriptor);
+ //only possible reason for that is unexpected Vlan tag
+ //shall I count it as error?
+ pContext->Statistics.ifInErrors++;
+ pContext->Statistics.ifInDiscards++;
+ }
+ else
+ {
+ nReceived++;
+ nReported++;
+ pContext->Statistics.ifHCInOctets += length;
+ switch(packetType)
+ {
+ case iptBroadcast:
+ pContext->Statistics.ifHCInBroadcastPkts++;
+ pContext->Statistics.ifHCInBroadcastOctets += length;
+ break;
+ case iptMulticast:
+ pContext->Statistics.ifHCInMulticastPkts++;
+ pContext->Statistics.ifHCInMulticastOctets += length;
+ break;
+ default:
+ pContext->Statistics.ifHCInUcastPkts++;
+ pContext->Statistics.ifHCInUcastOctets += length;
+ break;
+ }
+ if (pBatchOfPackets && nReceived == maxPacketsInBatch)
+ {
+ DPrintf(1, ("[%s] received %d buffers of max %d",
__FUNCTION__, nReceived, ulMaxPacketsToIndicate));
+ NdisReleaseSpinLock(&pContext->ReceiveLock);
+ ParaNdis_IndicateReceivedBatch(pContext, pBatchOfPackets,
nReceived);
+ NdisAcquireSpinLock(&pContext->ReceiveLock);
+ nReceived = 0;
+ }
+ }
+ }
+ else
+ {
+ // reuse packet, there is no data or the RX is suppressed
+ pContext->ReuseBufferProc(pContext, pBuffersDescriptor);
+ }
+ }
+ ParaNdis_DebugHistory(pContext, hopReceiveStat, NULL, nRetrieved, nReported,
pContext->NetNofReceiveBuffers);
+ NdisReleaseSpinLock(&pContext->ReceiveLock);
+ if (nReceived && pBatchOfPackets)
+ {
+ DPrintf(1, ("[%s]%d: received %d buffers of max %d", __FUNCTION__,
KeGetCurrentProcessorNumber(), nReceived, ulMaxPacketsToIndicate));
+ ParaNdis_IndicateReceivedBatch(pContext, pBatchOfPackets, nReceived);
+ }
+ if (pBatchOfPackets) NdisFreeMemory(pBatchOfPackets, 0, 0);
+ return nReported;
+}
+
+void ParaNdis_ReportLinkStatus(PARANDIS_ADAPTER *pContext, BOOLEAN bForce)
+{
+ BOOLEAN bConnected = TRUE;
+ if (pContext->bLinkDetectSupported)
+ {
+ USHORT linkStatus = 0;
+ USHORT offset = sizeof(pContext->CurrentMacAddress);
+ // link changed
+ virtio_get_config(&pContext->IODevice, offset, &linkStatus,
sizeof(linkStatus));
+ bConnected = (linkStatus & VIRTIO_NET_S_LINK_UP) != 0;
+ }
+ ParaNdis_IndicateConnect(pContext, bConnected, bForce);
+}
+
+static BOOLEAN RestartQueueSynchronously(tSynchronizedContext *SyncContext)
+{
+ struct virtqueue * _vq = (struct virtqueue *) SyncContext->Parameter;
+ bool res = true;
+ if (!virtqueue_enable_cb(_vq))
+ {
+ virtqueue_disable_cb(_vq);
+ res = false;
+ }
+
+ ParaNdis_DebugHistory(SyncContext->pContext, hopDPC,
(PVOID)SyncContext->Parameter, 0x20, res, 0);
+ return !res;
+}
+/**********************************************************
+DPC implementation, common for both NDIS
+Parameters:
+ context
+***********************************************************/
+ULONG ParaNdis_DPCWorkBody(PARANDIS_ADAPTER *pContext, ULONG ulMaxPacketsToIndicate)
+{
+ ULONG stillRequiresProcessing = 0;
+ ULONG interruptSources;
+ UINT uIndicatedRXPackets = 0;
+ UINT numOfPacketsToIndicate = min(ulMaxPacketsToIndicate,
pContext->uNumberOfHandledRXPacketsInDPC);
+
+ DEBUG_ENTRY(5);
+ if (pContext->bEnableInterruptHandlingDPC)
+ {
+ InterlockedIncrement(&pContext->counterDPCInside);
+ if (pContext->bEnableInterruptHandlingDPC)
+ {
+ BOOLEAN bDoKick = FALSE;
+
+ InterlockedExchange(&pContext->bDPCInactive, 0);
+ interruptSources = InterlockedExchange(&pContext->InterruptStatus,
0);
+ ParaNdis_DebugHistory(pContext, hopDPC, (PVOID)1, interruptSources, 0, 0);
+ if ((interruptSources & isControl) &&
pContext->bLinkDetectSupported)
+ {
+ ParaNdis_ReportLinkStatus(pContext, FALSE);
+ }
+ if (interruptSources & isTransmit)
+ {
+ bDoKick = ParaNdis_ProcessTx(pContext, TRUE, TRUE);
+ }
+ if (interruptSources & isReceive)
+ {
+ int nRestartResult = 0;
+
+ do
+ {
+ LONG rxActive =
InterlockedIncrement(&pContext->dpcReceiveActive);
+ if (rxActive == 1)
+ {
+ uIndicatedRXPackets += ParaNdis_ProcessRxPath(pContext,
numOfPacketsToIndicate - uIndicatedRXPackets);
+ InterlockedDecrement(&pContext->dpcReceiveActive);
+ NdisAcquireSpinLock(&pContext->ReceiveLock);
+ nRestartResult = ParaNdis_SynchronizeWithInterrupt(
+ pContext, pContext->ulRxMessage,
RestartQueueSynchronously, pContext->NetReceiveQueue);
+ ParaNdis_DebugHistory(pContext, hopDPC, (PVOID)3, nRestartResult,
0, 0);
+ NdisReleaseSpinLock(&pContext->ReceiveLock);
+ DPrintf(nRestartResult ? 2 : 6, ("[%s] queue
restarted%s", __FUNCTION__, nRestartResult ? "(Rerun)" :
"(Done)"));
+
+ if (uIndicatedRXPackets < numOfPacketsToIndicate)
+ {
+
+ }
+ else if (uIndicatedRXPackets == numOfPacketsToIndicate)
+ {
+ DPrintf(1, ("[%s] Breaking Rx loop after %d
indications", __FUNCTION__, uIndicatedRXPackets));
+ ParaNdis_DebugHistory(pContext, hopDPC, (PVOID)4,
nRestartResult, 0, 0);
+ break;
+ }
+ else
+ {
+ DPrintf(0, ("[%s] Glitch found: %d allowed, %d
indicated", __FUNCTION__, numOfPacketsToIndicate, uIndicatedRXPackets));
+ ParaNdis_DebugHistory(pContext, hopDPC, (PVOID)6,
nRestartResult, 0, 0);
+ }
+ }
+ else
+ {
+ InterlockedDecrement(&pContext->dpcReceiveActive);
+ if (!nRestartResult)
+ {
+ NdisAcquireSpinLock(&pContext->ReceiveLock);
+ nRestartResult = ParaNdis_SynchronizeWithInterrupt(
+ pContext, pContext->ulRxMessage,
RestartQueueSynchronously, pContext->NetReceiveQueue);
+ ParaNdis_DebugHistory(pContext, hopDPC, (PVOID)5,
nRestartResult, 0, 0);
+ NdisReleaseSpinLock(&pContext->ReceiveLock);
+ }
+ DPrintf(1, ("[%s] Skip Rx processing no.%d",
__FUNCTION__, rxActive));
+ break;
+ }
+ } while (nRestartResult);
+
+ if (nRestartResult) stillRequiresProcessing |= isReceive;
+ }
+
+ if (interruptSources & isTransmit)
+ {
+ NdisAcquireSpinLock(&pContext->SendLock);
+ if (ParaNdis_SynchronizeWithInterrupt(pContext, pContext->ulTxMessage,
RestartQueueSynchronously, pContext->NetSendQueue))
+ stillRequiresProcessing |= isTransmit;
+ if(bDoKick)
+ {
+#ifdef PARANDIS_TEST_TX_KICK_ALWAYS
+ virtqueue_kick_always(pContext->NetSendQueue);
+#else
+ virtqueue_kick(pContext->NetSendQueue);
+#endif
+ }
+ NdisReleaseSpinLock(&pContext->SendLock);
+ }
+ }
+ InterlockedDecrement(&pContext->counterDPCInside);
+ ParaNdis_DebugHistory(pContext, hopDPC, NULL, stillRequiresProcessing,
pContext->nofFreeHardwareBuffers, pContext->nofFreeTxDescriptors);
+ }
+ return stillRequiresProcessing;
+}
+
+/**********************************************************
+Periodically called procedure, checking dpc activity
+If DPC are not running, it does exactly the same that the DPC
+Parameters:
+ context
+***********************************************************/
+static BOOLEAN CheckRunningDpc(PARANDIS_ADAPTER *pContext)
+{
+ BOOLEAN bStopped;
+ BOOLEAN bReportHang = FALSE;
+ bStopped = 0 != InterlockedExchange(&pContext->bDPCInactive, TRUE);
+
+ if (bStopped)
+ {
+ pContext->nDetectedInactivity++;
+ if (pContext->nEnableDPCChecker)
+ {
+ if (pContext->NetTxPacketsToReturn)
+ {
+ DPrintf(0, ("[%s] - NO ACTIVITY!", __FUNCTION__));
+ if (!pContext->Limits.nPrintDiagnostic) PrintStatistics(pContext);
+ if (pContext->nEnableDPCChecker > 1)
+ {
+ int isrStatus1, isrStatus2;
+ isrStatus1 = virtio_read_isr_status(&pContext->IODevice);
+ isrStatus2 = virtio_read_isr_status(&pContext->IODevice);
+ if (isrStatus1 || isrStatus2)
+ {
+ DPrintf(0, ("WARNING: Interrupt status %d=>%d",
isrStatus1, isrStatus2));
+ }
+ }
+ // simulateDPC
+ InterlockedOr(&pContext->InterruptStatus, isAny);
+ ParaNdis_DPCWorkBody(pContext, PARANDIS_UNLIMITED_PACKETS_TO_INDICATE);
+ }
+ }
+ }
+ else
+ {
+ pContext->nDetectedInactivity = 0;
+ }
+
+ NdisAcquireSpinLock(&pContext->SendLock);
+ if (pContext->nofFreeHardwareBuffers != pContext->maxFreeHardwareBuffers)
+ {
+ if (pContext->nDetectedStoppedTx++ > 1)
+ {
+ DPrintf(0, ("[%s] - Suspicious Tx inactivity (%d)!", __FUNCTION__,
pContext->nofFreeHardwareBuffers));
+ //bReportHang = TRUE;
+#ifdef DBG_USE_VIRTIO_PCI_ISR_FOR_HOST_REPORT
+ WriteVirtIODeviceByte(pContext->IODevice.isr, 0);
+#endif
+ }
+ }
+ NdisReleaseSpinLock(&pContext->SendLock);
+
+
+ if (pContext->Limits.nPrintDiagnostic &&
+ ++pContext->Counters.nPrintDiagnostic >=
pContext->Limits.nPrintDiagnostic)
+ {
+ pContext->Counters.nPrintDiagnostic = 0;
+ // todo - collect more and put out optionally
+ PrintStatistics(pContext);
+ }
+
+ if (pContext->Statistics.ifHCInOctets == pContext->Counters.prevIn)
+ {
+ pContext->Counters.nRxInactivity++;
+ if (pContext->Counters.nRxInactivity >= 10)
+ {
+//#define CRASH_ON_NO_RX
+#if defined(CRASH_ON_NO_RX)
+ ONPAUSECOMPLETEPROC proc = (ONPAUSECOMPLETEPROC)(PVOID)1;
+ proc(pContext);
+#endif
+ }
+ }
+ else
+ {
+ pContext->Counters.nRxInactivity = 0;
+ pContext->Counters.prevIn = pContext->Statistics.ifHCInOctets;
+ }
+ return bReportHang;
+}
+
+/**********************************************************
+Common implementation of periodic poll
+Parameters:
+ context
+Return:
+ TRUE, if reset required
+***********************************************************/
+BOOLEAN ParaNdis_CheckForHang(PARANDIS_ADAPTER *pContext)
+{
+ static int nHangOn = 0;
+ BOOLEAN b = nHangOn >= 3 && nHangOn < 6;
+ DEBUG_ENTRY(3);
+ b |= CheckRunningDpc(pContext);
+ //uncomment to cause 3 consecutive resets
+ //nHangOn++;
+ DEBUG_EXIT_STATUS(b ? 0 : 6, b);
+ return b;
+}
+
+/**********************************************************
+Common handler of multicast address configuration
+Parameters:
+ PVOID Buffer array of addresses from NDIS
+ ULONG BufferSize size of incoming buffer
+ PUINT pBytesRead update on success
+ PUINT pBytesNeeded update on wrong buffer size
+Return value:
+ SUCCESS or kind of failure
+***********************************************************/
+NDIS_STATUS ParaNdis_SetMulticastList(
+ PARANDIS_ADAPTER *pContext,
+ PVOID Buffer,
+ ULONG BufferSize,
+ PUINT pBytesRead,
+ PUINT pBytesNeeded)
+{
+ NDIS_STATUS status;
+ ULONG length = BufferSize;
+ if (length > sizeof(pContext->MulticastData.MulticastList))
+ {
+ status = NDIS_STATUS_MULTICAST_FULL;
+ *pBytesNeeded = sizeof(pContext->MulticastData.MulticastList);
+ }
+ else if (length % ETH_LENGTH_OF_ADDRESS)
+ {
+ status = NDIS_STATUS_INVALID_LENGTH;
+ *pBytesNeeded = (length / ETH_LENGTH_OF_ADDRESS) * ETH_LENGTH_OF_ADDRESS;
+ }
+ else
+ {
+ NdisZeroMemory(pContext->MulticastData.MulticastList,
sizeof(pContext->MulticastData.MulticastList));
+ if (length)
+ NdisMoveMemory(pContext->MulticastData.MulticastList, Buffer, length);
+ pContext->MulticastData.nofMulticastEntries = length / ETH_LENGTH_OF_ADDRESS;
+ DPrintf(1, ("[%s] New multicast list of %d bytes", __FUNCTION__,
length));
+ *pBytesRead = length;
+ status = NDIS_STATUS_SUCCESS;
+ }
+ return status;
+}
+
+/**********************************************************
+Callable from synchronized routine or interrupt handler
+to enable or disable Rx and/or Tx interrupt generation
+Parameters:
+ context
+ interruptSource - isReceive, isTransmit
+ b - 1/0 enable/disable
+***********************************************************/
+VOID ParaNdis_VirtIOEnableIrqSynchronized(PARANDIS_ADAPTER *pContext, ULONG
interruptSource)
+{
+ if (interruptSource & isTransmit)
+ virtqueue_enable_cb(pContext->NetSendQueue);
+ if (interruptSource & isReceive)
+ virtqueue_enable_cb(pContext->NetReceiveQueue);
+ ParaNdis_DebugHistory(pContext, hopDPC, (PVOID)0x10, interruptSource, TRUE, 0);
+}
+
+VOID ParaNdis_VirtIODisableIrqSynchronized(PARANDIS_ADAPTER *pContext, ULONG
interruptSource)
+{
+ if (interruptSource & isTransmit)
+ virtqueue_disable_cb(pContext->NetSendQueue);
+ if (interruptSource & isReceive)
+ virtqueue_disable_cb(pContext->NetReceiveQueue);
+ ParaNdis_DebugHistory(pContext, hopDPC, (PVOID)0x10, interruptSource, FALSE, 0);
+}
+
+/**********************************************************
+Common handler of PnP events
+Parameters:
+Return value:
+***********************************************************/
+VOID ParaNdis_OnPnPEvent(
+ PARANDIS_ADAPTER *pContext,
+ NDIS_DEVICE_PNP_EVENT pEvent,
+ PVOID pInfo,
+ ULONG ulSize)
+{
+ const char *pName = "";
+ DEBUG_ENTRY(0);
+#undef MAKECASE
+#define MAKECASE(x) case (x): pName = #x; break;
+ switch (pEvent)
+ {
+ MAKECASE(NdisDevicePnPEventQueryRemoved)
+ MAKECASE(NdisDevicePnPEventRemoved)
+ MAKECASE(NdisDevicePnPEventSurpriseRemoved)
+ MAKECASE(NdisDevicePnPEventQueryStopped)
+ MAKECASE(NdisDevicePnPEventStopped)
+ MAKECASE(NdisDevicePnPEventPowerProfileChanged)
+ default:
+ break;
+ }
+ ParaNdis_DebugHistory(pContext, hopPnpEvent, NULL, pEvent, 0, 0);
+ DPrintf(0, ("[%s] (%s)", __FUNCTION__, pName));
+ if (pEvent == NdisDevicePnPEventSurpriseRemoved)
+ {
+ // on simulated surprise removal (under PnpTest) we need to reset the device
+ // to prevent any access of device queues to memory buffers
+ pContext->bSurprizeRemoved = TRUE;
+ ParaNdis_ResetVirtIONetDevice(pContext);
+ }
+ pContext->PnpEvents[pContext->nPnpEventIndex++] = pEvent;
+ if (pContext->nPnpEventIndex >
sizeof(pContext->PnpEvents)/sizeof(pContext->PnpEvents[0]))
+ pContext->nPnpEventIndex = 0;
+}
+
+static BOOLEAN SendControlMessage(
+ PARANDIS_ADAPTER *pContext,
+ UCHAR cls,
+ UCHAR cmd,
+ PVOID buffer1,
+ ULONG size1,
+ PVOID buffer2,
+ ULONG size2,
+ int levelIfOK
+ )
+{
+ BOOLEAN bOK = FALSE;
+ NdisAcquireSpinLock(&pContext->ReceiveLock);
+ if (pContext->ControlData.Virtual && pContext->ControlData.size >
(size1 + size2 + 16))
+ {
+ struct VirtIOBufferDescriptor sg[4];
+ PUCHAR pBase = (PUCHAR)pContext->ControlData.Virtual;
+ PHYSICAL_ADDRESS phBase = pContext->ControlData.Physical;
+ ULONG offset = 0;
+ UINT nOut = 1;
+
+ ((virtio_net_ctrl_hdr *)pBase)->class_of_command = cls;
+ ((virtio_net_ctrl_hdr *)pBase)->cmd = cmd;
+ sg[0].physAddr = phBase;
+ sg[0].length = sizeof(virtio_net_ctrl_hdr);
+ offset += sg[0].length;
+ offset = (offset + 3) & ~3;
+ if (size1)
+ {
+ NdisMoveMemory(pBase + offset, buffer1, size1);
+ sg[nOut].physAddr = phBase;
+ sg[nOut].physAddr.QuadPart += offset;
+ sg[nOut].length = size1;
+ offset += size1;
+ offset = (offset + 3) & ~3;
+ nOut++;
+ }
+ if (size2)
+ {
+ NdisMoveMemory(pBase + offset, buffer2, size2);
+ sg[nOut].physAddr = phBase;
+ sg[nOut].physAddr.QuadPart += offset;
+ sg[nOut].length = size2;
+ offset += size2;
+ offset = (offset + 3) & ~3;
+ nOut++;
+ }
+ sg[nOut].physAddr = phBase;
+ sg[nOut].physAddr.QuadPart += offset;
+ sg[nOut].length = sizeof(virtio_net_ctrl_ack);
+ *(virtio_net_ctrl_ack *)(pBase + offset) = VIRTIO_NET_ERR;
+
+ if (0 <= virtqueue_add_buf(pContext->NetControlQueue, sg, nOut, 1,
(PVOID)1, NULL, 0))
+ {
+ UINT len;
+ void *p;
+ virtqueue_kick_always(pContext->NetControlQueue);
+ p = virtqueue_get_buf(pContext->NetControlQueue, &len);
+ if (!p)
+ {
+ DPrintf(0, ("%s - ERROR: get_buf failed", __FUNCTION__));
+ }
+ else if (len != sizeof(virtio_net_ctrl_ack))
+ {
+ DPrintf(0, ("%s - ERROR: wrong len %d", __FUNCTION__, len));
+ }
+ else if (*(virtio_net_ctrl_ack *)(pBase + offset) != VIRTIO_NET_OK)
+ {
+ DPrintf(0, ("%s - ERROR: error %d returned", __FUNCTION__,
*(virtio_net_ctrl_ack *)(pBase + offset)));
+ }
+ else
+ {
+ // everything is OK
+ DPrintf(levelIfOK, ("%s OK(%d.%d,buffers of %d and %d) ",
__FUNCTION__, cls, cmd, size1, size2));
+ bOK = TRUE;
+ }
+ }
+ else
+ {
+ DPrintf(0, ("%s - ERROR: add_buf failed", __FUNCTION__));
+ }
+ }
+ else
+ {
+ DPrintf(0, ("%s (buffer %d,%d) - ERROR: message too LARGE",
__FUNCTION__, size1, size2));
+ }
+ NdisReleaseSpinLock(&pContext->ReceiveLock);
+ return bOK;
+}
+
+static VOID ParaNdis_DeviceFiltersUpdateRxMode(PARANDIS_ADAPTER *pContext)
+{
+ u8 val;
+ ULONG f = pContext->PacketFilter;
+ val = (f & NDIS_PACKET_TYPE_ALL_MULTICAST) ? 1 : 0;
+ SendControlMessage(pContext, VIRTIO_NET_CTRL_RX_MODE,
VIRTIO_NET_CTRL_RX_MODE_ALLMULTI, &val, sizeof(val), NULL, 0, 2);
+ //SendControlMessage(pContext, VIRTIO_NET_CTRL_RX_MODE,
VIRTIO_NET_CTRL_RX_MODE_ALLUNI, &val, sizeof(val), NULL, 0, 2);
+ val = (f & (NDIS_PACKET_TYPE_MULTICAST | NDIS_PACKET_TYPE_ALL_MULTICAST)) ? 0 :
1;
+ SendControlMessage(pContext, VIRTIO_NET_CTRL_RX_MODE,
VIRTIO_NET_CTRL_RX_MODE_NOMULTI, &val, sizeof(val), NULL, 0, 2);
+ val = (f & NDIS_PACKET_TYPE_DIRECTED) ? 0 : 1;
+ SendControlMessage(pContext, VIRTIO_NET_CTRL_RX_MODE, VIRTIO_NET_CTRL_RX_MODE_NOUNI,
&val, sizeof(val), NULL, 0, 2);
+ val = (f & NDIS_PACKET_TYPE_BROADCAST) ? 0 : 1;
+ SendControlMessage(pContext, VIRTIO_NET_CTRL_RX_MODE,
VIRTIO_NET_CTRL_RX_MODE_NOBCAST, &val, sizeof(val), NULL, 0, 2);
+ val = (f & NDIS_PACKET_TYPE_PROMISCUOUS) ? 1 : 0;
+ SendControlMessage(pContext, VIRTIO_NET_CTRL_RX_MODE,
VIRTIO_NET_CTRL_RX_MODE_PROMISC, &val, sizeof(val), NULL, 0, 2);
+}
+
+static VOID ParaNdis_DeviceFiltersUpdateAddresses(PARANDIS_ADAPTER *pContext)
+{
+ struct
+ {
+ struct virtio_net_ctrl_mac header;
+ UCHAR addr[ETH_LENGTH_OF_ADDRESS];
+ } uCast;
+ uCast.header.entries = 1;
+ NdisMoveMemory(uCast.addr, pContext->CurrentMacAddress, sizeof(uCast.addr));
+ SendControlMessage(pContext, VIRTIO_NET_CTRL_MAC, VIRTIO_NET_CTRL_MAC_TABLE_SET,
+ &uCast, sizeof(uCast),
&pContext->MulticastData,sizeof(pContext->MulticastData.nofMulticastEntries) +
pContext->MulticastData.nofMulticastEntries * ETH_ALEN, 2);
+}
+
+static VOID SetSingleVlanFilter(PARANDIS_ADAPTER *pContext, ULONG vlanId, BOOLEAN bOn,
int levelIfOK)
+{
+ u16 val = vlanId & 0xfff;
+ UCHAR cmd = bOn ? VIRTIO_NET_CTRL_VLAN_ADD : VIRTIO_NET_CTRL_VLAN_DEL;
+ SendControlMessage(pContext, VIRTIO_NET_CTRL_VLAN, cmd, &val, sizeof(val), NULL,
0, levelIfOK);
+}
+
+static VOID SetAllVlanFilters(PARANDIS_ADAPTER *pContext, BOOLEAN bOn)
+{
+ ULONG i;
+ for (i = 0; i <= MAX_VLAN_ID; ++i)
+ SetSingleVlanFilter(pContext, i, bOn, 7);
+}
+
+/*
+ possible values of filter set (pContext->ulCurrentVlansFilterSet):
+ 0 - all disabled
+ 1..4095 - one selected enabled
+ 4096 - all enabled
+ Note that only 0th vlan can't be enabled
+*/
+VOID ParaNdis_DeviceFiltersUpdateVlanId(PARANDIS_ADAPTER *pContext)
+{
+ if (pContext->bHasHardwareFilters)
+ {
+ ULONG newFilterSet;
+ if (IsVlanSupported(pContext))
+ newFilterSet = pContext->VlanId ? pContext->VlanId : (MAX_VLAN_ID +
1);
+ else
+ newFilterSet = IsPrioritySupported(pContext) ? (MAX_VLAN_ID + 1) : 0;
+ if (newFilterSet != pContext->ulCurrentVlansFilterSet)
+ {
+ if (pContext->ulCurrentVlansFilterSet > MAX_VLAN_ID)
+ SetAllVlanFilters(pContext, FALSE);
+ else if (pContext->ulCurrentVlansFilterSet)
+ SetSingleVlanFilter(pContext, pContext->ulCurrentVlansFilterSet,
FALSE, 2);
+
+ pContext->ulCurrentVlansFilterSet = newFilterSet;
+
+ if (pContext->ulCurrentVlansFilterSet > MAX_VLAN_ID)
+ SetAllVlanFilters(pContext, TRUE);
+ else if (pContext->ulCurrentVlansFilterSet)
+ SetSingleVlanFilter(pContext, pContext->ulCurrentVlansFilterSet, TRUE,
2);
+ }
+ }
+}
+
+VOID ParaNdis_UpdateDeviceFilters(PARANDIS_ADAPTER *pContext)
+{
+ if (pContext->bHasHardwareFilters)
+ {
+ ParaNdis_DeviceFiltersUpdateRxMode(pContext);
+ ParaNdis_DeviceFiltersUpdateAddresses(pContext);
+ ParaNdis_DeviceFiltersUpdateVlanId(pContext);
+ }
+}
+
+NDIS_STATUS ParaNdis_PowerOn(PARANDIS_ADAPTER *pContext)
+{
+ LIST_ENTRY TempList;
+ NDIS_STATUS status;
+ DEBUG_ENTRY(0);
+ ParaNdis_DebugHistory(pContext, hopPowerOn, NULL, 1, 0, 0);
+ ParaNdis_ResetVirtIONetDevice(pContext);
+ virtio_add_status(&pContext->IODevice, VIRTIO_CONFIG_S_ACKNOWLEDGE |
VIRTIO_CONFIG_S_DRIVER);
+ /* virtio_get_features must be called once upon device initialization:
+ otherwise the device will not work properly */
+ (void)virtio_get_features(&pContext->IODevice);
+
+ if (pContext->bUseMergedBuffers)
+ VirtIODeviceEnableGuestFeature(pContext, VIRTIO_NET_F_MRG_RXBUF);
+ if (VirtIODeviceGetHostFeature(pContext, VIRTIO_RING_F_EVENT_IDX))
+ VirtIODeviceEnableGuestFeature(pContext, VIRTIO_RING_F_EVENT_IDX);
+ if (pContext->bDoGuestChecksumOnReceive)
+ VirtIODeviceEnableGuestFeature(pContext, VIRTIO_NET_F_GUEST_CSUM);
+ if (VirtIODeviceGetHostFeature(pContext, VIRTIO_F_VERSION_1))
+ VirtIODeviceEnableGuestFeature(pContext, VIRTIO_F_VERSION_1);
+ if (VirtIODeviceGetHostFeature(pContext, VIRTIO_F_ANY_LAYOUT))
+ VirtIODeviceEnableGuestFeature(pContext, VIRTIO_F_ANY_LAYOUT);
+
+ status = FinalizeFeatures(pContext);
+ if (status == NDIS_STATUS_SUCCESS) {
+ status = FindNetQueues(pContext);
+ }
+ if (status != NDIS_STATUS_SUCCESS) {
+ virtio_add_status(&pContext->IODevice, VIRTIO_CONFIG_S_FAILED);
+ return status;
+ }
+
+ ParaNdis_RestoreDeviceConfigurationAfterReset(pContext);
+
+ ParaNdis_UpdateDeviceFilters(pContext);
+
+ InitializeListHead(&TempList);
+
+ /* submit all the receive buffers */
+ NdisAcquireSpinLock(&pContext->ReceiveLock);
+
+ pContext->ReuseBufferProc = (tReuseReceiveBufferProc)ReuseReceiveBufferRegular;
+
+ while (!IsListEmpty(&pContext->NetReceiveBuffers))
+ {
+ pIONetDescriptor pBufferDescriptor =
+ (pIONetDescriptor)RemoveHeadList(&pContext->NetReceiveBuffers);
+ InsertTailList(&TempList, &pBufferDescriptor->listEntry);
+ }
+ pContext->NetNofReceiveBuffers = 0;
+ while (!IsListEmpty(&TempList))
+ {
+ pIONetDescriptor pBufferDescriptor =
+ (pIONetDescriptor)RemoveHeadList(&TempList);
+ if (AddRxBufferToQueue(pContext, pBufferDescriptor))
+ {
+ InsertTailList(&pContext->NetReceiveBuffers,
&pBufferDescriptor->listEntry);
+ pContext->NetNofReceiveBuffers++;
+ }
+ else
+ {
+ DPrintf(0, ("FAILED TO REUSE THE BUFFER!!!!"));
+ VirtIONetFreeBufferDescriptor(pContext, pBufferDescriptor);
+ pContext->NetMaxReceiveBuffers--;
+ }
+ }
+ virtqueue_kick(pContext->NetReceiveQueue);
+ ParaNdis_SetPowerState(pContext, NdisDeviceStateD0);
+ pContext->bEnableInterruptHandlingDPC = TRUE;
+ virtio_device_ready(&pContext->IODevice);
+
+ NdisReleaseSpinLock(&pContext->ReceiveLock);
+
+ // if bFastSuspendInProcess is set by Win8 power-off procedure,
+ // the ParaNdis_Resume enables Tx and RX
+ // otherwise it does not do anything in Vista+ (Tx and RX are enabled after power-on
by Restart)
+ ParaNdis_Resume(pContext);
+ pContext->bFastSuspendInProcess = FALSE;
+
+ ParaNdis_ReportLinkStatus(pContext, TRUE);
+ ParaNdis_DebugHistory(pContext, hopPowerOn, NULL, 0, 0, 0);
+
+ return status;
+}
+
+VOID ParaNdis_PowerOff(PARANDIS_ADAPTER *pContext)
+{
+ DEBUG_ENTRY(0);
+ ParaNdis_DebugHistory(pContext, hopPowerOff, NULL, 1, 0, 0);
+
+ ParaNdis_IndicateConnect(pContext, FALSE, FALSE);
+
+ // if bFastSuspendInProcess is set by Win8 power-off procedure
+ // the ParaNdis_Suspend does fast Rx stop without waiting (=>srsPausing, if there
are some RX packets in Ndis)
+ pContext->bFastSuspendInProcess = pContext->bNoPauseOnSuspend &&
pContext->ReceiveState == srsEnabled;
+ ParaNdis_Suspend(pContext);
+ if (pContext->IODevice.addr)
+ {
+ /* back compat - remove the OK flag only in legacy mode */
+ VirtIODeviceRemoveStatus(&pContext->IODevice, VIRTIO_CONFIG_S_DRIVER_OK);
+ }
+
+ if (pContext->bFastSuspendInProcess)
+ {
+ NdisAcquireSpinLock(&pContext->ReceiveLock);
+ pContext->ReuseBufferProc =
(tReuseReceiveBufferProc)ReuseReceiveBufferPowerOff;
+ NdisReleaseSpinLock(&pContext->ReceiveLock);
+ }
+
+ ParaNdis_SetPowerState(pContext, NdisDeviceStateD3);
+
+ PreventDPCServicing(pContext);
+
+ /*******************************************************************
+ shutdown queues to have all the receive buffers under our control
+ all the transmit buffers move to list of free buffers
+ ********************************************************************/
+
+ NdisAcquireSpinLock(&pContext->SendLock);
+ virtqueue_shutdown(pContext->NetSendQueue);
+ while (!IsListEmpty(&pContext->NetSendBuffersInUse))
+ {
+ pIONetDescriptor pBufferDescriptor =
+ (pIONetDescriptor)RemoveHeadList(&pContext->NetSendBuffersInUse);
+ InsertTailList(&pContext->NetFreeSendBuffers,
&pBufferDescriptor->listEntry);
+ pContext->nofFreeTxDescriptors++;
+ pContext->nofFreeHardwareBuffers += pBufferDescriptor->nofUsedBuffers;
+ }
+ NdisReleaseSpinLock(&pContext->SendLock);
+
+ NdisAcquireSpinLock(&pContext->ReceiveLock);
+ virtqueue_shutdown(pContext->NetReceiveQueue);
+ NdisReleaseSpinLock(&pContext->ReceiveLock);
+ if (pContext->NetControlQueue) {
+ virtqueue_shutdown(pContext->NetControlQueue);
+ }
+
+ DPrintf(0, ("WARNING: deleting queues!!!!!!!!!"));
+ DeleteNetQueues(pContext);
+ pContext->NetSendQueue = NULL;
+ pContext->NetReceiveQueue = NULL;
+ pContext->NetControlQueue = NULL;
+
+ ParaNdis_ResetVirtIONetDevice(pContext);
+ ParaNdis_DebugHistory(pContext, hopPowerOff, NULL, 0, 0, 0);
+}
+
+void ParaNdis_CallOnBugCheck(PARANDIS_ADAPTER *pContext)
+{
+ if (pContext->IODevice.isr)
+ {
+#ifdef DBG_USE_VIRTIO_PCI_ISR_FOR_HOST_REPORT
+ WriteVirtIODeviceByte(pContext->IODevice.isr, 1);
+#endif
+ }
+}
+
+tChecksumCheckResult ParaNdis_CheckRxChecksum(PARANDIS_ADAPTER *pContext, ULONG
virtioFlags, PVOID pRxPacket, ULONG len)
+{
+ tOffloadSettingsFlags f = pContext->Offload.flags;
+ tChecksumCheckResult res, resIp;
+ PVOID pIpHeader = RtlOffsetToPointer(pRxPacket, ETH_HEADER_SIZE);
+ tTcpIpPacketParsingResult ppr;
+ ULONG flagsToCalculate = 0;
+ res.value = 0;
+ resIp.value = 0;
+
+ //VIRTIO_NET_HDR_F_NEEDS_CSUM - we need to calculate TCP/UDP CS
+ //VIRTIO_NET_HDR_F_DATA_VALID - host tells us TCP/UDP CS is OK
+
+ if (f.fRxIPChecksum) flagsToCalculate |= pcrIpChecksum; // check only
+
+ if (!(virtioFlags & VIRTIO_NET_HDR_F_DATA_VALID))
+ {
+ if (virtioFlags & VIRTIO_NET_HDR_F_NEEDS_CSUM)
+ {
+ flagsToCalculate |= pcrFixXxpChecksum | pcrTcpChecksum | pcrUdpChecksum;
+ }
+ else
+ {
+ if (f.fRxTCPChecksum) flagsToCalculate |= pcrTcpV4Checksum;
+ if (f.fRxUDPChecksum) flagsToCalculate |= pcrUdpV4Checksum;
+ if (f.fRxTCPv6Checksum) flagsToCalculate |= pcrTcpV6Checksum;
+ if (f.fRxUDPv6Checksum) flagsToCalculate |= pcrUdpV6Checksum;
+ }
+ }
+
+ ppr = ParaNdis_CheckSumVerify(pIpHeader, len - ETH_HEADER_SIZE, flagsToCalculate,
__FUNCTION__);
+
+ if (virtioFlags & VIRTIO_NET_HDR_F_DATA_VALID)
+ {
+ pContext->extraStatistics.framesRxCSHwOK++;
+ ppr.xxpCheckSum = ppresCSOK;
+ }
+
+ if (ppr.ipStatus == ppresIPV4 && !ppr.IsFragment)
+ {
+ if (f.fRxIPChecksum)
+ {
+ res.flags.IpOK = ppr.ipCheckSum == ppresCSOK;
+ res.flags.IpFailed = ppr.ipCheckSum == ppresCSBad;
+ }
+ if(ppr.xxpStatus == ppresXxpKnown)
+ {
+ if(ppr.TcpUdp == ppresIsTCP) /* TCP */
+ {
+ if (f.fRxTCPChecksum)
+ {
+ res.flags.TcpOK = ppr.xxpCheckSum == ppresCSOK || ppr.fixedXxpCS;
+ res.flags.TcpFailed = !res.flags.TcpOK;
+ }
+ }
+ else /* UDP */
+ {
+ if (f.fRxUDPChecksum)
+ {
+ res.flags.UdpOK = ppr.xxpCheckSum == ppresCSOK || ppr.fixedXxpCS;
+ res.flags.UdpFailed = !res.flags.UdpOK;
+ }
+ }
+ }
+ }
+ else if (ppr.ipStatus == ppresIPV6)
+ {
+ if(ppr.xxpStatus == ppresXxpKnown)
+ {
+ if(ppr.TcpUdp == ppresIsTCP) /* TCP */
+ {
+ if (f.fRxTCPv6Checksum)
+ {
+ res.flags.TcpOK = ppr.xxpCheckSum == ppresCSOK || ppr.fixedXxpCS;
+ res.flags.TcpFailed = !res.flags.TcpOK;
+ }
+ }
+ else /* UDP */
+ {
+ if (f.fRxUDPv6Checksum)
+ {
+ res.flags.UdpOK = ppr.xxpCheckSum == ppresCSOK || ppr.fixedXxpCS;
+ res.flags.UdpFailed = !res.flags.UdpOK;
+ }
+ }
+ }
+ }
+
+ if (pContext->bDoIPCheckRx &&
+ (f.fRxIPChecksum || f.fRxTCPChecksum || f.fRxUDPChecksum || f.fRxTCPv6Checksum ||
f.fRxUDPv6Checksum))
+ {
+ ppr = ParaNdis_CheckSumVerify(pIpHeader, len - ETH_HEADER_SIZE, pcrAnyChecksum,
__FUNCTION__);
+ if (ppr.ipStatus == ppresIPV4 && !ppr.IsFragment)
+ {
+ resIp.flags.IpOK = !!f.fRxIPChecksum && ppr.ipCheckSum == ppresCSOK;
+ resIp.flags.IpFailed = !!f.fRxIPChecksum && ppr.ipCheckSum ==
ppresCSBad;
+ if (f.fRxTCPChecksum && ppr.xxpStatus == ppresXxpKnown &&
ppr.TcpUdp == ppresIsTCP)
+ {
+ resIp.flags.TcpOK = ppr.xxpCheckSum == ppresCSOK;
+ resIp.flags.TcpFailed = ppr.xxpCheckSum == ppresCSBad;
+ }
+ if (f.fRxUDPChecksum && ppr.xxpStatus == ppresXxpKnown &&
ppr.TcpUdp == ppresIsUDP)
+ {
+ resIp.flags.UdpOK = ppr.xxpCheckSum == ppresCSOK;
+ resIp.flags.UdpFailed = ppr.xxpCheckSum == ppresCSBad;
+ }
+ }
+ else if (ppr.ipStatus == ppresIPV6)
+ {
+ if (f.fRxTCPv6Checksum && ppr.xxpStatus == ppresXxpKnown &&
ppr.TcpUdp == ppresIsTCP)
+ {
+ resIp.flags.TcpOK = ppr.xxpCheckSum == ppresCSOK;
+ resIp.flags.TcpFailed = ppr.xxpCheckSum == ppresCSBad;
+ }
+ if (f.fRxUDPv6Checksum && ppr.xxpStatus == ppresXxpKnown &&
ppr.TcpUdp == ppresIsUDP)
+ {
+ resIp.flags.UdpOK = ppr.xxpCheckSum == ppresCSOK;
+ resIp.flags.UdpFailed = ppr.xxpCheckSum == ppresCSBad;
+ }
+ }
+
+ if (res.value != resIp.value)
+ {
+ // if HW did not set some bits that IP checker set, it is a mistake:
+ // or GOOD CS is not labeled, or BAD checksum is not labeled
+ tChecksumCheckResult diff;
+ diff.value = resIp.value & ~res.value;
+ if (diff.flags.IpFailed || diff.flags.TcpFailed || diff.flags.UdpFailed)
+ pContext->extraStatistics.framesRxCSHwMissedBad++;
+ if (diff.flags.IpOK || diff.flags.TcpOK || diff.flags.UdpOK)
+ pContext->extraStatistics.framesRxCSHwMissedGood++;
+ if (diff.value)
+ {
+ DPrintf(0, ("[%s] real %X <> %X (virtio %X)",
__FUNCTION__, resIp.value, res.value, virtioFlags));
+ }
+ res.value = resIp.value;
+ }
+ }
+
+ return res;
+}
diff --git a/drivers/network/dd/netkvm/Common/ParaNdis-Debug.c
b/drivers/network/dd/netkvm/Common/ParaNdis-Debug.c
new file mode 100644
index 00000000000..f66b92502f8
--- /dev/null
+++ b/drivers/network/dd/netkvm/Common/ParaNdis-Debug.c
@@ -0,0 +1,394 @@
+/*
+ * This file contains debug support procedures, common for NDIS5 and NDIS6
+ *
+ * Copyright (c) 2008-2017 Red Hat, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met :
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and / or other materials provided with the distribution.
+ * 3. Neither the names of the copyright holders nor the names of their contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS''
AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+#include "ndis56common.h"
+#include "stdarg.h"
+#include "ntstrsafe.h"
+
+//#define OVERRIDE_DEBUG_BREAK
+
+#ifdef WPP_EVENT_TRACING
+#include "ParaNdis-Debug.tmh"
+#endif
+
+int virtioDebugLevel = 1;
+int nDebugLevel = 1;
+int bDebugPrint = 1;
+
+static NDIS_SPIN_LOCK CrashLock;
+
+static KBUGCHECK_REASON_CALLBACK_ROUTINE ParaNdis_OnBugCheck;
+static VOID NTAPI ParaNdis_OnBugCheck(
+ IN KBUGCHECK_CALLBACK_REASON Reason,
+ IN PKBUGCHECK_REASON_CALLBACK_RECORD Record,
+ IN OUT PVOID ReasonSpecificData,
+ IN ULONG ReasonSpecificDataLength
+);
+static VOID ParaNdis_PrepareBugCheckData();
+
+typedef BOOLEAN (*KeRegisterBugCheckReasonCallbackType) (
+ __out PKBUGCHECK_REASON_CALLBACK_RECORD CallbackRecord,
+ __in PKBUGCHECK_REASON_CALLBACK_ROUTINE CallbackRoutine,
+ __in KBUGCHECK_CALLBACK_REASON Reason,
+ __in PUCHAR Component
+ );
+
+typedef BOOLEAN (*KeDeregisterBugCheckReasonCallbackType) (
+ __inout PKBUGCHECK_REASON_CALLBACK_RECORD CallbackRecord
+ );
+
+typedef ULONG (*vDbgPrintExType)(
+ __in ULONG ComponentId,
+ __in ULONG Level,
+ __in PCCH Format,
+ __in va_list arglist
+ );
+
+static ULONG DummyPrintProcedure(
+ __in ULONG ComponentId,
+ __in ULONG Level,
+ __in PCCH Format,
+ __in va_list arglist
+ )
+{
+ return 0;
+}
+static BOOLEAN KeRegisterBugCheckReasonCallbackDummyProc(
+ __out PKBUGCHECK_REASON_CALLBACK_RECORD CallbackRecord,
+ __in PKBUGCHECK_REASON_CALLBACK_ROUTINE CallbackRoutine,
+ __in KBUGCHECK_CALLBACK_REASON Reason,
+ __in PUCHAR Component
+ )
+{
+ CallbackRecord->State = 0;
+ return FALSE;
+}
+
+BOOLEAN KeDeregisterBugCheckReasonCallbackDummyProc(
+ __inout PKBUGCHECK_REASON_CALLBACK_RECORD CallbackRecord
+ )
+{
+ return FALSE;
+}
+
+static vDbgPrintExType PrintProcedure = DummyPrintProcedure;
+static KeRegisterBugCheckReasonCallbackType BugCheckRegisterCallback =
KeRegisterBugCheckReasonCallbackDummyProc;
+static KeDeregisterBugCheckReasonCallbackType BugCheckDeregisterCallback =
KeDeregisterBugCheckReasonCallbackDummyProc;
+KBUGCHECK_REASON_CALLBACK_RECORD CallbackRecord;
+
+#if !defined(WPP_EVENT_TRACING) || defined(WPP_USE_BYPASS)
+#if defined(DPFLTR_MASK)
+
+//common case, except Win2K
+static void DebugPrint(const char *fmt, ...)
+{
+ va_list list;
+ va_start(list, fmt);
+ PrintProcedure(DPFLTR_DEFAULT_ID, 9 | DPFLTR_MASK, fmt, list);
+#if defined(VIRTIO_DBG_USE_IOPORT)
+ {
+ NTSTATUS status;
+ // use this way of output only for DISPATCH_LEVEL,
+ // higher requires more protection
+ if (KeGetCurrentIrql() <= DISPATCH_LEVEL)
+ {
+ char buf[256];
+ size_t len, i;
+ buf[0] = 0;
+ status = RtlStringCbVPrintfA(buf, sizeof(buf), fmt, list);
+ if (status == STATUS_SUCCESS) len = strlen(buf);
+ else if (status == STATUS_BUFFER_OVERFLOW) len = sizeof(buf);
+ else { memcpy(buf, "Can't print", 11); len = 11; }
+ NdisAcquireSpinLock(&CrashLock);
+ for (i = 0; i < len; ++i)
+ {
+ NdisRawWritePortUchar(VIRTIO_DBG_USE_IOPORT, buf[i]);
+ }
+ NdisRawWritePortUchar(VIRTIO_DBG_USE_IOPORT, '\n');
+ NdisReleaseSpinLock(&CrashLock);
+ }
+ }
+#endif
+}
+
+DEBUGPRINTFUNC pDebugPrint = DebugPrint;
+DEBUGPRINTFUNC VirtioDebugPrintProc = DebugPrint;
+
+#else //DPFLTR_MASK
+#pragma message("DebugPrint for Win2K")
+
+DEBUGPRINTFUNC pDebugPrint = DbgPrint;
+DEBUGPRINTFUNC VirtioDebugPrintProc = DbgPrint;
+
+#endif //DPFLTR_MASK
+#endif //!defined(WPP_EVENT_TRACING) || defined(WPP_USE_BYPASS)
+
+
+
+void _LogOutEntry(int level, const char *s)
+{
+ DPrintf(level, ("[%s]=>", s));
+}
+
+void _LogOutExitValue(int level, const char *s, ULONG value)
+{
+ DPrintf(level, ("[%s]<=0x%X", s, value));
+}
+
+void _LogOutString(int level, const char *s)
+{
+ DPrintf(level, ("[%s]", s));
+}
+
+VOID WppEnableCallback(
+ __in LPCGUID Guid,
+ __in __int64 Logger,
+ __in BOOLEAN Enable,
+ __in ULONG Flags,
+ __in UCHAR Level)
+{
+#if WPP_USE_BYPASS
+ DPrintfBypass(0, ("[%s] %s, flags %X, level %d",
+ __FUNCTION__, Enable ? "enabled" : "disabled",
+ Flags, (ULONG)Level));
+#endif
+ nDebugLevel = Level;
+ bDebugPrint = Enable;
+}
+
+
+#ifdef OVERRIDE_DEBUG_BREAK
+static PUCHAR pDbgBreakPoint;
+static UCHAR DbgBreakPointChunk[5];
+static void AnotherDbgBreak()
+{
+ DPrintf(0, ("Somebody tried to break into the debugger!"));
+}
+#endif
+
+void ParaNdis_DebugInitialize(PVOID DriverObject,PVOID RegistryPath)
+{
+ NDIS_STRING usRegister, usDeregister, usPrint;
+ PVOID pr, pd;
+ BOOLEAN res;
+ WPP_INIT_TRACING(DriverObject, RegistryPath);
+
+ NdisAllocateSpinLock(&CrashLock);
+ KeInitializeCallbackRecord(&CallbackRecord);
+ ParaNdis_PrepareBugCheckData();
+ NdisInitUnicodeString(&usPrint, L"vDbgPrintEx");
+ NdisInitUnicodeString(&usRegister,
L"KeRegisterBugCheckReasonCallback");
+ NdisInitUnicodeString(&usDeregister,
L"KeDeregisterBugCheckReasonCallback");
+ pd = MmGetSystemRoutineAddress(&usPrint);
+ if (pd) PrintProcedure = (vDbgPrintExType)pd;
+ pr = MmGetSystemRoutineAddress(&usRegister);
+ pd = MmGetSystemRoutineAddress(&usDeregister);
+ if (pr && pd)
+ {
+ BugCheckRegisterCallback = (KeRegisterBugCheckReasonCallbackType)pr;
+ BugCheckDeregisterCallback = (KeDeregisterBugCheckReasonCallbackType)pd;
+ }
+ res = BugCheckRegisterCallback(&CallbackRecord, ParaNdis_OnBugCheck,
KbCallbackSecondaryDumpData, "NetKvm");
+ DPrintf(0, ("[%s] Crash callback %sregistered", __FUNCTION__, res ?
"" : "NOT "));
+
+#ifdef OVERRIDE_DEBUG_BREAK
+ if (sizeof(PVOID) == sizeof(ULONG))
+ {
+ UCHAR replace[5] = {0xe9,0,0,0,0};
+ ULONG replacement;
+ NDIS_STRING usDbgBreakPointName;
+ NdisInitUnicodeString(&usDbgBreakPointName, L"DbgBreakPoint");
+ pDbgBreakPoint = (PUCHAR)MmGetSystemRoutineAddress(&usDbgBreakPointName);
+ if (pDbgBreakPoint)
+ {
+ DPrintf(0, ("Replacing original BP handler at %p",
pDbgBreakPoint));
+ replacement = RtlPointerToOffset(pDbgBreakPoint + 5, AnotherDbgBreak);
+ RtlCopyMemory(replace + 1, &replacement, sizeof(replacement));
+ RtlCopyMemory(DbgBreakPointChunk, pDbgBreakPoint,
sizeof(DbgBreakPointChunk));
+ RtlCopyMemory(pDbgBreakPoint, replace, sizeof(replace));
+ }
+ }
+#endif
+}
+
+void ParaNdis_DebugCleanup(PDRIVER_OBJECT pDriverObject)
+{
+#ifdef OVERRIDE_DEBUG_BREAK
+ if (sizeof(PVOID) == sizeof(ULONG) && pDbgBreakPoint)
+ {
+ DPrintf(0, ("Restoring original BP handler at %p", pDbgBreakPoint));
+ RtlCopyMemory(pDbgBreakPoint, DbgBreakPointChunk, sizeof(DbgBreakPointChunk));
+ }
+#endif
+ BugCheckDeregisterCallback(&CallbackRecord);
+ WPP_CLEANUP(pDriverObject);
+}
+
+
+#define MAX_CONTEXTS 4
+#if defined(ENABLE_HISTORY_LOG)
+#define MAX_HISTORY 0x40000
+#else
+#define MAX_HISTORY 2
+#endif
+typedef struct _tagBugCheckStaticData
+{
+ tBugCheckStaticDataHeader Header;
+ tBugCheckPerNicDataContent PerNicData[MAX_CONTEXTS];
+ tBugCheckStaticDataContent Data;
+ tBugCheckHistoryDataEntry History[MAX_HISTORY];
+}tBugCheckStaticData;
+
+
+typedef struct _tagBugCheckData
+{
+ tBugCheckStaticData StaticData;
+ tBugCheckDataLocation Location;
+}tBugCheckData;
+
+static tBugCheckData BugCheckData;
+static BOOLEAN bNative = TRUE;
+
+VOID ParaNdis_PrepareBugCheckData()
+{
+ BugCheckData.StaticData.Header.StaticDataVersion =
PARANDIS_DEBUG_STATIC_DATA_VERSION;
+ BugCheckData.StaticData.Header.PerNicDataVersion =
PARANDIS_DEBUG_PER_NIC_DATA_VERSION;
+ BugCheckData.StaticData.Header.ulMaxContexts = MAX_CONTEXTS;
+ BugCheckData.StaticData.Header.SizeOfPointer = sizeof(PVOID);
+ BugCheckData.StaticData.Header.PerNicData =
(UINT_PTR)(PVOID)BugCheckData.StaticData.PerNicData;
+ BugCheckData.StaticData.Header.DataArea = (UINT64)&BugCheckData.StaticData.Data;
+ BugCheckData.StaticData.Header.DataAreaSize = sizeof(BugCheckData.StaticData.Data);
+ BugCheckData.StaticData.Data.HistoryDataVersion =
PARANDIS_DEBUG_HISTORY_DATA_VERSION;
+ BugCheckData.StaticData.Data.SizeOfHistory = MAX_HISTORY;
+ BugCheckData.StaticData.Data.SizeOfHistoryEntry = sizeof(tBugCheckHistoryDataEntry);
+ BugCheckData.StaticData.Data.HistoryData =
(UINT_PTR)(PVOID)BugCheckData.StaticData.History;
+ BugCheckData.Location.Address = (UINT64)&BugCheckData;
+ BugCheckData.Location.Size = sizeof(BugCheckData);
+}
+
+void ParaNdis_DebugRegisterMiniport(PARANDIS_ADAPTER *pContext, BOOLEAN bRegister)
+{
+ UINT i;
+ NdisAcquireSpinLock(&CrashLock);
+ for (i = 0; i < MAX_CONTEXTS; ++i)
+ {
+ UINT64 val1 = bRegister ? 0 : (UINT_PTR)pContext;
+ UINT64 val2 = bRegister ? (UINT_PTR)pContext : 0;
+ if (BugCheckData.StaticData.PerNicData[i].Context != val1) continue;
+ BugCheckData.StaticData.PerNicData[i].Context = val2;
+ break;
+ }
+ NdisReleaseSpinLock(&CrashLock);
+}
+
+static UINT FillDataOnBugCheck()
+{
+ UINT i, n = 0;
+ NdisGetCurrentSystemTime(&BugCheckData.StaticData.Header.qCrashTime);
+ for (i = 0; i < MAX_CONTEXTS; ++i)
+ {
+ tBugCheckPerNicDataContent *pSave = &BugCheckData.StaticData.PerNicData[i];
+ PARANDIS_ADAPTER *p = (PARANDIS_ADAPTER *)pSave->Context;
+ if (!p) continue;
+ pSave->nofPacketsToComplete = p->NetTxPacketsToReturn;
+ pSave->nofReadyTxBuffers = p->nofFreeHardwareBuffers;
+ pSave->LastInterruptTimeStamp.QuadPart =
PARANDIS_GET_LAST_INTERRUPT_TIMESTAMP(p);
+ pSave->LastTxCompletionTimeStamp = p->LastTxCompletionTimeStamp;
+ ParaNdis_CallOnBugCheck(p);
+ ++n;
+ }
+ return n;
+}
+
+VOID NTAPI ParaNdis_OnBugCheck(
+ IN KBUGCHECK_CALLBACK_REASON Reason,
+ IN PKBUGCHECK_REASON_CALLBACK_RECORD Record,
+ IN OUT PVOID ReasonSpecificData,
+ IN ULONG ReasonSpecificDataLength
+ )
+{
+ KBUGCHECK_SECONDARY_DUMP_DATA *pDump = (KBUGCHECK_SECONDARY_DUMP_DATA
*)ReasonSpecificData;
+ if (KbCallbackSecondaryDumpData == Reason && ReasonSpecificDataLength >=
sizeof(*pDump))
+ {
+ ULONG dumpSize = sizeof(BugCheckData.Location);
+ if (!pDump->OutBuffer)
+ {
+ UINT nSaved;
+ nSaved = FillDataOnBugCheck();
+ if (pDump->InBufferLength >= dumpSize)
+ {
+ pDump->OutBuffer = pDump->InBuffer;
+ pDump->OutBufferLength = dumpSize;
+ }
+ else
+ {
+ pDump->OutBuffer = &BugCheckData.Location;
+ pDump->OutBufferLength = dumpSize;
+ bNative = FALSE;
+ }
+ DPrintf(0, ("[%s] system buffer of %d, saving data for %d NIC",
__FUNCTION__,pDump->InBufferLength, nSaved));
+ DPrintf(0, ("[%s] using %s buffer", __FUNCTION__, bNative ?
"native" : "own"));
+ }
+ else if (pDump->OutBuffer == pDump->InBuffer)
+ {
+ RtlCopyMemory(&pDump->Guid, &ParaNdis_CrashGuid,
sizeof(pDump->Guid));
+ RtlCopyMemory(pDump->InBuffer, &BugCheckData.Location, dumpSize);
+ pDump->OutBufferLength = dumpSize;
+ DPrintf(0, ("[%s] written %d to %p", __FUNCTION__,
(ULONG)BugCheckData.Location.Size, (UINT_PTR)BugCheckData.Location.Address ));
+ DPrintf(0, ("[%s] dump data (%d) at %p", __FUNCTION__,
pDump->OutBufferLength, pDump->OutBuffer));
+ }
+ }
+}
+
+#if defined(ENABLE_HISTORY_LOG)
+void ParaNdis_DebugHistory(
+ PARANDIS_ADAPTER *pContext,
+ eHistoryLogOperation op,
+ PVOID pParam1,
+ ULONG lParam2,
+ ULONG lParam3,
+ ULONG lParam4)
+{
+ tBugCheckHistoryDataEntry *phe;
+ ULONG index =
InterlockedIncrement(&BugCheckData.StaticData.Data.CurrentHistoryIndex);
+ index = (index - 1) % MAX_HISTORY;
+ phe = &BugCheckData.StaticData.History[index];
+ phe->Context = (UINT_PTR)pContext;
+ phe->operation = op;
+ phe->pParam1 = (UINT_PTR)pParam1;
+ phe->lParam2 = lParam2;
+ phe->lParam3 = lParam3;
+ phe->lParam4 = lParam4;
+#if (PARANDIS_DEBUG_HISTORY_DATA_VERSION == 1)
+ phe->uIRQL = KeGetCurrentIrql();
+ phe->uProcessor = KeGetCurrentProcessorNumber();
+#endif
+ NdisGetCurrentSystemTime(&phe->TimeStamp);
+}
+
+#endif
diff --git a/drivers/network/dd/netkvm/Common/ParaNdis-Oid.c
b/drivers/network/dd/netkvm/Common/ParaNdis-Oid.c
new file mode 100644
index 00000000000..8d54b445dca
--- /dev/null
+++ b/drivers/network/dd/netkvm/Common/ParaNdis-Oid.c
@@ -0,0 +1,677 @@
+/*
+ * This file contains NDIS OID support procedures, common for NDIS5 and NDIS6
+ *
+ * Copyright (c) 2008-2017 Red Hat, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met :
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and / or other materials provided with the distribution.
+ * 3. Neither the names of the copyright holders nor the names of their contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS''
AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+#include "ParaNdis-Oid.h"
+
+#ifdef WPP_EVENT_TRACING
+#include "ParaNdis-Oid.tmh"
+#endif
+#include <sal.h>
+
+static const char VendorName[] = "Red Hat";
+
+static UCHAR FORCEINLINE hexdigit(UCHAR nibble)
+{
+ UCHAR c = nibble & 0xf;
+ c += (c <= 9) ? 0 : 7;
+ c += '0';
+ return c;
+}
+
+/**********************************************************
+Common implementation of copy operation when OID is set
+pOid->Flags (if used) controls when the source data may be truncated or padded on
copy
+Parameters:
+ tOidDesc *pOid - descriptor of OID
+ PVOID pDest - buffer to receive data sent by NDIS
+ ULONG ulSize - size of data to copy
+Return value:
+ SUCCESS or NDIS error code if target buffer size is wrong
+Rules:
+
+PDEST <>OK SIZE PAYLOAD SZ
+NULL any n/a any fail
+BUFF any 0 any success, none copied
+BUFF any SZ ==SZ success, copied SZ
+BUFF !lessok SZ <SZ fail (small), none copied
+BUFF !moreok SZ >SZ fail (overflow), none copied
+BUFF lessok SZ <SZ success, SZ cleared, payload sz
copied
+BUFF moreok SZ >SZ success, copied SZ
+***************************************************/
+NDIS_STATUS ParaNdis_OidSetCopy(
+ tOidDesc *pOid,
+ PVOID pDest,
+ ULONG ulSize)
+{
+ NDIS_STATUS status = NDIS_STATUS_SUCCESS;
+ if (!pDest)
+ {
+ status = NDIS_STATUS_INVALID_OID;
+ *(pOid->pBytesRead) = 0;
+ *(pOid->pBytesNeeded) = 0;
+ }
+ else if (ulSize)
+ {
+ if (pOid->InformationBufferLength < ulSize)
+ {
+ if (pOid->ulToDoFlags & ohfSetLessOK)
+ {
+ *(pOid->pBytesRead) = pOid->InformationBufferLength;
+ NdisZeroMemory(pDest, ulSize);
+ NdisMoveMemory(pDest, pOid->InformationBuffer,
pOid->InformationBufferLength);
+ }
+ else
+ {
+ status = NDIS_STATUS_BUFFER_TOO_SHORT;
+ *(pOid->pBytesRead) = 0;
+ *(pOid->pBytesNeeded) = ulSize;
+ }
+ }
+ else if (pOid->InformationBufferLength == ulSize || (pOid->ulToDoFlags
& ohfSetMoreOK))
+ {
+ *(pOid->pBytesRead) = ulSize;
+ NdisMoveMemory(pDest, pOid->InformationBuffer, ulSize);
+ }
+ else
+ {
+ status = NDIS_STATUS_BUFFER_OVERFLOW;
+ *(pOid->pBytesNeeded) = ulSize;
+ *(pOid->pBytesRead) = 0;
+ }
+ }
+ else
+ {
+ *(pOid->pBytesRead) = pOid->InformationBufferLength;
+ }
+ return status;
+}
+
+
+/**********************************************************
+Common handler of setting packet filter
+***********************************************************/
+NDIS_STATUS ParaNdis_OnSetPacketFilter(PARANDIS_ADAPTER *pContext, tOidDesc *pOid)
+{
+ ULONG newValue;
+ NDIS_STATUS status = ParaNdis_OidSetCopy(
+ pOid,
+ &newValue,
+ sizeof(newValue));
+
+ if (newValue & ~PARANDIS_PACKET_FILTERS)
+ status = NDIS_STATUS_INVALID_DATA;
+
+ if (status == NDIS_STATUS_SUCCESS)
+ {
+ pContext->PacketFilter = newValue;
+ DPrintf(1, ("[%s] PACKET FILTER SET TO %x", __FUNCTION__,
pContext->PacketFilter));
+ ParaNdis_UpdateDeviceFilters(pContext);
+ }
+ return status;
+}
+
+void ParaNdis_FillPowerCapabilities(PNDIS_PNP_CAPABILITIES pCaps)
+{
+ NdisZeroMemory(pCaps, sizeof(*pCaps));
+ pCaps->WakeUpCapabilities.MinMagicPacketWakeUp = NdisDeviceStateUnspecified;
+ pCaps->WakeUpCapabilities.MinPatternWakeUp = NdisDeviceStateUnspecified;
+ pCaps->WakeUpCapabilities.MinLinkChangeWakeUp = NdisDeviceStateUnspecified;
+}
+
+
+/**********************************************************
+Common handler of setting multicast list
+***********************************************************/
+NDIS_STATUS ParaNdis_OnOidSetMulticastList(PARANDIS_ADAPTER *pContext, tOidDesc *pOid)
+{
+ NDIS_STATUS status;
+ status = ParaNdis_SetMulticastList(
+ pContext,
+ pOid->InformationBuffer,
+ pOid->InformationBufferLength,
+ pOid->pBytesRead,
+ pOid->pBytesNeeded);
+ ParaNdis_UpdateDeviceFilters(pContext);
+ return status;
+}
+
+/**********************************************************
+Common helper of copy operation on GET OID
+Copies data from specified location to NDIS buffer
+64-bit variable will be casted to 32-bit, if specified on pOid->Flags
+
+Parameters:
+ tOidDesc *pOid - descriptor of OID
+ PVOID pInfo - source to copy from
+ ULONG ulSize - source info size
+Return value:
+ SUCCESS or kind of failure when the dest buffer size is wrong
+Comments:
+pInfo must be non-NULL, otherwise error returned
+ulSize may be 0, then SUCCESS returned without copy
+***********************************************************/
+NDIS_STATUS ParaNdis_OidQueryCopy(
+ tOidDesc *pOid,
+ PVOID pInfo,
+ ULONG ulSize,
+ BOOLEAN bFreeInfo)
+{
+ NDIS_STATUS status = NDIS_STATUS_SUCCESS;
+ *(pOid->pBytesNeeded) = ulSize;
+ if (!pInfo)
+ {
+ status = NDIS_STATUS_INVALID_OID;
+ *(pOid->pBytesWritten) = 0;
+ *(pOid->pBytesNeeded) = 0;
+ }
+ else if (pOid->InformationBufferLength >= ulSize)
+ {
+ if (ulSize) NdisMoveMemory(pOid->InformationBuffer, pInfo, ulSize);
+ *(pOid->pBytesWritten) = ulSize;
+ *(pOid->pBytesNeeded) = 0;
+ }
+ else if ((pOid->ulToDoFlags & ohfQuery3264) &&
pOid->InformationBufferLength == sizeof(ULONG) && ulSize == sizeof(ULONG64))
+ {
+ ULONG64 u64 = *(ULONG64 *)pInfo;
+ ULONG ul = (ULONG)u64;
+ NdisMoveMemory(pOid->InformationBuffer, &ul, sizeof(ul));
+ *(pOid->pBytesWritten) = sizeof(ul);
+ }
+ else
+ {
+ status = NDIS_STATUS_BUFFER_TOO_SHORT;
+ *(pOid->pBytesWritten) = 0;
+ }
+ if (bFreeInfo && pInfo)
+ {
+ NdisFreeMemory(pInfo, 0, 0);
+ }
+ return status;
+}
+
+/**********************************************************
+Common handler of Oid queries
+Parameters:
+ context
+ tOidDesc *pOid - filled descriptor of OID operation
+Return value:
+ SUCCESS or kind of failure
+***********************************************************/
+NDIS_STATUS ParaNdis_OidQueryCommon(PARANDIS_ADAPTER *pContext, tOidDesc *pOid)
+{
+ NDIS_STATUS status = NDIS_STATUS_SUCCESS;
+ PVOID pInfo = NULL;
+ ULONG ulSize = 0;
+ BOOLEAN bFreeInfo = FALSE;
+ union _tagtemp
+ {
+ NDIS_MEDIUM Medium;
+ ULONG64 ul64;
+ ULONG ul;
+ USHORT us;
+ NDIS_PNP_CAPABILITIES PMCaps;
+ } u;
+#if defined(_MSC_VER)
+ #define CONCATFIELD(object, field) object.##field
+#else
+ #define CONCATFIELD(object, field) object.field
+#endif
+#define SETINFO(field, value) pInfo = CONCATFIELD(&u, field); ulSize =
sizeof(CONCATFIELD(u, field)); CONCATFIELD(u, field) = (value)
+ switch (pOid->Oid)
+ {
+ case OID_GEN_SUPPORTED_LIST:
+ ParaNdis_GetSupportedOid(&pInfo, &ulSize);
+ break;
+ case OID_GEN_HARDWARE_STATUS:
+ SETINFO(ul, NdisHardwareStatusReady);
+ break;
+ case OID_GEN_MEDIA_SUPPORTED:
+ __fallthrough;
+ case OID_GEN_MEDIA_IN_USE:
+ SETINFO(Medium, NdisMedium802_3);
+ break;
+ case OID_GEN_MAXIMUM_LOOKAHEAD:
+ SETINFO(ul, pContext->MaxPacketSize.nMaxFullSizeOS);
+ break;
+ case OID_GEN_MAXIMUM_FRAME_SIZE:
+ SETINFO(ul, pContext->MaxPacketSize.nMaxDataSize);
+ break;
+ case OID_GEN_TRANSMIT_BUFFER_SPACE:
+ SETINFO(ul, pContext->MaxPacketSize.nMaxFullSizeOS *
pContext->nofFreeTxDescriptors);
+ break;
+ case OID_GEN_RECEIVE_BUFFER_SPACE:
+ SETINFO(ul, pContext->MaxPacketSize.nMaxFullSizeOS *
pContext->NetMaxReceiveBuffers);
+ break;
+ case OID_GEN_RECEIVE_BLOCK_SIZE:
+ __fallthrough;
+ case OID_GEN_TRANSMIT_BLOCK_SIZE:
+ __fallthrough;
+ case OID_GEN_MAXIMUM_TOTAL_SIZE:
+ SETINFO(ul, pContext->MaxPacketSize.nMaxFullSizeOS);
+ break;
+ case OID_GEN_TRANSMIT_QUEUE_LENGTH:
+ // TODO: this is not completely correct, but only if
+ // the TX queue is not full
+ SETINFO(ul, pContext->maxFreeTxDescriptors -
pContext->nofFreeTxDescriptors);
+ break;
+ case OID_GEN_VENDOR_ID:
+ SETINFO(ul, 0x00ffffff);
+ break;
+ case OID_GEN_VENDOR_DESCRIPTION:
+ pInfo = (PVOID)VendorName;
+ ulSize = sizeof(VendorName);
+ break;
+
+ case OID_GEN_VENDOR_DRIVER_VERSION:
+ SETINFO(ul, (NDIS_MINIPORT_MAJOR_VERSION << 16) |
NDIS_MINIPORT_MINOR_VERSION);
+ break;
+ case OID_GEN_CURRENT_PACKET_FILTER:
+ pInfo = &pContext->PacketFilter;
+ ulSize = sizeof(pContext->PacketFilter);
+ break;
+ case OID_GEN_DRIVER_VERSION:
+ SETINFO(us, ((NDIS_MINIPORT_MAJOR_VERSION << 8) |
NDIS_MINIPORT_MINOR_VERSION));
+ break;
+ case OID_GEN_MAC_OPTIONS:
+ {
+ ULONG options = NDIS_MAC_OPTION_COPY_LOOKAHEAD_DATA |
+ NDIS_MAC_OPTION_TRANSFERS_NOT_PEND |
+ NDIS_MAC_OPTION_NO_LOOPBACK;
+ if (IsPrioritySupported(pContext))
+ options |= NDIS_MAC_OPTION_8021P_PRIORITY;
+ if (IsVlanSupported(pContext))
+ options |= NDIS_MAC_OPTION_8021Q_VLAN;
+ SETINFO(ul, options);
+ }
+ break;
+ case OID_GEN_MEDIA_CONNECT_STATUS:
+ SETINFO(ul, pContext->bConnected ? NdisMediaStateConnected :
NdisMediaStateDisconnected);
+ //NdisMediaStateConnected:
+ break;
+ case OID_GEN_MAXIMUM_SEND_PACKETS:
+ // NDIS ignores it for deserialized drivers
+ SETINFO(ul,pContext->nofFreeTxDescriptors);
+ break;
+ case OID_802_3_PERMANENT_ADDRESS:
+ pInfo = pContext->PermanentMacAddress;
+ ulSize = sizeof(pContext->PermanentMacAddress);
+ break;
+ case OID_802_3_CURRENT_ADDRESS:
+ pInfo = pContext->CurrentMacAddress;
+ ulSize = sizeof(pContext->CurrentMacAddress);
+ break;
+ case OID_PNP_QUERY_POWER:
+ // size if 0, just to indicate success
+ pInfo = &status;
+ break;
+ case OID_GEN_DIRECTED_BYTES_XMIT:
+ SETINFO(ul64, pContext->Statistics.ifHCOutUcastOctets);
+ break;
+ case OID_GEN_DIRECTED_FRAMES_XMIT:
+ SETINFO(ul64, pContext->Statistics.ifHCOutUcastPkts);
+ break;
+ case OID_GEN_MULTICAST_BYTES_XMIT:
+ SETINFO(ul64, pContext->Statistics.ifHCOutMulticastOctets);
+ break;
+ case OID_GEN_MULTICAST_FRAMES_XMIT:
+ SETINFO(ul64, pContext->Statistics.ifHCOutMulticastPkts);
+ break;
+ case OID_GEN_BROADCAST_BYTES_XMIT:
+ SETINFO(ul64, pContext->Statistics.ifHCOutBroadcastOctets);
+ break;
+ case OID_GEN_BROADCAST_FRAMES_XMIT:
+ SETINFO(ul64, pContext->Statistics.ifHCOutBroadcastPkts);
+ break;
+ case OID_GEN_DIRECTED_BYTES_RCV:
+ SETINFO(ul64, pContext->Statistics.ifHCInUcastOctets);
+ break;
+ case OID_GEN_DIRECTED_FRAMES_RCV:
+ SETINFO(ul64, pContext->Statistics.ifHCInUcastPkts);
+ break;
+ case OID_GEN_MULTICAST_BYTES_RCV:
+ SETINFO(ul64, pContext->Statistics.ifHCInMulticastOctets);
+ break;
+ case OID_GEN_MULTICAST_FRAMES_RCV:
+ SETINFO(ul64, pContext->Statistics.ifHCInMulticastPkts);
+ break;
+ case OID_GEN_BROADCAST_BYTES_RCV:
+ SETINFO(ul64, pContext->Statistics.ifHCInBroadcastOctets);
+ break;
+ case OID_GEN_BROADCAST_FRAMES_RCV:
+ SETINFO(ul64, pContext->Statistics.ifHCInBroadcastPkts);
+ break;
+ case OID_GEN_XMIT_OK:
+ SETINFO(ul64,
+ pContext->Statistics.ifHCOutUcastPkts +
+ pContext->Statistics.ifHCOutMulticastPkts +
+ pContext->Statistics.ifHCOutBroadcastPkts);
+ break;
+ case OID_GEN_RCV_OK:
+ SETINFO(ul64,
+ pContext->Statistics.ifHCInUcastPkts +
+ pContext->Statistics.ifHCInMulticastPkts +
+ pContext->Statistics.ifHCInBroadcastPkts);
+ DPrintf(4, ("[%s] Total frames %I64u", __FUNCTION__, u.ul64));
+ break;
+ case OID_GEN_XMIT_ERROR:
+ SETINFO(ul64, pContext->Statistics.ifOutErrors );
+ break;
+ case OID_GEN_RCV_ERROR:
+ __fallthrough;
+ case OID_GEN_RCV_NO_BUFFER:
+ __fallthrough;
+ case OID_802_3_RCV_OVERRUN:
+ __fallthrough;
+ case OID_GEN_RCV_CRC_ERROR:
+ __fallthrough;
+ case OID_802_3_RCV_ERROR_ALIGNMENT:
+ __fallthrough;
+ case OID_802_3_XMIT_UNDERRUN:
+ __fallthrough;
+ case OID_802_3_XMIT_ONE_COLLISION:
+ __fallthrough;
+ case OID_802_3_XMIT_DEFERRED:
+ __fallthrough;
+ case OID_802_3_XMIT_MAX_COLLISIONS:
+ __fallthrough;
+ case OID_802_3_XMIT_MORE_COLLISIONS:
+ __fallthrough;
+ case OID_802_3_XMIT_HEARTBEAT_FAILURE:
+ __fallthrough;
+ case OID_802_3_XMIT_TIMES_CRS_LOST:
+ __fallthrough;
+ case OID_802_3_XMIT_LATE_COLLISIONS:
+ SETINFO(ul64, 0);
+ break;
+ case OID_802_3_MULTICAST_LIST:
+ pInfo = pContext->MulticastData.MulticastList;
+ ulSize = pContext->MulticastData.nofMulticastEntries * ETH_LENGTH_OF_ADDRESS;
+ break;
+ case OID_802_3_MAXIMUM_LIST_SIZE:
+ SETINFO(ul, PARANDIS_MULTICAST_LIST_SIZE);
+ break;
+ case OID_PNP_CAPABILITIES:
+ pInfo = &u.PMCaps;
+ ulSize = sizeof(u.PMCaps);
+ ParaNdis_FillPowerCapabilities(&u.PMCaps);
+ break;
+ case OID_802_3_MAC_OPTIONS:
+ SETINFO(ul, 0);
+ break;
+ case OID_GEN_VLAN_ID:
+ SETINFO(ul, pContext->VlanId);
+ if (!IsVlanSupported(pContext))
+ status = NDIS_STATUS_NOT_SUPPORTED;
+ break;
+ case OID_GEN_CURRENT_LOOKAHEAD:
+ if (!pContext->DummyLookAhead) pContext->DummyLookAhead =
pContext->MaxPacketSize.nMaxFullSizeOS;
+ pInfo = &pContext->DummyLookAhead;
+ ulSize = sizeof(pContext->DummyLookAhead);
+ break;
+ case OID_PNP_ENABLE_WAKE_UP:
+ SETINFO(ul, pContext->ulEnableWakeup);
+ break;
+ default:
+ status = NDIS_STATUS_NOT_SUPPORTED;
+ break;
+ }
+
+ if (status == NDIS_STATUS_SUCCESS)
+ {
+ status = ParaNdis_OidQueryCopy(pOid, pInfo, ulSize, bFreeInfo);
+ }
+
+ return status;
+}
+
+
+/**********************************************************
+ Just gets OID name
+***********************************************************/
+const char *ParaNdis_OidName(NDIS_OID oid)
+{
+#undef MAKECASE
+#define MAKECASE(id) case id: return #id;
+ switch (oid)
+ {
+ MAKECASE(OID_GEN_SUPPORTED_LIST)
+ MAKECASE(OID_GEN_HARDWARE_STATUS)
+ MAKECASE(OID_GEN_MEDIA_SUPPORTED)
+ MAKECASE(OID_GEN_MEDIA_IN_USE)
+ MAKECASE(OID_GEN_MAXIMUM_LOOKAHEAD)
+ MAKECASE(OID_GEN_MAXIMUM_FRAME_SIZE)
+ MAKECASE(OID_GEN_LINK_SPEED)
+ MAKECASE(OID_GEN_TRANSMIT_BUFFER_SPACE)
+ MAKECASE(OID_GEN_RECEIVE_BUFFER_SPACE)
+ MAKECASE(OID_GEN_TRANSMIT_BLOCK_SIZE)
+ MAKECASE(OID_GEN_RECEIVE_BLOCK_SIZE)
+ MAKECASE(OID_GEN_VENDOR_ID)
+ MAKECASE(OID_GEN_VENDOR_DESCRIPTION)
+ MAKECASE(OID_GEN_CURRENT_PACKET_FILTER)
+ MAKECASE(OID_GEN_CURRENT_LOOKAHEAD)
+ MAKECASE(OID_GEN_DRIVER_VERSION)
+ MAKECASE(OID_GEN_MAXIMUM_TOTAL_SIZE)
+ MAKECASE(OID_GEN_PROTOCOL_OPTIONS)
+ MAKECASE(OID_GEN_MAC_OPTIONS)
+ MAKECASE(OID_GEN_MEDIA_CONNECT_STATUS)
+ MAKECASE(OID_GEN_MAXIMUM_SEND_PACKETS)
+ MAKECASE(OID_GEN_VENDOR_DRIVER_VERSION)
+ MAKECASE(OID_GEN_SUPPORTED_GUIDS)
+ MAKECASE(OID_GEN_TRANSPORT_HEADER_OFFSET)
+ MAKECASE(OID_GEN_MEDIA_CAPABILITIES)
+ MAKECASE(OID_GEN_PHYSICAL_MEDIUM)
+ MAKECASE(OID_GEN_XMIT_OK)
+ MAKECASE(OID_GEN_RCV_OK)
+ MAKECASE(OID_GEN_XMIT_ERROR)
+ MAKECASE(OID_GEN_RCV_ERROR)
+ MAKECASE(OID_GEN_RCV_NO_BUFFER)
+ MAKECASE(OID_GEN_DIRECTED_BYTES_XMIT)
+ MAKECASE(OID_GEN_DIRECTED_FRAMES_XMIT)
+ MAKECASE(OID_GEN_MULTICAST_BYTES_XMIT)
+ MAKECASE(OID_GEN_MULTICAST_FRAMES_XMIT)
+ MAKECASE(OID_GEN_BROADCAST_BYTES_XMIT)
+ MAKECASE(OID_GEN_BROADCAST_FRAMES_XMIT)
+ MAKECASE(OID_GEN_DIRECTED_BYTES_RCV)
+ MAKECASE(OID_GEN_DIRECTED_FRAMES_RCV)
+ MAKECASE(OID_GEN_MULTICAST_BYTES_RCV)
+ MAKECASE(OID_GEN_MULTICAST_FRAMES_RCV)
+ MAKECASE(OID_GEN_BROADCAST_BYTES_RCV)
+ MAKECASE(OID_GEN_BROADCAST_FRAMES_RCV)
+ MAKECASE(OID_GEN_RCV_CRC_ERROR)
+ MAKECASE(OID_GEN_TRANSMIT_QUEUE_LENGTH)
+ MAKECASE(OID_GEN_GET_TIME_CAPS)
+ MAKECASE(OID_GEN_GET_NETCARD_TIME)
+ MAKECASE(OID_GEN_NETCARD_LOAD)
+ MAKECASE(OID_GEN_DEVICE_PROFILE)
+ MAKECASE(OID_GEN_INIT_TIME_MS)
+ MAKECASE(OID_GEN_RESET_COUNTS)
+ MAKECASE(OID_GEN_MEDIA_SENSE_COUNTS)
+ MAKECASE(OID_GEN_VLAN_ID)
+ MAKECASE(OID_PNP_CAPABILITIES)
+ MAKECASE(OID_PNP_SET_POWER)
+ MAKECASE(OID_PNP_QUERY_POWER)
+ MAKECASE(OID_PNP_ADD_WAKE_UP_PATTERN)
+ MAKECASE(OID_PNP_REMOVE_WAKE_UP_PATTERN)
+ MAKECASE(OID_PNP_ENABLE_WAKE_UP)
+ MAKECASE(OID_802_3_PERMANENT_ADDRESS)
+ MAKECASE(OID_802_3_CURRENT_ADDRESS)
+ MAKECASE(OID_802_3_MULTICAST_LIST)
+ MAKECASE(OID_802_3_MAXIMUM_LIST_SIZE)
+ MAKECASE(OID_802_3_MAC_OPTIONS)
+ MAKECASE(OID_802_3_RCV_ERROR_ALIGNMENT)
+ MAKECASE(OID_802_3_XMIT_ONE_COLLISION)
+ MAKECASE(OID_802_3_XMIT_MORE_COLLISIONS)
+ MAKECASE(OID_802_3_XMIT_DEFERRED)
+ MAKECASE(OID_802_3_XMIT_MAX_COLLISIONS)
+ MAKECASE(OID_802_3_RCV_OVERRUN)
+ MAKECASE(OID_802_3_XMIT_UNDERRUN)
+ MAKECASE(OID_802_3_XMIT_HEARTBEAT_FAILURE)
+ MAKECASE(OID_802_3_XMIT_TIMES_CRS_LOST)
+ MAKECASE(OID_802_3_XMIT_LATE_COLLISIONS)
+ MAKECASE(OID_GEN_MACHINE_NAME)
+ MAKECASE(OID_TCP_TASK_OFFLOAD)
+ MAKECASE(OID_TCP_OFFLOAD_PARAMETERS)
+ MAKECASE(OID_OFFLOAD_ENCAPSULATION)
+ MAKECASE(OID_IP4_OFFLOAD_STATS)
+ MAKECASE(OID_IP6_OFFLOAD_STATS)
+ default:
+ {
+ static UCHAR buffer[9];
+ UINT i;
+ for (i = 0; i < 8; ++i)
+ {
+ UCHAR nibble = (UCHAR)((oid >> (28 - i * 4)) & 0xf);
+ buffer[i] = hexdigit(nibble);
+ }
+ return (char *)buffer;
+ }
+ }
+}
+
+/**********************************************************
+Checker of valid size of provided wake-up patter
+Return value: SUCCESS or kind of failure where the buffer is wrong
+***********************************************************/
+static NDIS_STATUS ValidateWakeupPattern(PNDIS_PM_PACKET_PATTERN p, PULONG pValidSize)
+{
+ NDIS_STATUS status = NDIS_STATUS_BUFFER_TOO_SHORT;
+
+ if (*pValidSize < sizeof(*p))
+ {
+ *pValidSize = sizeof(*p);
+ }
+ else
+ {
+ ULONG ul = p->PatternOffset + p->PatternSize;
+ if (*pValidSize >= ul) status = NDIS_STATUS_SUCCESS;
+ *pValidSize = ul;
+ DPrintf(2, ("[%s] pattern of %d at %d, mask %d (%s)",
+ __FUNCTION__, p->PatternSize, p->PatternOffset, p->MaskSize,
+ status == NDIS_STATUS_SUCCESS ? "OK" : "Fail"));
+ }
+ return status;
+}
+
+
+/**********************************************************
+Common handler of wake-up pattern addition
+***********************************************************/
+NDIS_STATUS ParaNdis_OnAddWakeupPattern(PARANDIS_ADAPTER *pContext, tOidDesc *pOid)
+{
+ NDIS_STATUS status;
+ PNDIS_PM_PACKET_PATTERN pPmPattern = (PNDIS_PM_PACKET_PATTERN)
pOid->InformationBuffer;
+ ULONG ulValidSize = pOid->InformationBufferLength;
+ status = ValidateWakeupPattern(pPmPattern, &ulValidSize);
+ if (status == NDIS_STATUS_SUCCESS)
+ {
+ *pOid->pBytesRead = ulValidSize;
+ }
+ else
+ {
+ *pOid->pBytesRead = 0;
+ *pOid->pBytesNeeded = ulValidSize;
+ }
+ // TODO: Apply
+ return status;
+}
+
+/**********************************************************
+Common handler of wake-up pattern removal
+***********************************************************/
+NDIS_STATUS ParaNdis_OnRemoveWakeupPattern(PARANDIS_ADAPTER *pContext, tOidDesc *pOid)
+{
+ NDIS_STATUS status;
+ PNDIS_PM_PACKET_PATTERN pPmPattern = (PNDIS_PM_PACKET_PATTERN)
pOid->InformationBuffer;
+ ULONG ulValidSize = pOid->InformationBufferLength;
+ status = ValidateWakeupPattern(pPmPattern, &ulValidSize);
+ if (status == NDIS_STATUS_SUCCESS)
+ {
+ *pOid->pBytesRead = ulValidSize;
+ }
+ else
+ {
+ *pOid->pBytesRead = 0;
+ *pOid->pBytesNeeded = ulValidSize;
+ }
+ return status;
+}
+
+/**********************************************************
+Common handler of wake-up enabling upon standby
+***********************************************************/
+NDIS_STATUS ParaNdis_OnEnableWakeup(PARANDIS_ADAPTER *pContext, tOidDesc *pOid)
+{
+ NDIS_STATUS status = ParaNdis_OidSetCopy(pOid, &pContext->ulEnableWakeup,
sizeof(pContext->ulEnableWakeup));
+ if (status == NDIS_STATUS_SUCCESS)
+ {
+ DPrintf(0, ("[%s] new value %lX", __FUNCTION__,
pContext->ulEnableWakeup));
+ }
+ return status;
+}
+
+/**********************************************************
+Dummy implementation
+***********************************************************/
+NDIS_STATUS ParaNdis_OnSetLookahead(PARANDIS_ADAPTER *pContext, tOidDesc *pOid)
+{
+ return ParaNdis_OidSetCopy(pOid, &pContext->DummyLookAhead,
sizeof(pContext->DummyLookAhead));
+}
+
+NDIS_STATUS ParaNdis_OnSetVlanId(PARANDIS_ADAPTER *pContext, tOidDesc *pOid)
+{
+ NDIS_STATUS status = NDIS_STATUS_NOT_SUPPORTED;
+ if (IsVlanSupported(pContext))
+ {
+ status = ParaNdis_OidSetCopy(pOid, &pContext->VlanId,
sizeof(pContext->VlanId));
+ pContext->VlanId &= 0xfff;
+ DPrintf(0, ("[%s] new value %d on MAC %X", __FUNCTION__,
pContext->VlanId, pContext->CurrentMacAddress[5]));
+ ParaNdis_DeviceFiltersUpdateVlanId(pContext);
+ }
+ return status;
+}
+
+/**********************************************************
+Retrieves support rules for specific OID
+***********************************************************/
+void ParaNdis_GetOidSupportRules(NDIS_OID oid, tOidWhatToDo *pRule, const tOidWhatToDo
*Table)
+{
+ static const tOidWhatToDo defaultRule = { 0, 0, 0, 0, 0, NULL, "Unknown
OID" };
+ UINT i;
+ *pRule = defaultRule;
+ pRule->oid = oid;
+
+ for (i = 0; Table[i].oid != 0; ++i)
+ {
+ if (Table[i].oid == oid)
+ {
+ *pRule = Table[i];
+ break;
+ }
+ }
+ pRule->name = ParaNdis_OidName(oid);
+}
diff --git a/drivers/network/dd/netkvm/Common/ParaNdis-Oid.h
b/drivers/network/dd/netkvm/Common/ParaNdis-Oid.h
new file mode 100644
index 00000000000..aac453ab97c
--- /dev/null
+++ b/drivers/network/dd/netkvm/Common/ParaNdis-Oid.h
@@ -0,0 +1,104 @@
+/*
+ * This file contains common for NDIS5/NDIS6 definition,
+ * related to OID support
+ *
+ * Copyright (c) 2008-2017 Red Hat, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met :
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and / or other materials provided with the distribution.
+ * 3. Neither the names of the copyright holders nor the names of their contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS''
AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+#ifndef PARANDIS_COMMON_OID_H
+#define PARANDIS_COMMON_OID_H
+
+#include "ndis56common.h"
+
+/**********************************************************
+Wrapper for all the data, related to any OID request
+***********************************************************/
+typedef struct _tagOidDesc
+{
+ NDIS_OID Oid; // oid code
+ ULONG ulToDoFlags; // combination of
eOidHelperFlags
+ PVOID InformationBuffer; // buffer received from NDIS
+ UINT InformationBufferLength; // its length
+ PUINT pBytesWritten; // OUT for query/method
+ PUINT pBytesNeeded; // OUT for query/set/method when
length of buffer is wrong
+ PUINT pBytesRead; // OUT for set/method
+ PVOID Reserved; // Reserved for pending requests
+} tOidDesc;
+
+typedef NDIS_STATUS (*OIDHANDLERPROC)(PARANDIS_ADAPTER *pContext, tOidDesc *pOid);
+
+typedef struct _tagOidWhatToDo
+{
+ NDIS_OID oid; // oid number
+ int nEntryLevel; // do print on entry level
+ int nExitFailLevel; // do print on exit if failed
+ int nExitOKLevel; // do print on exit if OK
+ UINT Flags;
+ OIDHANDLERPROC OidSetProc; // procedure to call on SET
+ const char *name; // printable name
+}tOidWhatToDo;
+
+
+typedef enum _tageOidHelperFlags {
+ ohfQuery = 1, // can be queried
+ ohfSet = 2, // can be set
+ ohfQuerySet = ohfQuery | ohfSet,
+ ohfQueryStatOnly = 4, // redirect query stat to query
+ ohfQueryStat = ohfQueryStatOnly | ohfQuery,
+ ohfQuery3264 = 8 | ohfQuery, // convert 64 to 32 on query
+ ohfQueryStat3264 = 8 | ohfQueryStat, // convert 64 to 32 on query stat
+ ohfSetLessOK = 16, // on set: if buffer is smaller,
cleanup and copy
+ ohfSetMoreOK = 32 // on set: if buffer is larger, copy
anyway
+} eOidHelperFlags;
+
+
+
+
+/**********************************************************
+Common procedures related to OID support
+***********************************************************/
+NDIS_STATUS ParaNdis_OidQueryCommon(PARANDIS_ADAPTER *pContext, tOidDesc *pOid);
+NDIS_STATUS ParaNdis_OidQueryCopy(tOidDesc *pOid, PVOID pInfo, ULONG ulSize, BOOLEAN
bFreeInfo);
+static NDIS_STATUS ParaNdis_OidQuery(PARANDIS_ADAPTER *pContext, tOidDesc *pOid);
+NDIS_STATUS ParaNdis_OnOidSetMulticastList(PARANDIS_ADAPTER *pContext, tOidDesc *pOid);
+NDIS_STATUS ParaNdis_OnSetPacketFilter(PARANDIS_ADAPTER *pContext, tOidDesc *pOid);
+NDIS_STATUS ParaNdis_OnAddWakeupPattern(PARANDIS_ADAPTER *pContext, tOidDesc *pOid);
+NDIS_STATUS ParaNdis_OnRemoveWakeupPattern(PARANDIS_ADAPTER *pContext, tOidDesc *pOid);
+NDIS_STATUS ParaNdis_OnEnableWakeup(PARANDIS_ADAPTER *pContext, tOidDesc *pOid);
+NDIS_STATUS ParaNdis_OnSetLookahead(PARANDIS_ADAPTER *pContext, tOidDesc *pOid);
+NDIS_STATUS ParaNdis_OnSetVlanId(PARANDIS_ADAPTER *pContext, tOidDesc *pOid);
+NDIS_STATUS ParaNdis_OidSetCopy(tOidDesc *pOid, PVOID pDest, ULONG ulSize);
+void ParaNdis_FillPowerCapabilities(PNDIS_PNP_CAPABILITIES pCaps);
+void ParaNdis_GetOidSupportRules(NDIS_OID oid, tOidWhatToDo *pRule, const tOidWhatToDo
*Table);
+
+
+const char *ParaNdis_OidName(NDIS_OID oid);
+/**********************************************************
+Procedures to be implemented in NDIS5/NDIS6 specific modules
+***********************************************************/
+void ParaNdis_GetSupportedOid(PVOID *pOidsArray, PULONG pLength);
+NDIS_STATUS ParaNdis_OnSetPower(PARANDIS_ADAPTER *pContext, tOidDesc *pOid);
+
+#endif
diff --git a/drivers/network/dd/netkvm/Common/ParaNdis-VirtIO.c
b/drivers/network/dd/netkvm/Common/ParaNdis-VirtIO.c
new file mode 100644
index 00000000000..a1525502696
--- /dev/null
+++ b/drivers/network/dd/netkvm/Common/ParaNdis-VirtIO.c
@@ -0,0 +1,389 @@
+/*
+ * This file contains NDIS driver VirtIO callbacks
+ *
+ * Copyright (c) 2008-2017 Red Hat, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met :
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and / or other materials provided with the distribution.
+ * 3. Neither the names of the copyright holders nor the names of their contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS''
AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+#include "ndis56common.h"
+
+/////////////////////////////////////////////////////////////////////////////////////
+//
+// ReadVirtIODeviceRegister\WriteVirtIODeviceRegister
+// NDIS specific implementation of the IO and memory space read\write
+//
+// The lower 64k of memory is never mapped so we can use the same routines
+// for both port I/O and memory access and use the address alone to decide
+// which space to use.
+/////////////////////////////////////////////////////////////////////////////////////
+
+#define PORT_MASK 0xFFFF
+
+static u32 ReadVirtIODeviceRegister(ULONG_PTR ulRegister)
+{
+ ULONG ulValue;
+
+ if (ulRegister & ~PORT_MASK) {
+ NdisReadRegisterUlong(ulRegister, &ulValue);
+ } else {
+ NdisRawReadPortUlong(ulRegister, &ulValue);
+ }
+
+ DPrintf(6, ("[%s]R[%x]=%x", __FUNCTION__, (ULONG)ulRegister, ulValue));
+ return ulValue;
+}
+
+static void WriteVirtIODeviceRegister(ULONG_PTR ulRegister, u32 ulValue)
+{
+ DPrintf(6, ("[%s]R[%x]=%x", __FUNCTION__, (ULONG)ulRegister, ulValue));
+
+ if (ulRegister & ~PORT_MASK) {
+ NdisWriteRegisterUlong((PULONG)ulRegister, ulValue);
+ } else {
+ NdisRawWritePortUlong(ulRegister, ulValue);
+ }
+}
+
+static u8 ReadVirtIODeviceByte(ULONG_PTR ulRegister)
+{
+ u8 bValue;
+
+ if (ulRegister & ~PORT_MASK) {
+ NdisReadRegisterUchar(ulRegister, &bValue);
+ } else {
+ NdisRawReadPortUchar(ulRegister, &bValue);
+ }
+
+ DPrintf(6, ("[%s]R[%x]=%x", __FUNCTION__, (ULONG)ulRegister, bValue));
+ return bValue;
+}
+
+static void WriteVirtIODeviceByte(ULONG_PTR ulRegister, u8 bValue)
+{
+ DPrintf(6, ("[%s]R[%x]=%x", __FUNCTION__, (ULONG)ulRegister, bValue));
+
+ if (ulRegister & ~PORT_MASK) {
+ NdisWriteRegisterUchar((PUCHAR)ulRegister, bValue);
+ } else {
+ NdisRawWritePortUchar(ulRegister, bValue);
+ }
+}
+
+static u16 ReadVirtIODeviceWord(ULONG_PTR ulRegister)
+{
+ u16 wValue;
+
+ if (ulRegister & ~PORT_MASK) {
+ NdisReadRegisterUshort(ulRegister, &wValue);
+ } else {
+ NdisRawReadPortUshort(ulRegister, &wValue);
+ }
+
+ DPrintf(6, ("[%s]R[%x]=%x\n", __FUNCTION__, (ULONG)ulRegister, wValue));
+ return wValue;
+}
+
+static void WriteVirtIODeviceWord(ULONG_PTR ulRegister, u16 wValue)
+{
+#if 1
+ if (ulRegister & ~PORT_MASK) {
+ NdisWriteRegisterUshort((PUSHORT)ulRegister, wValue);
+ } else {
+ NdisRawWritePortUshort(ulRegister, wValue);
+ }
+#else
+ // test only to cause long TX waiting queue of NDIS packets
+ // to recognize it and request for reset via Hang handler
+ static int nCounterToFail = 0;
+ static const int StartFail = 200, StopFail = 600;
+ BOOLEAN bFail = FALSE;
+ DPrintf(6, ("%s> R[%x] = %x\n", __FUNCTION__, (ULONG)ulRegister,
wValue));
+ if ((ulRegister & 0x1F) == 0x10)
+ {
+ nCounterToFail++;
+ bFail = nCounterToFail >= StartFail && nCounterToFail < StopFail;
+ }
+ if (!bFail) NdisRawWritePortUshort(ulRegister, wValue);
+ else
+ {
+ DPrintf(0, ("%s> FAILING R[%x] = %x\n", __FUNCTION__,
(ULONG)ulRegister, wValue));
+ }
+#endif
+}
+
+static void *mem_alloc_contiguous_pages(void *context, size_t size)
+{
+ PARANDIS_ADAPTER *pContext = (PARANDIS_ADAPTER *)context;
+ PVOID retVal = NULL;
+ ULONG i;
+
+ /* find the first unused memory range of the requested size */
+ for (i = 0; i < MAX_NUM_OF_QUEUES; i++) {
+ if (pContext->SharedMemoryRanges[i].pBase != NULL &&
+ pContext->SharedMemoryRanges[i].bUsed == FALSE &&
+ pContext->SharedMemoryRanges[i].uLength == (ULONG)size) {
+ retVal = pContext->SharedMemoryRanges[i].pBase;
+ pContext->SharedMemoryRanges[i].bUsed = TRUE;
+ break;
+ }
+ }
+
+ if (!retVal) {
+ /* find the first null memory range descriptor and allocate */
+ for (i = 0; i < MAX_NUM_OF_QUEUES; i++) {
+ if (pContext->SharedMemoryRanges[i].pBase == NULL) {
+ break;
+ }
+ }
+ if (i < MAX_NUM_OF_QUEUES) {
+ NdisMAllocateSharedMemory(
+ pContext->MiniportHandle,
+ (ULONG)size,
+ TRUE /* Cached */,
+ &pContext->SharedMemoryRanges[i].pBase,
+ &pContext->SharedMemoryRanges[i].BasePA);
+ retVal = pContext->SharedMemoryRanges[i].pBase;
+ if (retVal) {
+ NdisZeroMemory(retVal, size);
+ pContext->SharedMemoryRanges[i].uLength = (ULONG)size;
+ pContext->SharedMemoryRanges[i].bUsed = TRUE;
+ }
+ }
+ }
+
+ if (retVal) {
+ DPrintf(6, ("[%s] returning %p, size %x\n", __FUNCTION__, retVal,
(ULONG)size));
+ } else {
+ DPrintf(0, ("[%s] failed to allocate size %x\n", __FUNCTION__,
(ULONG)size));
+ }
+ return retVal;
+}
+
+static void mem_free_contiguous_pages(void *context, void *virt)
+{
+ PARANDIS_ADAPTER *pContext = (PARANDIS_ADAPTER *)context;
+ ULONG i;
+
+ for (i = 0; i < MAX_NUM_OF_QUEUES; i++) {
+ if (pContext->SharedMemoryRanges[i].pBase == virt) {
+ pContext->SharedMemoryRanges[i].bUsed = FALSE;
+ break;
+ }
+ }
+
+ if (i < MAX_NUM_OF_QUEUES) {
+ DPrintf(6, ("[%s] freed %p at index %d\n", __FUNCTION__, virt, i));
+ } else {
+ DPrintf(0, ("[%s] failed to free %p\n", __FUNCTION__, virt));
+ }
+}
+
+static ULONGLONG mem_get_physical_address(void *context, void *virt)
+{
+ PARANDIS_ADAPTER *pContext = (PARANDIS_ADAPTER *)context;
+ ULONG_PTR uAddr = (ULONG_PTR)virt;
+ ULONG i;
+
+ for (i = 0; i < MAX_NUM_OF_QUEUES; i++) {
+ ULONG_PTR uBase = (ULONG_PTR)pContext->SharedMemoryRanges[i].pBase;
+ if (uAddr >= uBase && uAddr < (uBase +
pContext->SharedMemoryRanges[i].uLength)) {
+ ULONGLONG retVal = pContext->SharedMemoryRanges[i].BasePA.QuadPart +
(uAddr - uBase);
+
+ DPrintf(6, ("[%s] translated %p to %I64X\n", __FUNCTION__, virt,
retVal));
+ return retVal;
+ }
+ }
+
+ DPrintf(0, ("[%s] failed to translate %p\n", __FUNCTION__, virt));
+ return 0;
+}
+
+static void *mem_alloc_nonpaged_block(void *context, size_t size)
+{
+ PVOID retVal;
+
+ if (NdisAllocateMemoryWithTag(
+ &retVal,
+ (UINT)size,
+ PARANDIS_MEMORY_TAG) != NDIS_STATUS_SUCCESS) {
+ retVal = NULL;
+ }
+
+ if (retVal) {
+ NdisZeroMemory(retVal, size);
+ DPrintf(6, ("[%s] returning %p, len %x\n", __FUNCTION__, retVal,
(ULONG)size));
+ } else {
+ DPrintf(0, ("[%s] failed to allocate size %x\n", __FUNCTION__,
(ULONG)size));
+ }
+ return retVal;
+}
+
+static void mem_free_nonpaged_block(void *context, void *addr)
+{
+ UNREFERENCED_PARAMETER(context);
+
+ NdisFreeMemory(addr, 0, 0);
+ DPrintf(6, ("[%s] freed %p\n", __FUNCTION__, addr));
+}
+
+static int PCIReadConfig(PPARANDIS_ADAPTER pContext,
+ int where,
+ void *buffer,
+ size_t length)
+{
+ ULONG read;
+
+ read = NdisReadPciSlotInformation(
+ pContext->MiniportHandle,
+ 0 /* SlotNumber */,
+ where,
+ buffer,
+ (ULONG)length);
+
+ if (read == length) {
+ DPrintf(6, ("[%s] read %d bytes at %d\n", __FUNCTION__, read, where));
+ return 0;
+ } else {
+ DPrintf(0, ("[%s] failed to read %d bytes at %d\n", __FUNCTION__, read,
where));
+ return -1;
+ }
+}
+
+static int pci_read_config_byte(void *context, int where, u8 *bVal)
+{
+ return PCIReadConfig((PPARANDIS_ADAPTER)context, where, bVal, sizeof(*bVal));
+}
+
+static int pci_read_config_word(void *context, int where, u16 *wVal)
+{
+ return PCIReadConfig((PPARANDIS_ADAPTER)context, where, wVal, sizeof(*wVal));
+}
+
+static int pci_read_config_dword(void *context, int where, u32 *dwVal)
+{
+ return PCIReadConfig((PPARANDIS_ADAPTER)context, where, dwVal, sizeof(*dwVal));
+}
+
+static size_t pci_get_resource_len(void *context, int bar)
+{
+ PARANDIS_ADAPTER *pContext = (PARANDIS_ADAPTER *)context;
+
+ if (bar < PCI_TYPE0_ADDRESSES) {
+ return pContext->AdapterResources.PciBars[bar].uLength;
+ }
+
+ DPrintf(0, ("[%s] queried invalid BAR %d\n", __FUNCTION__, bar));
+ return 0;
+}
+
+static void *pci_map_address_range(void *context, int bar, size_t offset, size_t maxlen)
+{
+ PARANDIS_ADAPTER *pContext = (PARANDIS_ADAPTER *)context;
+
+ if (bar < PCI_TYPE0_ADDRESSES) {
+ tBusResource *pRes = &pContext->AdapterResources.PciBars[bar];
+ if (pRes->pBase == NULL) {
+ /* BAR not mapped yet */
+ if (pRes->bPortSpace) {
+ if (NDIS_STATUS_SUCCESS == NdisMRegisterIoPortRange(
+ &pRes->pBase,
+ pContext->MiniportHandle,
+ pRes->BasePA.LowPart,
+ pRes->uLength)) {
+ DPrintf(6, ("[%s] mapped port BAR at %x\n", __FUNCTION__,
pRes->BasePA.LowPart));
+ } else {
+ pRes->pBase = NULL;
+ DPrintf(0, ("[%s] failed to map port BAR at %x\n",
__FUNCTION__, pRes->BasePA.LowPart));
+ }
+ } else {
+ if (NDIS_STATUS_SUCCESS == NdisMMapIoSpace(
+ &pRes->pBase,
+ pContext->MiniportHandle,
+ pRes->BasePA,
+ pRes->uLength)) {
+ DPrintf(6, ("[%s] mapped memory BAR at %I64x\n",
__FUNCTION__, pRes->BasePA.QuadPart));
+ } else {
+ pRes->pBase = NULL;
+ DPrintf(0, ("[%s] failed to map memory BAR at %I64x\n",
__FUNCTION__, pRes->BasePA.QuadPart));
+ }
+ }
+ }
+ if (pRes->pBase != NULL && offset < pRes->uLength) {
+ if (pRes->bPortSpace) {
... 9139 lines suppressed ...