https://git.reactos.org/?p=reactos.git;a=commitdiff;h=4c37757e81109128109feb...
commit 4c37757e81109128109feb0a0a9342452c6caf78 Author: Nguyen Trung Khanh nguyentrungkhanh97@gmail.com AuthorDate: Thu Mar 5 10:58:56 2020 +0700 Commit: Victor Perevertkin victor.perevertkin@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 ...