New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Implementation of a Windows IVSHMEM driver #174
Conversation
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>
Signed-off-by: Geoffrey McRae <geoff@hostfission.com>
Rev 0 devices have an additional BAR for MSI-X, and IRQ resources These are currently not implemented, but if the device is configured this way the shared memory functionallity will still correctly work Signed-off-by: Geoffrey McRae <geoff@hostfission.com>
This also applies fixes suggested by Ladi Prosek to address the following: * IOCTL codes corrected to use the correct windows structure * Corrected possible race condition by switching the queue from Parallel to Sequential. * Added an exception handler to MmMapLockedPagesSpecifyCache as per Microsoft documentation. Signed-off-by: Geoffrey McRae <geoff@hostfission.com>
Also moved the test project into a subdirectory of the driver Signed-off-by: Geoffrey McRae <geoff@hostfission.com>
Signed-off-by: Geoffrey McRae <geoff@hostfission.com>
It is now possible to create events and pass them to the driver using IOCTL_IVSHMEM_REGISTER_EVENT, these events will be set when the the specified interrupt arrives. Signed-off-by: Geoffrey McRae <geoff@hostfission.com>
Signed-off-by: Geoffrey McRae <geoff@hostfission.com>
Signed-off-by: Geoffrey McRae <geoff@hostfission.com>
Signed-off-by: Geoffrey McRae <geoff@hostfission.com>
Signed-off-by: Geoffrey McRae <geoff@hostfission.com>
If the IRQ configuration failed the count of used IRQs would be out by one which was causing a BUGCHECK on driver unload. Signed-off-by: Geoffrey McRae <geoff@hostfission.com>
Signed-off-by: Geoffrey McRae <geoff@hostfission.com>
While this works fine under Windows 10 Creators Edition, earlier versions fail the call to WdfInterruptCreate with the status STATUS_INVALID_PARAMETER. Signed-off-by: Geoffrey McRae <geoff@hostfission.com>
Signed-off-by: Geoffrey McRae <geoff@hostfission.com>
Signed-off-by: Geoffrey McRae <geoff@hostfission.com>
Signed-off-by: Geoffrey McRae <geoff@hostfission.com>
Hi, Thanks a lot! Could you add the build of the driver and test application to build.bat in the root directory (build.bat and other drivers directories)? In this way Appveyor CI will test the build of the ivshmem driver and test app as well. Thanks, |
These spin locks were required when the interrupt isr was running at the passive level, since we switched back to running at DIRQL the locks are not needed, in fact, they actually can and do cause a deadlock. Signed-off-by: Geoffrey McRae <geoff@hostfission.com>
Signed-off-by: Geoffrey McRae <geoff@hostfission.com>
@YanVugenfirer Thanks & done ;). Please note that working with visual studio is not my strong suit, I can see that there are more changes needed with the clean scripts and the likes but I am unsure what needs to be changed as these scripts perform tasks based on the build directories which I have also likely not set correctly. |
On Oct 19, 2017, at 18:03, Geoffrey McRae ***@***.***> wrote:
@YanVugenfirer Thanks & done ;).
Please note that working with visual studio is not my strong suit, I can see that there are more changes needed with the clean scripts and the likes but I am unsure what needs to be changed as these scripts perform tasks based on the build directories which I have also likely not set correctly.
No problem. I can adjust clean scripts later. For now I would like to have the driver in the main buildall.bat so CI will pick it up.
… —
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub, or mute the thread.
|
This reverts commit d46e4bc. This assumption is inaccurate, there is a locking issue here still but this is not the solution. Signed-off-by: Geoffrey McRae <geoff@hostfission.com>
Implemented proper deferred ISR to process the interrupts without deadlocking the kernel. Signed-off-by: Geoffrey McRae <geoff@hostfission.com>
With that last change set it is running flawless, all known issues have been resolved. |
Changed the device and queue to run at a passive level and fixed the use of KeAcquireSpinLock in the DPC which should have been KeAcquireSpinLockAtDpcLevel. Signed-off-by: Geoffrey McRae <geoff@hostfission.com>
According to MSDN allocating memory dynamically like this for this purpose is to be avoided. Also freeing the allocated memory in the InterruptDPC is dangerous. This patch switches to using a block of events in the device context. It also adds cleanup of events in preperation of adding support for continually running events. Signed-off-by: Geoffrey McRae <geoff@hostfission.com>
It is now possible to register an event that is not removed from the event list each time it is triggered. This avoids a spin lock just to install the event if it is continually being reused. The down side to this is currently the driver can not tell if the event has been released. Releasing a registered event will not cause any problems as the driver holds a reference to the object, but it will use up a slot until the user mode application closes it's handle to the device which will trigger a cleanup of it's event objects. Signed-off-by: Geoffrey McRae <geoff@hostfission.com>
Signed-off-by: Geoffrey McRae <geoff@hostfission.com>
Merging. One comment for next commits - please separate cosmetics (indentations for example) from functional changes |
Thanks a lot! |
Noted for future. Thanks! How soon can I expect to see a build containing this driver? I require it for a production system that can not run in test signing mode. |
I need to check when we plan to release next version. In any case, first, the driver will go through our internal test pipe and we will check if it passes HCK. |
Thanks, I appreciate it. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks again for posting this!
I have added a few inline comments. Most are nits and suggestions but I believe that there's also a bug or two.
|
||
if (deviceContext->interruptCount > 0) | ||
{ | ||
deviceContext->interrupts = (WDFINTERRUPT*)MmAllocateNonCachedMemory( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Any reason to allocate non-cached memory here? ExAllocatePoolWithTag(NonPagedPool, ...) would likely be a better choice.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Lack of experience :)
if (!deviceContext->interrupts) | ||
{ | ||
DEBUG_ERROR("Failed to allocate space for %d interrupts", deviceContext->interrupts); | ||
return STATUS_DEVICE_CONFIGURATION_ERROR; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
STATUS_INSUFFICIENT_RESOURCES is a more appropriate error in this case.
} | ||
|
||
if (++deviceContext->interruptsUsed == 64) | ||
DEBUG_INFO("%s", "This driver does not support > 64 interrupts, they will be ignored in the ISR."); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: This will be printed after creating 64 interrupts, not "> 64 interrupts".
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is correct, it only needs to warn once.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, but it incorrectly warns if the # of interrupts is 64. It probably should print the message when it finds the 65th interrupt. Hope I didn't misunderstand.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, I understand, it's out by one.
if (!NT_SUCCESS(MmAllocateMdlForIoSpace(&deviceContext->shmemAddr, 1, &deviceContext->shmemMDL))) | ||
{ | ||
DEBUG_ERROR("%s", "Call to MmAllocateMdlForIoSpace failed"); | ||
result = STATUS_DEVICE_HARDWARE_ERROR; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would propagate the result of MmAllocateMdlForIoSpace up instead of hardcoding STATUS_DEVICE_HARDWARE_ERROR.
} | ||
|
||
if (memIndex == 0) | ||
result = STATUS_DEVICE_HARDWARE_ERROR; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This may override the result as set by the loop above. Also, deviceContext->shmemMDL should probably also be checked for non-NULL after the loop to make sure that that part of the code has run and all BARs have been found.
} | ||
|
||
IVSHMEM_PEERID *out = NULL; | ||
if (!NT_SUCCESS(WdfRequestRetrieveOutputBuffer(Request, OutputBufferLength, (PVOID)&out, NULL))) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: (PVOID *)&out
WinXP Release|x64 = WinXP Release|x64 | ||
WinXP Release|x86 = WinXP Release|x86 | ||
EndGlobalSection | ||
GlobalSection(ProjectConfigurationPlatforms) = postSolution | ||
{01D87C47-437A-4A16-8FD9-33FA5C99339E}.Debug|ARM.ActiveCfg = Win8.1 Release|Win32 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd prefer all these changes adding ARM targets to be reverted as we don't support ARM yet.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Adding ARM targets was in error, I will remove these
} | ||
|
||
// early non locked quick check to see if we are out of event space | ||
if (deviceContext->eventBufferUsed == MAX_EVENTS) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wouldn't try to optimize an error path.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is done here to avoid holding the spin lock unnecessarily and thus holding up the ISR.
LONG64 pendingISR; // flags for ISRs pending processing | ||
|
||
KSPIN_LOCK eventListLock; // spinlock for the below event list | ||
IVSHMEMEventListEntry eventBuffer[MAX_EVENTS]; // buffer of pre-allocated events |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(optional ideas):
Using WdfLookasideListCreate / WdfMemoryCreateFromLookaside might be slightly nicer as we would be able to get rid of the MAX_EVENTS limit and the loop that searches for a free slot.
Also, WDF allows you to add a driver-defined context to interrupt objects. I wonder if maybe having the list of pending events attached to each interrupt like this wouldn't be better (no need to loop over all pending events in the DPC). Removing the pendingISR field and running DPC per-interrupt would also eliminate the other artificial limit of 64 interrupts.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great idea, I will update to implement this!
deviceContext = DeviceGetContext(device); | ||
|
||
pending = InterlockedExchange64(&deviceContext->pendingISR, 0); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if (pending == 0) return;
This patch set implements a windows driver for the ivshmem device allowing a user space application to map shared memory into it's context, ring doorbells and wait on IRQ events.
For more information please see:
http://lists.nongnu.org/archive/html/qemu-devel/2017-10/msg03354.html