From patchwork Thu Aug 20 22:23:55 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Narcisa Ana Maria Vasile X-Patchwork-Id: 75796 X-Patchwork-Delegate: thomas@monjalon.net Return-Path: X-Original-To: patchwork@inbox.dpdk.org Delivered-To: patchwork@inbox.dpdk.org Received: from dpdk.org (dpdk.org [92.243.14.124]) by inbox.dpdk.org (Postfix) with ESMTP id 05DABA04AF; Fri, 21 Aug 2020 00:24:34 +0200 (CEST) Received: from [92.243.14.124] (localhost [127.0.0.1]) by dpdk.org (Postfix) with ESMTP id BF88F1BEC3; Fri, 21 Aug 2020 00:24:33 +0200 (CEST) Received: from linux.microsoft.com (linux.microsoft.com [13.77.154.182]) by dpdk.org (Postfix) with ESMTP id 42766AAD5 for ; Fri, 21 Aug 2020 00:24:32 +0200 (CEST) Received: by linux.microsoft.com (Postfix, from userid 1059) id 56CC420B4908; Thu, 20 Aug 2020 15:24:31 -0700 (PDT) DKIM-Filter: OpenDKIM Filter v2.11.0 linux.microsoft.com 56CC420B4908 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linux.microsoft.com; s=default; t=1597962271; bh=rghLGBpWFoi8Dqg3cKzxdtV6qbPz2PV87o8/anF8doA=; h=From:To:Cc:Subject:Date:From; b=oxQjgDA6wQ/WRGYdlhNH/U6JWqiE5x+8sqcsWDwphSjh/jqAeElOjauXK9KRakUcL U8+cmP4YGCcYHMvF8nah4TsWyUafWEilyXpPnEdj19e4i94/q1ZHloHj/6BTM9jHlQ qEWXgxyXbSTQAdweLOsad+GdrWSs1rbI7nDkjxp4= From: Narcisa Ana Maria Vasile To: dev@dpdk.org, thomas@monjalon.net, ocardona@microsoft.com, haramakr@linux.microsoft.com, pallavi.kadam@intel.com, ranjit.menon@intel.com Cc: dmitry.kozliuk@gmail.com, dmitrym@microsoft.com, Narcisa Vasile , Harini Ramakrishnan Date: Thu, 20 Aug 2020 15:23:55 -0700 Message-Id: <1597962235-4787-1-git-send-email-navasile@linux.microsoft.com> X-Mailer: git-send-email 1.8.3.1 MIME-Version: 1.0 Subject: [dpdk-dev] [PATCH v2] windows/netuio: add Windows NetUIO kernel driver X-BeenThere: dev@dpdk.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: DPDK patches and discussions List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: dev-bounces@dpdk.org Sender: "dev" From: Narcisa Vasile The Windows NetUIO kernel driver allows the DPDK userspace application to directly access the hardware. Cc: Harini Ramakrishnan Cc: Omar Cardona Signed-off-by: Narcisa Vasile --- 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 +#include "netuio_drv.h" + +#include + +#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 _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_ 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 +#include + +#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(¶ms); + WdfRequestGetParameters(Request, ¶ms); + + 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(¶ms); + WdfRequestGetParameters(Request, ¶ms); + + // 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 @@ + + + + + Debug + x64 + + + Release + x64 + + + + {66EC91EF-AC5B-4D1E-8314-9B3E2855CCF6} + {497e31cb-056b-4f31-abb8-447fd55ee5a5} + v4.5 + 12.0 + Debug + Win32 + netuio + $(LatestTargetPlatformVersion) + + + + + + true + WindowsKernelModeDriver10.0 + Driver + KMDF + Desktop + + + + + false + WindowsKernelModeDriver10.0 + Driver + KMDF + Desktop + + + + + + + + + + + DbgengKernelDebugger + $(SolutionDir)..\..\..\..\$(Platform)\$(ConfigurationName)\$(MSBuildProjectName)\ + $(SolutionDir)..\$(Platform)\$(ConfigurationName)\$(MSBuildProjectName)\ + true + + + DbgengKernelDebugger + $(SolutionDir)..\..\..\..\$(Platform)\$(ConfigurationName)\$(MSBuildProjectName)\ + $(SolutionDir)..\$(Platform)\$(ConfigurationName)\$(MSBuildProjectName)\ + true + + + + false + true + trace.h + true + false + + + 0.6.0.5 + + + %(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 + + + + + false + true + trace.h + true + false + + + 0.6.0.5 + + + %(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 + + + + + + + + + + + + + + + + + + + + + + + + \ 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 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + {8E41214B-6785-4CFE-B992-037D68949A14} + inf;inv;inx;mof;mc; + + + + + Driver Files + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + + + Source Files + + + Source Files + + + Source Files + + + \ 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 @@ + + + + + + + + + + + \ No newline at end of file