repository: C:/dev/kvm-guest-drivers-windows
branch: master
commit a1d1275e735a02450bc70c262e343c356da1bb34
Author: Yan Vugenfirer <yvuge...@redhat.com>
Date:   Thu Oct 22 13:44:42 2009 +0200

    [WIN-GUEST-DRIVERS] Initial support for MSI interrupts and DPC\Interrupt 
behaviour

diff --git a/NetKVM/Common/ParaNdis-Common.c b/NetKVM/Common/ParaNdis-Common.c
index 3f9faa1..896e6dc 100644
--- a/NetKVM/Common/ParaNdis-Common.c
+++ b/NetKVM/Common/ParaNdis-Common.c
@@ -406,7 +406,7 @@ static BOOLEAN GetAdapterResources(PNDIS_RESOURCE_LIST 
RList, tAdapterResources
                        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 level %d, vector %d, 
affinity %X, flags %X",
+                       DPrintf(0, ("Found Interrupt vector %d, level %d, 
affinity %X, flags %X",
                                pResources->Vector, pResources->Level, 
(ULONG)pResources->Affinity, pResources->InterruptFlags));
                }
        }
@@ -529,6 +529,12 @@ NDIS_STATUS ParaNdis_InitializeContext(
                        pContext->AdapterResources.IOLength)
                )
        {
+               if (pContext->AdapterResources.InterruptFlags & 
CM_RESOURCE_INTERRUPT_MESSAGE)
+               {
+                       DPrintf(0, ("[%s] Message interrupt assigned", 
__FUNCTION__));
+                       pContext->bUsingMSIX = TRUE;
+               }
+
                VirtIODeviceSetIOAddress(&pContext->IODevice, 
pContext->AdapterResources.ulIOAddress);
                JustForCheckClearInterrupt(pContext, "init 0");
                ParaNdis_ResetVirtIONetDevice(pContext);
@@ -557,7 +563,7 @@ NDIS_STATUS ParaNdis_InitializeContext(
                {
                        VirtIODeviceGet(
                                &pContext->IODevice,
-                               0, // offsetof(struct virtio_net_config, mac)
+                               pContext->bUsingMSIX ? 4 : 0, // 
offsetof(struct virtio_net_config, mac)
                                &pContext->PermanentMacAddress,
                                ETH_LENGTH_OF_ADDRESS);
                        if 
(!ParaNdis_ValidateMacAddress(pContext->PermanentMacAddress, FALSE))
@@ -1133,6 +1139,7 @@ BOOLEAN ParaNdis_OnInterrupt(
        if (b)
        {
                NdisGetCurrentSystemTime(&pContext->LastInterruptTimeStamp);
+               ParaNdis_VirtIOEnableInterrupt(pContext, FALSE);
        }
        return b;
 }
@@ -1698,12 +1705,24 @@ void ParaNdis_ReportLinkStatus(PARANDIS_ADAPTER 
*pContext)
        if (pContext->bLinkDetectSupported)
        {
                USHORT linkStatus = 0;
+               USHORT offset = (pContext->bUsingMSIX ? 4 : 0) + 
sizeof(pContext->CurrentMacAddress);
                // link changed
-               VirtIODeviceGet(&pContext->IODevice, 
sizeof(pContext->CurrentMacAddress), &linkStatus, sizeof(linkStatus));
+               VirtIODeviceGet(&pContext->IODevice, offset, &linkStatus, 
sizeof(linkStatus));
                bConnected = (linkStatus & VIRTIO_NET_S_LINK_UP) != 0;
        }
        ParaNdis_IndicateConnect(pContext, bConnected, FALSE);
 }
+
+BOOLEAN ParaNdis_MiniportSynchronizeInterruptEnable(IN PVOID  
SynchronizeContext)
+{
+       PARANDIS_ADAPTER *pContext = (PARANDIS_ADAPTER *)SynchronizeContext;
+
+       DEBUG_ENTRY(6);
+       ParaNdis_VirtIOEnableInterrupt(pContext, TRUE);
+
+       return TRUE;
+}
+
 /**********************************************************
 DPC implementation, common for both NDIS
 Parameters:
@@ -1757,6 +1776,8 @@ void ParaNdis_DPCWorkBody(PARANDIS_ADAPTER *pContext)
        }
        ParaNdis_DebugHistory(pContext, hopDPC, NULL, 0, 
pContext->nofFreeHardwareBuffers, pContext->nofFreeTxDescriptors);
        NdisReleaseSpinLock(&pContext->DPCLock);
+
+       ParaNdis_SyncInterruptEnable(pContext);
 }
 
 /**********************************************************
@@ -1967,7 +1988,7 @@ NDIS_STATUS ParaNdis_SetMulticastList(
 }
 
 /**********************************************************
-TODO
+
 Parameters:
 Return value:
 ***********************************************************/
@@ -1975,7 +1996,8 @@ VOID ParaNdis_VirtIOEnableInterrupt(
        PARANDIS_ADAPTER *pContext,
        BOOLEAN bEnable)
 {
-       DPrintf(0, ("[%s] NOT IMPLEMENTED", __FUNCTION__));
+       
pContext->NetSendQueue->vq_ops->enable_interrupt(pContext->NetSendQueue, 
bEnable);
+       
pContext->NetReceiveQueue->vq_ops->enable_interrupt(pContext->NetReceiveQueue, 
bEnable);
 }
 
 /**********************************************************
diff --git a/NetKVM/Common/ParaNdis-Debug.c b/NetKVM/Common/ParaNdis-Debug.c
index ef56651..22d3b73 100644
--- a/NetKVM/Common/ParaNdis-Debug.c
+++ b/NetKVM/Common/ParaNdis-Debug.c
@@ -11,6 +11,7 @@
 **********************************************************************/
 #include "ndis56common.h"
 #include "stdarg.h"
+#include "ntstrsafe.h"
 
 //#define OVERRIDE_DEBUG_BREAK
 
@@ -65,12 +66,31 @@ 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, ...)
 {
+       NTSTATUS status;
        va_list list;
        va_start(list, fmt);
        vDbgPrintEx(DPFLTR_DEFAULT_ID, 9 | DPFLTR_MASK, fmt, list);
+#if defined(VIRTIO_DBG_USE_IOPORT)
+       if (1)
+       {
+               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; }
+               for (i = 0; i < len; ++i)
+               {
+                       NdisRawWritePortUchar(VIRTIO_DBG_USE_IOPORT, buf[i]);
+               }
+               NdisRawWritePortUchar(VIRTIO_DBG_USE_IOPORT, '\n');
+       }
+#endif
 }
 
 DEBUGPRINTFUNC pDebugPrint = DebugPrint;
