forked from virtio-win/kvm-guest-drivers-windows
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Browse files
Browse the repository at this point in the history
Added initial Windows IVSHMEM driver & test app
This is an initial implementation of a windows driver for the
Inter-VM Shared Memory device. At current this only exposes the
shared memory region (BAR2) to user mode by means of
DeviceIoControl calls to the device (See test application for
example usage).
Limitations:
* Only one IVSHMEM device is supported, additional devices will
be ignored.
* Only one user mode application may hold the mapping at a time
and may not open the mapping more then once. The driver will
cleanup if the user mode application crashes or fails to unmap
the memory before termination.
Signed-off-by: Geoffrey McRae <geoff@hostfission.com>- Loading branch information
Showing
18 changed files
with
1,378 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,8 @@ | ||
| // stdafx.cpp : source file that includes just the standard includes | ||
| // vioivshmem-test.pch will be the pre-compiled header | ||
| // stdafx.obj will contain the pre-compiled type information | ||
|
|
||
| #include "stdafx.h" | ||
|
|
||
| // TODO: reference any additional headers you need in STDAFX.H | ||
| // and not in this file |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,17 @@ | ||
| // stdafx.h : include file for standard system include files, | ||
| // or project specific include files that are used frequently, but | ||
| // are changed infrequently | ||
| // | ||
|
|
||
| #pragma once | ||
|
|
||
| #include "targetver.h" | ||
|
|
||
| #include <stdio.h> | ||
| #include <tchar.h> | ||
| #include <Windows.h> | ||
| #include <SetupAPI.h> | ||
|
|
||
| #include "vioivshmem\Public.h" | ||
|
|
||
| // TODO: reference additional headers your program requires here |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,8 @@ | ||
| #pragma once | ||
|
|
||
| // Including SDKDDKVer.h defines the highest available Windows platform. | ||
|
|
||
| // If you wish to build your application for a previous Windows platform, include WinSDKVer.h and | ||
| // set the _WIN32_WINNT macro to the platform you wish to support before including SDKDDKVer.h. | ||
|
|
||
| #include <SDKDDKVer.h> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,197 @@ | ||
| // vioivshmem-test.cpp : Defines the entry point for the console application. | ||
| // | ||
|
|
||
| #include "stdafx.h" | ||
|
|
||
| #define TEST_START(name) printf("Test: %s...", name) | ||
| #define TEST_PASS() printf("PASS\n") | ||
| #define TEST_FAIL(reason) printf("FAIL - %s\n", reason) | ||
|
|
||
| int main() | ||
| { | ||
| HDEVINFO deviceInfoSet; | ||
| DWORD deviceIndex; | ||
|
|
||
| PSP_DEVICE_INTERFACE_DETAIL_DATA infData = NULL; | ||
| HANDLE devHandle = INVALID_HANDLE_VALUE; | ||
|
|
||
| deviceInfoSet = SetupDiGetClassDevs(NULL, NULL, NULL, DIGCF_PRESENT | DIGCF_ALLCLASSES | DIGCF_DEVICEINTERFACE); | ||
| SP_DEVICE_INTERFACE_DATA deviceInterfaceData; | ||
| ZeroMemory(&deviceInterfaceData, sizeof(SP_DEVICE_INTERFACE_DATA)); | ||
| deviceInterfaceData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA); | ||
|
|
||
| deviceIndex = 0; | ||
| while (1) | ||
| { | ||
| TEST_START("Find device"); | ||
| if (SetupDiEnumDeviceInterfaces(deviceInfoSet, NULL, &GUID_DEVINTERFACE_VIOIVSHMEM, 0, &deviceInterfaceData) == FALSE) | ||
| { | ||
| DWORD error = GetLastError(); | ||
| if (error == ERROR_NO_MORE_ITEMS) | ||
| { | ||
| TEST_FAIL("Unable to enumerate the device, is it attached?"); | ||
| break; | ||
| } | ||
|
|
||
|
|
||
| TEST_FAIL("SetupDiEnumDeviceInterfaces failed"); | ||
| break; | ||
| } | ||
| TEST_PASS(); | ||
|
|
||
| TEST_START("Get device name length"); | ||
| DWORD reqSize = 0; | ||
| // this returns false even though we succeed in getting the size | ||
| SetupDiGetDeviceInterfaceDetail(deviceInfoSet, &deviceInterfaceData, NULL, 0, &reqSize, NULL); | ||
| if (!reqSize) | ||
| { | ||
| TEST_FAIL("SetupDiGetDeviceInterfaceDetail"); | ||
| break; | ||
| } | ||
| TEST_PASS(); | ||
|
|
||
| TEST_START("Get device name"); | ||
| infData = (PSP_DEVICE_INTERFACE_DETAIL_DATA)malloc(reqSize); | ||
| infData->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA); | ||
| if (!SetupDiGetDeviceInterfaceDetail(deviceInfoSet, &deviceInterfaceData, infData, reqSize, NULL, NULL)) | ||
| { | ||
| TEST_FAIL("SetupDiGetDeviceInterfaceDetail"); | ||
| break; | ||
| } | ||
| TEST_PASS(); | ||
|
|
||
| TEST_START("Open device"); | ||
| devHandle = CreateFile(infData->DevicePath, 0, 0, NULL, OPEN_EXISTING, 0, 0); | ||
| if (devHandle == INVALID_HANDLE_VALUE) | ||
| { | ||
| TEST_FAIL("CreateFile returned INVALID_HANDLE_VALUE"); | ||
| break; | ||
| } | ||
| TEST_PASS(); | ||
|
|
||
| TEST_START("VIOIVSHMEM_IOCTL_REQUEST_SIZE"); | ||
| size_t size = 0; | ||
| if (!DeviceIoControl(devHandle, VIOIVSHMEM_IOCTL_REQUEST_SIZE, NULL, 0, &size, sizeof(size_t), NULL, NULL)) | ||
| { | ||
| TEST_FAIL("DeviceIoControl"); | ||
| break; | ||
| } | ||
|
|
||
| if (size == 0) | ||
| { | ||
| TEST_FAIL("Size should not be zero"); | ||
| break; | ||
| } | ||
| TEST_PASS(); | ||
|
|
||
| printf("Size: %u\n", size); | ||
|
|
||
| TEST_START("VIOIVSHMEM_IOCTL_REQUEST_MMAP"); | ||
| VIOIVSHMEM_MMAP map; | ||
| ZeroMemory(&map, sizeof(VIOIVSHMEM_MMAP)); | ||
| if (!DeviceIoControl(devHandle, VIOIVSHMEM_IOCTL_REQUEST_MMAP, NULL, 0, &map, sizeof(VIOIVSHMEM_MMAP), NULL, NULL)) | ||
| { | ||
| TEST_FAIL("DeviceIoControl"); | ||
| break; | ||
| } | ||
|
|
||
| if (!map.ptr) | ||
| { | ||
| TEST_FAIL("NULL pointer to mapping returned"); | ||
| break; | ||
| } | ||
|
|
||
| if (map.size != size) | ||
| { | ||
| TEST_FAIL("Incorrect size"); | ||
| break; | ||
| } | ||
| TEST_PASS(); | ||
|
|
||
| TEST_START("Mapping more then once fails"); | ||
| if (DeviceIoControl(devHandle, VIOIVSHMEM_IOCTL_REQUEST_MMAP, NULL, 0, &map, sizeof(VIOIVSHMEM_MMAP), NULL, NULL)) | ||
| { | ||
| TEST_FAIL("mapping succeeded, this should not happen!"); | ||
| break; | ||
| } | ||
| TEST_PASS(); | ||
|
|
||
| TEST_START("Mapping from another handle fails"); | ||
| HANDLE devHandle2 = CreateFile(infData->DevicePath, 0, 0, NULL, OPEN_EXISTING, 0, 0); | ||
| if (!devHandle2) | ||
| { | ||
| TEST_FAIL("Failed to open second handle"); | ||
| break; | ||
| } | ||
| if (DeviceIoControl(devHandle, VIOIVSHMEM_IOCTL_REQUEST_MMAP, NULL, 0, &map, sizeof(VIOIVSHMEM_MMAP), NULL, NULL)) | ||
| { | ||
| TEST_FAIL("mapping succeeded, this should not happen!"); | ||
| break; | ||
| } | ||
| CloseHandle(devHandle2); | ||
| TEST_PASS(); | ||
|
|
||
| TEST_START("VIOIVSHMEM_IOCTL_RELEASE_MMAP"); | ||
| if (!DeviceIoControl(devHandle, VIOIVSHMEM_IOCTL_RELEASE_MMAP, NULL, 0, NULL, 0, NULL, NULL)) | ||
| { | ||
| TEST_FAIL("DeviceIoControl"); | ||
| break; | ||
| } | ||
| TEST_PASS(); | ||
|
|
||
| TEST_START("Closing handle releases mapping"); | ||
| CloseHandle(devHandle); | ||
| devHandle = CreateFile(infData->DevicePath, 0, 0, NULL, OPEN_EXISTING, 0, 0); | ||
| if (devHandle == INVALID_HANDLE_VALUE) | ||
| { | ||
| TEST_FAIL("Failed to re-open handle"); | ||
| break; | ||
| } | ||
| if (!DeviceIoControl(devHandle, VIOIVSHMEM_IOCTL_REQUEST_MMAP, NULL, 0, &map, sizeof(VIOIVSHMEM_MMAP), NULL, NULL)) | ||
| { | ||
| TEST_FAIL("Mapping failed!"); | ||
| break; | ||
| } | ||
| TEST_PASS(); | ||
|
|
||
| TEST_START("Shared memory actually works"); | ||
| memset(map.ptr, 0xAA, map.size); | ||
| CloseHandle(devHandle); | ||
| devHandle = CreateFile(infData->DevicePath, 0, 0, NULL, OPEN_EXISTING, 0, 0); | ||
| if (devHandle == INVALID_HANDLE_VALUE) | ||
| { | ||
| TEST_FAIL("Failed to re-open handle"); | ||
| break; | ||
| } | ||
| if (!DeviceIoControl(devHandle, VIOIVSHMEM_IOCTL_REQUEST_MMAP, NULL, 0, &map, sizeof(VIOIVSHMEM_MMAP), NULL, NULL)) | ||
| { | ||
| TEST_FAIL("Mapping failed!"); | ||
| break; | ||
| } | ||
|
|
||
| bool fail = false; | ||
| unsigned char *data = (unsigned char *)map.ptr; | ||
| for(int i = 0; i < map.size; ++i) | ||
| if (data[i] != 0xAA) | ||
| { | ||
| TEST_FAIL("Invalid data read back"); | ||
| fail = true; | ||
| break; | ||
| } | ||
| if (fail) | ||
| break; | ||
| TEST_PASS(); | ||
|
|
||
| break; | ||
| } | ||
|
|
||
| if (devHandle != INVALID_HANDLE_VALUE) | ||
| CloseHandle(devHandle); | ||
|
|
||
| if (infData) | ||
| free(infData); | ||
|
|
||
| SetupDiDestroyDeviceInfoList(deviceInfoSet); | ||
|
|
||
| return 0; | ||
| } |
Oops, something went wrong.