DPDK patches and discussions
 help / color / mirror / Atom feed
* [dpdk-dev] [PATCH v2] windows/netuio: add Windows NetUIO kernel driver
@ 2020-08-20 22:23 Narcisa Ana Maria Vasile
  2020-08-21  1:32 ` Ranjit Menon
                   ` (2 more replies)
  0 siblings, 3 replies; 16+ messages in thread
From: Narcisa Ana Maria Vasile @ 2020-08-20 22:23 UTC (permalink / raw)
  To: dev, thomas, ocardona, haramakr, pallavi.kadam, ranjit.menon
  Cc: dmitry.kozliuk, dmitrym, Narcisa Vasile, Harini Ramakrishnan

From: Narcisa Vasile <navasile@microsoft.com>

The Windows NetUIO kernel driver allows the DPDK userspace
application to directly access the hardware.

Cc: Harini Ramakrishnan <Harini.Ramakrishnan@microsoft.com>
Cc: Omar Cardona <ocardona@microsoft.com>
Signed-off-by: Narcisa Vasile <navasile@microsoft.com>
---

v2:
  Fix license message in each file
  Minor fixes in README
  Remove RC file as it is autogenerated
  Remove HW Ids from Inf
  Resubmit as single patch

 .gitattributes                                |   4 +
 .gitignore                                    |   2 +
 windows/netuio/kernel/README_NetUIO.rst       |  64 +++
 .../netuio/kernel/windows/netuio/netuio.inf   |  78 ++++
 .../netuio/kernel/windows/netuio/netuio_dev.c | 388 +++++++++++++++++
 .../netuio/kernel/windows/netuio/netuio_dev.h |  61 +++
 .../netuio/kernel/windows/netuio/netuio_drv.c | 146 +++++++
 .../netuio/kernel/windows/netuio/netuio_drv.h |  32 ++
 .../kernel/windows/netuio/netuio_interface.h  |  73 ++++
 .../kernel/windows/netuio/netuio_queue.c      | 397 ++++++++++++++++++
 .../kernel/windows/netuio/netuio_queue.h      |  31 ++
 .../netuio/kernel/windows/netuio/resource.h   |  14 +
 .../mk/exec-env/windows/netuio/netuio.sln     |  24 ++
 .../mk/exec-env/windows/netuio/netuio.vcxproj | 113 +++++
 .../windows/netuio/netuio.vcxproj.filters     |  54 +++
 .../windows/netuio/netuio.vcxproj.user        |  11 +
 16 files changed, 1492 insertions(+)
 create mode 100644 .gitattributes
 create mode 100644 .gitignore
 create mode 100644 windows/netuio/kernel/README_NetUIO.rst
 create mode 100644 windows/netuio/kernel/windows/netuio/netuio.inf
 create mode 100644 windows/netuio/kernel/windows/netuio/netuio_dev.c
 create mode 100644 windows/netuio/kernel/windows/netuio/netuio_dev.h
 create mode 100644 windows/netuio/kernel/windows/netuio/netuio_drv.c
 create mode 100644 windows/netuio/kernel/windows/netuio/netuio_drv.h
 create mode 100644 windows/netuio/kernel/windows/netuio/netuio_interface.h
 create mode 100644 windows/netuio/kernel/windows/netuio/netuio_queue.c
 create mode 100644 windows/netuio/kernel/windows/netuio/netuio_queue.h
 create mode 100644 windows/netuio/kernel/windows/netuio/resource.h
 create mode 100644 windows/netuio/mk/exec-env/windows/netuio/netuio.sln
 create mode 100644 windows/netuio/mk/exec-env/windows/netuio/netuio.vcxproj
 create mode 100644 windows/netuio/mk/exec-env/windows/netuio/netuio.vcxproj.filters
 create mode 100644 windows/netuio/mk/exec-env/windows/netuio/netuio.vcxproj.user

diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 000000000..13482db3d
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1,4 @@
+* text=auto
+
+*.sln text eol=crlf
+*.vcxproj text eol=crlf
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 000000000..543281e8f
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,2 @@
+x64/
+.vs/
diff --git a/windows/netuio/kernel/README_NetUIO.rst b/windows/netuio/kernel/README_NetUIO.rst
new file mode 100644
index 000000000..a290fcf20
--- /dev/null
+++ b/windows/netuio/kernel/README_NetUIO.rst
@@ -0,0 +1,64 @@
+..  SPDX-License-Identifier: BSD-3-Clause
+    Copyright(c) 2020 Microsoft Corporation.
+
+Compiling the NetUIO Driver from Source
+=======================================
+
+Operating System
+~~~~~~~~~~~~~~~~
+
+The NetUIO source has been validated against the following operating systems:
+
+* Windows Server 2016
+* Windows Server 2019
+
+Hardware Requirements
+~~~~~~~~~~~~~~~~~~~~~
+The NetUIO driver has been validated using the following network adapters on the Windows platform:
+
+*
+*
+
+Software Requirements
+~~~~~~~~~~~~~~~~~~~~~
+
+* Install Microsoft Visual Studio 2017 or Visual Stuido Studio 2019 Enterprise from https://visualstudio.microsoft.com/downloads/ 
+    * During installation ensure following components are selected:
+        Windows 10 SDK (10.0.15063.0)
+        Windows 10 SDK (10.0.17763.0)
+        Windows Drivers Kit
+
+* Install WDK for Windows 10 (10.0.17763.1) from https://docs.microsoft.com/en-us/windows-hardware/drivers/download-the-wdk 
+
+Building the NetUIO Driver
+--------------------------
+Follow the steps below to build the NetUIO driver and install the driver for the network adapter. 
+
+* Clone the dpdk-kmods repository: git clone git://dpdk.org/dpdk-kmods
+* Navigate to \dpdk-kmods\windows\netuio\mk\exec-env\windows\netuio
+* Load netuio.sln in Microsoft Visual Studio 2017 or 2019
+* Choose Release as the configuration mode and Build the solution
+* The resultant output files can be found in \dpdk-kmods\windows\netuio\x64\Release\netuio
+ 
+Installing netuio.sys Driver for development
+--------------------------------------------
+.. note::
+
+In a development environment, NetUIO driver can be loaded by enabling test-signing.
+Please implement adequate precautionary measures before installing a test-signed driver, replacing a signed-driver.
+
+To ensure test-signed kernel-mode drivers can load on Windows, enable test-signing, using the following BCDEdit command.
+
+C:\windows\system32>Bcdedit.exe -set TESTSIGNING ON
+
+Windows displays the text “Test Mode” to remind users the system has test-signing enabled. 
+Refer to the MSDN documentation on how to Test-Sign a Driver Package.
+ 
+To procure a WHQL signed NetUIO driver for Windows, please reach out to dpdkwin@microsoft.com
+
+* Go to Device Manager -> Network Adapters.
+* Right Click on target network adapter -> Select Update Driver.
+* Select "Browse my computer for driver software".
+* In the resultant window, select "Let me pick from a list of available drivers on my computer".
+* Select "DPDK netUIO for Network Adapter" from the list of drivers.
+* The NetUIO.sys driver is successfully installed.
diff --git a/windows/netuio/kernel/windows/netuio/netuio.inf b/windows/netuio/kernel/windows/netuio/netuio.inf
new file mode 100644
index 000000000..88e90b365
--- /dev/null
+++ b/windows/netuio/kernel/windows/netuio/netuio.inf
@@ -0,0 +1,78 @@
+; SPDX-License-Identifier: BSD-3-Clause
+; Copyright (c) Microsoft Corporation. All rights reserved
+;
+; netuio.inf
+;
+
+[Version]
+Signature="$WINDOWS NT$"
+Class=%ClassName%
+ClassGuid={78912BC1-CB8E-4B28-A329-F322EBADBE0F}
+Provider=%Intel%
+CatalogFile=netuio.cat
+DriverVer=
+
+[ClassInstall32]
+Addreg=netuioClassReg
+
+[netuioClassReg]
+HKR,,,0,%ClassName%
+HKR,,Icon,,-5
+
+;*****************************************
+; Install Section
+;*****************************************
+
+[Manufacturer]
+
+[netuio_Device.NT]
+CopyFiles=Drivers_Dir
+
+[Drivers_Dir]
+netuio.sys
+
+[netuio_Device.NT.HW]
+AddReg=Device.HW.Registry
+
+[Device.HW.Registry]
+; Ensure that only administrators can access our device object.
+HKR,,Security,,"D:P(A;;GA;;;SY)(A;;GA;;;BA)"
+
+;-------------- Service installation
+[netuio_Device.NT.Services]
+AddService = netuio,%SPSVCINST_ASSOCSERVICE%, netuio_Service_Inst
+
+; -------------- netuio driver install sections
+[netuio_Service_Inst]
+DisplayName    = %netuio.SVCDESC%
+ServiceType    = 1               ; SERVICE_KERNEL_DRIVER
+StartType      = 3               ; SERVICE_DEMAND_START
+ErrorControl   = 1               ; SERVICE_ERROR_NORMAL
+ServiceBinary  = %12%\netuio.sys
+AddReg         = DMAr.reg
+
+[DestinationDirs]
+DefaultDestDir = 12
+
+[SourceDisksNames]
+1 = %DiskName%,,,""
+
+[SourceDisksFiles]
+netuio.sys  = 1,,
+
+[netuio_Device.NT.Wdf]
+KmdfService =  netuio, netuio_wdfsect
+[netuio_wdfsect]
+KmdfLibraryVersion = $KMDFVERSION$
+
+[Strings]
+SPSVCINST_ASSOCSERVICE= 0x00000002
+Intel = "Intel"
+Broadcom = "Broadcom Corporation"
+ClassName = "Windows UIO"
+DiskName = "DPDK netUIO Installation Disk"
+netuio.DeviceDesc = "netuio Device"
+netuio.SVCDESC = "netuio Service"
+
+[DMAr.reg]
+HKR,Parameters,DmaRemappingCompatible,0x00010001,1
diff --git a/windows/netuio/kernel/windows/netuio/netuio_dev.c b/windows/netuio/kernel/windows/netuio/netuio_dev.c
new file mode 100644
index 000000000..6394bb5d1
--- /dev/null
+++ b/windows/netuio/kernel/windows/netuio/netuio_dev.c
@@ -0,0 +1,388 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) Microsoft Corporation. All rights reserved
+ */
+
+#include <stdio.h>
+#include "netuio_drv.h"
+
+#include <wdmguid.h>
+
+#ifdef ALLOC_PRAGMA
+#pragma alloc_text (PAGE, netuio_create_device)
+#pragma alloc_text (PAGE, netuio_evt_device_context_cleanup)
+#pragma alloc_text (PAGE, netuio_map_hw_resources)
+#pragma alloc_text (PAGE, netuio_free_hw_resources)
+#endif
+
+/*
+Routine Description:
+    Worker routine called to create a device and its software resources.
+
+Return Value:
+    NTSTATUS
+ */
+NTSTATUS
+netuio_create_device(_Inout_ PWDFDEVICE_INIT DeviceInit)
+{
+    NTSTATUS status;
+    WDFDEVICE device = NULL;
+
+    WDF_OBJECT_ATTRIBUTES deviceAttributes;
+    WDF_PNPPOWER_EVENT_CALLBACKS pnpPowerCallbacks;
+    WDF_FILEOBJECT_CONFIG fileConfig;
+
+    PAGED_CODE();
+
+    // Register PnP power management callbacks
+    WDF_PNPPOWER_EVENT_CALLBACKS_INIT(&pnpPowerCallbacks);
+    pnpPowerCallbacks.EvtDevicePrepareHardware = netuio_evt_prepare_hw;
+    pnpPowerCallbacks.EvtDeviceReleaseHardware = netuio_evt_release_hw;
+    WdfDeviceInitSetPnpPowerEventCallbacks(DeviceInit, &pnpPowerCallbacks);
+
+    // Register callbacks for when a HANDLE is opened or closed.
+    WDF_FILEOBJECT_CONFIG_INIT(&fileConfig, WDF_NO_EVENT_CALLBACK, WDF_NO_EVENT_CALLBACK, netuio_evt_file_cleanup);
+    WdfDeviceInitSetFileObjectConfig(DeviceInit, &fileConfig, WDF_NO_OBJECT_ATTRIBUTES);
+
+    // Set the device context cleanup callback.
+    // This function will be called when the WDF Device Object associated to the current device is destroyed
+    WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&deviceAttributes, NETUIO_CONTEXT_DATA);
+    deviceAttributes.EvtCleanupCallback = netuio_evt_device_context_cleanup;
+    WdfDeviceInitSetIoInCallerContextCallback(DeviceInit, netuio_evt_IO_in_caller_context);
+
+    status = WdfDeviceCreate(&DeviceInit, &deviceAttributes, &device);
+
+	if (NT_SUCCESS(status)) {
+		// Create a device interface so that applications can find and talk to us.
+		status = WdfDeviceCreateDeviceInterface(device, &GUID_DEVINTERFACE_netUIO, NULL);
+	}
+
+    if (NT_SUCCESS(status)) {
+        // Retrieve and store PCI information
+        status = get_pci_device_info(device);
+    }
+
+    if (NT_SUCCESS(status)) {
+        // Create a symbolic link name for user-space access
+        status = create_device_specific_symbolic_link(device);
+    }
+
+    if (NT_SUCCESS(status)) {
+        // Initialize the I/O Package and any Queues
+        status = netuio_queue_initialize(device);
+    }
+
+    if (NT_SUCCESS(status)) {
+        // Allocate physically contiguous memory for user process use. We'll map it later
+        status = allocate_usermemory_segment(device);
+    }
+
+    return status;
+}
+
+/*
+Routine Description:
+    Free all the resources allocated in AdfEvtDeviceAdd.
+
+Return Value:
+    None
+ */
+VOID
+netuio_evt_device_context_cleanup(_In_ WDFOBJECT Device)
+{
+    free_usermemory_segment(Device);
+    return;
+}
+
+NTSTATUS
+netuio_map_hw_resources(_In_ WDFDEVICE Device, _In_ WDFCMRESLIST Resources, _In_ WDFCMRESLIST ResourcesTranslated)
+{
+    UNREFERENCED_PARAMETER(Resources);
+
+    NTSTATUS status;
+
+    PNETUIO_CONTEXT_DATA  netuio_contextdata;
+    netuio_contextdata = netuio_get_context_data(Device);
+
+    if (!netuio_contextdata) {
+        return STATUS_UNSUCCESSFUL;
+    }
+
+    PCI_COMMON_HEADER pci_config = {0};
+    ULONG bytes_returned;
+
+    // Read PCI configuration space
+    bytes_returned = netuio_contextdata->bus_interface.GetBusData(
+        netuio_contextdata->bus_interface.Context,
+        PCI_WHICHSPACE_CONFIG,
+        &pci_config,
+        0,
+        sizeof(pci_config));
+
+    if (bytes_returned != sizeof(pci_config)) {
+        status = STATUS_NOT_SUPPORTED;
+        goto end;
+    }
+
+    // Device type is implictly enforced by .inf
+    ASSERT(PCI_CONFIGURATION_TYPE(&pci_config) == PCI_DEVICE_TYPE);
+
+    PCM_PARTIAL_RESOURCE_DESCRIPTOR descriptor;
+    ULONG next_descriptor = 0;
+    ULONGLONG bar_addr = 0;
+    ULONG curr_bar = 0;
+    ULONG prev_bar = 0;
+
+    for (INT bar_index = 0; bar_index < PCI_MAX_BAR; bar_index++) {
+        prev_bar = curr_bar;
+        curr_bar = pci_config.u.type0.BaseAddresses[bar_index];
+        if (curr_bar == 0 || (prev_bar & PCI_TYPE_64BIT)) {
+            // Skip this bar
+            netuio_contextdata->bar[bar_index].base_addr.QuadPart = 0;
+            netuio_contextdata->bar[bar_index].size = 0;
+            netuio_contextdata->bar[bar_index].virt_addr = 0;
+
+            netuio_contextdata->dpdk_hw[bar_index].mdl = NULL;
+            netuio_contextdata->dpdk_hw[bar_index].mem.size = 0;
+
+            continue;
+        }
+
+        // Find next CmResourceTypeMemory
+        do {
+            descriptor = WdfCmResourceListGetDescriptor(ResourcesTranslated, next_descriptor);
+            next_descriptor++;
+
+            if (descriptor == NULL) {
+                status = STATUS_DEVICE_CONFIGURATION_ERROR;
+                goto end;
+            }
+        } while (descriptor->Type != CmResourceTypeMemory);
+
+        // Assert that we have the correct descriptor
+        ASSERT((descriptor->Flags & CM_RESOURCE_MEMORY_BAR) != 0);
+
+        if (curr_bar & PCI_TYPE_64BIT) {
+            ASSERT(bar_index != PCI_TYPE0_ADDRESSES - 1);
+            bar_addr = ((ULONGLONG)pci_config.u.type0.BaseAddresses[bar_index + 1] << 32) | (curr_bar & PCI_ADDRESS_MEMORY_ADDRESS_MASK);
+        }
+        else
+        {
+            bar_addr = curr_bar & PCI_ADDRESS_MEMORY_ADDRESS_MASK;
+        }
+
+        ASSERT((ULONGLONG)descriptor->u.Memory.Start.QuadPart == bar_addr);
+
+        // Retrieve and map the BARs
+        netuio_contextdata->bar[bar_index].base_addr.QuadPart = descriptor->u.Memory.Start.QuadPart;
+        netuio_contextdata->bar[bar_index].size = descriptor->u.Memory.Length;
+        netuio_contextdata->bar[bar_index].virt_addr = MmMapIoSpace(descriptor->u.Memory.Start,
+                                                                    descriptor->u.Memory.Length,
+                                                                    MmNonCached);
+        if (netuio_contextdata->bar[bar_index].virt_addr == NULL) {
+            status = STATUS_INSUFFICIENT_RESOURCES;
+            goto end;
+        }
+
+        // Allocate an MDL for the device BAR, so we can map it to the user's process context later.
+        netuio_contextdata->dpdk_hw[bar_index].mdl = IoAllocateMdl(netuio_contextdata->bar[bar_index].virt_addr,
+                                                                   (ULONG)netuio_contextdata->bar[bar_index].size,
+                                                                   FALSE,
+                                                                   FALSE,
+                                                                   NULL);
+        if (!netuio_contextdata->dpdk_hw[bar_index].mdl) {
+            status = STATUS_INSUFFICIENT_RESOURCES;
+            goto end;
+        }
+
+        netuio_contextdata->dpdk_hw[bar_index].mem.size = netuio_contextdata->bar[bar_index].size;
+    } // for bar_index
+
+    status = STATUS_SUCCESS;
+
+end:
+    if (status != STATUS_SUCCESS) {
+        netuio_free_hw_resources(Device);
+    }
+
+    return status;
+}
+
+VOID
+netuio_free_hw_resources(_In_ WDFDEVICE Device)
+{
+    PNETUIO_CONTEXT_DATA  netuio_contextdata;
+    netuio_contextdata = netuio_get_context_data(Device);
+
+    if (netuio_contextdata) {
+        for (UINT8 bar_index = 0; bar_index < PCI_MAX_BAR; bar_index++) {
+
+            // Free the allocated MDLs
+            if (netuio_contextdata->dpdk_hw[bar_index].mdl) {
+                IoFreeMdl(netuio_contextdata->dpdk_hw[bar_index].mdl);
+            }
+
+            // Unmap all the BAR regions previously mapped
+            if (netuio_contextdata->bar[bar_index].virt_addr) {
+                MmUnmapIoSpace(netuio_contextdata->bar[bar_index].virt_addr, netuio_contextdata->bar[bar_index].size);
+            }
+        }
+
+        RtlZeroMemory(netuio_contextdata->dpdk_hw, sizeof(netuio_contextdata->dpdk_hw));
+        RtlZeroMemory(netuio_contextdata->bar, sizeof(netuio_contextdata->bar));
+    }
+}
+
+
+static NTSTATUS
+get_pci_device_info(_In_ WDFOBJECT device)
+{
+    NTSTATUS status = STATUS_UNSUCCESSFUL;
+
+    PNETUIO_CONTEXT_DATA  netuio_contextdata;
+    netuio_contextdata = netuio_get_context_data(device);
+
+    if (!netuio_contextdata)
+        return status;
+
+    netuio_contextdata->wdf_device = device;  // Store for later use
+
+    // Obtain the BUS_INTERFACE_STANDARD interface from the Bus Driver
+    status = WdfFdoQueryForInterface(device, &GUID_BUS_INTERFACE_STANDARD,
+                                    (PINTERFACE)&netuio_contextdata->bus_interface,
+                                    sizeof(BUS_INTERFACE_STANDARD), 1, NULL);
+    if (!NT_SUCCESS(status))
+        return status;
+
+    // Retrieve the B:D:F details of our device
+    PDEVICE_OBJECT pdo = NULL;
+    pdo = WdfDeviceWdmGetPhysicalDevice(device);
+    if (pdo) {
+        ULONG prop = 0, length = 0;
+        status = IoGetDeviceProperty(pdo, DevicePropertyBusNumber, sizeof(ULONG), (PVOID)&netuio_contextdata->addr.bus_num, &length);
+        status = IoGetDeviceProperty(pdo, DevicePropertyAddress, sizeof(ULONG), (PVOID)&prop, &length);
+
+        if (NT_SUCCESS(status)) {
+            netuio_contextdata->addr.func_num = prop & 0x0000FFFF;
+            netuio_contextdata->addr.dev_num = ((prop >> 16) & 0x0000FFFF);
+        }
+        // Also, retrieve the NUMA node of the device
+        USHORT numaNode;
+        status = IoGetDeviceNumaNode(pdo, &numaNode);
+        if (NT_SUCCESS(status)) {
+            netuio_contextdata->dev_numa_node = numaNode;
+        }
+    }
+
+    return status;
+}
+
+static NTSTATUS
+create_device_specific_symbolic_link(_In_ WDFOBJECT device)
+{
+    NTSTATUS status = STATUS_UNSUCCESSFUL;
+    UNICODE_STRING netuio_symbolic_link;
+
+    PNETUIO_CONTEXT_DATA  netuio_contextdata;
+    netuio_contextdata = netuio_get_context_data(device);
+
+    if (!netuio_contextdata)
+        return status;
+
+    // Build symbolic link name as <netuio_symbolic_link>_BDF  (bus/device/func)
+    CHAR  symbolic_link[64] = { 0 };
+    sprintf_s(symbolic_link, sizeof(symbolic_link), "%s_%04d%02d%02d",
+                            NETUIO_DEVICE_SYMBOLIC_LINK_ANSI, netuio_contextdata->addr.bus_num,
+                            netuio_contextdata->addr.dev_num, netuio_contextdata->addr.func_num);
+
+    ANSI_STRING ansi_symbolic_link;
+    RtlInitAnsiString(&ansi_symbolic_link, symbolic_link);
+
+    status = RtlAnsiStringToUnicodeString(&netuio_symbolic_link, &ansi_symbolic_link, TRUE);
+    if (!NT_SUCCESS(status))
+        return status;
+
+    status = WdfDeviceCreateSymbolicLink(device, &netuio_symbolic_link);
+
+    RtlFreeUnicodeString(&netuio_symbolic_link);
+
+    return status;
+}
+
+static NTSTATUS
+allocate_usermemory_segment(_In_ WDFOBJECT device)
+{
+    NTSTATUS status;
+
+    PNETUIO_CONTEXT_DATA  netuio_contextdata;
+    netuio_contextdata = netuio_get_context_data(device);
+
+    if (!netuio_contextdata)
+    {
+        status = STATUS_INVALID_DEVICE_STATE;
+        goto end;
+    }
+
+    PHYSICAL_ADDRESS lowest_acceptable_address;
+    PHYSICAL_ADDRESS highest_acceptable_address;
+    PHYSICAL_ADDRESS boundary_address_multiple;
+
+    lowest_acceptable_address.QuadPart = 0x0000000000000000;
+    highest_acceptable_address.QuadPart = 0xFFFFFFFFFFFFFFFF;
+    boundary_address_multiple.QuadPart = 0;
+
+    // Allocate physically contiguous memory for user process use
+    netuio_contextdata->dpdk_seg.mem.virt_addr =
+                MmAllocateContiguousMemorySpecifyCache(USER_MEMORY_SEGMENT_SIZE,
+                                                       lowest_acceptable_address,
+                                                       highest_acceptable_address,
+                                                       boundary_address_multiple,
+                                                       MmCached);
+
+    if (!netuio_contextdata->dpdk_seg.mem.virt_addr) {
+        status = STATUS_NO_MEMORY;
+        goto end;
+    }
+
+    netuio_contextdata->dpdk_seg.mem.size = USER_MEMORY_SEGMENT_SIZE;
+
+    // Allocate an MDL for this memory region - so that we can map it into the user's process context later
+    netuio_contextdata->dpdk_seg.mdl = IoAllocateMdl((PVOID)netuio_contextdata->dpdk_seg.mem.virt_addr, USER_MEMORY_SEGMENT_SIZE, FALSE, FALSE, NULL);
+    if (netuio_contextdata->dpdk_seg.mdl == NULL) {
+        status = STATUS_NO_MEMORY;
+        goto end;
+    }
+
+    // Store the region's physical address
+    netuio_contextdata->dpdk_seg.mem.phys_addr = MmGetPhysicalAddress(netuio_contextdata->dpdk_seg.mem.virt_addr);
+
+    status = STATUS_SUCCESS;
+
+end:
+    if (status != STATUS_SUCCESS)
+    {
+        free_usermemory_segment(device);
+    }
+
+    return status;
+}
+
+static VOID
+free_usermemory_segment(_In_ WDFOBJECT device)
+{
+    PNETUIO_CONTEXT_DATA  netuio_contextdata;
+    netuio_contextdata = netuio_get_context_data(device);
+
+    if (netuio_contextdata) {
+        if (netuio_contextdata->dpdk_seg.mdl)
+        {
+            IoFreeMdl(netuio_contextdata->dpdk_seg.mdl);
+            netuio_contextdata->dpdk_seg.mdl = NULL;
+        }
+
+        if (netuio_contextdata->dpdk_seg.mem.virt_addr)
+        {
+            MmFreeContiguousMemory(netuio_contextdata->dpdk_seg.mem.virt_addr);
+            netuio_contextdata->dpdk_seg.mem.virt_addr = NULL;
+        }
+    }
+}
diff --git a/windows/netuio/kernel/windows/netuio/netuio_dev.h b/windows/netuio/kernel/windows/netuio/netuio_dev.h
new file mode 100644
index 000000000..82071a220
--- /dev/null
+++ b/windows/netuio/kernel/windows/netuio/netuio_dev.h
@@ -0,0 +1,61 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) Microsoft Corporation. All rights reserved
+ */
+
+#ifndef NETUIO_DEV_H
+#define NETUIO_DEV_H
+
+EXTERN_C_START
+
+#include "netuio_interface.h"
+
+// Constants
+#define USER_MEMORY_SEGMENT_SIZE (256ULL * 1024ULL * 1024ULL)   // 256MB
+
+struct pci_bar {
+    PHYSICAL_ADDRESS base_addr;
+    PVOID            virt_addr;
+    UINT64           size;
+};
+
+struct mem_map_region {
+    PMDL               mdl;    // MDL describing the memory region
+    struct mem_region  mem;    // Memory region details
+};
+
+// The device context performs the same job as a WDM device extension in the driver frameworks
+typedef struct _NETUIO_CONTEXT_DATA
+{
+    WDFDEVICE               wdf_device;             // WDF device handle to the FDO
+    BUS_INTERFACE_STANDARD  bus_interface;          // Bus interface for config space access
+    struct pci_bar          bar[PCI_MAX_BAR];       // device BARs
+    struct dev_addr         addr;                   // B:D:F details of device
+    USHORT                  dev_numa_node;          // The NUMA node of the device
+    struct mem_map_region   dpdk_hw[PCI_MAX_BAR];   // mapped region for the device's register space
+    struct mem_map_region   dpdk_seg;               // mapped region allocated for DPDK process use
+} NETUIO_CONTEXT_DATA, *PNETUIO_CONTEXT_DATA;
+
+
+// This macro will generate an inline function called DeviceGetContext
+// which will be used to get a pointer to the device context memory in a type safe manner.
+WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(NETUIO_CONTEXT_DATA, netuio_get_context_data)
+
+
+// Function to initialize the device and its callbacks
+NTSTATUS netuio_create_device(_Inout_ PWDFDEVICE_INIT DeviceInit);
+NTSTATUS netuio_map_hw_resources(_In_ WDFDEVICE Device, _In_ WDFCMRESLIST Resources, _In_ WDFCMRESLIST ResourcesTranslated);
+VOID netuio_free_hw_resources(_In_ WDFDEVICE Device);
+
+
+// Function called for cleanup when device object is being destroyed
+VOID netuio_evt_device_context_cleanup(_In_ WDFOBJECT Device);
+
+// Local function protoyypes
+static NTSTATUS get_pci_device_info(_In_ WDFOBJECT device);
+static NTSTATUS create_device_specific_symbolic_link(_In_ WDFOBJECT device);
+static NTSTATUS allocate_usermemory_segment(_In_ WDFOBJECT device);
+static VOID free_usermemory_segment(_In_ WDFOBJECT device);
+
+EXTERN_C_END
+
+#endif // NETUIO_DEV_H
diff --git a/windows/netuio/kernel/windows/netuio/netuio_drv.c b/windows/netuio/kernel/windows/netuio/netuio_drv.c
new file mode 100644
index 000000000..4b00b01f0
--- /dev/null
+++ b/windows/netuio/kernel/windows/netuio/netuio_drv.c
@@ -0,0 +1,146 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) Microsoft Corporation. All rights reserved
+ */
+
+#include "netuio_drv.h"
+
+#ifdef ALLOC_PRAGMA
+#pragma alloc_text (INIT, DriverEntry)
+#pragma alloc_text (PAGE, netuio_evt_device_add)
+#pragma alloc_text (PAGE, netuio_evt_driver_context_cleanup)
+#endif
+
+
+/*
+Routine Description:
+    DriverEntry initializes the driver and is the first routine called by the
+    system after the driver is loaded. DriverEntry specifies the other entry
+    points in the function driver, such as EvtDevice and DriverUnload.
+
+Return Value:
+    STATUS_SUCCESS if successful,
+    STATUS_UNSUCCESSFUL otherwise.
+ */
+NTSTATUS
+DriverEntry(_In_ PDRIVER_OBJECT DriverObject, _In_ PUNICODE_STRING RegistryPath)
+{
+    WDF_DRIVER_CONFIG config;
+    NTSTATUS status;
+    WDF_OBJECT_ATTRIBUTES attributes;
+
+    // Register a cleanup callback so that we can call WPP_CLEANUP when
+    // the framework driver object is deleted during driver unload.
+    WDF_OBJECT_ATTRIBUTES_INIT(&attributes);
+    attributes.EvtCleanupCallback = netuio_evt_driver_context_cleanup;
+
+    WDF_DRIVER_CONFIG_INIT(&config, netuio_evt_device_add);
+
+    status = WdfDriverCreate(DriverObject, RegistryPath,
+                             &attributes, &config,
+                             WDF_NO_HANDLE);
+
+    if (!NT_SUCCESS(status)) {
+        return status;
+    }
+
+    return status;
+}
+
+
+/*
+Routine Description:
+    netuio_evt_device_add is called by the framework in response to AddDevice
+    call from the PnP manager. We create and initialize a device object to
+    represent a new instance of the device.
+
+Return Value:
+    NTSTATUS
+ */
+NTSTATUS
+netuio_evt_device_add(_In_ WDFDRIVER Driver, _Inout_ PWDFDEVICE_INIT DeviceInit)
+{
+    UNREFERENCED_PARAMETER(Driver);
+    return netuio_create_device(DeviceInit);
+}
+
+/*
+Routine Description :
+    Maps HW resources and retrieves the PCI BAR address(es) of the device
+
+Return Value :
+    STATUS_SUCCESS is successful.
+    STATUS_<ERROR> otherwise
+-*/
+NTSTATUS
+netuio_evt_prepare_hw(_In_ WDFDEVICE Device, _In_ WDFCMRESLIST Resources, _In_ WDFCMRESLIST ResourcesTranslated)
+{
+    NTSTATUS status;
+
+    status = netuio_map_hw_resources(Device, Resources, ResourcesTranslated);
+
+    if (NT_SUCCESS(status)) {
+        PNETUIO_CONTEXT_DATA  netuio_contextdata;
+        netuio_contextdata = netuio_get_context_data(Device);
+        if (netuio_contextdata) {
+            DbgPrintEx(DPFLTR_IHVNETWORK_ID, DPFLTR_NETUIO_INFO_LEVEL, "netUIO Driver loaded...on device (B:D:F) %04d:%02d:%02d\n",
+                                             netuio_contextdata->addr.bus_num, netuio_contextdata->addr.dev_num, netuio_contextdata->addr.func_num);
+        }
+    }
+    return status;
+}
+
+/*
+Routine Description :
+    Releases the resource mapped by netuio_evt_prepare_hw
+
+Return Value :
+    STATUS_SUCCESS always.
+-*/
+NTSTATUS
+netuio_evt_release_hw(_In_ WDFDEVICE Device, _In_ WDFCMRESLIST ResourcesTranslated)
+{
+    UNREFERENCED_PARAMETER(ResourcesTranslated);
+
+    netuio_free_hw_resources(Device);
+
+    return STATUS_SUCCESS;
+}
+
+/*
+Routine Description:
+    Free all the resources allocated in DriverEntry.
+
+Return Value:
+    None
+-*/
+VOID
+netuio_evt_driver_context_cleanup(_In_ WDFOBJECT DriverObject)
+{
+    UNREFERENCED_PARAMETER(DriverObject);
+    DbgPrintEx(DPFLTR_IHVNETWORK_ID, DPFLTR_NETUIO_INFO_LEVEL, "netUIO Driver unloaded.\n");
+    PAGED_CODE();
+}
+
+/*
+Routine Description :
+    EVT_WDF_FILE_CLEANUP callback, called when user process handle is closed.
+
+    Undoes IOCTL_NETUIO_MAP_HW_INTO_USERMODE.
+
+Return value :
+    None
+-*/
+VOID
+netuio_evt_file_cleanup(_In_ WDFFILEOBJECT FileObject)
+{
+    WDFDEVICE device;
+    PNETUIO_CONTEXT_DATA netuio_contextdata;
+
+    device = WdfFileObjectGetDevice(FileObject);
+    netuio_contextdata = netuio_get_context_data(device);
+
+    if (netuio_contextdata)
+    {
+        netuio_unmap_address_from_user_process(netuio_contextdata);
+    }
+}
diff --git a/windows/netuio/kernel/windows/netuio/netuio_drv.h b/windows/netuio/kernel/windows/netuio/netuio_drv.h
new file mode 100644
index 000000000..07743bf86
--- /dev/null
+++ b/windows/netuio/kernel/windows/netuio/netuio_drv.h
@@ -0,0 +1,32 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) Microsoft Corporation. All rights reserved
+ */
+
+
+#ifndef NETUIO_DRV_H
+#define NETUIO_DRV_H
+
+#define INITGUID
+
+#include <ntddk.h>
+#include <wdf.h>
+
+#include "netuio_dev.h"
+#include "netuio_queue.h"
+
+EXTERN_C_START
+
+// Print output constants
+#define DPFLTR_NETUIO_INFO_LEVEL   35
+
+// WDFDRIVER Events
+DRIVER_INITIALIZE DriverEntry;
+EVT_WDF_DRIVER_DEVICE_ADD       netuio_evt_device_add;
+EVT_WDF_OBJECT_CONTEXT_CLEANUP  netuio_evt_driver_context_cleanup;
+EVT_WDF_DEVICE_PREPARE_HARDWARE netuio_evt_prepare_hw;
+EVT_WDF_DEVICE_RELEASE_HARDWARE netuio_evt_release_hw;
+EVT_WDF_FILE_CLOSE              netuio_evt_file_cleanup;
+
+EXTERN_C_END
+
+#endif // NETUIO_DRV_H
diff --git a/windows/netuio/kernel/windows/netuio/netuio_interface.h b/windows/netuio/kernel/windows/netuio/netuio_interface.h
new file mode 100644
index 000000000..6674931d2
--- /dev/null
+++ b/windows/netuio/kernel/windows/netuio/netuio_interface.h
@@ -0,0 +1,73 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) Microsoft Corporation. All rights reserved
+ */
+
+#ifndef NETUIO_INTERFACE_H
+#define NETUIO_INTERFACE_H
+
+// All structures in this file are packed on an 8B boundary. 
+#pragma pack(push)
+#pragma pack(8)
+
+// Define an Interface Guid so that any app can find the device and talk to it.
+DEFINE_GUID (GUID_DEVINTERFACE_netUIO, 0x08336f60,0x0679,0x4c6c,0x85,0xd2,0xae,0x7c,0xed,0x65,0xff,0xf7); // {08336f60-0679-4c6c-85d2-ae7ced65fff7}
+
+// Device name definitions
+#define NETUIO_DEVICE_SYMBOLIC_LINK_ANSI    "\\DosDevices\\netuio"
+
+// netUIO driver symbolic name (prefix)
+#define NETUIO_DRIVER_NAME  _T("netuio")
+
+// IOCTL code definitions
+#define IOCTL_NETUIO_MAP_HW_INTO_USERMODE CTL_CODE(FILE_DEVICE_NETWORK, 51, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
+#define IOCTL_NETUIO_PCI_CONFIG_IO        CTL_CODE(FILE_DEVICE_NETWORK, 52, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
+
+struct mem_region {
+    UINT64           size;       // memory region size
+    PHYSICAL_ADDRESS phys_addr;  // physical address of the memory region
+    PVOID            virt_addr;  // virtual address of the memory region
+    PVOID            user_mapped_virt_addr;  // virtual address of the region mapped into user process context
+};
+
+struct dev_addr {
+    ULONG   bus_num;
+    USHORT  dev_num;
+    USHORT  func_num;
+};
+
+enum pci_io {
+    PCI_IO_READ = 0,
+    PCI_IO_WRITE = 1
+};
+
+#define PCI_MAX_BAR 6
+
+struct dpdk_private_info
+{
+    struct mem_region   hw[PCI_MAX_BAR];
+    struct mem_region   ms;
+    struct dev_addr     dev_addr;
+    UINT16              dev_id;
+    UINT16              sub_dev_id;
+    USHORT              dev_numa_node;
+    USHORT              reserved;
+};
+
+struct dpdk_pci_config_io
+{
+    struct dev_addr     dev_addr;
+    UINT32              offset;
+    enum pci_io         op;
+    UINT32              access_size; // 1, 2, 4, or 8 bytes
+
+    union dpdk_pci_config_io_data {
+        UINT8			u8;
+        UINT16			u16;
+        UINT32			u32;
+        UINT64			u64;
+    } data;
+};
+
+#pragma pack(pop)
+
+#endif // NETUIO_INTERFACE_H
diff --git a/windows/netuio/kernel/windows/netuio/netuio_queue.c b/windows/netuio/kernel/windows/netuio/netuio_queue.c
new file mode 100644
index 000000000..a2d16c417
--- /dev/null
+++ b/windows/netuio/kernel/windows/netuio/netuio_queue.c
@@ -0,0 +1,397 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) Microsoft Corporation. All rights reserved
+ */
+
+#include "netuio_drv.h"
+
+#ifdef ALLOC_PRAGMA
+#pragma alloc_text (PAGE, netuio_queue_initialize)
+#endif
+
+static void
+netuio_handle_get_hw_data_request(_In_ WDFREQUEST Request, _In_ PNETUIO_CONTEXT_DATA netuio_contextdata,
+                                   _In_ PVOID outputBuf, _In_ size_t outputBufSize)
+{
+    WDF_REQUEST_PARAMETERS params;
+    WDF_REQUEST_PARAMETERS_INIT(&params);
+    WdfRequestGetParameters(Request, &params);
+
+    ASSERT(outputBufSize == sizeof(struct dpdk_private_info));
+
+    struct dpdk_private_info *dpdk_pvt_info = (struct dpdk_private_info *)outputBuf;
+    RtlZeroMemory(dpdk_pvt_info, outputBufSize);
+
+    for (ULONG idx = 0; idx < PCI_MAX_BAR; idx++) {
+        dpdk_pvt_info->hw[idx].phys_addr.QuadPart = netuio_contextdata->bar[idx].base_addr.QuadPart;
+        dpdk_pvt_info->hw[idx].user_mapped_virt_addr = netuio_contextdata->dpdk_hw[idx].mem.user_mapped_virt_addr;
+        dpdk_pvt_info->hw[idx].size = netuio_contextdata->bar[idx].size;
+    }
+
+    dpdk_pvt_info->ms.phys_addr.QuadPart = netuio_contextdata->dpdk_seg.mem.phys_addr.QuadPart;
+    dpdk_pvt_info->ms.user_mapped_virt_addr = netuio_contextdata->dpdk_seg.mem.user_mapped_virt_addr;
+    dpdk_pvt_info->ms.size = netuio_contextdata->dpdk_seg.mem.size;
+}
+
+/*
+Routine Description:
+    Maps address ranges into the usermode process's address space.  The following
+    ranges are mapped:
+
+        * Any PCI BARs that our device was assigned
+        * The scratch buffer of contiguous pages
+
+Return Value:
+    NTSTATUS
+*/
+static NTSTATUS
+netuio_map_address_into_user_process(_In_ PNETUIO_CONTEXT_DATA netuio_contextdata)
+{
+    NTSTATUS status = STATUS_SUCCESS;
+
+    // Map the scratch memory regions to the user's process context
+    MmBuildMdlForNonPagedPool(netuio_contextdata->dpdk_seg.mdl);
+    __try {
+        netuio_contextdata->dpdk_seg.mem.user_mapped_virt_addr =
+            MmMapLockedPagesSpecifyCache(netuio_contextdata->dpdk_seg.mdl, UserMode,
+                                         MmCached, NULL, FALSE, NormalPagePriority);
+
+        if (netuio_contextdata->dpdk_seg.mem.user_mapped_virt_addr == NULL) {
+            status = STATUS_INSUFFICIENT_RESOURCES;
+            goto end;
+        }
+    }
+    __except (EXCEPTION_EXECUTE_HANDLER) {
+        status = GetExceptionCode();
+        goto end;
+    }
+
+    // Map any device BAR(s) to the user's process context
+    for (INT idx = 0; idx < PCI_MAX_BAR; idx++) {
+        if (netuio_contextdata->dpdk_hw[idx].mdl == NULL) {
+            continue;
+        }
+
+        MmBuildMdlForNonPagedPool(netuio_contextdata->dpdk_hw[idx].mdl);
+        __try {
+            netuio_contextdata->dpdk_hw[idx].mem.user_mapped_virt_addr =
+                MmMapLockedPagesSpecifyCache(netuio_contextdata->dpdk_hw[idx].mdl, UserMode,
+                                             MmCached, NULL, FALSE, NormalPagePriority);
+
+            if (netuio_contextdata->dpdk_hw[idx].mem.user_mapped_virt_addr == NULL) {
+                status = STATUS_INSUFFICIENT_RESOURCES;
+                goto end;
+            }
+        }
+        __except (EXCEPTION_EXECUTE_HANDLER) {
+            status = GetExceptionCode();
+            goto end;
+        }
+    }
+
+end:
+    if (status != STATUS_SUCCESS) {
+		netuio_unmap_address_from_user_process(netuio_contextdata);
+    }
+
+    return status;
+}
+
+/*
+Routine Description:
+    Unmaps all address ranges from the usermode process address space.
+    MUST be called in the context of the same process which created
+    the mapping.
+
+Return Value:
+    None
+ */
+VOID
+netuio_unmap_address_from_user_process(_In_ PNETUIO_CONTEXT_DATA netuio_contextdata)
+{
+    if (netuio_contextdata->dpdk_seg.mem.user_mapped_virt_addr != NULL) {
+        MmUnmapLockedPages(
+            netuio_contextdata->dpdk_seg.mem.user_mapped_virt_addr,
+            netuio_contextdata->dpdk_seg.mdl);
+        netuio_contextdata->dpdk_seg.mem.user_mapped_virt_addr = NULL;
+    }
+
+    for (INT idx = 0; idx < PCI_MAX_BAR; idx++) {
+        if (netuio_contextdata->dpdk_hw[idx].mem.user_mapped_virt_addr != NULL) {
+            MmUnmapLockedPages(
+                netuio_contextdata->dpdk_hw[idx].mem.user_mapped_virt_addr,
+                netuio_contextdata->dpdk_hw[idx].mdl);
+            netuio_contextdata->dpdk_hw[idx].mem.user_mapped_virt_addr = NULL;
+        }
+    }
+}
+
+/*
+Routine Description:
+    The I/O dispatch callbacks for the frameworks device object are configured here.
+    A single default I/O Queue is configured for parallel request processing, and a
+    driver context memory allocation is created to hold our structure QUEUE_CONTEXT.
+
+Return Value:
+    None
+ */
+NTSTATUS
+netuio_queue_initialize(_In_ WDFDEVICE Device)
+{
+    WDFQUEUE queue;
+    NTSTATUS status;
+    WDF_IO_QUEUE_CONFIG    queueConfig;
+
+    PAGED_CODE();
+
+    // Configure a default queue so that requests that are not
+    // configure-fowarded using WdfDeviceConfigureRequestDispatching to goto
+    // other queues get dispatched here.
+    WDF_IO_QUEUE_CONFIG_INIT_DEFAULT_QUEUE(&queueConfig, WdfIoQueueDispatchParallel);
+
+    queueConfig.EvtIoDeviceControl = netuio_evt_IO_device_control;
+    queueConfig.EvtIoStop = netuio_evt_IO_stop;
+
+    status = WdfIoQueueCreate(Device,
+                              &queueConfig,
+                              WDF_NO_OBJECT_ATTRIBUTES,
+                              &queue);
+
+    if( !NT_SUCCESS(status) ) {
+        return status;
+    }
+
+    return status;
+}
+
+/*
+Routine Description:
+    This routine is invoked to preprocess an I/O request before being placed into a queue.
+    It is guaranteed that it executes in the context of the process that generated the request.
+
+Return Value:
+    None
+ */
+_Use_decl_annotations_
+VOID
+netuio_evt_IO_in_caller_context(
+    IN WDFDEVICE  Device,
+    IN WDFREQUEST Request
+)
+{
+    WDF_REQUEST_PARAMETERS params = { 0 };
+    NTSTATUS status = STATUS_SUCCESS;
+    PVOID    input_buf = NULL, output_buf = NULL;
+    size_t   input_buf_size, output_buf_size;
+    size_t  bytes_returned = 0;
+    PNETUIO_CONTEXT_DATA  netuio_contextdata = NULL;
+
+    netuio_contextdata = netuio_get_context_data(Device);
+
+    WDF_REQUEST_PARAMETERS_INIT(&params);
+    WdfRequestGetParameters(Request, &params);
+
+    // We only need to be in the context of the process that initiated the request
+    //when we need to map memory to userspace. Otherwise, send the request back to framework.
+    if (!((params.Type == WdfRequestTypeDeviceControl) &&
+        (params.Parameters.DeviceIoControl.IoControlCode == IOCTL_NETUIO_MAP_HW_INTO_USERMODE)))
+    {
+        status = WdfDeviceEnqueueRequest(Device, Request);
+
+        if (!NT_SUCCESS(status))
+        {
+            WdfRequestCompleteWithInformation(Request, status, bytes_returned);
+        }
+        return;
+    }
+
+    // First retrieve the input buffer and see if it matches our device
+    status = WdfRequestRetrieveInputBuffer(Request, sizeof(struct dpdk_private_info), &input_buf, &input_buf_size);
+    if (!NT_SUCCESS(status)) {
+        status = STATUS_INVALID_BUFFER_SIZE;
+        goto end;
+    }
+
+    struct dpdk_private_info* dpdk_pvt_info = (struct dpdk_private_info*)input_buf;
+    // Ensure that the B:D:F match - otherwise, fail the IOCTL
+    if ((netuio_contextdata->addr.bus_num != dpdk_pvt_info->dev_addr.bus_num) ||
+        (netuio_contextdata->addr.dev_num != dpdk_pvt_info->dev_addr.dev_num) ||
+        (netuio_contextdata->addr.func_num != dpdk_pvt_info->dev_addr.func_num)) {
+        status = STATUS_NOT_SAME_DEVICE;
+        goto end;
+    }
+
+    if (netuio_contextdata->dpdk_seg.mem.user_mapped_virt_addr != NULL) {
+        status = STATUS_ALREADY_COMMITTED;
+        goto end;
+    }
+
+    // Return relevant data to the caller
+    status = WdfRequestRetrieveOutputBuffer(Request, sizeof(struct dpdk_private_info), &output_buf, &output_buf_size);
+    if (!NT_SUCCESS(status)) {
+        status = STATUS_INVALID_BUFFER_SIZE;
+        goto end;
+    }
+
+    // Zero out the physically contiguous block
+    RtlZeroMemory(netuio_contextdata->dpdk_seg.mem.virt_addr, netuio_contextdata->dpdk_seg.mem.size);
+
+    status = netuio_map_address_into_user_process(netuio_contextdata);
+    if (status != STATUS_SUCCESS) {
+        goto end;
+    }
+
+    netuio_handle_get_hw_data_request(Request, netuio_contextdata, output_buf, output_buf_size);
+    bytes_returned = output_buf_size;
+
+end:
+    WdfRequestCompleteWithInformation(Request, status, bytes_returned);
+
+    return;
+}
+
+/*
+Routine Description:
+    This event is invoked when the framework receives IRP_MJ_DEVICE_CONTROL request.
+
+Return Value:
+    None
+ */
+VOID
+netuio_evt_IO_device_control(_In_ WDFQUEUE Queue, _In_ WDFREQUEST Request,
+                              _In_ size_t OutputBufferLength, _In_ size_t InputBufferLength,
+                              _In_ ULONG IoControlCode)
+{
+    UNREFERENCED_PARAMETER(OutputBufferLength);
+    UNREFERENCED_PARAMETER(InputBufferLength);
+
+    NTSTATUS status = STATUS_SUCCESS;
+    PVOID    input_buf = NULL, output_buf = NULL;
+    size_t   input_buf_size, output_buf_size;
+    size_t  bytes_returned = 0;
+
+    WDFDEVICE device = WdfIoQueueGetDevice(Queue);
+
+    PNETUIO_CONTEXT_DATA  netuio_contextdata;
+    netuio_contextdata = netuio_get_context_data(device);
+
+    if (IoControlCode != IOCTL_NETUIO_PCI_CONFIG_IO)
+    {
+        status = STATUS_INVALID_DEVICE_REQUEST;
+        goto end;
+    }
+
+    // First retrieve the input buffer and see if it matches our device
+    status = WdfRequestRetrieveInputBuffer(Request, sizeof(struct dpdk_pci_config_io), &input_buf, &input_buf_size);
+    if (!NT_SUCCESS(status)) {
+        status = STATUS_INVALID_BUFFER_SIZE;
+        goto end;
+    }
+
+    struct dpdk_pci_config_io *dpdk_pci_io_input = (struct dpdk_pci_config_io *)input_buf;
+
+    if (dpdk_pci_io_input->access_size != 1 &&
+        dpdk_pci_io_input->access_size != 2 &&
+        dpdk_pci_io_input->access_size != 4 &&
+        dpdk_pci_io_input->access_size != 8) {
+        status = STATUS_INVALID_PARAMETER;
+        goto end;
+    }
+
+    // Ensure that the B:D:F match - otherwise, fail the IOCTL
+    if ((netuio_contextdata->addr.bus_num != dpdk_pci_io_input->dev_addr.bus_num) ||
+        (netuio_contextdata->addr.dev_num != dpdk_pci_io_input->dev_addr.dev_num) ||
+        (netuio_contextdata->addr.func_num != dpdk_pci_io_input->dev_addr.func_num)) {
+        status = STATUS_NOT_SAME_DEVICE;
+        goto end;
+    }
+    // Retrieve output buffer
+    status = WdfRequestRetrieveOutputBuffer(Request, sizeof(UINT64), &output_buf, &output_buf_size);
+    if (!NT_SUCCESS(status)) {
+        status = STATUS_INVALID_BUFFER_SIZE;
+        goto end;
+    }
+    ASSERT(output_buf_size == OutputBufferLength);
+
+    if (dpdk_pci_io_input->op == PCI_IO_READ) {
+        *(UINT64 *)output_buf = 0;
+        bytes_returned = netuio_contextdata->bus_interface.GetBusData(
+            netuio_contextdata->bus_interface.Context,
+            PCI_WHICHSPACE_CONFIG,
+            output_buf,
+            dpdk_pci_io_input->offset,
+            dpdk_pci_io_input->access_size);
+    }
+    else if (dpdk_pci_io_input->op == PCI_IO_WRITE) {
+        // returns bytes written
+        bytes_returned = netuio_contextdata->bus_interface.SetBusData(
+            netuio_contextdata->bus_interface.Context,
+            PCI_WHICHSPACE_CONFIG,
+            (PVOID)&dpdk_pci_io_input->data,
+            dpdk_pci_io_input->offset,
+            dpdk_pci_io_input->access_size);
+    }
+    else {
+        status = STATUS_INVALID_PARAMETER;
+        goto end;
+    }
+
+end:
+    WdfRequestCompleteWithInformation(Request, status, bytes_returned);
+
+    return;
+}
+
+/*
+Routine Description:
+    This event is invoked for a power-managed queue before the device leaves the working state (D0).
+
+Return Value:
+    None
+ */
+VOID
+netuio_evt_IO_stop(_In_ WDFQUEUE Queue, _In_ WDFREQUEST Request,_In_ ULONG ActionFlags)
+{
+    //
+    // In most cases, the EvtIoStop callback function completes, cancels, or postpones
+    // further processing of the I/O request.
+    //
+    // Typically, the driver uses the following rules:
+    //
+    // - If the driver owns the I/O request, it calls WdfRequestUnmarkCancelable
+    //   (if the request is cancelable) and either calls WdfRequestStopAcknowledge
+    //   with a Requeue value of TRUE, or it calls WdfRequestComplete with a
+    //   completion status value of STATUS_SUCCESS or STATUS_CANCELLED.
+    //
+    //   Before it can call these methods safely, the driver must make sure that
+    //   its implementation of EvtIoStop has exclusive access to the request.
+    //
+    //   In order to do that, the driver must synchronize access to the request
+    //   to prevent other threads from manipulating the request concurrently.
+    //   The synchronization method you choose will depend on your driver's design.
+    //
+    //   For example, if the request is held in a shared context, the EvtIoStop callback
+    //   might acquire an internal driver lock, take the request from the shared context,
+    //   and then release the lock. At this point, the EvtIoStop callback owns the request
+    //   and can safely complete or requeue the request.
+    //
+    // - If the driver has forwarded the I/O request to an I/O target, it either calls
+    //   WdfRequestCancelSentRequest to attempt to cancel the request, or it postpones
+    //   further processing of the request and calls WdfRequestStopAcknowledge with
+    //   a Requeue value of FALSE.
+    //
+    // A driver might choose to take no action in EvtIoStop for requests that are
+    // guaranteed to complete in a small amount of time.
+    //
+    // In this case, the framework waits until the specified request is complete
+    // before moving the device (or system) to a lower power state or removing the device.
+    // Potentially, this inaction can prevent a system from entering its hibernation state
+    // or another low system power state. In extreme cases, it can cause the system
+    // to crash with bugcheck code 9F.
+    //
+    UNREFERENCED_PARAMETER(Queue);
+    UNREFERENCED_PARAMETER(Request);
+    UNREFERENCED_PARAMETER(ActionFlags);
+
+    return;
+}
+
diff --git a/windows/netuio/kernel/windows/netuio/netuio_queue.h b/windows/netuio/kernel/windows/netuio/netuio_queue.h
new file mode 100644
index 000000000..9173a77b9
--- /dev/null
+++ b/windows/netuio/kernel/windows/netuio/netuio_queue.h
@@ -0,0 +1,31 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) Microsoft Corporation. All rights reserved
+ */
+
+#ifndef NETUIO_QUEUE_H
+#define NETUIO_QUEUE_H
+
+EXTERN_C_START
+
+// This is the context that can be placed per queue and would contain per queue information.
+typedef struct _QUEUE_CONTEXT {
+    ULONG PrivateDeviceData;  // just a placeholder
+} QUEUE_CONTEXT, *PQUEUE_CONTEXT;
+
+WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(QUEUE_CONTEXT, QueueGetContext)
+
+VOID
+netuio_unmap_address_from_user_process(_In_ PNETUIO_CONTEXT_DATA netuio_contextdata);
+
+NTSTATUS
+netuio_queue_initialize(_In_ WDFDEVICE hDevice);
+
+// Events from the IoQueue object
+EVT_WDF_IO_QUEUE_IO_DEVICE_CONTROL netuio_evt_IO_device_control;
+EVT_WDF_IO_QUEUE_IO_STOP netuio_evt_IO_stop;
+
+EVT_WDF_IO_IN_CALLER_CONTEXT netuio_evt_IO_in_caller_context;
+
+EXTERN_C_END
+
+#endif // NETUIO_QUEUE_H
diff --git a/windows/netuio/kernel/windows/netuio/resource.h b/windows/netuio/kernel/windows/netuio/resource.h
new file mode 100644
index 000000000..9789ffdf3
--- /dev/null
+++ b/windows/netuio/kernel/windows/netuio/resource.h
@@ -0,0 +1,14 @@
+//{{NO_DEPENDENCIES}}
+// Microsoft Visual C++ generated include file.
+// Used by netuio.rc
+
+// Next default values for new objects
+// 
+#ifdef APSTUDIO_INVOKED
+#ifndef APSTUDIO_READONLY_SYMBOLS
+#define _APS_NEXT_RESOURCE_VALUE        101
+#define _APS_NEXT_COMMAND_VALUE         40001
+#define _APS_NEXT_CONTROL_VALUE         1001
+#define _APS_NEXT_SYMED_VALUE           101
+#endif
+#endif
diff --git a/windows/netuio/mk/exec-env/windows/netuio/netuio.sln b/windows/netuio/mk/exec-env/windows/netuio/netuio.sln
new file mode 100644
index 000000000..15c26e6b9
--- /dev/null
+++ b/windows/netuio/mk/exec-env/windows/netuio/netuio.sln
@@ -0,0 +1,24 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio 14
+VisualStudioVersion = 14.0.25420.1
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "netuio", "netuio.vcxproj", "{66EC91EF-AC5B-4D1E-8314-9B3E2855CCF6}"
+EndProject
+Global
+	GlobalSection(SolutionConfigurationPlatforms) = preSolution
+		Debug|x64 = Debug|x64
+		Release|x64 = Release|x64
+	EndGlobalSection
+	GlobalSection(ProjectConfigurationPlatforms) = postSolution
+		{66EC91EF-AC5B-4D1E-8314-9B3E2855CCF6}.Debug|x64.ActiveCfg = Debug|x64
+		{66EC91EF-AC5B-4D1E-8314-9B3E2855CCF6}.Debug|x64.Build.0 = Debug|x64
+		{66EC91EF-AC5B-4D1E-8314-9B3E2855CCF6}.Debug|x64.Deploy.0 = Debug|x64
+		{66EC91EF-AC5B-4D1E-8314-9B3E2855CCF6}.Release|x64.ActiveCfg = Release|x64
+		{66EC91EF-AC5B-4D1E-8314-9B3E2855CCF6}.Release|x64.Build.0 = Release|x64
+		{66EC91EF-AC5B-4D1E-8314-9B3E2855CCF6}.Release|x64.Deploy.0 = Release|x64
+	EndGlobalSection
+	GlobalSection(SolutionProperties) = preSolution
+		HideSolutionNode = FALSE
+	EndGlobalSection
+EndGlobal
diff --git a/windows/netuio/mk/exec-env/windows/netuio/netuio.vcxproj b/windows/netuio/mk/exec-env/windows/netuio/netuio.vcxproj
new file mode 100644
index 000000000..fba0f8a1b
--- /dev/null
+++ b/windows/netuio/mk/exec-env/windows/netuio/netuio.vcxproj
@@ -0,0 +1,113 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <ItemGroup Label="ProjectConfigurations">
+    <ProjectConfiguration Include="Debug|x64">
+      <Configuration>Debug</Configuration>
+      <Platform>x64</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Release|x64">
+      <Configuration>Release</Configuration>
+      <Platform>x64</Platform>
+    </ProjectConfiguration>
+  </ItemGroup>
+  <PropertyGroup Label="Globals">
+    <ProjectGuid>{66EC91EF-AC5B-4D1E-8314-9B3E2855CCF6}</ProjectGuid>
+    <TemplateGuid>{497e31cb-056b-4f31-abb8-447fd55ee5a5}</TemplateGuid>
+    <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
+    <MinimumVisualStudioVersion>12.0</MinimumVisualStudioVersion>
+    <Configuration>Debug</Configuration>
+    <Platform Condition="'$(Platform)' == ''">Win32</Platform>
+    <RootNamespace>netuio</RootNamespace>
+    <WindowsTargetPlatformVersion>$(LatestTargetPlatformVersion)</WindowsTargetPlatformVersion>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
+    <TargetVersion>
+    </TargetVersion>
+    <UseDebugLibraries>true</UseDebugLibraries>
+    <PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset>
+    <ConfigurationType>Driver</ConfigurationType>
+    <DriverType>KMDF</DriverType>
+    <DriverTargetPlatform>Desktop</DriverTargetPlatform>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+    <TargetVersion>
+    </TargetVersion>
+    <UseDebugLibraries>false</UseDebugLibraries>
+    <PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset>
+    <ConfigurationType>Driver</ConfigurationType>
+    <DriverType>KMDF</DriverType>
+    <DriverTargetPlatform>Desktop</DriverTargetPlatform>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+  <ImportGroup Label="ExtensionSettings">
+  </ImportGroup>
+  <ImportGroup Label="PropertySheets">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <PropertyGroup Label="UserMacros" />
+  <PropertyGroup />
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+    <DebuggerFlavor>DbgengKernelDebugger</DebuggerFlavor>
+    <OutDir>$(SolutionDir)..\..\..\..\$(Platform)\$(ConfigurationName)\$(MSBuildProjectName)\</OutDir>
+    <IntDir>$(SolutionDir)..\$(Platform)\$(ConfigurationName)\$(MSBuildProjectName)\</IntDir>
+    <Inf2CatUseLocalTime>true</Inf2CatUseLocalTime>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+    <DebuggerFlavor>DbgengKernelDebugger</DebuggerFlavor>
+    <OutDir>$(SolutionDir)..\..\..\..\$(Platform)\$(ConfigurationName)\$(MSBuildProjectName)\</OutDir>
+    <IntDir>$(SolutionDir)..\$(Platform)\$(ConfigurationName)\$(MSBuildProjectName)\</IntDir>
+    <Inf2CatUseLocalTime>true</Inf2CatUseLocalTime>
+  </PropertyGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+    <ClCompile>
+      <WppEnabled>false</WppEnabled>
+      <WppRecorderEnabled>true</WppRecorderEnabled>
+      <WppScanConfigurationData Condition="'%(ClCompile.ScanConfigurationData)' == ''">trace.h</WppScanConfigurationData>
+      <WppKernelMode>true</WppKernelMode>
+      <WppMinimalRebuildFromTracking>false</WppMinimalRebuildFromTracking>
+    </ClCompile>
+    <Inf>
+      <TimeStamp>0.6.0.5</TimeStamp>
+    </Inf>
+    <Link>
+      <AdditionalDependencies>%(AdditionalDependencies);$(KernelBufferOverflowLib);$(DDK_LIB_PATH)ntoskrnl.lib;$(DDK_LIB_PATH)hal.lib;$(DDK_LIB_PATH)wmilib.lib;$(KMDF_LIB_PATH)$(KMDF_VER_PATH)\WdfLdr.lib;$(KMDF_LIB_PATH)$(KMDF_VER_PATH)\WdfDriverEntry.lib;$(DDK_LIB_PATH)\wdmsec.lib</AdditionalDependencies>
+    </Link>
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+    <ClCompile>
+      <WppEnabled>false</WppEnabled>
+      <WppRecorderEnabled>true</WppRecorderEnabled>
+      <WppScanConfigurationData Condition="'%(ClCompile.ScanConfigurationData)' == ''">trace.h</WppScanConfigurationData>
+      <WppKernelMode>true</WppKernelMode>
+      <WppMinimalRebuildFromTracking>false</WppMinimalRebuildFromTracking>
+    </ClCompile>
+    <Inf>
+      <TimeStamp>0.6.0.5</TimeStamp>
+    </Inf>
+    <Link>
+      <AdditionalDependencies>%(AdditionalDependencies);$(KernelBufferOverflowLib);$(DDK_LIB_PATH)ntoskrnl.lib;$(DDK_LIB_PATH)hal.lib;$(DDK_LIB_PATH)wmilib.lib;$(KMDF_LIB_PATH)$(KMDF_VER_PATH)\WdfLdr.lib;$(KMDF_LIB_PATH)$(KMDF_VER_PATH)\WdfDriverEntry.lib;$(DDK_LIB_PATH)\wdmsec.lib</AdditionalDependencies>
+    </Link>
+  </ItemDefinitionGroup>
+  <ItemGroup>
+    <FilesToPackage Include="$(TargetPath)" />
+  </ItemGroup>
+  <ItemGroup>
+    <Inf Include="..\..\..\..\kernel\windows\netuio\netuio.inf" />
+  </ItemGroup>
+  <ItemGroup>
+    <ClInclude Include="..\..\..\..\kernel\windows\netuio\netuio_dev.h" />
+    <ClInclude Include="..\..\..\..\kernel\windows\netuio\netuio_drv.h" />
+    <ClInclude Include="..\..\..\..\kernel\windows\netuio\netuio_interface.h" />
+    <ClInclude Include="..\..\..\..\kernel\windows\netuio\netuio_queue.h" />
+    <ClInclude Include="..\..\..\..\kernel\windows\netuio\resource.h" />
+  </ItemGroup>
+  <ItemGroup>
+    <ClCompile Include="..\..\..\..\kernel\windows\netuio\netuio_dev.c" />
+    <ClCompile Include="..\..\..\..\kernel\windows\netuio\netuio_drv.c" />
+    <ClCompile Include="..\..\..\..\kernel\windows\netuio\netuio_queue.c" />
+  </ItemGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+  <ImportGroup Label="ExtensionTargets">
+  </ImportGroup>
+</Project>
\ No newline at end of file
diff --git a/windows/netuio/mk/exec-env/windows/netuio/netuio.vcxproj.filters b/windows/netuio/mk/exec-env/windows/netuio/netuio.vcxproj.filters
new file mode 100644
index 000000000..36d8e2a02
--- /dev/null
+++ b/windows/netuio/mk/exec-env/windows/netuio/netuio.vcxproj.filters
@@ -0,0 +1,54 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <ItemGroup>
+    <Filter Include="Source Files">
+      <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
+      <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
+    </Filter>
+    <Filter Include="Header Files">
+      <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
+      <Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions>
+    </Filter>
+    <Filter Include="Resource Files">
+      <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
+      <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
+    </Filter>
+    <Filter Include="Driver Files">
+      <UniqueIdentifier>{8E41214B-6785-4CFE-B992-037D68949A14}</UniqueIdentifier>
+      <Extensions>inf;inv;inx;mof;mc;</Extensions>
+    </Filter>
+  </ItemGroup>
+  <ItemGroup>
+    <Inf Include="..\..\..\..\kernel\windows\netuio\netuio.inf">
+      <Filter>Driver Files</Filter>
+    </Inf>
+  </ItemGroup>
+  <ItemGroup>
+    <ClInclude Include="..\..\..\..\kernel\windows\netuio\netuio_interface.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\..\..\kernel\windows\netuio\netuio_dev.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\..\..\kernel\windows\netuio\netuio_drv.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\..\..\kernel\windows\netuio\netuio_queue.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="..\..\..\..\kernel\windows\netuio\resource.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+  </ItemGroup>
+  <ItemGroup>
+    <ClCompile Include="..\..\..\..\kernel\windows\netuio\netuio_queue.c">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\..\..\kernel\windows\netuio\netuio_dev.c">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="..\..\..\..\kernel\windows\netuio\netuio_drv.c">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+  </ItemGroup>
+</Project>
\ No newline at end of file
diff --git a/windows/netuio/mk/exec-env/windows/netuio/netuio.vcxproj.user b/windows/netuio/mk/exec-env/windows/netuio/netuio.vcxproj.user
new file mode 100644
index 000000000..771ce8681
--- /dev/null
+++ b/windows/netuio/mk/exec-env/windows/netuio/netuio.vcxproj.user
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+    <TestCertificate>
+    </TestCertificate>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+    <TestCertificate>
+    </TestCertificate>
+  </PropertyGroup>
+</Project>
\ No newline at end of file
-- 
2.23.0.vfs.1.1.63.g5a5ad7f