diff --git a/NetKVM/Common/ndis56common.h b/NetKVM/Common/ndis56common.h
index c8564e9..3fd23a8 100644
--- a/NetKVM/Common/ndis56common.h
+++ b/NetKVM/Common/ndis56common.h
@@ -54,6 +54,19 @@
 #error "Something is wrong with our versioning"
 #endif
 
+//define to start really using MSI-X interrupt in Vista+
+//#define VIRTIO_USE_MSIX_INTERRUPT
+
+//define to see when the status register is unreadable(see 
ParaNdis_ResetVirtIONetDevice)
+//#define VIRTIO_RESET_VERIFY
+
+//define to if hardware raise interrupt on error (see ParaNdis_DPCWorkBody)
+//#define VIRTIO_SIGNAL_ERROR
+
+// define if qemu supports logging to static IO port for synchronization
+// of driver output with qemu printouts; in this case define the port number
+//#define VIRTIO_DBG_USE_IOPORT        0x99
+
 
 /* The feature bitmap for virtio net */
 #define VIRTIO_NET_F_CSUM      0       /* Host handles pkts w/ partial csum */
@@ -245,6 +258,7 @@ typedef struct _tagPARANDIS_ADAPTER
        BOOLEAN                                 bUseMergedBuffers;
        BOOLEAN                                 bDoKickOnNoBuffer;
        BOOLEAN                                 bSurprizeRemoved;
+       BOOLEAN                                 bUsingMSIX;
        NDIS_DEVICE_POWER_STATE powerState;
        LONG                                    InterruptStatus;
        ULONG                                   ulPriorityVlanSetting;
@@ -319,6 +333,7 @@ typedef struct _tagPARANDIS_ADAPTER
        NDIS_OFFLOAD_PARAMETERS InitialOffloadParameters;
 #if NDIS60_MINIPORT
 // Vista +
+       PIO_INTERRUPT_MESSAGE_INFO      pMSIXInfoTable;
        PNET_BUFFER_LIST                        SendHead;
        PNET_BUFFER_LIST                        SendTail;
        PNET_BUFFER_LIST                        SendWaitingList;
