From bb4899ec468ef5db744291cffa5d8bbd64cdfa40 Mon Sep 17 00:00:00 2001 From: Yuri Benditovich Date: Fri, 8 May 2020 18:11:48 +0300 Subject: [PATCH] netkvm: initial implementation of teaming protocol driver Signed-off-by: Annie Li Signed-off-by: Yuri Benditovich --- NetKVM/Common/ParaNdis_Protocol.cpp | 852 ++++++++++++++++++ NetKVM/Common/ParaNdis_Protocol.h | 9 + NetKVM/NetKVM-VS2015.sln | 27 + NetKVM/NetKVM-VS2015.vcxproj | 2 + NetKVM/NetKVM-VS2015.vcxproj.filters | 7 + NetKVM/NotifyObject/LICENSE | 29 + NetKVM/NotifyObject/Readme.md | 56 ++ NetKVM/NotifyObject/netkvmno.RGS | 13 + NetKVM/NotifyObject/netkvmno.cpp | 283 ++++++ NetKVM/NotifyObject/netkvmno.def | 6 + NetKVM/NotifyObject/netkvmno.h | 85 ++ NetKVM/NotifyObject/netkvmno.rc | 17 + NetKVM/NotifyObject/netkvmnoif.idl | 24 + NetKVM/NotifyObject/notifyobject.props | 18 + NetKVM/NotifyObject/notifyobject.vcxproj | 356 ++++++++ .../NotifyObject/notifyobject.vcxproj.Filters | 58 ++ NetKVM/NotifyObject/notifyobject.vcxproj.user | 4 + NetKVM/NotifyObject/resource.h | 6 + NetKVM/NotifyObject/stdafx.h | 47 + NetKVM/NotifyObject/utils.cpp | 98 ++ NetKVM/NotifyObject/vioprot.inf | 90 ++ NetKVM/wlh/ParaNdis6_Driver.cpp | 54 +- 22 files changed, 2136 insertions(+), 5 deletions(-) create mode 100644 NetKVM/Common/ParaNdis_Protocol.cpp create mode 100644 NetKVM/Common/ParaNdis_Protocol.h create mode 100644 NetKVM/NotifyObject/LICENSE create mode 100644 NetKVM/NotifyObject/Readme.md create mode 100644 NetKVM/NotifyObject/netkvmno.RGS create mode 100644 NetKVM/NotifyObject/netkvmno.cpp create mode 100644 NetKVM/NotifyObject/netkvmno.def create mode 100644 NetKVM/NotifyObject/netkvmno.h create mode 100644 NetKVM/NotifyObject/netkvmno.rc create mode 100644 NetKVM/NotifyObject/netkvmnoif.idl create mode 100644 NetKVM/NotifyObject/notifyobject.props create mode 100644 NetKVM/NotifyObject/notifyobject.vcxproj create mode 100644 NetKVM/NotifyObject/notifyobject.vcxproj.Filters create mode 100644 NetKVM/NotifyObject/notifyobject.vcxproj.user create mode 100644 NetKVM/NotifyObject/resource.h create mode 100644 NetKVM/NotifyObject/stdafx.h create mode 100644 NetKVM/NotifyObject/utils.cpp create mode 100644 NetKVM/NotifyObject/vioprot.inf diff --git a/NetKVM/Common/ParaNdis_Protocol.cpp b/NetKVM/Common/ParaNdis_Protocol.cpp new file mode 100644 index 00000000..1d4066c9 --- /dev/null +++ b/NetKVM/Common/ParaNdis_Protocol.cpp @@ -0,0 +1,852 @@ +/* + * This file contains implementation of protocol for binding + * virtio-net and SRIOV device + * + * Copyright (c) 2020 Red Hat, Inc. + * Copyright (c) 2020 Oracle Corporation + * + * + * 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_Protocol.h" +#include "ParaNdis-SM.h" +#include "ParaNdis-Oid.h" +#include "Trace.h" + +#if NDIS_SUPPORT_NDIS630 + +#ifdef NETKVM_WPP_ENABLED +#include "ParaNdis_Protocol.tmh" +#endif + +static PVOID ParaNdis_ReferenceBinding(PARANDIS_ADAPTER *pContext) +{ + return pContext->m_StateMachine.ReferenceSriovBinding(); +} + +static void ParaNdis_DereferenceBinding(PARANDIS_ADAPTER *pContext) +{ + pContext->m_StateMachine.DereferenceSriovBinding(); +} + +class CAdapterEntry : public CNdisAllocatable +{ +public: + CAdapterEntry(PARANDIS_ADAPTER *pContext) : m_Adapter(pContext), m_Binding(NULL) + { + NdisMoveMemory(m_MacAddress, pContext->CurrentMacAddress, sizeof(m_MacAddress)); + } + CAdapterEntry(PVOID Binding, const UINT8 *MacAddress) : m_Adapter(NULL), m_Binding(Binding) + { + NdisMoveMemory(m_MacAddress, MacAddress, sizeof(m_MacAddress)); + } + bool MatchMac(UCHAR *MacAddress) const + { + bool diff; + ETH_COMPARE_NETWORK_ADDRESSES_EQ_SAFE(MacAddress, m_MacAddress, &diff); + return !diff; + } + void NotifyAdapterRemoval() + { + Notifier(m_Binding, Removal); + } + void NotifyAdapterArrival() + { + Notifier(m_Binding, Arrival, m_Adapter); + } + void NotifyAdapterDetach() + { + Notifier(m_Binding, Detach); + } + PARANDIS_ADAPTER *m_Adapter; + PVOID m_Binding; +private: + enum NotifyEvent + { + Arrival, + Removal, + Detach + }; + UINT8 m_MacAddress[ETH_HARDWARE_ADDRESS_SIZE]; + static void Notifier(PVOID Binding, NotifyEvent, PARANDIS_ADAPTER *Adapter = NULL); + DECLARE_CNDISLIST_ENTRY(CAdapterEntry); +}; + +class COidWrapper : public CNdisAllocatable +{ +public: + COidWrapper(NDIS_REQUEST_TYPE RequestType, NDIS_OID oid) + { + NdisZeroMemory(&m_Request, sizeof(m_Request)); + PVOID data = this; + NdisMoveMemory(m_Request.SourceReserved, &data, sizeof(data)); + m_Request.Header.Type = NDIS_OBJECT_TYPE_OID_REQUEST; + m_Request.Header.Revision = NDIS_OID_REQUEST_REVISION_1; + m_Request.Header.Size = NDIS_SIZEOF_OID_REQUEST_REVISION_1; + m_Request.PortNumber = NDIS_DEFAULT_PORT_NUMBER; + m_Request.RequestType = RequestType; + m_Request.DATA.QUERY_INFORMATION.Oid = oid; + } + virtual void Complete(NDIS_STATUS status) + { + TraceNoPrefix(0, "[%s] %s\n", __FUNCTION__, ParaNdis_OidName(m_Request.DATA.SET_INFORMATION.Oid)); + m_Status = status; + m_Event.Notify(); + } + void Run(NDIS_HANDLE Owner) + { + bool synchronous = m_Synchronous; + NDIS_STATUS status = NdisOidRequest(Owner, &m_Request); + if (status == NDIS_STATUS_PENDING) + { + // it's risky to touch the object, it may be async + if (synchronous) + { + Wait(); + } + } + else + { + Complete(status); + } + } + NDIS_STATUS Status() const { return m_Status; } + NDIS_OID_REQUEST m_Request; + virtual ~COidWrapper() { } +protected: + bool m_Synchronous = true; +private: + void Wait() + { + m_Event.Wait(); + } + CNdisEvent m_Event; + NDIS_STATUS m_Status; +}; + +class COidWrapperAsync : public COidWrapper +{ +public: + COidWrapperAsync(PARANDIS_ADAPTER *Adapter, NDIS_REQUEST_TYPE RequestType, NDIS_OID oid, PVOID buffer, ULONG length) : + COidWrapper(RequestType, oid), + m_Length(length), + m_Adapter(Adapter), + m_Handle(Adapter->MiniportHandle) + { + m_Synchronous = false; + if (m_Length) + { + m_Data = NdisAllocateMemoryWithTagPriority(m_Handle, length, DataTag, NormalPoolPriority); + } + if (m_Data) + { + NdisMoveMemory(m_Data, buffer, length); + m_Request.DATA.SET_INFORMATION.InformationBuffer = m_Data; + m_Request.DATA.SET_INFORMATION.InformationBufferLength = m_Length; + } + } + void Complete(NDIS_STATUS status) override + { + __super::Complete(status); + ParaNdis_DereferenceBinding(m_Adapter); + Destroy(this, m_Handle); + } + ~COidWrapperAsync() + { + if (m_Data) + { + NdisFreeMemoryWithTagPriority(m_Handle, m_Data, DataTag); + } + } +private: + const ULONG DataTag = '3ORP'; + PVOID m_Data = NULL; + ULONG m_Length = 0; + NDIS_HANDLE m_Handle; + PARANDIS_ADAPTER *m_Adapter; +}; + +class CParaNdisProtocol; + +class CProtocolBinding : public CNdisAllocatable, public CRefCountingObject +{ +public: + CProtocolBinding(CParaNdisProtocol& Protocol, NDIS_HANDLE BindContext) : + m_Protocol(Protocol), + m_BindContext(BindContext), + m_Status(NDIS_STATUS_ADAPTER_NOT_OPEN), + m_BindingHandle(NULL), + m_BoundAdapter(NULL) + { + TraceNoPrefix(0, "[%s] %p\n", __FUNCTION__, this); + } + ~CProtocolBinding() + { + TraceNoPrefix(0, "[%s] %p\n", __FUNCTION__, this); + } + NDIS_STATUS Bind(PNDIS_BIND_PARAMETERS BindParameters); + NDIS_STATUS Unbind(NDIS_HANDLE UnbindContext); + void CheckCapabilities(PNDIS_BIND_PARAMETERS) { } + void Complete(NDIS_STATUS Status) + { + m_Status = Status; + m_Event.Notify(); + } + void OidComplete(PNDIS_OID_REQUEST OidRequest, NDIS_STATUS Status) + { + PVOID data; + NdisMoveMemory(&data, OidRequest->SourceReserved, sizeof(data)); + COidWrapper *pOid = (COidWrapper *)data; + pOid->Complete(Status); + } + void OnReceive(PNET_BUFFER_LIST Nbls, ULONG NofNbls, ULONG Flags); + void OnSendCompletion(PNET_BUFFER_LIST Nbls, ULONG Flags); + // called under protocol mutex from NETKVM's Halt() + void OnAdapterHalted() + { + m_TxStateMachine.Stop(); + m_RxStateMachine.Stop(); + m_BoundAdapter = NULL; + } + // called under protocol mutex + // when netkvm adapter comes and binding present + // when binding to VFIO comes and netkvm adapter present + void OnAdapterAttach(PARANDIS_ADAPTER *Adapter) + { + TraceNoPrefix(0, "[%s] Found failover adapter %p\n", __FUNCTION__, Adapter); + SetOid(OID_GEN_CURRENT_PACKET_FILTER, &Adapter->PacketFilter, sizeof(Adapter->PacketFilter)); + if (Adapter->MulticastData.nofMulticastEntries) + { + SetOid(OID_802_3_MULTICAST_LIST, Adapter->MulticastData.MulticastList, + ETH_ALEN * Adapter->MulticastData.nofMulticastEntries); + } + // TODO: some other OIDs? + + m_BoundAdapter = Adapter; + Adapter->m_StateMachine.NotifyBindSriov(this); + m_TxStateMachine.Start(); + m_RxStateMachine.Start(); + } + // called under protocol mutex before close VFIO adapter + void OnAdapterDetach() + { + m_TxStateMachine.Stop(); + m_RxStateMachine.Stop(); + m_BoundAdapter->m_StateMachine.NotifyUnbindSriov(); + ParaNdis_SendGratuitousArpPacket(m_BoundAdapter); + m_BoundAdapter = NULL; + } + void OnStatusIndication(PNDIS_STATUS_INDICATION StatusIndication) + { + TraceNoPrefix(0, "[%s] code %X\n", __FUNCTION__, StatusIndication->StatusCode); + } + void OnPnPEvent(PNET_PNP_EVENT_NOTIFICATION NetPnPEventNotification) + { + TraceNoPrefix(0, "[%s] event %X\n", __FUNCTION__, NetPnPEventNotification->NetPnPEvent.NetEvent); + } + CFlowStateMachine m_RxStateMachine; + CFlowStateMachine m_TxStateMachine; + bool Send(PNET_BUFFER_LIST Nbl, ULONG Count); + void ReturnNbls(PNET_BUFFER_LIST pNBL, ULONG numNBLs); + void SetOidAsync(ULONG oid, PVOID data, ULONG size); + void SetOid(ULONG oid, PVOID data, ULONG size); +private: + void OnLastReferenceGone() override; + CParaNdisProtocol& m_Protocol; + NDIS_HANDLE m_BindContext; + NDIS_HANDLE m_BindingHandle; + PARANDIS_ADAPTER *m_BoundAdapter; + CNdisEvent m_Event; + NDIS_STATUS m_Status; +}; + +static CParaNdisProtocol *ProtocolData = NULL; + +class CParaNdisProtocol : + public CNdisAllocatable, + public CRefCountingObject +{ +public: + CParaNdisProtocol(NDIS_HANDLE DriverHandle) : + m_DriverHandle(DriverHandle), + m_ProtocolHandle(NULL) + { + NDIS_PROTOCOL_DRIVER_CHARACTERISTICS pchs = {}; + pchs.Name = NDIS_STRING_CONST("netkvmp"); + //pchs.Name = NDIS_STRING_CONST("NDISPROT"); + pchs.Header.Type = NDIS_OBJECT_TYPE_PROTOCOL_DRIVER_CHARACTERISTICS; + pchs.Header.Revision = NDIS_PROTOCOL_DRIVER_CHARACTERISTICS_REVISION_2; + pchs.Header.Size = NDIS_SIZEOF_PROTOCOL_DRIVER_CHARACTERISTICS_REVISION_2; + pchs.MajorNdisVersion = 6; + pchs.MinorNdisVersion = 30; + pchs.MajorDriverVersion = (UCHAR)(PARANDIS_MAJOR_DRIVER_VERSION & 0xFF); + pchs.MinorDriverVersion = (UCHAR)(PARANDIS_MINOR_DRIVER_VERSION & 0xFF); + pchs.BindAdapterHandlerEx = []( + _In_ NDIS_HANDLE ProtocolDriverContext, + _In_ NDIS_HANDLE BindContext, + _In_ PNDIS_BIND_PARAMETERS BindParameters) + { + TraceNoPrefix(0, "[BindAdapterHandlerEx] binding %p\n", BindContext); + return ((CParaNdisProtocol *)ProtocolDriverContext)->OnBindAdapter(BindContext, BindParameters); + }; + pchs.OpenAdapterCompleteHandlerEx = []( + _In_ NDIS_HANDLE ProtocolBindingContext, + _In_ NDIS_STATUS Status) + { + TraceNoPrefix(0, "[OpenAdapterCompleteHandlerEx] %X, ctx %p\n", Status, ProtocolBindingContext); + CProtocolBinding *binding = (CProtocolBinding *)ProtocolBindingContext; + binding->Complete(Status); + }; + pchs.UnbindAdapterHandlerEx = []( + _In_ NDIS_HANDLE UnbindContext, + _In_ NDIS_HANDLE ProtocolBindingContext) + { + TraceNoPrefix(0, "[UnbindAdapterHandlerEx] ctx %p, binding %p\n", UnbindContext, ProtocolBindingContext); + CProtocolBinding *binding = (CProtocolBinding *)ProtocolBindingContext; + return binding->Unbind(UnbindContext); + }; + pchs.CloseAdapterCompleteHandlerEx = []( + _In_ NDIS_HANDLE ProtocolBindingContext) + { + TraceNoPrefix(0, "[CloseAdapterCompleteHandlerEx] ctx %p\n", ProtocolBindingContext); + CProtocolBinding *binding = (CProtocolBinding *)ProtocolBindingContext; + binding->Complete(NDIS_STATUS_SUCCESS); + }; + pchs.SendNetBufferListsCompleteHandler = []( + _In_ NDIS_HANDLE ProtocolBindingContext, + _In_ PNET_BUFFER_LIST NetBufferList, + _In_ ULONG SendCompleteFlags) + { + TraceNoPrefix(0, "[SendNetBufferListsCompleteHandler] ctx %p\n", ProtocolBindingContext); + CProtocolBinding *binding = (CProtocolBinding *)ProtocolBindingContext; + binding->OnSendCompletion(NetBufferList, SendCompleteFlags); + }; + pchs.ReceiveNetBufferListsHandler = []( + _In_ NDIS_HANDLE ProtocolBindingContext, + _In_ PNET_BUFFER_LIST NetBufferLists, + _In_ NDIS_PORT_NUMBER PortNumber, + _In_ ULONG NumberOfNetBufferLists, + _In_ ULONG ReceiveFlags) + { + UNREFERENCED_PARAMETER(PortNumber); + CProtocolBinding *binding = (CProtocolBinding *)ProtocolBindingContext; + binding->OnReceive(NetBufferLists, NumberOfNetBufferLists, ReceiveFlags); + }; + pchs.OidRequestCompleteHandler = []( + _In_ NDIS_HANDLE ProtocolBindingContext, + _In_ PNDIS_OID_REQUEST OidRequest, + _In_ NDIS_STATUS Status + ) + { + CProtocolBinding *binding = (CProtocolBinding *)ProtocolBindingContext; + binding->OidComplete(OidRequest, Status); + }; + pchs.UninstallHandler = []() + { + }; + pchs.StatusHandlerEx = []( + _In_ NDIS_HANDLE ProtocolBindingContext, + _In_ PNDIS_STATUS_INDICATION StatusIndication + ) + { + CProtocolBinding *binding = (CProtocolBinding *)ProtocolBindingContext; + binding->OnStatusIndication(StatusIndication); + }; + pchs.NetPnPEventHandler = []( + _In_ NDIS_HANDLE ProtocolBindingContext, + _In_ PNET_PNP_EVENT_NOTIFICATION NetPnPEventNotification + ) + { + CProtocolBinding *binding = (CProtocolBinding *)ProtocolBindingContext; + binding->OnPnPEvent(NetPnPEventNotification); + return NDIS_STATUS_SUCCESS; + }; + NDIS_STATUS status = NdisRegisterProtocolDriver(this, &pchs, &m_ProtocolHandle); + TraceNoPrefix(0, "[%s] Registering protocol %wZ = %X\n", __FUNCTION__, &pchs.Name, status); + } + ~CParaNdisProtocol() + { + if (m_ProtocolHandle) + { + TraceNoPrefix(0, "[%s] Deregistering protocol\n", __FUNCTION__); + NdisDeregisterProtocolDriver(m_ProtocolHandle); + TraceNoPrefix(0, "[%s] Deregistering protocol done\n", __FUNCTION__); + } + } + NDIS_STATUS OnBindAdapter(_In_ NDIS_HANDLE BindContext, _In_ PNDIS_BIND_PARAMETERS BindParameters) + { + CProtocolBinding *binding = new (m_DriverHandle)CProtocolBinding(*this, BindContext); + if (!binding) + { + return NDIS_STATUS_RESOURCES; + } + NDIS_STATUS status = binding->Bind(BindParameters); + return status; + } + void AddAdapter(PARANDIS_ADAPTER *pContext) + { + LPCSTR func = __FUNCTION__; + bool Done = false; + + CMutexLockedContext protect(m_Mutex); + + // find existing entry, if binding exists + m_Adapters.ForEach([&](CAdapterEntry *e) + { + if (e->MatchMac(pContext->CurrentMacAddress)) + { + TraceNoPrefix(0, "[%s] found entry %p for adapter %p\n", func, e, pContext); + e->m_Adapter = pContext; + e->NotifyAdapterArrival(); + Done = true; + return false; + } + return true; + }); + if (Done) + { + return; + } + + // create a new one, if binding does not exists + CAdapterEntry *e = new (m_DriverHandle)CAdapterEntry(pContext); + if (e) + { + TraceNoPrefix(0, "[%s] new entry %p for adapter %p\n", func, e, pContext); + m_Adapters.PushBack(e); + } + } + bool RemoveAdapter(PARANDIS_ADAPTER *pContext) + { + LPCSTR func = __FUNCTION__; + + CMutexLockedContext protect(m_Mutex); + + m_Adapters.ForEach([&](CAdapterEntry *e) + { + if (e->m_Adapter != pContext) { + return true; + } + e->m_Adapter = NULL; + if (e->m_Binding) { + TraceNoPrefix(0, "[%s] bound entry %p for adapter %p\n", func, e, pContext); + e->NotifyAdapterRemoval(); + } else { + TraceNoPrefix(0, "[%s] unbound entry %p for adapter %p\n", func, e, pContext); + m_Adapters.Remove(e); + e->Destroy(e, m_DriverHandle); + } + return false; + }); + bool bDelete = true; + m_Adapters.ForEach([&](CAdapterEntry *e) + { + if (e->m_Adapter) + { + TraceNoPrefix(0, "[%s] existing entry %p for adapter %p\n", func, e, pContext); + bDelete = false; + } + }); + if (bDelete) + { + TraceNoPrefix(0, "[%s] no more adapters\n", func); + } + return bDelete; + } + void AddBinding(UCHAR *MacAddress, PVOID Binding) + { + LPCSTR func = __FUNCTION__; + bool Done = false; + + CMutexLockedContext protect(m_Mutex); + + m_Adapters.ForEach([&](CAdapterEntry *e) + { + if (!e->MatchMac(MacAddress)) { + return true; + } + e->m_Binding = Binding; + e->NotifyAdapterArrival(); + Done = true; + return false; + }); + if (Done) { + return; + } + CAdapterEntry *e = new (m_DriverHandle)CAdapterEntry(Binding, MacAddress); + if (e) + { + TraceNoPrefix(0, "[%s] new entry %p for binding %p\n", func, e, Binding); + m_Adapters.PushBack(e); + } + } + void RemoveBinding(PVOID Binding) + { + LPCSTR func = __FUNCTION__; + + CMutexLockedContext protect(m_Mutex); + + m_Adapters.ForEach([&](CAdapterEntry *e) + { + if (e->m_Binding != Binding) { + return true; + } + if (e->m_Adapter) { + TraceNoPrefix(0, "[%s] bound entry %p for binding %p\n", func, e, Binding); + e->NotifyAdapterDetach(); + e->m_Binding = NULL; + } else { + TraceNoPrefix(0, "[%s] unbound entry %p for binding %p\n", func, e, Binding); + // the list uses mutex for sync, so we can use 'Remove' here + m_Adapters.Remove(e); + e->Destroy(e, m_DriverHandle); + } + return false; + }); + } + NDIS_HANDLE DriverHandle() const { return m_DriverHandle; } + NDIS_HANDLE ProtocolHandle() const { return m_ProtocolHandle; } +private: + CNdisList m_Adapters; + // there are procedures with several operations on the list, + // we need to protected them together, so use separate mutex + CMutexProtectedAccess m_Mutex; + NDIS_HANDLE m_DriverHandle; + NDIS_HANDLE m_ProtocolHandle; + void OnLastReferenceGone() override + { + Destroy(this, m_DriverHandle); + } +}; + +void ParaNdis_ProtocolInitialize(NDIS_HANDLE DriverHandle) +{ + if (ProtocolData) + { + return; + } + ProtocolData = new (DriverHandle) CParaNdisProtocol(DriverHandle); +} + +void ParaNdis_ProtocolRegisterAdapter(PARANDIS_ADAPTER *pContext) +{ + if (!ProtocolData) + return; + ProtocolData->AddAdapter(pContext); +} + +void ParaNdis_ProtocolUnregisterAdapter(PARANDIS_ADAPTER *pContext) +{ + if (!ProtocolData) + return; + if (ProtocolData->RemoveAdapter(pContext)) + { + ProtocolData->Release(); + ProtocolData = NULL; + } +} + +NDIS_STATUS CProtocolBinding::Bind(PNDIS_BIND_PARAMETERS BindParameters) +{ + NDIS_OPEN_PARAMETERS openParams = {}; + NDIS_MEDIUM medium = NdisMedium802_3; + UINT mediumIndex; + NET_FRAME_TYPE frameTypes[2] = { NDIS_ETH_TYPE_802_1X, NDIS_ETH_TYPE_802_1Q }; + + AddRef(); + + openParams.Header.Type = NDIS_OBJECT_TYPE_OPEN_PARAMETERS; + openParams.Header.Revision = NDIS_OPEN_PARAMETERS_REVISION_1; + openParams.Header.Size = NDIS_SIZEOF_OPEN_PARAMETERS_REVISION_1; + openParams.AdapterName = BindParameters->AdapterName; + openParams.MediumArray = &medium; + openParams.MediumArraySize = 1; + openParams.SelectedMediumIndex = &mediumIndex; + openParams.FrameTypeArray = frameTypes; + openParams.FrameTypeArraySize = ARRAYSIZE(frameTypes); + + NDIS_STATUS status = NdisOpenAdapterEx(m_Protocol.ProtocolHandle(), this, &openParams, m_BindContext, &m_BindingHandle); + + if (status == STATUS_SUCCESS) + { + TraceNoPrefix(0, "[%s] %p done\n", __FUNCTION__, this); + m_Status = STATUS_SUCCESS; + } + else if (status == STATUS_PENDING) + { + m_Event.Wait(); + status = m_Status; + } + if (!NT_SUCCESS(status)) + { + TraceNoPrefix(0, "[%s] %p, failed %X\n", __FUNCTION__, this, status); + Unbind(m_BindContext); + } + + CheckCapabilities(BindParameters); + + if (NT_SUCCESS(status)) + { + m_Protocol.AddBinding(BindParameters->CurrentMacAddress, this); + } + Release(); + + return status; +} + +NDIS_STATUS CProtocolBinding::Unbind(NDIS_HANDLE UnbindContext) +{ + NDIS_STATUS status = STATUS_SUCCESS; + UNREFERENCED_PARAMETER(UnbindContext); + + if (m_BindingHandle) + { + ULONG valueNone = 0; + + m_Protocol.RemoveBinding(this); + + SetOid(OID_GEN_CURRENT_PACKET_FILTER, &valueNone, sizeof(valueNone)); + SetOid(OID_802_3_MULTICAST_LIST, NULL, 0); + + m_Event.Clear(); + status = NdisCloseAdapterEx(m_BindingHandle); + switch (status) + { + case NDIS_STATUS_PENDING: + m_Event.Wait(); + status = NDIS_STATUS_SUCCESS; + break; + case NDIS_STATUS_SUCCESS: + break; + default: + TraceNoPrefix(0, "[%s] %p, failed %X\n", __FUNCTION__, this, status); + return status; + } + m_BindingHandle = NULL; + } + Release(); + return status; +} + +void CProtocolBinding::OnLastReferenceGone() +{ + Destroy(this, m_Protocol.DriverHandle()); +} + +void CProtocolBinding::OnReceive(PNET_BUFFER_LIST Nbls, ULONG NofNbls, ULONG Flags) +{ + // Flags may contain RESOURCES (but why this should make difference?) + bool bDrop = true; + if (m_RxStateMachine.RegisterOutstandingItems(NofNbls)) + { + if (m_BoundAdapter) + { + TraceNoPrefix(0, "[%s] %d NBLs\n", __FUNCTION__, NofNbls); + NdisMIndicateReceiveNetBufferLists( + m_BoundAdapter->MiniportHandle, Nbls, + NDIS_DEFAULT_PORT_NUMBER, NofNbls, Flags); + bDrop = false; + } + } + if (bDrop) + { + TraceNoPrefix(0, "[%s] dropped %d NBLs\n", __FUNCTION__, NofNbls); + NdisReturnNetBufferLists(m_BindingHandle, Nbls, 0); + } +} + +void CProtocolBinding::ReturnNbls(PNET_BUFFER_LIST pNBL, ULONG numNBLs) +{ + NdisReturnNetBufferLists(m_BindingHandle, pNBL, 0); + m_RxStateMachine.UnregisterOutstandingItems(numNBLs); +} + +void ParaNdis_ProtocolReturnNbls(PARANDIS_ADAPTER *pContext, PNET_BUFFER_LIST pNBL, ULONG numNBLs) +{ + // ensure the adapter has a binding and if yes, reference it + CProtocolBinding *pb = (CProtocolBinding *)ParaNdis_ReferenceBinding(pContext); + if (pb) + { + TraceNoPrefix(0, "[%s] %d NBLs\n", __FUNCTION__, numNBLs); + pb->ReturnNbls(pNBL, numNBLs); + ParaNdis_DereferenceBinding(pContext); + } + else + { + TraceNoPrefix(0, "[%s] ERROR: Can't return %d NBL\n", __FUNCTION__, numNBLs); + } +} + +void CProtocolBinding::SetOid(ULONG oid, PVOID data, ULONG size) +{ + COidWrapper *p = new (m_Protocol.DriverHandle()) COidWrapper(NdisRequestSetInformation, oid); + if (!p) + { + return; + } + p->m_Request.DATA.SET_INFORMATION.InformationBuffer = data; + p->m_Request.DATA.SET_INFORMATION.InformationBufferLength = size; + p->Run(m_BindingHandle); + p->Destroy(p, m_Protocol.DriverHandle()); +} + +void CProtocolBinding::SetOidAsync(ULONG oid, PVOID data, ULONG size) +{ + COidWrapperAsync *p = new (m_Protocol.DriverHandle()) COidWrapperAsync(m_BoundAdapter, NdisRequestSetInformation, oid, data, size); + if (!p) + { + return; + } + p->Run(m_BindingHandle); +} + +static bool KeepSourceHandleInContext(PNET_BUFFER_LIST Nbl) +{ +#if 0 + NDIS_STATUS status = NdisAllocateNetBufferListContext(Nbl, sizeof(NDIS_HANDLE), 0, '3ORP'); + if (!NT_SUCCESS(status)) + { + TraceNoPrefix(0, "[%s] error %X", __FUNCTION__, status); + return false; + } + *(PNDIS_HANDLE)NET_BUFFER_LIST_CONTEXT_DATA_START(Nbl) = Nbl->SourceHandle; +#else + NET_BUFFER_LIST_INFO(Nbl, TcpRecvSegCoalesceInfo) = Nbl->SourceHandle; +#endif + return true; +} + +static void RetrieveSourceHandle(PNET_BUFFER_LIST start, PNET_BUFFER_LIST stopAt = NULL) +{ + PNET_BUFFER_LIST current = start; + while (current && current != stopAt) + { +#if 0 + current->SourceHandle = *(PNDIS_HANDLE)NET_BUFFER_LIST_CONTEXT_DATA_START(current); + NdisFreeNetBufferListContext(current, sizeof(NDIS_HANDLE)); +#else + current->SourceHandle = NET_BUFFER_LIST_INFO(current, TcpRecvSegCoalesceInfo); +#endif + current = NET_BUFFER_LIST_NEXT_NBL(current); + } +} + +bool CProtocolBinding::Send(PNET_BUFFER_LIST Nbl, ULONG Count) +{ + if (!m_TxStateMachine.RegisterOutstandingItems(Count)) + { + return false; + } + PNET_BUFFER_LIST current = Nbl; + while (current) + { + if (!KeepSourceHandleInContext(current)) + { + RetrieveSourceHandle(Nbl, current); + m_TxStateMachine.UnregisterOutstandingItems(Count); + // TODO: can we transmit the packets via NETKVM??? + return false; + } + current->SourceHandle = m_BindingHandle; + current = NET_BUFFER_LIST_NEXT_NBL(current); + } + TraceNoPrefix(0, "[%s] %d nbls", __FUNCTION__, Count); + NdisSendNetBufferLists(m_BindingHandle, Nbl, NDIS_DEFAULT_PORT_NUMBER, 0); + return true; +} + +static ULONG CountNblsAndErrors(PNET_BUFFER_LIST NBL, ULONG& errors) +{ + ULONG result; + for (result = 0, errors = 0; NBL != nullptr; NBL = NET_BUFFER_LIST_NEXT_NBL(NBL), result++) + { + if (NET_BUFFER_LIST_STATUS(NBL) != NDIS_STATUS_SUCCESS) + errors++; + } + return result; +} + +void CProtocolBinding::OnSendCompletion(PNET_BUFFER_LIST Nbls, ULONG Flags) +{ + ULONG errors; + ULONG count = CountNblsAndErrors(Nbls, errors); + TraceNoPrefix(0, "[%s] %d nbls(%d errors)", __FUNCTION__, count, errors); + RetrieveSourceHandle(Nbls); + NdisMSendNetBufferListsComplete(m_BoundAdapter->MiniportHandle, Nbls, Flags); + m_TxStateMachine.UnregisterOutstandingItems(count); +} + +void CAdapterEntry::Notifier(PVOID Binding, NotifyEvent Event, PARANDIS_ADAPTER *Adapter) +{ + CProtocolBinding *pb = (CProtocolBinding *)Binding; + switch (Event) + { + case Arrival: + pb->OnAdapterAttach(Adapter); + break; + case Removal: + pb->OnAdapterHalted(); + break; + case Detach: + pb->OnAdapterDetach(); + break; + default: + break; + } +} + +bool ParaNdis_ProtocolSend(PARANDIS_ADAPTER *pContext, PNET_BUFFER_LIST Nbl) +{ + // ensure the adapter has a binding and if yes, reference it + CProtocolBinding *pb = (CProtocolBinding *)ParaNdis_ReferenceBinding(pContext); + if (!pb) + { + return false; + } + ULONG count = ParaNdis_CountNBLs(Nbl); + bool b = pb->Send(Nbl, count); + ParaNdis_DereferenceBinding(pContext); + return b; +} + +VOID ParaNdis_PropagateOid(PARANDIS_ADAPTER *pContext, NDIS_OID oid, PVOID buffer, UINT length) +{ + CProtocolBinding *pb = (CProtocolBinding *)ParaNdis_ReferenceBinding(pContext); + if (!pb) + { + return; + } + pb->SetOidAsync(oid, buffer, length); +} + +#else + +void ParaNdis_ProtocolUnregisterAdapter(PARANDIS_ADAPTER *) { } +void ParaNdis_ProtocolRegisterAdapter(PARANDIS_ADAPTER *) { } +void ParaNdis_ProtocolInitialize(NDIS_HANDLE) { } +bool ParaNdis_ProtocolSend(PARANDIS_ADAPTER *, PNET_BUFFER_LIST) { return false; } +void ParaNdis_PropagateOid(PARANDIS_ADAPTER *, NDIS_OID, PVOID, UINT) { } +void ParaNdis_ProtocolReturnNbls(PARANDIS_ADAPTER *, PNET_BUFFER_LIST, ULONG) { } + +#endif //NDIS_SUPPORT_NDIS630 diff --git a/NetKVM/Common/ParaNdis_Protocol.h b/NetKVM/Common/ParaNdis_Protocol.h new file mode 100644 index 00000000..200b75d3 --- /dev/null +++ b/NetKVM/Common/ParaNdis_Protocol.h @@ -0,0 +1,9 @@ +#pragma once + +#include "ndis56common.h" + +void ParaNdis_ProtocolInitialize(NDIS_HANDLE DriverHandle); +void ParaNdis_ProtocolRegisterAdapter(PARANDIS_ADAPTER *pContext); +void ParaNdis_ProtocolUnregisterAdapter(PARANDIS_ADAPTER *pContext); +bool ParaNdis_ProtocolSend(PARANDIS_ADAPTER *pContext, PNET_BUFFER_LIST pNBL); +void ParaNdis_ProtocolReturnNbls(PARANDIS_ADAPTER *pContext, PNET_BUFFER_LIST pNBL, ULONG numNBLs); diff --git a/NetKVM/NetKVM-VS2015.sln b/NetKVM/NetKVM-VS2015.sln index bc03c2d8..d3d2e1ed 100644 --- a/NetKVM/NetKVM-VS2015.sln +++ b/NetKVM/NetKVM-VS2015.sln @@ -18,6 +18,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "VirtioLib", "..\VirtIO\Virt EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Mof", "Mof\Mof.vcxproj", "{09672FE6-A18C-4B3B-B1DC-1C7C1F61F9B4}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "NotifyObject", "NotifyObject\notifyobject.vcxproj", "{92E3B437-C258-47FB-8856-D3FEA56A3BCC}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Vista Release|ARM64 = Vista Release|ARM64 @@ -167,6 +169,31 @@ Global {09672FE6-A18C-4B3B-B1DC-1C7C1F61F9B4}.Win8.1 Release|x64.Build.0 = Header|x64 {09672FE6-A18C-4B3B-B1DC-1C7C1F61F9B4}.Win8.1 Release|x86.ActiveCfg = Header|Win32 {09672FE6-A18C-4B3B-B1DC-1C7C1F61F9B4}.Win8.1 Release|x86.Build.0 = Header|Win32 + {92E3B437-C258-47FB-8856-D3FEA56A3BCC}.Vista Release|ARM64.ActiveCfg = Vista Release|Win32 + {92E3B437-C258-47FB-8856-D3FEA56A3BCC}.Vista Release|x64.ActiveCfg = Vista Release|x64 + {92E3B437-C258-47FB-8856-D3FEA56A3BCC}.Vista Release|x64.Build.0 = Vista Release|x64 + {92E3B437-C258-47FB-8856-D3FEA56A3BCC}.Vista Release|x86.ActiveCfg = Vista Release|Win32 + {92E3B437-C258-47FB-8856-D3FEA56A3BCC}.Vista Release|x86.Build.0 = Vista Release|Win32 + {92E3B437-C258-47FB-8856-D3FEA56A3BCC}.Win10 Release|ARM64.ActiveCfg = Win10 Release|Win32 + {92E3B437-C258-47FB-8856-D3FEA56A3BCC}.Win10 Release|x64.ActiveCfg = Win10 Release|x64 + {92E3B437-C258-47FB-8856-D3FEA56A3BCC}.Win10 Release|x64.Build.0 = Win10 Release|x64 + {92E3B437-C258-47FB-8856-D3FEA56A3BCC}.Win10 Release|x86.ActiveCfg = Win10 Release|Win32 + {92E3B437-C258-47FB-8856-D3FEA56A3BCC}.Win10 Release|x86.Build.0 = Win10 Release|Win32 + {92E3B437-C258-47FB-8856-D3FEA56A3BCC}.Win7 Release|ARM64.ActiveCfg = Win7 Release|Win32 + {92E3B437-C258-47FB-8856-D3FEA56A3BCC}.Win7 Release|x64.ActiveCfg = Win7 Release|x64 + {92E3B437-C258-47FB-8856-D3FEA56A3BCC}.Win7 Release|x64.Build.0 = Win7 Release|x64 + {92E3B437-C258-47FB-8856-D3FEA56A3BCC}.Win7 Release|x86.ActiveCfg = Win7 Release|Win32 + {92E3B437-C258-47FB-8856-D3FEA56A3BCC}.Win7 Release|x86.Build.0 = Win7 Release|Win32 + {92E3B437-C258-47FB-8856-D3FEA56A3BCC}.Win8 Release|ARM64.ActiveCfg = Win8 Release|Win32 + {92E3B437-C258-47FB-8856-D3FEA56A3BCC}.Win8 Release|x64.ActiveCfg = Win8 Release|x64 + {92E3B437-C258-47FB-8856-D3FEA56A3BCC}.Win8 Release|x64.Build.0 = Win8 Release|x64 + {92E3B437-C258-47FB-8856-D3FEA56A3BCC}.Win8 Release|x86.ActiveCfg = Win8 Release|Win32 + {92E3B437-C258-47FB-8856-D3FEA56A3BCC}.Win8 Release|x86.Build.0 = Win8 Release|Win32 + {92E3B437-C258-47FB-8856-D3FEA56A3BCC}.Win8.1 Release|ARM64.ActiveCfg = Win8.1 Release|Win32 + {92E3B437-C258-47FB-8856-D3FEA56A3BCC}.Win8.1 Release|x64.ActiveCfg = Win8.1 Release|x64 + {92E3B437-C258-47FB-8856-D3FEA56A3BCC}.Win8.1 Release|x64.Build.0 = Win8.1 Release|x64 + {92E3B437-C258-47FB-8856-D3FEA56A3BCC}.Win8.1 Release|x86.ActiveCfg = Win8.1 Release|Win32 + {92E3B437-C258-47FB-8856-D3FEA56A3BCC}.Win8.1 Release|x86.Build.0 = Win8.1 Release|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/NetKVM/NetKVM-VS2015.vcxproj b/NetKVM/NetKVM-VS2015.vcxproj index 14932fc8..5c3b37a5 100644 --- a/NetKVM/NetKVM-VS2015.vcxproj +++ b/NetKVM/NetKVM-VS2015.vcxproj @@ -361,6 +361,7 @@ + @@ -390,6 +391,7 @@ + diff --git a/NetKVM/NetKVM-VS2015.vcxproj.filters b/NetKVM/NetKVM-VS2015.vcxproj.filters index 83552683..9eacdbe3 100644 --- a/NetKVM/NetKVM-VS2015.vcxproj.filters +++ b/NetKVM/NetKVM-VS2015.vcxproj.filters @@ -103,6 +103,9 @@ Header Files\Common + + Header Files\Common + @@ -161,6 +164,9 @@ Source Files\Common + + Source Files\Common + @@ -176,5 +182,6 @@ + \ No newline at end of file diff --git a/NetKVM/NotifyObject/LICENSE b/NetKVM/NotifyObject/LICENSE new file mode 100644 index 00000000..99859551 --- /dev/null +++ b/NetKVM/NotifyObject/LICENSE @@ -0,0 +1,29 @@ +Copyright (c) 2020 Red Hat, Inc. +Copyright (c) 2020 Oracle Corporation + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + +Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + +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. + +Neither the name of the copyright holder nor the names of its +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 +HOLDER 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. diff --git a/NetKVM/NotifyObject/Readme.md b/NetKVM/NotifyObject/Readme.md new file mode 100644 index 00000000..60d0ddce --- /dev/null +++ b/NetKVM/NotifyObject/Readme.md @@ -0,0 +1,56 @@ +# Notify object of VIOPROT protocol supported by NETKVM + +The notify object runs on protocol installation (it checks all the +existing adapters) and on installation of new adapter (it checks the +new adapter): + +If the adapter's PnP ID matches the list of supported ones, the notify +object unbinds it from all the protocols and binds only to VIOPROT. +It there is NETKVM adapter with the MAC address identical to one of +checked netwrok adapter, these 2 adapters with the same MAC address +will be 'teamed' (subject to compatibility check) and the NETKVM uses +the teamed adapter for RX and TX. + +## Protocol installation + +(by admin, from the directory where vioprot.inf is located) + + netcfg -v -l vioprot.inf -c p -i VIOPROT + +## Protocol uninstallation (example of the batch): + + netcfg -v -u VIOPROT + timeout /t 3 + for %%f in (c:\windows\inf\oem*.inf) do call :checkinf %%f + echo Done + timeout /t 3 + goto :eof + :checkinf + type %1 | findstr /i vioprot.cat + if not errorlevel 1 goto :removeinf + echo %1 is not VIOPROT inf file + goto :eof + :removeinf + echo This is VIOPROT inf file + pnputil /d "%~nx1" + timeout /t 2 + goto :eof + +## Supported adapters + +Built-in list at the moment of writing: +* "ven_8086&dev_1515" - Intel X540 Virtual Function +* "ven_8086&dev_10ca" - Intel 82576 Virtual Function +* "ven_8086&dev_15a8" - Intel Ethernet Connection X552 +* "ven_15b3&dev_101a" - Mellanox MT28800 Family + +## Change the list of supported adapter without rebuilding the binary + +Registry key: **HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\VioProt** + +*Name* | *Type* | *Value* | *Note* +-------|--------|---------|-------- +**NoDefaults** | DWORD | 1 | Disables built-in list +**Supported** | MULTI_SZ | List | Substrings in format "ven_XXXX&dev_XXXX", for example: "ven_8086&dev_1520XXXX" + +**Note: Never include PnP ID of NETKVM adapter!** diff --git a/NetKVM/NotifyObject/netkvmno.RGS b/NetKVM/NotifyObject/netkvmno.RGS new file mode 100644 index 00000000..4198747d --- /dev/null +++ b/NetKVM/NotifyObject/netkvmno.RGS @@ -0,0 +1,13 @@ +HKCR +{ + NoRemove CLSID + { + ForceRemove {F69513F1-8E1A-4F35-82D9-046406970E6D} = s 'NetKVM SR-IOV Notify Object' + { + InProcServer32 = s '%MODULE%' + { + val ThreadingModel = s 'Both' + } + } + } +} diff --git a/NetKVM/NotifyObject/netkvmno.cpp b/NetKVM/NotifyObject/netkvmno.cpp new file mode 100644 index 00000000..425776e5 --- /dev/null +++ b/NetKVM/NotifyObject/netkvmno.cpp @@ -0,0 +1,283 @@ +/* + * Copyright (c) 2008-2017 Red Hat, Inc. + * Copyright (c) 2020 Oracle and/or its affiliates + * + * 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 "stdafx.h" + +#define VFDEV 0x0001 +#define VIOPRO 0x0010 + +#define SET_FLAGS(Flag, Val) (Flag) = ((Flag) | (Val)) +#define TEST_FLAGS(Flag, Val) ((Flag) & (Val)) + +/* + * Check whether the binding is the one we need - the upper is the VirtIO + * protocol and the lower is the VF miniport driver. + * Return value - iRet, its bitmap(VFDEV/VIOPRO) represents existence + * of the VirtIO protocol and VF miniport in the binding. + */ +INT CNotifyObject::CheckProtocolandDevInf(INetCfgBindingPath *pNetCfgBindingPath, + INetCfgComponent **ppUpNetCfgCom, + INetCfgComponent **ppLowNetCfgCom +) +{ + IEnumNetCfgBindingInterface *pEnumNetCfgBindIf = NULL; + INetCfgBindingInterface *pNetCfgBindIf = NULL; + ULONG ulNum; + LPWSTR pszwLowInfId = NULL; + LPWSTR pszwUpInfId = NULL; + INT ret = 0; + + Trace("%s", __FUNCTION__); + + if (S_OK == pNetCfgBindingPath->EnumBindingInterfaces(&pEnumNetCfgBindIf) && + S_OK == pEnumNetCfgBindIf->Next(1, &pNetCfgBindIf, &ulNum) && + S_OK == pNetCfgBindIf->GetUpperComponent(ppUpNetCfgCom) && + S_OK == pNetCfgBindIf->GetLowerComponent(ppLowNetCfgCom) && + S_OK == (*ppUpNetCfgCom)->GetId(&pszwUpInfId) && + S_OK == (*ppLowNetCfgCom)->GetId(&pszwLowInfId)) + { + // Upper is VIO protocol + if (!_wcsicmp(pszwUpInfId, NETKVM_PROTOCOL_NAME_W)) + SET_FLAGS(ret, VIOPRO); + // Lower is VF device miniport + if (IsSupportedSRIOVAdapter(pszwLowInfId)) + SET_FLAGS(ret, VFDEV); + Trace("pszwUpInfId %S, pszwLowInfId %S", pszwUpInfId, pszwLowInfId); + } + + if (pszwLowInfId) + CoTaskMemFree(pszwLowInfId); + if (pszwUpInfId) + CoTaskMemFree(pszwUpInfId); + ReleaseObj(pNetCfgBindIf); + ReleaseObj(pEnumNetCfgBindIf); + + Trace("%s <=iRet 0x%X", __FUNCTION__, ret); + return ret; +} + +/* + * When the new binding(VirtIO Protocol<->VF Miniport) is added, enumerate + * all other bindings(non VirtIO Protocol<->VF Miniport) and disable them. + * When the a binding(VirtIO Protocol<->VF Miniport) is removed, enumerate + * all other bindings(non VirtIO Protocol<->VF Miniport) and enable them. + */ +STDMETHODIMP CNotifyObject::NotifyBindingPath(IN DWORD dwChangeFlag, IN INetCfgBindingPath *pNetCfgBindPath) +{ + INetCfgComponent *pUpNetCfgCom = NULL; + INetCfgComponent *pLowNetCfgCom = NULL; + BOOL bAdd, bRemove; + INT iRet = 0; + + Trace("-->%s change flags 0x%X", __FUNCTION__, dwChangeFlag); + + bAdd = dwChangeFlag & NCN_ADD; + bRemove = dwChangeFlag & NCN_REMOVE; + + // Check and operate when binding is being added or removed + if (bAdd || bRemove) { + iRet = CheckProtocolandDevInf(pNetCfgBindPath, + &pUpNetCfgCom, &pLowNetCfgCom); + if (TEST_FLAGS(iRet, VFDEV) && TEST_FLAGS(iRet, VIOPRO)) { + if (bAdd) { + // Enumerate and disable other bindings except for + // VIOprotocol<->VF miniport + if (EnableVFBindings(pLowNetCfgCom, FALSE)) + Trace("Failed to disable non VIO protocol to VF miniport"); + } + else { + // Enumerate and enable other bindings except for + // VIOprotocol<->VF miniport + if (EnableVFBindings(pLowNetCfgCom, TRUE)) + Trace("Failed to enable non VIO protocol to VF miniport"); + } + } + } + + ReleaseObj(pUpNetCfgCom); + ReleaseObj(pLowNetCfgCom); + + Trace("<--%s", __FUNCTION__); + + return S_OK; +} + +//---------------------------------------------------------------------------- +// INetCfgComponentNotifyGlobal +//---------------------------------------------------------------------------- + +//When addition of a binding path is about to occur, +//disable it if it is VirtIO protocol<->non VF miniport binding. +STDMETHODIMP CNotifyObject::SysQueryBindingPath(DWORD dwChangeFlag, + INetCfgBindingPath *pNetCfgBindPath) +{ + INetCfgComponent *pUpNetCfgCom; + INetCfgComponent *pLowNetCfgCom; + INT iRet = 0; + HRESULT hResult = S_OK; + + pUpNetCfgCom = NULL; + pLowNetCfgCom = NULL; + Trace("-->%s", __FUNCTION__); + + if (dwChangeFlag & (NCN_ENABLE | NCN_ADD)) { + iRet = CheckProtocolandDevInf(pNetCfgBindPath, + &pUpNetCfgCom, &pLowNetCfgCom); + if (!TEST_FLAGS(iRet, VFDEV) && TEST_FLAGS(iRet, VIOPRO)) { + // Upper protocol is virtio protocol and lower id is not + // vf device id, disable the binding. + hResult = NETCFG_S_DISABLE_QUERY; + } + + ReleaseObj(pUpNetCfgCom); + ReleaseObj(pLowNetCfgCom); + } + Trace("<--%s HRESULT = %x", __FUNCTION__, hResult); + + return hResult; +} + +//Enable/Disable the bindings of non VIO protocols to the VF miniport +HRESULT CNotifyObject::EnableVFBindings(INetCfgComponent *pNetCfgCom, + BOOL bEnable) +{ + IEnumNetCfgBindingPath *pEnumNetCfgBindPath; + INetCfgBindingPath *pNetCfgBindPath; + INetCfgComponentBindings *pNetCfgComBind; + HRESULT hResult; + ULONG ulNum; + + Trace("-->%s bEnable = %d", __FUNCTION__, bEnable); + + // Get the binding path enumerator. + pEnumNetCfgBindPath = NULL; + pNetCfgComBind = NULL; + hResult = pNetCfgCom->QueryInterface(IID_INetCfgComponentBindings, + (PVOID *)&pNetCfgComBind); + if (S_OK == hResult) { + hResult = pNetCfgComBind->EnumBindingPaths(EBP_ABOVE, + &pEnumNetCfgBindPath); + ReleaseObj(pNetCfgComBind); + } + else + return hResult; + + if (hResult == S_OK) { + pNetCfgBindPath = NULL; + hResult = pEnumNetCfgBindPath->Next(1, &pNetCfgBindPath, &ulNum); + // Enumerate every binding path. + while (hResult == S_OK) { + EnableBinding(pNetCfgBindPath, bEnable); + ReleaseObj(pNetCfgBindPath); + pNetCfgBindPath = NULL; + hResult = pEnumNetCfgBindPath->Next(1, &pNetCfgBindPath, &ulNum); + } + ReleaseObj(pEnumNetCfgBindPath); + } + else { + Trace("Failed to get the binding path enumerator, " + "bindings will not be %s.", bEnable ? "enabled" : "disabled"); + } + + Trace("%s\n", __FUNCTION__); + + return hResult; +} + +//Enable or disable bindings with non VIO protocol +VOID CNotifyObject::EnableBinding(INetCfgBindingPath *pNetCfgBindPath, BOOL bEnable) +{ + INetCfgComponent *pUpNetCfgCom = NULL; + INetCfgComponent *pLowNetCfgCom = NULL; + INT iRet; + + iRet = CheckProtocolandDevInf(pNetCfgBindPath, + &pUpNetCfgCom, &pLowNetCfgCom); + if (!TEST_FLAGS(iRet, VIOPRO)) + pNetCfgBindPath->Enable(bEnable); + + ReleaseObj(pUpNetCfgCom); + ReleaseObj(pLowNetCfgCom); +} + +CComModule _Module; + +BEGIN_OBJECT_MAP(ObjectMap) + OBJECT_ENTRY(CLSID_CNotifyObject, CNotifyObject) +END_OBJECT_MAP() + +// required DLL exports +EXTERN_C_START + +STDAPI DllRegisterServer(void) +{ + HRESULT hr = _Module.RegisterServer(true); + Trace("%s hr %d", __FUNCTION__, hr); + return hr; +} + +// required DLL export +STDAPI DllUnregisterServer(void) +{ + HRESULT hr = _Module.UnregisterServer(); + Trace("%s hr %d", __FUNCTION__, hr); + return S_OK; +} + +STDAPI DllCanUnloadNow(void) +{ + HRESULT hr = _Module.GetLockCount() ? S_OK : S_FALSE; + Trace("%s hr %d", __FUNCTION__, hr); + return hr; +} + +STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID* ppv) +{ + HRESULT hr = _Module.GetClassObject(rclsid, riid, ppv); + Trace("%s hr %d", __FUNCTION__, hr); + return hr; +} + +BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID) +{ + switch (dwReason) + { + case DLL_PROCESS_ATTACH: + Trace("%s: Attach =>", __FUNCTION__); + _Module.Init(ObjectMap, hInstance); + DisableThreadLibraryCalls(hInstance); + break; + case DLL_PROCESS_DETACH: + Trace("%s: Detach =>", __FUNCTION__); + _Module.Term(); + } + Trace("%s: <=", __FUNCTION__); + return true; +} + +EXTERN_C_END diff --git a/NetKVM/NotifyObject/netkvmno.def b/NetKVM/NotifyObject/netkvmno.def new file mode 100644 index 00000000..2c12f392 --- /dev/null +++ b/NetKVM/NotifyObject/netkvmno.def @@ -0,0 +1,6 @@ +LIBRARY NETKVMNO +EXPORTS + DllCanUnloadNow PRIVATE + DllGetClassObject PRIVATE + DllRegisterServer PRIVATE + DllUnregisterServer PRIVATE diff --git a/NetKVM/NotifyObject/netkvmno.h b/NetKVM/NotifyObject/netkvmno.h new file mode 100644 index 00000000..5a31cbac --- /dev/null +++ b/NetKVM/NotifyObject/netkvmno.h @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2020 Oracle Corporation + * + * 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. + */ + +#pragma once + +#include "netkvmnoif.h" + +class CNotifyObject : + public CComObjectRoot, + public CComCoClass, + public INetCfgComponentControl, + public INetCfgComponentSetup, + public INetCfgComponentNotifyBinding, + public INetCfgComponentNotifyGlobal +{ +private: + INT CheckProtocolandDevInf(INetCfgBindingPath *pNetCfgBindingPath, + INetCfgComponent **ppUpNetCfgCom, + INetCfgComponent **ppLowNetCfgCom); + HRESULT EnableVFBindings(INetCfgComponent *pNetCfgCom, BOOL bEnable); + VOID EnableBinding(INetCfgBindingPath *pNetCfgBindPath, BOOL bEnable); +public: + BEGIN_COM_MAP(CNotifyObject) + COM_INTERFACE_ENTRY(INetCfgComponentControl) + COM_INTERFACE_ENTRY(INetCfgComponentSetup) + COM_INTERFACE_ENTRY(INetCfgComponentNotifyBinding) + COM_INTERFACE_ENTRY(INetCfgComponentNotifyGlobal) + END_COM_MAP() + + DECLARE_REGISTRY_RESOURCEID(IDR_REG_NETKVM_NOTIFY_OBJECT) + + CNotifyObject() {} + ~CNotifyObject() {} +protected: + //INetCfgNotifyBinding + STDMETHOD(QueryBindingPath)(DWORD, INetCfgBindingPath *) { return S_OK; } + STDMETHOD(NotifyBindingPath)(IN DWORD dwChangeFlag, + IN INetCfgBindingPath *pNetCfgBindPath); + + //INetCfgNotifyGlobal + STDMETHOD(GetSupportedNotifications)(OUT DWORD *Flags) + { + *Flags = NCN_ADD | NCN_REMOVE | NCN_ENABLE | NCN_DISABLE | NCN_BINDING_PATH | NCN_NET | NCN_NETTRANS; + return S_OK; + } + STDMETHOD(SysQueryBindingPath)(IN DWORD dwChangeFlag, IN INetCfgBindingPath *pNetCfgBindPath); + STDMETHOD(SysNotifyBindingPath)(DWORD, INetCfgBindingPath *) { return S_OK; } + STDMETHOD(SysNotifyComponent)(DWORD, INetCfgComponent *) { return S_OK; } + + //INetCfgComponentControl (for future extension) + STDMETHOD(Initialize)(INetCfgComponent *, INetCfg *, BOOL) { return S_OK; } + STDMETHOD(CancelChanges)() { return S_OK; } + STDMETHOD(ApplyRegistryChanges)() { return S_OK; } + STDMETHOD(ApplyPnpChanges)(INetCfgPnpReconfigCallback*) { return S_OK; } + + //INetCfgComponentSetup (for future extension) + STDMETHOD(Install)(DWORD) { return S_OK; } + STDMETHOD(Upgrade)(DWORD, DWORD) { return S_OK; } + STDMETHOD(ReadAnswerFile)(PCWSTR, PCWSTR) { return S_OK; } + STDMETHOD(Removing)() { return S_OK; } +}; diff --git a/NetKVM/NotifyObject/netkvmno.rc b/NetKVM/NotifyObject/netkvmno.rc new file mode 100644 index 00000000..46b2ce35 --- /dev/null +++ b/NetKVM/NotifyObject/netkvmno.rc @@ -0,0 +1,17 @@ +#include +#include "resource.h" + +1 TYPELIB "netkvmnoif.tlb" +IDR_REG_NETKVM_NOTIFY_OBJECT REGISTRY "netkvmno.rgs" + +#include +#include "..\..\Tools\vendor.ver" +#undef __BUILDMACHINE__ +#undef VER_FILEDESCRIPTION_STR +#undef VER_INTERNALNAME_STR +#define VER_LANGNEUTRAL +#define VENDOR_VIRTIO_PRODUCT VENDOR_PRODUCT_PREFIX "VirtIO Ethernet Adapter" +#define VER_FILEDESCRIPTION_STR VENDOR_DESC_PREFIX "VirtIO Protocol Notify Object" VENDOR_DESC_POSTFIX +#define VER_INTERNALNAME_STR "netkvmno.dll" + +#include "common.ver" diff --git a/NetKVM/NotifyObject/netkvmnoif.idl b/NetKVM/NotifyObject/netkvmnoif.idl new file mode 100644 index 00000000..701967f3 --- /dev/null +++ b/NetKVM/NotifyObject/netkvmnoif.idl @@ -0,0 +1,24 @@ +// For the concept of notify object see +// https://docs.microsoft.com/en-us/windows-hardware/drivers/network/notify-object-diagram + +#include + +[ + uuid(05539BEB-A85E-4639-AF27-7F5663840DE6), + version(1.0), + helpstring("NetKVM SR-IOV Notify Object 1.0 Type Library") +] +library NetKVMNotifyLib +{ + [ + uuid(F69513F1-8E1A-4F35-82D9-046406970E6D), + helpstring("NetKVM SR-IOV Notify Class") + ] + coclass CNotifyObject + { + interface INetCfgComponentControl; + interface INetCfgComponentSetup; + interface INetCfgComponentNotifyBinding; + interface INetCfgComponentNotifyGlobal; + }; +}; diff --git a/NetKVM/NotifyObject/notifyobject.props b/NetKVM/NotifyObject/notifyobject.props new file mode 100644 index 00000000..3dccaacc --- /dev/null +++ b/NetKVM/NotifyObject/notifyobject.props @@ -0,0 +1,18 @@ + + + + + false + false + false + true + true + true + NetKVMCopyrightStrings + + diff --git a/NetKVM/NotifyObject/notifyobject.vcxproj b/NetKVM/NotifyObject/notifyobject.vcxproj new file mode 100644 index 00000000..fca4cb9a --- /dev/null +++ b/NetKVM/NotifyObject/notifyobject.vcxproj @@ -0,0 +1,356 @@ + + + + + Vista Release + Win32 + + + Vista Release + x64 + + + Win10 Release + Win32 + + + Win10 Release + x64 + + + Win7 Release + Win32 + + + Win7 Release + x64 + + + Win8.1 Release + Win32 + + + Win8.1 Release + x64 + + + Win8 Release + Win32 + + + Win8 Release + x64 + + + + {92E3B437-C258-47FB-8856-D3FEA56A3BCC} + $(MSBuildProjectName) + Debug + Win32 + {67118C02-9183-4F2D-8E60-3D4F02B62EA9} + $(LatestTargetPlatformVersion) + NotifyObject + Static + Static + WindowsApplicationForDrivers10.0 + DynamicLibrary + $(ProjectDir)Install + NotSet + netkvmno + False + + + + %(AdditionalDependencies);advapi32.lib;comctl32.lib;kernel32.lib;setupapi.lib;ole32.lib;oleaut32.lib;user32.lib;uuid.lib + netkvmno.def + + + + WindowsV6.3 + Desktop + + vioprot.inf + + + Windows8 + Desktop + + + + Windows10 + Desktop + + + + Windows7 + Desktop + + + + Windows7 + Desktop + + + + WindowsV6.3 + Desktop + + + + Windows8 + Desktop + + + + Windows10 + Desktop + + + + Windows7 + Desktop + + + + Windows7 + Desktop + + + + + + $(ProjectDir)$(PlatformTarget)\$(ConfigurationName)\ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + %(AdditionalOptions) -no_format_opt + + + %(AdditionalOptions) -i $(IntDir) -N + + + + + %(AdditionalOptions) -no_format_opt + + + %(AdditionalOptions) -i $(IntDir) -N + + + + + %(AdditionalOptions) -no_format_opt + + + %(AdditionalOptions) -i $(IntDir) -N + + + + + %(AdditionalOptions) -no_format_opt + + + %(AdditionalOptions) -i $(IntDir) -N + + + + + %(AdditionalOptions) -no_format_opt + + + %(AdditionalOptions) -i $(IntDir) -N + + + + + %(AdditionalOptions) -no_format_opt + + + %(AdditionalOptions) -i $(IntDir) -N + + + + + %(AdditionalOptions) -no_format_opt + + + %(AdditionalOptions) -i $(IntDir) -N + + + + + %(AdditionalOptions) -no_format_opt + + + %(AdditionalOptions) -i $(IntDir) -N + + + + + %(AdditionalOptions) -no_format_opt + + + %(AdditionalOptions) -i $(IntDir) -N + + + + + %(AdditionalOptions) -no_format_opt + + + %(AdditionalOptions) -i $(IntDir) -N + + + + + Sync + true + + + + + Sync + true + + + + + Sync + true + + + + + Sync + true + + + + + Sync + true + + + + + Sync + true + + + + + Sync + true + + + + + Sync + true + + + + + Sync + true + + + + + Sync + true + + + + $(PlatformTarget)\$(ConfigurationName)\ + + + $(PlatformTarget)\$(ConfigurationName)\ + + + $(PlatformTarget)\$(ConfigurationName)\ + + + $(PlatformTarget)\$(ConfigurationName)\ + + + $(PlatformTarget)\$(ConfigurationName)\ + + + $(PlatformTarget)\$(ConfigurationName)\ + + + + xcopy /y /q $(PlatformTarget)\$(ConfigurationName)\vioprot.inf $(InstallRoot)\$(PlatformTarget)\$(ConfigurationName)\ +xcopy /y /q $(PlatformTarget)\$(ConfigurationName)\netkvmno.dll $(InstallRoot)\$(PlatformTarget)\$(ConfigurationName)\ +xcopy /y /q $(PlatformTarget)\$(ConfigurationName)\netkvmno.pdb $(InstallRoot)\$(PlatformTarget)\$(ConfigurationName)\ +cd $(InstallRoot)\$(PlatformTarget)\$(ConfigurationName) +echo Signing DLL +signtool sign /a /fd sha256 /tr "http://sha256timestamp.ws.symantec.com/sha256/timestamp" netkvmno.dll +echo Generating CAT file +inf2cat /driver:. /os:$(Inf2CatWindowsVersionList) +echo Signing CAT file +signtool sign /a /fd sha256 /tr "http://sha256timestamp.ws.symantec.com/sha256/timestamp" vioprot.cat + + + + + + + + false + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/NetKVM/NotifyObject/notifyobject.vcxproj.Filters b/NetKVM/NotifyObject/notifyobject.vcxproj.Filters new file mode 100644 index 00000000..4812efec --- /dev/null +++ b/NetKVM/NotifyObject/notifyobject.vcxproj.Filters @@ -0,0 +1,58 @@ + + + + + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* + {45A756E2-A979-4163-A3FF-66314E15E3F3} + + + h;hpp;hxx;hm;inl;inc;xsd + {60F0A149-A97B-4AB6-A5FB-44A2D183B1ED} + + + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml + {EA529E48-8EB7-40F7-A3CA-AE7AC13FF93D} + + + + + Source Files + + + Source Files + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + + + Source Files + + + + Resource Files + + + + + Resource Files + + + + + Source Files + + + \ No newline at end of file diff --git a/NetKVM/NotifyObject/notifyobject.vcxproj.user b/NetKVM/NotifyObject/notifyobject.vcxproj.user new file mode 100644 index 00000000..be250787 --- /dev/null +++ b/NetKVM/NotifyObject/notifyobject.vcxproj.user @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/NetKVM/NotifyObject/resource.h b/NetKVM/NotifyObject/resource.h new file mode 100644 index 00000000..5a10f588 --- /dev/null +++ b/NetKVM/NotifyObject/resource.h @@ -0,0 +1,6 @@ +#if !defined(RESOURCE_H) +#define RESOURCE_H + +#define IDR_REG_NETKVM_NOTIFY_OBJECT 1000 + +#endif diff --git a/NetKVM/NotifyObject/stdafx.h b/NetKVM/NotifyObject/stdafx.h new file mode 100644 index 00000000..ff9d5aae --- /dev/null +++ b/NetKVM/NotifyObject/stdafx.h @@ -0,0 +1,47 @@ +/* + * General definitions for notify object + * + * Copyright (c) 2020 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. + */ + +#pragma once + +#include +#include +#include +#define _ATL_CSTRING_EXPLICIT_CONSTRUCTORS // some CString constructors will be explicit +#include +#include +#include +#include "resource.h" +#include "netkvmno.h" + +// netkvm protocol name +#define NETKVM_PROTOCOL_NAME_W L"vioprot" + +void Trace(LPCSTR format, ...); +bool IsSupportedSRIOVAdapter(LPCWSTR pnpId); +#define ReleaseObj(o) { if (o) (o)->Release(); } diff --git a/NetKVM/NotifyObject/utils.cpp b/NetKVM/NotifyObject/utils.cpp new file mode 100644 index 00000000..71e7d5b0 --- /dev/null +++ b/NetKVM/NotifyObject/utils.cpp @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2020 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 "stdafx.h" +#include "netkvmnoif_i.c" + +void Trace (LPCSTR szFormat, ...) +{ + va_list arglist; + va_start(arglist, szFormat); + CStringA s; + s.FormatV(szFormat, arglist); + s += '\n'; + OutputDebugString(s.GetBuffer()); + va_end(arglist); +} + +class CSupportedAdapters : public CAtlArray +{ +public: + CSupportedAdapters() + { + HKEY hKey; + ULONG NoDefaults = false; + if (!RegOpenKeyExA(HKEY_LOCAL_MACHINE, + "System\\CurrentControlSet\\Control\\VioProt", + 0, KEY_READ, &hKey)) { + ULONG len; + WCHAR buffer[1024] = {}; + len = sizeof(NoDefaults); + RegGetValueW(hKey, NULL, L"NoDefaults", RRF_RT_DWORD, NULL, &NoDefaults, &len); + len = sizeof(buffer); + if (!RegGetValueW(hKey, NULL, L"Supported", RRF_RT_REG_MULTI_SZ, NULL, buffer, &len)) { + for (UINT i = 0; i < len; ) + { + CStringW s = buffer + i; + if (s.IsEmpty()) + break; + s.MakeLower(); + Add(s); + i += s.GetLength() + 1; + } + } + RegCloseKey(hKey); + } + if (!NoDefaults) { + Add(L"ven_8086&dev_1515"); // Intel X540 Virtual Function + Add(L"ven_8086&dev_10ca"); // Intel 82576 Virtual Function + Add(L"ven_8086&dev_15a8"); // Intel Ethernet Connection X552 + Add(L"ven_15b3&dev_101a"); // Mellanox MT28800 Family + } + for (UINT i = 0; i < GetCount(); ++i) + { + CStringW& s = GetAt(i); + Trace("NotifyObj: supported %S", s.GetBuffer()); + } + } +}; + +static CSupportedAdapters SupportedAdapters; + +bool IsSupportedSRIOVAdapter(LPCWSTR pnpId) +{ + CStringW sId = pnpId; + sId.MakeLower(); + for (UINT i = 0; i < SupportedAdapters.GetCount(); ++i) + { + CStringW& s = SupportedAdapters.GetAt(i); + if (sId.Find(s) > 0) { + return true; + } + } + return false; +} diff --git a/NetKVM/NotifyObject/vioprot.inf b/NetKVM/NotifyObject/vioprot.inf new file mode 100644 index 00000000..510821d9 --- /dev/null +++ b/NetKVM/NotifyObject/vioprot.inf @@ -0,0 +1,90 @@ +;------------------------------------------------------------------------- +; VIOPROT.INF -- VirtIO NetKVM Protocol Driver +; +; Copyright (c) 2020 Oracle and/or its affiliates. +;------------------------------------------------------------------------- + +[version] +Signature = "$Windows NT$" +Class = NetTrans +ClassGUID = {4d36e975-e325-11ce-bfc1-08002be10318} +Provider = %VENDOR% +DriverVer = 01/01/2020,1.1 +CatalogFile = vioprot.cat + + +[Manufacturer] +%VENDOR%=Standard,NTx86,NTamd64 + +[Standard.NTx86] +%NETKVMP_Desc%=Install,VIOPROT + +[Standard.NTamd64] +%NETKVMP_Desc%=Install,VIOPROT + +;------------------------------------------------------------------------- +; Installation Section +;------------------------------------------------------------------------- +[Install] +AddReg=Inst_Ndi +Characteristics=0x0 +CopyFiles=Copy.Dll + +;------------------------------------------------------------------------- +; Ndi installation support +;------------------------------------------------------------------------- +[Inst_Ndi] +HKR, Ndi, ClsID, 0, {F69513F1-8E1A-4F35-82D9-046406970E6D} +HKR, Ndi, ComponentDll, , netkvmno.dll +HKR,Ndi,Service,,netkvmp +HKR,Ndi,HelpText,,%NETKVMP_HelpText% +HKR,Ndi\Interfaces, UpperRange,, noupper +HKR,Ndi\Interfaces, LowerRange,,"ndis5,ndis5_prot" + +;------------------------------------------------------------------------- +; Service installation support +;------------------------------------------------------------------------- +[Install.Services] +AddService=netkvmp,,NETKVMP.ServiceInst,NETKVMP.EventLog + +[NETKVMP.ServiceInst] +DisplayName = %NETKVMP_Desc% +ServiceType = 1 ;SERVICE_KERNEL_DRIVER +StartType = 3 ;SERVICE_DEMAND_START +ErrorControl = 1 ;SERVICE_ERROR_NORMAL +ServiceBinary = %12%\netkvm.sys +Description = %NETKVMP_Desc% + +[NETKVMP.EventLog] +AddReg = NETKVMP.EventLog.Reg + +[NETKVMP.EventLog.Reg] +HKR, , EventMessageFile, 0x20000, "%%SystemRoot%%\System32\netevent.dll" +HKR, , TypesSupported, 0x10001, 7 + +[Install.Remove.Services] +DelService=netkvmp,0x200 ;SPSVCINST_STOPSERVICE + +;------------------------------------------------------------------------- +; Declare Destination Directories for file copy/deletion +;------------------------------------------------------------------------- +[SourceDisksNames] +1=%DiskDescription%,"",, + +[SourceDisksFiles] +netkvmno.dll=1 + +[DestinationDirs] +Copy.Dll = 11 ; %windir%\System32 + +;------------------------------------------------------------------------- +; Files to Copy/Delete - Referenced by Install and Remove sections above +;------------------------------------------------------------------------- +[Copy.Dll] +netkvmno.dll,,,2 ; COPYFLG_NOSKIP + +[Strings] +VENDOR = "INX_COMPANY" +DiskDescription = "VirtIO NetKVM Protocol Driver Disk" +NETKVMP_Desc = "VirtIO NetKVM Protocol Driver" +NETKVMP_HelpText = "A driver to support SRIOV for VirtIO network devices" diff --git a/NetKVM/wlh/ParaNdis6_Driver.cpp b/NetKVM/wlh/ParaNdis6_Driver.cpp index 298ab88e..246bbe80 100755 --- a/NetKVM/wlh/ParaNdis6_Driver.cpp +++ b/NetKVM/wlh/ParaNdis6_Driver.cpp @@ -40,6 +40,7 @@ sdkddkver.h before ntddk.h cause compilation failure in wdm.h and ntddk.h */ #include "ParaNdis_Debug.h" #include "ParaNdis_DebugHistory.h" #include "ParaNdis6_Driver.h" +#include "ParaNdis_Protocol.h" #include "Trace.h" #ifdef NETKVM_WPP_ENABLED #include "ParaNdis6_Driver.tmh" @@ -381,6 +382,14 @@ static NDIS_STATUS ParaNdis6_Initialize( { pContext->m_StateMachine.NotifyInitialized(); ParaNdis_DebugRegisterMiniport(pContext, TRUE); + ParaNdis_ProtocolRegisterAdapter(pContext); + } + else + { + // In rare case of initialization failure we need to unregister the protocol. + // Pass dummy value that in no case can be a valid adapter pointer + PARANDIS_ADAPTER *pDummy = (PARANDIS_ADAPTER *)(ULONG_PTR)1; + ParaNdis_ProtocolUnregisterAdapter(pDummy, true); } DEBUG_EXIT_STATUS(status ? 0 : 2, status); @@ -397,6 +406,7 @@ static VOID ParaNdis6_Halt(NDIS_HANDLE miniportAdapterContext, NDIS_HALT_ACTION PARANDIS_ADAPTER *pContext = (PARANDIS_ADAPTER *)miniportAdapterContext; DEBUG_ENTRY(0); ParaNdis_DebugHistory(pContext, hopHalt, NULL, 1, haltAction, 0); + ParaNdis_ProtocolUnregisterAdapter(pContext); ParaNdis_CleanupContext(pContext); ParaNdis_DebugHistory(pContext, hopHalt, NULL, 0, 0, 0); ParaNdis_DebugRegisterMiniport(pContext, FALSE); @@ -465,6 +475,10 @@ static VOID ParaNdis6_SendNetBufferLists( PARANDIS_ADAPTER *pContext = (PARANDIS_ADAPTER *)miniportAdapterContext; UNREFERENCED_PARAMETER(portNumber); UNREFERENCED_PARAMETER(flags); + if (ParaNdis_ProtocolSend(pContext, pNBL)) + { + return; + } #ifdef PARANDIS_SUPPORT_RSS CNdisPassiveReadAutoLock autoLock(pContext->RSSParameters.rwLock); if (pContext->RSS2QueueMap != nullptr) @@ -508,16 +522,42 @@ VOID ParaNdis6_ReturnNetBufferLists( ULONG returnFlags) { PARANDIS_ADAPTER *pContext = (PARANDIS_ADAPTER *)miniportAdapterContext; - - auto NumNBLs = ParaNdis_CountNBLs(pNBL); + PNET_BUFFER_LIST netkvmHead = NULL, sriovHead = NULL; + PNET_BUFFER_LIST *netkvmTail = &netkvmHead, *sriovTail = &sriovHead; + ULONG nofNetkvm = 0, nofSriov = 0; UNREFERENCED_PARAMETER(returnFlags); - DEBUG_ENTRY(5); - ParaNdis_ReuseRxNBLs(pNBL); + while (pNBL) + { + PNET_BUFFER_LIST next = NET_BUFFER_LIST_NEXT_NBL(pNBL); + if (pNBL->NdisPoolHandle == pContext->BufferListsPool) + { + *netkvmTail = pNBL; + netkvmTail = &NET_BUFFER_LIST_NEXT_NBL(pNBL); + nofNetkvm++; + } + else + { + *sriovTail = pNBL; + sriovTail = &NET_BUFFER_LIST_NEXT_NBL(pNBL); + nofSriov++; + } + NET_BUFFER_LIST_NEXT_NBL(pNBL) = NULL; + pNBL = next; + } - pContext->m_RxStateMachine.UnregisterOutstandingItems(NumNBLs); + if (nofNetkvm) + { + ParaNdis_ReuseRxNBLs(netkvmHead); + pContext->m_RxStateMachine.UnregisterOutstandingItems(nofNetkvm); + } + + if (nofSriov) + { + ParaNdis_ProtocolReturnNbls(pContext, sriovHead, nofSriov); + } } /********************************************************** @@ -1163,6 +1203,10 @@ NTSTATUS DriverEntry(PDRIVER_OBJECT pDriverObject, PUNICODE_STRING pRegistryPath WPP_CLEANUP(pDriverObject); #endif // WPP } + if (NT_SUCCESS(status)) + { + ParaNdis_ProtocolInitialize(DriverHandle); + } return status; }