^ permalink raw reply	[flat|nested] 16+ messages in thread

end of thread, other threads:[~2020-10-14  9:29 UTC | newest]

Thread overview: 16+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-08-20 22:23 [dpdk-dev] [PATCH v2] windows/netuio: add Windows NetUIO kernel driver Narcisa Ana Maria Vasile
2020-08-21  1:32 ` Ranjit Menon
2020-09-09 18:58   ` Narcisa Ana Maria Vasile
2020-08-24 20:53 ` Dmitry Kozlyuk
2020-09-09 18:53   ` Narcisa Ana Maria Vasile
2020-09-13 21:39     ` Dmitry Kozlyuk
2020-09-09 18:41 ` [dpdk-dev] [PATCH v3] " Narcisa Ana Maria Vasile
2020-09-13 21:39   ` Dmitry Kozlyuk
2020-09-19  2:52   ` [dpdk-dev] [PATCH v4] " Narcisa Ana Maria Vasile
2020-09-22 21:25     ` Dmitry Kozlyuk
2020-09-22 21:36     ` Ranjit Menon
2020-10-01 22:55     ` [dpdk-dev] [PATCH v5] " Narcisa Ana Maria Vasile
2020-10-02 18:21       ` Ranjit Menon
2020-10-05 19:34         ` Narcisa Ana Maria Vasile
2020-10-14  9:29         ` Thomas Monjalon
2020-10-02 18:33       ` Dmitry Kozlyuk

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).