@@ -418,6 +433,12 @@ UINT ParaNdis_VirtIONetReleaseTransmitBuffers(
 void ParaNdis_DPCWorkBody(
        PARANDIS_ADAPTER *pContext);
 
+BOOLEAN ParaNdis_MiniportSynchronizeInterruptEnable(
+       IN PVOID SynchronizeContext);
+
+void ParaNdis_SyncInterruptEnable(
+       PARANDIS_ADAPTER *pContext);
+
 NDIS_STATUS ParaNdis_SetMulticastList(
        PARANDIS_ADAPTER *pContext,
        PVOID Buffer,
diff --git a/NetKVM/wlh/ParaNdis6-Driver.c b/NetKVM/wlh/ParaNdis6-Driver.c
index f3f60de..e7cf3ec 100644
--- a/NetKVM/wlh/ParaNdis6-Driver.c
+++ b/NetKVM/wlh/ParaNdis6-Driver.c
@@ -707,8 +707,256 @@ static VOID ParaNdis6_DevicePnPEvent(
 
 }
 
+static NDIS_STATUS  ParaNdis6_AddDevice(IN NDIS_HANDLE  MiniportAdapterHandle, 
IN NDIS_HANDLE  MiniportDriverContext)
+{
+       NDIS_MINIPORT_ADAPTER_ATTRIBUTES  MiniportAttributes;
+       NDIS_STATUS status;
+       DEBUG_ENTRY(0);
+       MiniportAttributes.AddDeviceRegistrationAttributes.Header.Type = 
NDIS_OBJECT_TYPE_MINIPORT_ADD_DEVICE_REGISTRATION_ATTRIBUTES;
+       MiniportAttributes.AddDeviceRegistrationAttributes.Header.Revision = 
NDIS_MINIPORT_ADD_DEVICE_REGISTRATION_ATTRIBUTES_REVISION_1;
+       MiniportAttributes.AddDeviceRegistrationAttributes.Header.Size = 
NDIS_SIZEOF_MINIPORT_ADD_DEVICE_REGISTRATION_ATTRIBUTES_REVISION_1;
+       
MiniportAttributes.AddDeviceRegistrationAttributes.MiniportAddDeviceContext = 
MiniportAdapterHandle;
+       MiniportAttributes.AddDeviceRegistrationAttributes.Flags = 0;
+       status = NdisMSetMiniportAttributes(MiniportAdapterHandle, 
&MiniportAttributes);
+       return status;
+}
+
+static VOID ParaNdis6_RemoveDevice (IN NDIS_HANDLE  MiniportAddDeviceContext)
+{
+       DEBUG_ENTRY(0);
+}
+
+static NDIS_STATUS ParaNdis6_StartDevice(IN NDIS_HANDLE  
MiniportAddDeviceContext, IN PIRP  Irp)
+{
+       NDIS_STATUS status = NDIS_STATUS_SUCCESS;
+       DEBUG_ENTRY(0);
+       return status;
+}
+
+typedef struct _tagRRLData
+{
+       PIO_RESOURCE_REQUIREMENTS_LIST prrl;
+       PIO_RESOURCE_LIST currentList;
+       PIO_RESOURCE_DESCRIPTOR currentDesc;
+}tRRLData;
+
+/******************************************************************
+Replacement of resource requirement list: initialize the new list
+*******************************************************************/
+static void InitializeNewResourceRequirementsList(
+       tRRLData *pData,
+       PIO_RESOURCE_REQUIREMENTS_LIST newList,
+       PIO_RESOURCE_REQUIREMENTS_LIST oldList)
+{
+       pData->prrl = newList;
+       pData->currentList = NULL;
+       pData->currentDesc = NULL;
+       if (pData->prrl)
+       {
+               ULONG len = RtlPointerToOffset(pData->prrl, 
&pData->prrl->List[0]);
+               RtlCopyMemory(newList, oldList, len);
+               newList->ListSize = len;
+               newList->AlternativeLists = 0;
+       }
+}
+
+/******************************************************************
+Replacement of resource requirement list: adding new resource list
+to existing resource requirement list
+*******************************************************************/
+static void AddNewResourceList(tRRLData *pData, PIO_RESOURCE_LIST pior)
+{
+       if (pData->prrl)
+       {
+               ULONG len = RtlPointerToOffset(pior, &pior->Descriptors[0]);
+               pData->currentList = 
(PIO_RESOURCE_LIST)RtlOffsetToPointer(pData->prrl, pData->prrl->ListSize);
+               RtlCopyMemory(pData->currentList, pior, len);
+               pData->currentList->Count = 0;
+               pData->prrl->ListSize += len;
+               pData->prrl->AlternativeLists++;
+               pData->currentDesc = &pData->currentList->Descriptors[0];
+       }
+}
+
+/******************************************************************
+Replacement of resource requirement list: done with new resource list,
+verify if it contains all the required resources
+*******************************************************************/
+static void FinalizeResourceList(tRRLData *pData)
+{
+       if (pData->prrl && pData->currentList)
+       {
+               BOOLEAN bFound = FALSE;
+               ULONG len = RtlPointerToOffset(pData->currentList, 
&pData->currentList->Descriptors[0]);
+               UINT i;
+               for (i = 0; i < pData->currentList->Count && !bFound; ++i)
+               {
+                       len += sizeof(IO_RESOURCE_DESCRIPTOR);
+                       if (pData->currentList->Descriptors[i].Type == 
CmResourceTypeInterrupt) bFound = TRUE;
+               }
+               if (!bFound)
+               {
+                       pData->prrl->AlternativeLists--;
+                       pData->prrl->ListSize -= len;
+               }
+       }
+}
 
 
+/******************************************************************
+Replacement of resource requirement list: adding new resource descriptor
+to current resource list
+*******************************************************************/
+static void AddNewResourceDescriptor(tRRLData *pData, PIO_RESOURCE_DESCRIPTOR 
prd)
+{
+       if (pData->prrl && pData->currentList && pData->currentDesc)
+       {
+               *(pData->currentDesc) = *prd;
+               pData->currentList->Count++;
+               pData->prrl->ListSize += sizeof(IO_RESOURCE_DESCRIPTOR);
+               pData->currentDesc++;
+       }
+}
+
+
+/******************************************************************
+Replacement of resource requirement list, when needed:
+The procedure traverses over all the resource lists in existing resource 
requirement list
+(we receive it in IRP information field).
+When the driver is not built to work with MSI resources, we must remove them 
from the
+resource requirement list, otherwise the driver will fail to initialize
+Typically MSI interrupts are labeled as preferred ones, when line interrupts 
are labeled as
+alternative resources. Removing message interrupts, remove also "alternative" 
label from line interrupts.
+*******************************************************************/
+static PIO_RESOURCE_REQUIREMENTS_LIST ParseFilterResourceIrp(
+       IN NDIS_HANDLE  MiniportAddDeviceContext,
+       PIO_RESOURCE_REQUIREMENTS_LIST prrl,
+       BOOLEAN bRemoveMSIResources)
+{
+       tRRLData newRRLData;
+       PIO_RESOURCE_REQUIREMENTS_LIST newPrrl = NULL;
+       DPrintf(0, ("[%s]%s", __FUNCTION__, bRemoveMSIResources ? "(Remove MSI 
resources...)" : ""));
+       if (MiniportAddDeviceContext && prrl) newPrrl = 
(PIO_RESOURCE_REQUIREMENTS_LIST)NdisAllocateMemoryWithTagPriority(
+                       MiniportAddDeviceContext,
+                       prrl->ListSize,
+                       PARANDIS_MEMORY_TAG,
+                       NormalPoolPriority);
+       InitializeNewResourceRequirementsList(&newRRLData, newPrrl, prrl);
+       if (prrl)
+       {
+               ULONG n, offset;
+               PVOID p = &prrl->List[0];
+               DPrintf(0, ("[%s] %d bytes, %d lists", __FUNCTION__, 
prrl->ListSize, prrl->AlternativeLists));
+               offset = RtlPointerToOffset(prrl, p);
+               for (n = 0; n < prrl->AlternativeLists && offset < 
prrl->ListSize; ++n)
+               {
+                       ULONG nDesc;
+                       IO_RESOURCE_LIST *pior = (IO_RESOURCE_LIST *)p;
+                       if ((offset + sizeof(*pior)) < prrl->ListSize)
+                       {
+                               IO_RESOURCE_DESCRIPTOR *pd = 
&pior->Descriptors[0];
+                               DPrintf(0, ("[%s]+%d %d:%d descriptors follow", 
__FUNCTION__, offset, n, pior->Count));
+                               offset += RtlPointerToOffset(p, pd);
+                               AddNewResourceList(&newRRLData, pior);
+                               for (nDesc = 0; nDesc < pior->Count; ++nDesc)
+                               {
+                                       BOOLEAN bRemove = FALSE;
+                                       if ((offset + sizeof(*pd)) <= 
prrl->ListSize)
+                                       {
+                                               DPrintf(0, ("[%s]+%d %d: type 
%d, flags %X, option %X",
+                                                       __FUNCTION__, offset, 
nDesc, pd->Type, pd->Flags, pd->Option));
+                                               if (pd->Type == 
CmResourceTypeInterrupt)
+                                               {
+                                                       if (pd->Flags & 
CM_RESOURCE_INTERRUPT_MESSAGE)
+                                                       {
+                                                               bRemove = 
bRemoveMSIResources;
+                                                       }
+                                                       else
+                                                       {
+                                                               // reset 
IO_RESOURCE_ALTERNATIVE attribute on Line Interrupt,
+                                                               // if we remove 
MSI vectors, otherwise Windows will not allocate it for the device
+                                                               if 
(bRemoveMSIResources && (pd->Option & IO_RESOURCE_ALTERNATIVE))
+                                                               {
+                                                                       
pd->Option &= ~IO_RESOURCE_ALTERNATIVE;
+                                                               }
+                                                       }
+                                               }
+                                               if (!bRemove) 
AddNewResourceDescriptor(&newRRLData, pd);
+                                       }
+                                       offset += sizeof(*pd);
+                                       pd = (IO_RESOURCE_DESCRIPTOR 
*)RtlOffsetToPointer(prrl, offset);
+                               }
+                               FinalizeResourceList(&newRRLData);
+                               p = pd;
+                       }
+               }
+       }
+
+       return newPrrl;
+}
+
+/******************************************************************
+Registered procedure for filtering if resource requirement
+Required when the device supports MSI, but the driver decides - will it work 
with MSI or not
+(currently MSI is supported but does not work, exactly our case).
+In this case the resource requirement list must be replaced - we need to 
remove from it
+all the "message interrupt" resources.
+
+When we are ready to work with MSI (VIRTIO_USE_MSIX_INTERRUPT is DEFINED),
+we just enumerate allocated resources and do not modify them.
+*******************************************************************/
+static NDIS_STATUS ParaNdis6_FilterResource(IN NDIS_HANDLE  
MiniportAddDeviceContext, IN PIRP  Irp)
+{
+       BOOLEAN bRemoveMSI = FALSE;
+       PIO_RESOURCE_REQUIREMENTS_LIST prrl = 
(PIO_RESOURCE_REQUIREMENTS_LIST)(PVOID)Irp->IoStatus.Information;
+#if !defined(VIRTIO_USE_MSIX_INTERRUPT)
+       bRemoveMSI = TRUE;
+#endif
+       if (bRemoveMSI)
+       {
+               // traverse the resource requirements list, clean up all the 
MSI resources
+               PIO_RESOURCE_REQUIREMENTS_LIST newPrrl = 
ParseFilterResourceIrp(MiniportAddDeviceContext, prrl, TRUE);
+               if (newPrrl)
+               {
+                       Irp->IoStatus.Information = (ULONG_PTR)newPrrl;
+                       NdisFreeMemory(prrl, 0, 0);
+                       DPrintf(0, ("[%s] Reparsing resources using new 
requirements...", __FUNCTION__));
+                       // just parse and print after MSI cleanup, this time do 
not remove amything and do not reallocate the list
+                       ParseFilterResourceIrp(NULL, newPrrl, FALSE);
+               }
+       }
+       else
+       {
+               // just parse and print, do not remove amything and do not 
reallocate the list
+               ParseFilterResourceIrp(NULL, prrl, FALSE);
+       }
+       return NDIS_STATUS_SUCCESS;
+}
+
+
+
+/******************************************************************************
+This procedure required when we want to be able filtering resource 
requirements.
+ParaNdis6_AddDevice need to register context (to allow other procedures to 
allocate memory)
+ParaNdis6_FilterResource able to replace resource requirements list if needed
+ParaNdis6_RemoveDevice does not do anything if other procedures do not 
allocate any resources
+ which must be freed upon device removal
+******************************************************************************/
+static NDIS_STATUS ParaNdis6_SetOptions(IN  NDIS_HANDLE NdisDriverHandle, IN  
NDIS_HANDLE DriverContext)
+{
+       NDIS_STATUS status;
+       NDIS_MINIPORT_PNP_CHARACTERISTICS pnpChars;
+       pnpChars.Header.Type = NDIS_OBJECT_TYPE_MINIPORT_PNP_CHARACTERISTICS;
+       pnpChars.Header.Revision = NDIS_MINIPORT_PNP_CHARACTERISTICS_REVISION_1;
+       pnpChars.Header.Size = 
NDIS_SIZEOF_MINIPORT_PNP_CHARACTERISTICS_REVISION_1;
+       pnpChars.MiniportAddDeviceHandler = ParaNdis6_AddDevice;
+       pnpChars.MiniportRemoveDeviceHandler = ParaNdis6_RemoveDevice;
+       pnpChars.MiniportStartDeviceHandler = ParaNdis6_StartDevice;
+       pnpChars.MiniportFilterResourceRequirementsHandler = 
ParaNdis6_FilterResource;
+       status = NdisSetOptionalHandlers(NdisDriverHandle, 
(PNDIS_DRIVER_OPTIONAL_HANDLERS)&pnpChars);
+       return status;
+}
+
 /**********************************************************
 Driver entry point:
 Register miniport driver
@@ -754,7 +1002,7 @@ NTSTATUS DriverEntry(PDRIVER_OBJECT pDriverObject, 
PUNICODE_STRING pRegistryPath
        chars.ResetHandlerEx                            = ParaNdis6_Reset;
        chars.ShutdownHandlerEx                         = 
ParaNdis6_AdapterShutdown;
        chars.DevicePnPEventNotifyHandler       = ParaNdis6_DevicePnPEvent;
-
+       chars.SetOptionsHandler                         = ParaNdis6_SetOptions;
        status = NdisMRegisterMiniportDriver(
                        pDriverObject,
                        pRegistryPath,
diff --git a/NetKVM/wlh/ParaNdis6-Impl.c b/NetKVM/wlh/ParaNdis6-Impl.c
index 7f8ec2e..d781fd8 100644
--- a/NetKVM/wlh/ParaNdis6-Impl.c
+++ b/NetKVM/wlh/ParaNdis6-Impl.c
@@ -178,30 +178,30 @@ VOID ParaNdis_FreePhysicalMemory(
 /**********************************************************
 NDIS-required procedure for hardware interrupt registration
 Parameters:
-       context
+       IN PVOID MiniportInterruptContext (actually Adapter context)
 ***********************************************************/
-static VOID MiniportDisableInterruptEx(IN PVOID  MiniportInterruptContext)
+static VOID MiniportDisableInterruptEx(IN PVOID MiniportInterruptContext)
 {
        DEBUG_ENTRY(0);
-       // TODO: implement
+       ParaNdis_VirtIOEnableInterrupt((PARANDIS_ADAPTER 
*)MiniportInterruptContext, FALSE);
 }
 
 /**********************************************************
 NDIS-required procedure for hardware interrupt registration
 Parameters:
-       context
+       IN PVOID MiniportInterruptContext (actually Adapter context)
 ***********************************************************/
-static VOID MiniportEnableInterruptEx(IN PVOID  MiniportInterruptContext)
+static VOID MiniportEnableInterruptEx(IN PVOID MiniportInterruptContext)
 {
        DEBUG_ENTRY(0);
-       // TODO: implement
+       ParaNdis_VirtIOEnableInterrupt((PARANDIS_ADAPTER 
*)MiniportInterruptContext, TRUE);
 }
 
 /**********************************************************
 NDIS-required procedure for hardware interrupt handling
 Parameters:
     IN PVOID  MiniportInterruptContext (actually Adapter context)
-    OUT PBOOLEAN  QueueDefaultInterruptDpc - set to TRUE for DPC spawning
+    OUT PBOOLEAN  QueueDefaultInterruptDpc - set to TRUE for default DPC 
spawning
     OUT PULONG  TargetProcessors
 Return value:
        TRUE if recognized
@@ -217,9 +217,43 @@ static BOOLEAN MiniportInterrupt(
        b = ParaNdis_OnInterrupt(pContext, QueueDefaultInterruptDpc);
        *TargetProcessors = 0;
        pContext->ulIrqReceived += b;
+
        return b;
 }
 
+/**********************************************************
+NDIS-required procedure for MSI hardware interrupt handling
+Parameters:
+    IN PVOID  MiniportInterruptContext (actually Adapter context)
+    IN ULONG  MessageId - specific interrupt index
+    OUT PBOOLEAN  QueueDefaultInterruptDpc - - set to TRUE for default DPC 
spawning
+    OUT PULONG  TargetProcessors
+Return value:
+       TRUE if recognized
+***********************************************************/
+static BOOLEAN MiniportMSIInterrupt(
+    IN PVOID  MiniportInterruptContext,
+    IN ULONG  MessageId,
+    OUT PBOOLEAN  QueueDefaultInterruptDpc,
+    OUT PULONG  TargetProcessors
+    )
+{
+       BOOLEAN b = MiniportInterrupt(MiniportInterruptContext, 
QueueDefaultInterruptDpc, TargetProcessors);
+       return b;
+}
+
+/**********************************************************
+Syncronize interrupt enable from DPC
+Parameters:
+       context
+***********************************************************/
+void ParaNdis_SyncInterruptEnable(PARANDIS_ADAPTER *pContext)
+{
+       NdisMSynchronizeWithInterruptEx(pContext->InterruptHandle,
+                                                                       0,
+                                                                       
ParaNdis_MiniportSynchronizeInterruptEnable,
+                                                                       
pContext);
+}
 
 /**********************************************************
 NDIS-required procedure for DPC handling
@@ -238,6 +272,43 @@ static VOID MiniportInterruptDPC(
        ParaNdis_DPCWorkBody(pContext);
 }
 
+/**********************************************************
+NDIS-required procedure for MSI DPC handling
+Parameters:
+       PVOID  MiniportInterruptContext (Adapter context)
+    IN ULONG  MessageId - specific interrupt index
+***********************************************************/
+static VOID MiniportMSIInterruptDpc(
+    IN PVOID  MiniportInterruptContext,
+    IN ULONG  MessageId,
+    IN PVOID  MiniportDpcContext,
+    IN PULONG  NdisReserved1,
+    IN PULONG  NdisReserved2
+    )
+{
+       PARANDIS_ADAPTER *pContext = (PARANDIS_ADAPTER 
*)MiniportInterruptContext;
+       DPrintf(0, ("[%s] (Message %d)", __FUNCTION__, MessageId));
+       ParaNdis_DPCWorkBody(pContext);
+}
+
+static VOID MiniportDisableMSIInterrupt(
+    IN PVOID  MiniportInterruptContext,
+    IN ULONG  MessageId
+    )
+{
+       DEBUG_ENTRY(0);
+       ParaNdis_VirtIOEnableInterrupt((PARANDIS_ADAPTER 
*)MiniportInterruptContext, FALSE);
+}
+
+static VOID MiniportEnableMSIInterrupt(
+    IN PVOID  MiniportInterruptContext,
+    IN ULONG  MessageId
+    )
+{
+       DEBUG_ENTRY(0);
+       ParaNdis_VirtIOEnableInterrupt((PARANDIS_ADAPTER 
*)MiniportInterruptContext, TRUE);
+}
+
 
 /**********************************************************
 NDIS required handler for run-time allocation of physical memory
@@ -256,6 +327,32 @@ static VOID SharedMemAllocateCompleteHandler(
 
 }
 
+static NDIS_STATUS SetInterruptMessage(PARANDIS_ADAPTER *pContext, UINT 
queueIndex, ULONG messageData)
+{
+       NDIS_STATUS status = NDIS_STATUS_SUCCESS;
+       ULONG val = messageData - 1;
+       switch (queueIndex)
+       {
+       case 0: case 1: // queue interrupt:
+               WriteVirtIODeviceWord(pContext->IODevice.addr + 
VIRTIO_PCI_QUEUE_SEL, (u16)queueIndex);
+               WriteVirtIODeviceWord(pContext->IODevice.addr + 
VIRTIO_PCI_CONFIG + 2, (u16)messageData);
+               val = ReadVirtIODeviceWord(pContext->IODevice.addr + 
VIRTIO_PCI_CONFIG + 2);
+               break;
+       case 2: // config interrupt
+               WriteVirtIODeviceWord(pContext->IODevice.addr + 
VIRTIO_PCI_CONFIG, (u16)messageData);
+               val = ReadVirtIODeviceWord(pContext->IODevice.addr + 
VIRTIO_PCI_CONFIG);
+               break;
+       default:
+               break;
+       }
+       if (val != messageData)
+       {
+               DPrintf(0, ("[%s] ERROR: Wrong MSI-X message for 
q%d(w%X,r%X)!", __FUNCTION__, queueIndex, messageData, val));
+               status = NDIS_STATUS_DEVICE_FAILED;
+       }
+       return status;
+}
+
 /**********************************************************
 NDIS6-related final initialization:
        Installing interrupt handler
@@ -276,12 +373,22 @@ NDIS_STATUS 
ParaNdis_FinishSpecificInitialization(PARANDIS_ADAPTER *pContext)
        NdisZeroMemory(&mic, sizeof(mic));
        mic.Header.Type = NDIS_OBJECT_TYPE_MINIPORT_INTERRUPT;
        mic.Header.Revision = NDIS_MINIPORT_INTERRUPT_REVISION_1;
-       mic.Header.Size = sizeof(mic);
+       mic.Header.Size = 
NDIS_SIZEOF_MINIPORT_INTERRUPT_CHARACTERISTICS_REVISION_1;
        mic.DisableInterruptHandler = MiniportDisableInterruptEx;
        mic.EnableInterruptHandler  = MiniportEnableInterruptEx;
        mic.InterruptDpcHandler = MiniportInterruptDPC;
        mic.InterruptHandler = MiniportInterrupt;
-
+#ifdef VIRTIO_USE_MSIX_INTERRUPT
+       if (pContext->bUsingMSIX)
+       {
+               mic.MsiSupported = TRUE;
+               mic.MsiSyncWithAllMessages = TRUE;
+               mic.EnableMessageInterruptHandler = MiniportEnableMSIInterrupt;
+               mic.DisableMessageInterruptHandler = 
MiniportDisableMSIInterrupt;
+               mic.MessageInterruptHandler = MiniportMSIInterrupt;
+               mic.MessageInterruptDpcHandler = MiniportMSIInterruptDpc;
+       }
+#endif
        PoolParams.Header.Type = NDIS_OBJECT_TYPE_DEFAULT;
        PoolParams.Header.Size = sizeof(PoolParams);
        PoolParams.Header.Revision = NET_BUFFER_LIST_POOL_PARAMETERS_REVISION_1;
@@ -325,6 +432,38 @@ NDIS_STATUS 
ParaNdis_FinishSpecificInitialization(PARANDIS_ADAPTER *pContext)
 
        if (status == NDIS_STATUS_SUCCESS)
        {
+               if (NDIS_CONNECT_MESSAGE_BASED == mic.InterruptType)
+               {
+                       UINT i;
+                       DPrintf(0, ("[%s] Using MSIX interrupts (%d messages, 
irql %d)",
+                               __FUNCTION__, 
mic.MessageInfoTable->MessageCount, mic.MessageInfoTable->UnifiedIrql));
+                       for (i = 0; mic.MessageInfoTable && i < 
mic.MessageInfoTable->MessageCount; ++i)
+                       {
+                               DPrintf(0, ("[%s] MSIX message%d=%08X=>%I64X",
+                                       __FUNCTION__, i,
+                                       
mic.MessageInfoTable->MessageInfo[i].MessageData,
+                                       
mic.MessageInfoTable->MessageInfo[i].MessageAddress));
+                       }
+                       status = SetInterruptMessage(pContext, 0, 0);
+                       if (NDIS_STATUS_SUCCESS == status)
+                       {
+                               i = mic.MessageInfoTable->MessageCount > 1 ? 1 
: 0;
+                               status = SetInterruptMessage(pContext, 1, i);
+                       }
+                       if (NDIS_STATUS_SUCCESS == status)
+                       {
+                               i = mic.MessageInfoTable->MessageCount > 2 ? 2 
: 0;
+                               status = SetInterruptMessage(pContext, 2, i);
+                       }
+                       pContext->pMSIXInfoTable = mic.MessageInfoTable;
+                       //pContext->bDoInterruptRecovery = FALSE;
+               }
+               else if (pContext->bUsingMSIX)
+               {
+                       DPrintf(0, ("[%s] ERROR: Interrupt type %d, message 
table %p",
+                               __FUNCTION__, mic.InterruptType, 
mic.MessageInfoTable));
+                       pContext->bUsingMSIX = FALSE;
+               }
                ParaNdis6_ApplyOffloadPersistentConfiguration(pContext);
        }
        DEBUG_EXIT_STATUS(0, status);
diff --git a/NetKVM/wlh/netkvm.inf b/NetKVM/wlh/netkvm.inf
index e2abef6..45dc7f3 100644
--- a/NetKVM/wlh/netkvm.inf
+++ b/NetKVM/wlh/netkvm.inf
@@ -23,6 +23,18 @@ DriverPackageDisplayName = %kvmnet6.DeviceDesc%
 [RedHat.NT$ARCH$]
 %kvmnet6.DeviceDesc%    = kvmnet6.ndi, PCI\VEN_1AF4&DEV_1000&SUBSYS_00011AF4
 
+[kvmnet6.ndi.hw]
+AddReg          = kvmnet6.EnableMSI
+
+[kvmnet6.EnableMSI]
+HKR, "Interrupt Management",, 0x00000010
+HKR, "Interrupt Management\MessageSignaledInterruptProperties",, 0x00000010
+HKR, "Interrupt Management\MessageSignaledInterruptProperties",        
MSISupported, 0x00010001, 1
+HKR, "Interrupt Management\MessageSignaledInterruptProperties", 
MessageNumberLimit, 0x00010001, 4
+HKR, "Interrupt Management\Affinity Policy",, 0x00000010
+HKR, "Interrupt Management\Affinity Policy", DevicePolicy, 0x00010001, 1
+HKR, "Interrupt Management\Affinity Policy", DevicePriority, 0x00010001, 2
+
 
 [kvmnet6.ndi]
 Characteristics = 0x84 ; NCF_PHYSICAL  | NCF_HAS_UI
diff --git a/NetKVM/wxp/ParaNdis5-Impl.c b/NetKVM/wxp/ParaNdis5-Impl.c
index 03c2033..9abbb98 100644
--- a/NetKVM/wxp/ParaNdis5-Impl.c
+++ b/NetKVM/wxp/ParaNdis5-Impl.c
@@ -1228,6 +1228,18 @@ NDIS_STATUS ParaNdis5_StopSend(PARANDIS_ADAPTER 
*pContext, BOOLEAN bStop, ONPAUS
 }
 
 /**********************************************************
+Syncronize interrupt enable from DPC
+Parameters:
+       context
+***********************************************************/
+void ParaNdis_SyncInterruptEnable(PARANDIS_ADAPTER *pContext)
+{
+       NdisMSynchronizeWithInterrupt(&pContext->Interrupt,
+                                                                 
ParaNdis_MiniportSynchronizeInterruptEnable,
+                                                                 pContext);
+}
+
+/**********************************************************
 Pause or resume receive operation:
 Parameters:
        context
--
To unsubscribe from this list: send the line "unsubscribe kvm-commits" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to