repository: C:/dev/kvm-guest-drivers-windows
branch: master
commit a1d1275e735a02450bc70c262e343c356da1bb34
Author: Yan Vugenfirer <[email protected]>
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 [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html