[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [win-pv-devel] [PATCH 4/5] Implement new IOCTL handlers
This patch implements new store, evtchn and gnttab IOCTLs. Handlers are split into separate files for readability. Signed-off-by: Rafal Wojdyla <omeg@xxxxxxxxxxxxxxxxxxxxxx> --- src/xeniface/fdo.c | 188 ++++++++-- src/xeniface/fdo.h | 25 +- src/xeniface/ioctl_evtchn.c | 467 ++++++++++++++++++++++++ src/xeniface/ioctl_gnttab.c | 765 +++++++++++++++++++++++++++++++++++++++ src/xeniface/ioctl_store.c | 574 +++++++++++++++++++++++++++++ src/xeniface/ioctls.c | 360 ++++++------------ src/xeniface/ioctls.h | 315 +++++++++++++++- src/xeniface/irp_queue.c | 131 +++++++ src/xeniface/irp_queue.h | 50 +++ vs2013/xeniface/xeniface.vcxproj | 4 + 10 files changed, 2602 insertions(+), 277 deletions(-) create mode 100644 src/xeniface/ioctl_evtchn.c create mode 100644 src/xeniface/ioctl_gnttab.c create mode 100644 src/xeniface/ioctl_store.c create mode 100644 src/xeniface/irp_queue.c create mode 100644 src/xeniface/irp_queue.h diff --git a/src/xeniface/fdo.c b/src/xeniface/fdo.c index 51bda24..338c8da 100644 --- a/src/xeniface/fdo.c +++ b/src/xeniface/fdo.c @@ -36,7 +36,8 @@ #include <stdlib.h> #include <store_interface.h> - +#include <evtchn_interface.h> +#include <gnttab_interface.h> #include <suspend_interface.h> @@ -52,6 +53,7 @@ #include "ioctls.h" #include "wmi.h" #include "xeniface_ioctls.h" +#include "irp_queue.h" #define FDO_POOL 'ODF' @@ -664,6 +666,25 @@ __FdoD3ToD0( if (!NT_SUCCESS(status)) goto fail1; + status = XENBUS_EVTCHN(Acquire, &Fdo->EvtchnInterface); + if (!NT_SUCCESS(status)) + goto fail2; + + status = XENBUS_GNTTAB(Acquire, &Fdo->GnttabInterface); + if (!NT_SUCCESS(status)) + goto fail3; + + status = XENBUS_GNTTAB(CreateCache, + &Fdo->GnttabInterface, + "xeniface-gnttab", + 0, + GnttabAcquireLock, + GnttabReleaseLock, + Fdo, + &Fdo->GnttabCache); + if (!NT_SUCCESS(status)) + goto fail4; + __FdoSetDevicePowerState(Fdo, PowerDeviceD0); PowerState.DeviceState = PowerDeviceD0; @@ -675,6 +696,18 @@ __FdoD3ToD0( return STATUS_SUCCESS; +fail4: + Error("fail4\n"); + XENBUS_GNTTAB(Release, &Fdo->GnttabInterface); + +fail3: + Error("fail3\n"); + XENBUS_EVTCHN(Release, &Fdo->EvtchnInterface); + +fail2: + Error("fail2\n"); + XENBUS_STORE(Release, &Fdo->StoreInterface); + fail1: Error("fail1 (%08x)\n", status); @@ -700,6 +733,9 @@ __FdoD0ToD3( __FdoSetDevicePowerState(Fdo, PowerDeviceD3); + XENBUS_GNTTAB(DestroyCache, &Fdo->GnttabInterface, Fdo->GnttabCache); + XENBUS_GNTTAB(Release, &Fdo->GnttabInterface); + XENBUS_EVTCHN(Release, &Fdo->EvtchnInterface); XENBUS_STORE(Release, &Fdo->StoreInterface); Trace("<====\n"); @@ -1991,27 +2027,25 @@ FdoDispatchDefault( NTSTATUS FdoCreateFile ( - __in PXENIFACE_FDO fdoData, - __inout PIRP Irp + __in PXENIFACE_FDO Fdo, + __inout PIRP Irp ) { - NTSTATUS status; - + PIO_STACK_LOCATION Stack = IoGetCurrentIrpStackLocation(Irp); + NTSTATUS status; - XenIfaceDebugPrint(TRACE, "Create \n"); + XenIfaceDebugPrint(TRACE, "FO %p, Process %p\n", Stack->FileObject, PsGetCurrentProcess()); - if (Deleted == fdoData->Dx->DevicePnpState) - { + if (Deleted == Fdo->Dx->DevicePnpState) { Irp->IoStatus.Status = STATUS_NO_SUCH_DEVICE; - IoCompleteRequest (Irp, IO_NO_INCREMENT); + IoCompleteRequest(Irp, IO_NO_INCREMENT); return STATUS_NO_SUCH_DEVICE; } - status = STATUS_SUCCESS; Irp->IoStatus.Information = 0; Irp->IoStatus.Status = status; - IoCompleteRequest (Irp, IO_NO_INCREMENT); + IoCompleteRequest(Irp, IO_NO_INCREMENT); return status; } @@ -2019,20 +2053,22 @@ FdoCreateFile ( NTSTATUS FdoClose ( - __in PXENIFACE_FDO fdoData, - __inout PIRP Irp + __in PXENIFACE_FDO Fdo, + __inout PIRP Irp ) { + PIO_STACK_LOCATION Stack = IoGetCurrentIrpStackLocation(Irp); + NTSTATUS status; - NTSTATUS status; + XenIfaceDebugPrint(TRACE, "FO %p, Process %p\n", Stack->FileObject, PsGetCurrentProcess()); - XenIfaceDebugPrint(TRACE, "Close \n"); + XenIfaceCleanup(Fdo, Stack->FileObject); status = STATUS_SUCCESS; Irp->IoStatus.Information = 0; Irp->IoStatus.Status = status; - IoCompleteRequest (Irp, IO_NO_INCREMENT); + IoCompleteRequest(Irp, IO_NO_INCREMENT); return status; } @@ -2080,9 +2116,9 @@ FdoDispatch( status = FdoDispatchPower(Fdo, Irp); break; - case IRP_MJ_DEVICE_CONTROL: - status = XenIFaceIoctl(Fdo, Irp); - break; + case IRP_MJ_DEVICE_CONTROL: + status = XenIfaceIoctl(Fdo, Irp); + break; case IRP_MJ_SYSTEM_CONTROL: status = XenIfaceSystemControl(Fdo, Irp); @@ -2206,6 +2242,7 @@ FdoCreate( WCHAR Name[MAXNAMELEN * sizeof (WCHAR)]; ULONG Size; NTSTATUS status; + ULONG ProcessorCount; #pragma prefast(suppress:28197) // Possibly leaking memory 'FunctionDeviceObject' status = IoCreateDevice(DriverObject, @@ -2296,6 +2333,24 @@ FdoCreate( if (!NT_SUCCESS(status)) goto fail10; + status = FDO_QUERY_INTERFACE(Fdo, + XENBUS, + EVTCHN, + (PINTERFACE)&Fdo->EvtchnInterface, + sizeof(Fdo->EvtchnInterface), + FALSE); + if (!NT_SUCCESS(status)) + goto fail11; + + status = FDO_QUERY_INTERFACE(Fdo, + XENBUS, + GNTTAB, + (PINTERFACE)&Fdo->GnttabInterface, + sizeof(Fdo->GnttabInterface), + FALSE); + if (!NT_SUCCESS(status)) + goto fail12; + InitializeMutex(&Fdo->Mutex); InitializeListHead(&Dx->ListEntry); Fdo->References = 1; @@ -2304,9 +2359,46 @@ FdoCreate( KeInitializeEvent(&Fdo->registryWriteEvent, NotificationEvent, FALSE); - status = ThreadCreate(FdoRegistryThreadHandler, Fdo, &Fdo->registryThread); - if (!NT_SUCCESS(status)) - goto fail11; + status = ThreadCreate(FdoRegistryThreadHandler, Fdo, &Fdo->registryThread); + if (!NT_SUCCESS(status)) + goto fail13; + + KeInitializeSpinLock(&Fdo->StoreWatchLock); + InitializeListHead(&Fdo->StoreWatchList); + + KeInitializeSpinLock(&Fdo->EvtchnLock); + InitializeListHead(&Fdo->EvtchnList); + + KeInitializeSpinLock(&Fdo->IrpQueueLock); + InitializeListHead(&Fdo->IrpList); + + KeInitializeSpinLock(&Fdo->GnttabCacheLock); + + status = IoCsqInitializeEx(&Fdo->IrpQueue, + CsqInsertIrpEx, + CsqRemoveIrp, + CsqPeekNextIrp, + CsqAcquireLock, + CsqReleaseLock, + CsqCompleteCanceledIrp); + if (!NT_SUCCESS(status)) + goto fail14; + + ProcessorCount = KeQueryActiveProcessorCountEx(ALL_PROCESSOR_GROUPS); + status = STATUS_NO_MEMORY; + Fdo->EvtchnDpc = __FdoAllocate(sizeof(KDPC) * ProcessorCount); + if (Fdo->EvtchnDpc == NULL) + goto fail15; + + for (ULONG i = 0; i < ProcessorCount; i++) { + PROCESSOR_NUMBER ProcNumber; + + status = KeGetProcessorNumberFromIndex(i, &ProcNumber); + ASSERT(NT_SUCCESS(status)); + KeInitializeDpc(&Fdo->EvtchnDpc[i], EvtchnNotificationDpc, NULL); + status = KeSetTargetProcessorDpcEx(&Fdo->EvtchnDpc[i], &ProcNumber); + ASSERT(NT_SUCCESS(status)); + } Info("%p (%s)\n", FunctionDeviceObject, @@ -2317,7 +2409,28 @@ FdoCreate( return STATUS_SUCCESS; - +fail15: + Error("fail15\n"); + +fail14: + Error("fail14\n"); + + ThreadAlert(Fdo->registryThread); + ThreadJoin(Fdo->registryThread); + Fdo->registryThread = NULL; + +fail13: + Error("fail13\n"); + + RtlZeroMemory(&Fdo->GnttabInterface, + sizeof (XENBUS_GNTTAB_INTERFACE)); + +fail12: + Error("fail12\n"); + + RtlZeroMemory(&Fdo->EvtchnInterface, + sizeof(XENBUS_EVTCHN_INTERFACE)); + fail11: Error("fail11\n"); @@ -2395,6 +2508,7 @@ FdoDestroy( { PXENIFACE_DX Dx = Fdo->Dx; PDEVICE_OBJECT FunctionDeviceObject = Dx->DeviceObject; + ULONG ProcessorCount; ASSERT(IsListEmpty(&Dx->ListEntry)); ASSERT3U(Fdo->References, ==, 0); @@ -2408,9 +2522,33 @@ FdoDestroy( Dx->Fdo = NULL; - RtlZeroMemory(&Fdo->Mutex, sizeof (XENIFACE_MUTEX)); + ProcessorCount = KeQueryActiveProcessorCountEx(ALL_PROCESSOR_GROUPS); + RtlZeroMemory(Fdo->EvtchnDpc, sizeof(KDPC)*ProcessorCount); + __FdoFree(Fdo->EvtchnDpc); - Fdo->InterfacesAcquired = FALSE; + RtlZeroMemory(&Fdo->GnttabCacheLock, sizeof(KSPIN_LOCK)); + ASSERT(IsListEmpty(&Fdo->IrpList)); + RtlZeroMemory(&Fdo->IrpList, sizeof(LIST_ENTRY)); + RtlZeroMemory(&Fdo->IrpQueueLock, sizeof(KSPIN_LOCK)); + RtlZeroMemory(&Fdo->IrpQueue, sizeof(IO_CSQ)); + + ASSERT(IsListEmpty(&Fdo->EvtchnList)); + RtlZeroMemory(&Fdo->EvtchnList, sizeof(LIST_ENTRY)); + RtlZeroMemory(&Fdo->EvtchnLock, sizeof(KSPIN_LOCK)); + + ASSERT(IsListEmpty(&Fdo->StoreWatchList)); + RtlZeroMemory(&Fdo->StoreWatchList, sizeof(LIST_ENTRY)); + RtlZeroMemory(&Fdo->StoreWatchLock, sizeof(KSPIN_LOCK)); + + RtlZeroMemory(&Fdo->Mutex, sizeof(XENIFACE_MUTEX)); + + Fdo->InterfacesAcquired = FALSE; + + RtlZeroMemory(&Fdo->GnttabInterface, + sizeof(XENBUS_GNTTAB_INTERFACE)); + + RtlZeroMemory(&Fdo->EvtchnInterface, + sizeof(XENBUS_EVTCHN_INTERFACE)); RtlZeroMemory(&Fdo->StoreInterface, sizeof (XENBUS_STORE_INTERFACE)); diff --git a/src/xeniface/fdo.h b/src/xeniface/fdo.h index 4416064..cbe5de3 100644 --- a/src/xeniface/fdo.h +++ b/src/xeniface/fdo.h @@ -34,6 +34,8 @@ #include <ntifs.h> #include <store_interface.h> +#include <evtchn_interface.h> +#include <gnttab_interface.h> #include <suspend_interface.h> #include <shared_info_interface.h> @@ -73,17 +75,30 @@ typedef struct _XENIFACE_FDO { FDO_RESOURCE Resource[RESOURCE_COUNT]; - XENBUS_STORE_INTERFACE StoreInterface; - XENBUS_SUSPEND_INTERFACE SuspendInterface; - - XENBUS_SHARED_INFO_INTERFACE SharedInfoInterface; - + XENBUS_SHARED_INFO_INTERFACE SharedInfoInterface; + XENBUS_EVTCHN_INTERFACE EvtchnInterface; + XENBUS_GNTTAB_INTERFACE GnttabInterface; PXENBUS_SUSPEND_CALLBACK SuspendCallbackLate; BOOLEAN InterfacesAcquired; + KSPIN_LOCK StoreWatchLock; + LIST_ENTRY StoreWatchList; + + KSPIN_LOCK EvtchnLock; + LIST_ENTRY EvtchnList; + PKDPC EvtchnDpc; + + KSPIN_LOCK GnttabCacheLock; + + IO_CSQ IrpQueue; + KSPIN_LOCK IrpQueueLock; + LIST_ENTRY IrpList; + + PXENBUS_GNTTAB_CACHE GnttabCache; + #define MAX_SESSIONS (65536) int WmiReady; diff --git a/src/xeniface/ioctl_evtchn.c b/src/xeniface/ioctl_evtchn.c new file mode 100644 index 0000000..d46894f --- /dev/null +++ b/src/xeniface/ioctl_evtchn.c @@ -0,0 +1,467 @@ +/* Copyright (c) Citrix Systems Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, + * with or without modification, are permitted provided + * that the following conditions are met: + * + * * Redistributions of source code must retain the above + * copyright notice, this list of conditions and the + * following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the + * following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "driver.h" +#include "ioctls.h" +#include "..\..\include\xeniface_ioctls.h" +#include "log.h" + +_Function_class_(KDEFERRED_ROUTINE) +_IRQL_requires_(DISPATCH_LEVEL) +_IRQL_requires_same_ +VOID +EvtchnNotificationDpc( + __in PKDPC Dpc, + __in_opt PVOID _Context, + __in_opt PVOID Argument1, + __in_opt PVOID Argument2 + ) +{ + PXENIFACE_EVTCHN_CONTEXT Context = (PXENIFACE_EVTCHN_CONTEXT)Argument1; + + UNREFERENCED_PARAMETER(Dpc); + UNREFERENCED_PARAMETER(_Context); + UNREFERENCED_PARAMETER(Argument2); + + ASSERT(Context); + +#if DBG + XenIfaceDebugPrint(INFO, "Channel %p, LocalPort %d, Active %d, Cpu %lu\n", + Context->Channel, Context->LocalPort, Context->Active, KeGetCurrentProcessorNumber()); +#endif + if (Context->Active) { + KeSetEvent(Context->Event, 0, FALSE); + + XENBUS_EVTCHN(Unmask, + &Context->Fdo->EvtchnInterface, + Context->Channel, + FALSE); + } +} + +_Function_class_(KSERVICE_ROUTINE) +_IRQL_requires_(HIGH_LEVEL) +_IRQL_requires_same_ +static DECLSPEC_NOINLINE +BOOLEAN +EvtchnInterruptHandler( + __in PKINTERRUPT Interrupt, + __in_opt PVOID Argument + ) +{ + PXENIFACE_EVTCHN_CONTEXT Context = (PXENIFACE_EVTCHN_CONTEXT)Argument; + PROCESSOR_NUMBER ProcNumber; + ULONG ProcIndex; + + UNREFERENCED_PARAMETER(Interrupt); + ASSERT(Context); + + KeGetCurrentProcessorNumberEx(&ProcNumber); + ProcIndex = KeGetProcessorIndexFromNumber(&ProcNumber); + if (Context->Active) + KeInsertQueueDpc(&Context->Fdo->EvtchnDpc[ProcIndex], Context, NULL); + + return TRUE; +} + +_IRQL_requires_(PASSIVE_LEVEL) // needed for KeFlushQueuedDpcs +VOID +EvtchnFree( + __in PXENIFACE_FDO Fdo, + __inout PXENIFACE_EVTCHN_CONTEXT Context + ) +{ + ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL); + + XenIfaceDebugPrint(TRACE, "Context %p, LocalPort %d, FO %p\n", + Context, Context->LocalPort, Context->FileObject); + + InterlockedExchange8(&Context->Active, 0); + + XENBUS_EVTCHN(Close, + &Fdo->EvtchnInterface, + Context->Channel); + + // There may still be a pending event at this time. + // Wait for our DPCs to complete. + KeFlushQueuedDpcs(); + + ObDereferenceObject(Context->Event); + RtlZeroMemory(Context, sizeof(XENIFACE_EVTCHN_CONTEXT)); + ExFreePoolWithTag(Context, XENIFACE_POOL_TAG); +} + +_Requires_exclusive_lock_held_(Fdo->EvtchnLock) +static +PXENIFACE_EVTCHN_CONTEXT +EvtchnFindChannel( + __in PXENIFACE_FDO Fdo, + __in ULONG LocalPort, + __in_opt PFILE_OBJECT FileObject + ) +{ + PXENIFACE_EVTCHN_CONTEXT Context, Found = NULL; + PLIST_ENTRY Node; + + Node = Fdo->EvtchnList.Flink; + while (Node->Flink != Fdo->EvtchnList.Flink) { + Context = CONTAINING_RECORD(Node, XENIFACE_EVTCHN_CONTEXT, Entry); + + Node = Node->Flink; + if (Context->LocalPort != LocalPort) + continue; + + if (FileObject != NULL && Context->FileObject != FileObject) + continue; + + Found = Context; + break; + } + + return Found; +} + +DECLSPEC_NOINLINE +NTSTATUS +IoctlEvtchnBindUnbound( + __in PXENIFACE_FDO Fdo, + __in PVOID Buffer, + __in ULONG InLen, + __in ULONG OutLen, + __in PFILE_OBJECT FileObject, + __out PULONG_PTR Info + ) +{ + NTSTATUS status; + PXENIFACE_EVTCHN_BIND_UNBOUND_IN In = Buffer; + PXENIFACE_EVTCHN_BIND_UNBOUND_OUT Out = Buffer; + PXENIFACE_EVTCHN_CONTEXT Context; + + status = STATUS_INVALID_BUFFER_SIZE; + if (InLen != sizeof(XENIFACE_EVTCHN_BIND_UNBOUND_IN) || OutLen != sizeof(XENIFACE_EVTCHN_BIND_UNBOUND_OUT)) + goto fail1; + + status = STATUS_NO_MEMORY; + Context = ExAllocatePoolWithTag(NonPagedPool, sizeof(XENIFACE_EVTCHN_CONTEXT), XENIFACE_POOL_TAG); + if (Context == NULL) + goto fail2; + + RtlZeroMemory(Context, sizeof(XENIFACE_EVTCHN_CONTEXT)); + Context->FileObject = FileObject; + + XenIfaceDebugPrint(TRACE, "> RemoteDomain %d, Mask %d, FO %p\n", + In->RemoteDomain, In->Mask, FileObject); + + status = ObReferenceObjectByHandle(In->Event, EVENT_MODIFY_STATE, *ExEventObjectType, UserMode, &Context->Event, NULL); + if (!NT_SUCCESS(status)) + goto fail3; + + status = STATUS_UNSUCCESSFUL; + Context->Channel = XENBUS_EVTCHN(Open, + &Fdo->EvtchnInterface, + XENBUS_EVTCHN_TYPE_UNBOUND, + EvtchnInterruptHandler, + Context, + In->RemoteDomain, + TRUE); + if (Context->Channel == NULL) + goto fail4; + + Context->LocalPort = XENBUS_EVTCHN(GetPort, + &Fdo->EvtchnInterface, + Context->Channel); + + Context->Fdo = Fdo; + + ExInterlockedInsertTailList(&Fdo->EvtchnList, &Context->Entry, &Fdo->EvtchnLock); + + InterlockedExchange8(&Context->Active, 1); + Out->LocalPort = Context->LocalPort; + *Info = sizeof(XENIFACE_EVTCHN_BIND_UNBOUND_OUT); + + if (!In->Mask) { + XENBUS_EVTCHN(Unmask, + &Fdo->EvtchnInterface, + Context->Channel, + FALSE); + } + + XenIfaceDebugPrint(TRACE, "< LocalPort %lu, Context %p\n", Context->LocalPort, Context); + return STATUS_SUCCESS; + +fail4: + XenIfaceDebugPrint(ERROR, "Fail4\n"); + ObDereferenceObject(Context->Event); +fail3: + XenIfaceDebugPrint(ERROR, "Fail3\n"); + RtlZeroMemory(Context, sizeof(XENIFACE_EVTCHN_CONTEXT)); + ExFreePoolWithTag(Context, XENIFACE_POOL_TAG); +fail2: + XenIfaceDebugPrint(ERROR, "Fail2\n"); +fail1: + XenIfaceDebugPrint(ERROR, "Fail1 (%08x)\n", status); + return status; +} + +DECLSPEC_NOINLINE +NTSTATUS +IoctlEvtchnBindInterdomain( + __in PXENIFACE_FDO Fdo, + __in PVOID Buffer, + __in ULONG InLen, + __in ULONG OutLen, + __in PFILE_OBJECT FileObject, + __out PULONG_PTR Info + ) +{ + NTSTATUS status; + PXENIFACE_EVTCHN_BIND_INTERDOMAIN_IN In = Buffer; + PXENIFACE_EVTCHN_BIND_INTERDOMAIN_OUT Out = Buffer; + PXENIFACE_EVTCHN_CONTEXT Context; + + status = STATUS_INVALID_BUFFER_SIZE; + if (InLen != sizeof(XENIFACE_EVTCHN_BIND_INTERDOMAIN_IN) || OutLen != sizeof(XENIFACE_EVTCHN_BIND_INTERDOMAIN_OUT)) + goto fail1; + + status = STATUS_NO_MEMORY; + Context = ExAllocatePoolWithTag(NonPagedPool, sizeof(XENIFACE_EVTCHN_CONTEXT), XENIFACE_POOL_TAG); + if (Context == NULL) + goto fail2; + + RtlZeroMemory(Context, sizeof(XENIFACE_EVTCHN_CONTEXT)); + Context->FileObject = FileObject; + + XenIfaceDebugPrint(TRACE, "> RemoteDomain %d, RemotePort %lu, Mask %d, FO %p\n", + In->RemoteDomain, In->RemotePort, In->Mask, FileObject); + + status = ObReferenceObjectByHandle(In->Event, EVENT_MODIFY_STATE, *ExEventObjectType, UserMode, &Context->Event, NULL); + if (!NT_SUCCESS(status)) + goto fail3; + + status = STATUS_UNSUCCESSFUL; + Context->Channel = XENBUS_EVTCHN(Open, + &Fdo->EvtchnInterface, + XENBUS_EVTCHN_TYPE_INTER_DOMAIN, + EvtchnInterruptHandler, + Context, + In->RemoteDomain, + In->RemotePort, + TRUE); + if (Context->Channel == NULL) + goto fail4; + + Context->LocalPort = XENBUS_EVTCHN(GetPort, + &Fdo->EvtchnInterface, + Context->Channel); + + Context->Fdo = Fdo; + + ExInterlockedInsertTailList(&Fdo->EvtchnList, &Context->Entry, &Fdo->EvtchnLock); + + InterlockedExchange8(&Context->Active, 1); + Out->LocalPort = Context->LocalPort; + *Info = sizeof(XENIFACE_EVTCHN_BIND_INTERDOMAIN_OUT); + + if (!In->Mask) { + XENBUS_EVTCHN(Unmask, + &Fdo->EvtchnInterface, + Context->Channel, + FALSE); + } + + XenIfaceDebugPrint(TRACE, "< LocalPort %lu, Context %p\n", Context->LocalPort, Context); + + return STATUS_SUCCESS; + +fail4: + XenIfaceDebugPrint(ERROR, "Fail4\n"); + ObDereferenceObject(Context->Event); +fail3: + XenIfaceDebugPrint(ERROR, "Fail3\n"); + RtlZeroMemory(Context, sizeof(XENIFACE_EVTCHN_CONTEXT)); + ExFreePoolWithTag(Context, XENIFACE_POOL_TAG); +fail2: + XenIfaceDebugPrint(ERROR, "Fail2\n"); +fail1: + XenIfaceDebugPrint(ERROR, "Fail1 (%08x)\n", status); + return status; +} + +DECLSPEC_NOINLINE +NTSTATUS +IoctlEvtchnClose( + __in PXENIFACE_FDO Fdo, + __in PVOID Buffer, + __in ULONG InLen, + __in ULONG OutLen, + __in PFILE_OBJECT FileObject + ) +{ + NTSTATUS status; + PXENIFACE_EVTCHN_CLOSE_IN In = Buffer; + PXENIFACE_EVTCHN_CONTEXT Context = NULL; + KIRQL Irql; + + status = STATUS_INVALID_BUFFER_SIZE; + if (InLen != sizeof(XENIFACE_EVTCHN_CLOSE_IN) || OutLen != 0) + goto fail1; + + XenIfaceDebugPrint(TRACE, "> LocalPort %lu, FO %p\n", In->LocalPort, FileObject); + + KeAcquireSpinLock(&Fdo->EvtchnLock, &Irql); + Context = EvtchnFindChannel(Fdo, In->LocalPort, FileObject); + if (Context != NULL) + RemoveEntryList(&Context->Entry); + KeReleaseSpinLock(&Fdo->EvtchnLock, Irql); + if (Context != NULL) + EvtchnFree(Fdo, Context); + + status = STATUS_NOT_FOUND; + if (Context == NULL) + goto fail2; + + return STATUS_SUCCESS; + +fail2: + XenIfaceDebugPrint(ERROR, "Fail2\n"); +fail1: + XenIfaceDebugPrint(ERROR, "Fail1 (%08x)\n", status); + return status; +} + +_Requires_lock_not_held_(Fdo->EvtchnLock) +DECLSPEC_NOINLINE +NTSTATUS +EvtchnNotify( + __in PXENIFACE_FDO Fdo, + __in ULONG LocalPort, + __in_opt PFILE_OBJECT FileObject + ) +{ + NTSTATUS status; + PXENIFACE_EVTCHN_CONTEXT Context = NULL; + KIRQL Irql; + + KeAcquireSpinLock(&Fdo->EvtchnLock, &Irql); + + Context = EvtchnFindChannel(Fdo, LocalPort, FileObject); + + status = STATUS_NOT_FOUND; + if (Context == NULL) + goto fail1; + + XENBUS_EVTCHN(Send, + &Fdo->EvtchnInterface, + Context->Channel); + + KeReleaseSpinLock(&Fdo->EvtchnLock, Irql); + + return STATUS_SUCCESS; + +fail1: + KeReleaseSpinLock(&Fdo->EvtchnLock, Irql); + XenIfaceDebugPrint(ERROR, "Fail1 (%08x)\n", status); + return status; +} + +DECLSPEC_NOINLINE +NTSTATUS +IoctlEvtchnNotify( + __in PXENIFACE_FDO Fdo, + __in PVOID Buffer, + __in ULONG InLen, + __in ULONG OutLen, + __in PFILE_OBJECT FileObject + ) +{ + NTSTATUS status; + PXENIFACE_EVTCHN_NOTIFY_IN In = Buffer; + + status = STATUS_INVALID_BUFFER_SIZE; + if (InLen != sizeof(XENIFACE_EVTCHN_NOTIFY_IN) || OutLen != 0) + goto fail1; +#if DBG + XenIfaceDebugPrint(INFO, "> LocalPort %d, FO %p\n", In->LocalPort, FileObject); +#endif + + return EvtchnNotify(Fdo, In->LocalPort, FileObject); + +fail1: + XenIfaceDebugPrint(ERROR, "Fail1 (%08x)\n", status); + return status; +} + +DECLSPEC_NOINLINE +NTSTATUS +IoctlEvtchnUnmask( + __in PXENIFACE_FDO Fdo, + __in PVOID Buffer, + __in ULONG InLen, + __in ULONG OutLen, + __in PFILE_OBJECT FileObject + ) +{ + NTSTATUS status; + PXENIFACE_EVTCHN_UNMASK_IN In = Buffer; + PXENIFACE_EVTCHN_CONTEXT Context = NULL; + KIRQL Irql; + + status = STATUS_INVALID_BUFFER_SIZE; + if (InLen != sizeof(XENIFACE_EVTCHN_UNMASK_IN) || OutLen != 0) + goto fail1; + + XenIfaceDebugPrint(TRACE, "> LocalPort %d, FO %p\n", In->LocalPort, FileObject); + + KeAcquireSpinLock(&Fdo->EvtchnLock, &Irql); + + Context = EvtchnFindChannel(Fdo, In->LocalPort, FileObject); + + status = STATUS_INVALID_PARAMETER; + if (Context == NULL) + goto fail2; + + XENBUS_EVTCHN(Unmask, + &Fdo->EvtchnInterface, + Context->Channel, + FALSE); + + KeReleaseSpinLock(&Fdo->EvtchnLock, Irql); + + return STATUS_SUCCESS; + +fail2: + KeReleaseSpinLock(&Fdo->EvtchnLock, Irql); + XenIfaceDebugPrint(ERROR, "Fail2\n"); + +fail1: + XenIfaceDebugPrint(ERROR, "Fail1 (%08x)\n", status); + return status; +} diff --git a/src/xeniface/ioctl_gnttab.c b/src/xeniface/ioctl_gnttab.c new file mode 100644 index 0000000..a279b77 --- /dev/null +++ b/src/xeniface/ioctl_gnttab.c @@ -0,0 +1,765 @@ +/* Copyright (c) Citrix Systems Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, + * with or without modification, are permitted provided + * that the following conditions are met: + * + * * Redistributions of source code must retain the above + * copyright notice, this list of conditions and the + * following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the + * following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "driver.h" +#include "ioctls.h" +#include "..\..\include\xeniface_ioctls.h" +#include "log.h" +#include "irp_queue.h" + +// Complete a canceled gnttab IRP, cleanup associated grant/map. +_Function_class_(IO_WORKITEM_ROUTINE) +VOID +CompleteGnttabIrp( + __in PDEVICE_OBJECT DeviceObject, + __in_opt PVOID Context + ) +{ + PXENIFACE_DX Dx = (PXENIFACE_DX)DeviceObject->DeviceExtension; + PXENIFACE_FDO Fdo = Dx->Fdo; + PIRP Irp = Context; + PXENIFACE_CONTEXT_ID Id; + PIO_WORKITEM WorkItem; + KAPC_STATE ApcState; + BOOLEAN ChangeProcess; + + ASSERT(Context != NULL); + + Id = Irp->Tail.Overlay.DriverContext[0]; + WorkItem = Irp->Tail.Overlay.DriverContext[1]; + + // We are not guaranteed to be in the context of the process that initiated the IRP, + // but we need to be there to unmap memory. + ChangeProcess = PsGetCurrentProcess() != Id->Process; + if (ChangeProcess) { + XenIfaceDebugPrint(TRACE, "Changing process from %p to %p\n", PsGetCurrentProcess(), Id->Process); + KeStackAttachProcess(Id->Process, &ApcState); + } + + XenIfaceDebugPrint(TRACE, "Irp %p, Process %p, Id %lu, Type %d, IRQL %d\n", + Irp, Id->Process, Id->RequestId, Id->Type, KeGetCurrentIrql()); + + switch (Id->Type) { + + case XENIFACE_CONTEXT_GRANT: + GnttabFreeGrant(Fdo, CONTAINING_RECORD(Id, XENIFACE_GRANT_CONTEXT, Id)); + break; + + case XENIFACE_CONTEXT_MAP: + GnttabFreeMap(Fdo, CONTAINING_RECORD(Id, XENIFACE_MAP_CONTEXT, Id)); + break; + + default: + ASSERT(FALSE); + } + + if (ChangeProcess) + KeUnstackDetachProcess(&ApcState); + + IoFreeWorkItem(WorkItem); + + Irp->IoStatus.Status = STATUS_CANCELLED; + Irp->IoStatus.Information = 0; + IoCompleteRequest(Irp, IO_NO_INCREMENT); +} + +_Acquires_exclusive_lock_(((PXENIFACE_FDO)Argument)->GnttabCacheLock) +_IRQL_requires_(DISPATCH_LEVEL) +VOID +GnttabAcquireLock( + __in PVOID Argument + ) +{ + PXENIFACE_FDO Fdo = Argument; + + ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL); + + KeAcquireSpinLockAtDpcLevel(&Fdo->GnttabCacheLock); +} + +_Releases_exclusive_lock_(((PXENIFACE_FDO)Argument)->GnttabCacheLock) +_IRQL_requires_(DISPATCH_LEVEL) +VOID +GnttabReleaseLock( + __in PVOID Argument + ) +{ + PXENIFACE_FDO Fdo = Argument; + + ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL); + + KeReleaseSpinLockFromDpcLevel(&Fdo->GnttabCacheLock); +} + +_Requires_lock_not_held_(Fdo->IrpQueueLock) +static +PIRP +FindGnttabIrp( + __in PXENIFACE_FDO Fdo, + __in PXENIFACE_CONTEXT_ID Id + ) +{ + KIRQL Irql; + PIRP Irp; + + CsqAcquireLock(&Fdo->IrpQueue, &Irql); + Irp = CsqPeekNextIrp(&Fdo->IrpQueue, NULL, Id); + CsqReleaseLock(&Fdo->IrpQueue, Irql); + return Irp; +} + +DECLSPEC_NOINLINE +NTSTATUS +IoctlGnttabPermitForeignAccess( + __in PXENIFACE_FDO Fdo, + __in PVOID Buffer, + __in ULONG InLen, + __in ULONG OutLen, + __inout PIRP Irp + ) +{ + NTSTATUS status; + PXENIFACE_GNTTAB_PERMIT_FOREIGN_ACCESS_IN In = Buffer; + PXENIFACE_GRANT_CONTEXT Context; + ULONG Page; + + status = STATUS_INVALID_BUFFER_SIZE; + if (InLen != sizeof(XENIFACE_GNTTAB_PERMIT_FOREIGN_ACCESS_IN) || OutLen != 0) + goto fail1; + + status = STATUS_INVALID_PARAMETER; + if ((In->NumberPages == 0) || (In->NumberPages > 1024 * 1024) || + ((In->Flags & XENIFACE_GNTTAB_USE_NOTIFY_OFFSET) && (In->NotifyOffset >= In->NumberPages * PAGE_SIZE)) + ) + goto fail2; + + status = STATUS_NO_MEMORY; + Context = ExAllocatePoolWithTag(NonPagedPool, sizeof(XENIFACE_GRANT_CONTEXT), XENIFACE_POOL_TAG); + if (Context == NULL) + goto fail3; + + RtlZeroMemory(Context, sizeof(XENIFACE_GRANT_CONTEXT)); + Context->Id.Type = XENIFACE_CONTEXT_GRANT; + Context->Id.Process = PsGetCurrentProcess(); + Context->Id.RequestId = In->RequestId; + Context->RemoteDomain = In->RemoteDomain; + Context->NumberPages = In->NumberPages; + Context->Flags = In->Flags; + Context->NotifyOffset = In->NotifyOffset; + Context->NotifyPort = In->NotifyPort; + + XenIfaceDebugPrint(TRACE, "> RemoteDomain %d, NumberPages %lu, Flags 0x%x, Offset 0x%x, Port %d, Process %p, Id %lu\n", + Context->RemoteDomain, Context->NumberPages, Context->Flags, Context->NotifyOffset, Context->NotifyPort, + Context->Id.Process, Context->Id.RequestId); + + // Check if the request ID is unique. + // This doesn't protect us from simultaneous requests with the same ID arriving here + // but another check for duplicate ID is performed when the context/IRP is queued at the end. + // Ideally we would lock the whole section but that's not really an option since we touch user memory. + status = STATUS_INVALID_PARAMETER; + if (FindGnttabIrp(Fdo, &Context->Id) != NULL) + goto fail4; + + status = STATUS_NO_MEMORY; + Context->Grants = ExAllocatePoolWithTag(NonPagedPool, Context->NumberPages * sizeof(PXENBUS_GNTTAB_ENTRY), XENIFACE_POOL_TAG); + if (Context->Grants == NULL) + goto fail5; + + RtlZeroMemory(Context->Grants, Context->NumberPages * sizeof(PXENBUS_GNTTAB_ENTRY)); + + // allocate memory to share + status = STATUS_NO_MEMORY; + Context->KernelVa = ExAllocatePoolWithTag(NonPagedPool, Context->NumberPages * PAGE_SIZE, XENIFACE_POOL_TAG); + if (Context->KernelVa == NULL) + goto fail6; + + RtlZeroMemory(Context->KernelVa, Context->NumberPages * PAGE_SIZE); + Context->Mdl = IoAllocateMdl(Context->KernelVa, Context->NumberPages * PAGE_SIZE, FALSE, FALSE, NULL); + if (Context->Mdl == NULL) + goto fail7; + + MmBuildMdlForNonPagedPool(Context->Mdl); + ASSERT(MmGetMdlByteCount(Context->Mdl) == Context->NumberPages * PAGE_SIZE); + + // perform sharing + for (Page = 0; Page < Context->NumberPages; Page++) { + status = XENBUS_GNTTAB(PermitForeignAccess, + &Fdo->GnttabInterface, + Fdo->GnttabCache, + FALSE, + Context->RemoteDomain, + MmGetMdlPfnArray(Context->Mdl)[Page], + (Context->Flags & XENIFACE_GNTTAB_READONLY) != 0, + &(Context->Grants[Page])); + +// prefast somehow thinks that this call can modify Page... +#pragma prefast(suppress:6385) + XenIfaceDebugPrint(INFO, "Grants[%lu] = %p\n", Page, Context->Grants[Page]); + if (!NT_SUCCESS(status)) + goto fail8; + } + + // map into user mode +#pragma prefast(suppress:6320) // we want to catch all exceptions + __try { + Context->UserVa = MmMapLockedPagesSpecifyCache(Context->Mdl, UserMode, MmCached, NULL, FALSE, NormalPagePriority); + } + __except (EXCEPTION_EXECUTE_HANDLER) { + status = GetExceptionCode(); + goto fail9; + } + + status = STATUS_UNSUCCESSFUL; + if (Context->UserVa == NULL) + goto fail10; + + XenIfaceDebugPrint(TRACE, "< Context %p, Irp %p, KernelVa %p, UserVa %p\n", Context, Irp, Context->KernelVa, Context->UserVa); + + // Insert the IRP/context into the pending queue. + // This also checks (again) if the request ID is unique. + Irp->Tail.Overlay.DriverContext[0] = &Context->Id; + status = IoCsqInsertIrpEx(&Fdo->IrpQueue, Irp, NULL, &Context->Id); + if (!NT_SUCCESS(status)) + goto fail11; + + return STATUS_PENDING; + +fail11: + XenIfaceDebugPrint(ERROR, "Fail11\n"); + MmUnmapLockedPages(Context->UserVa, Context->Mdl); + +fail10: + XenIfaceDebugPrint(ERROR, "Fail10\n"); + +fail9: + XenIfaceDebugPrint(ERROR, "Fail9\n"); + +fail8: + XenIfaceDebugPrint(ERROR, "Fail8: Page = %lu\n", Page); + + while (Page > 0) { + ASSERT(NT_SUCCESS(XENBUS_GNTTAB(RevokeForeignAccess, + &Fdo->GnttabInterface, + Fdo->GnttabCache, + FALSE, + Context->Grants[Page - 1]))); + + --Page; + } + IoFreeMdl(Context->Mdl); + +fail7: + XenIfaceDebugPrint(ERROR, "Fail7\n"); + ExFreePoolWithTag(Context->KernelVa, XENIFACE_POOL_TAG); + +fail6: + XenIfaceDebugPrint(ERROR, "Fail6\n"); + ExFreePoolWithTag(Context->Grants, XENIFACE_POOL_TAG); + +fail5: + XenIfaceDebugPrint(ERROR, "Fail5\n"); + +fail4: + XenIfaceDebugPrint(ERROR, "Fail4\n"); + RtlZeroMemory(Context, sizeof(XENIFACE_GRANT_CONTEXT)); + ExFreePoolWithTag(Context, XENIFACE_POOL_TAG); + +fail3: + XenIfaceDebugPrint(ERROR, "Fail3\n"); + +fail2: + XenIfaceDebugPrint(ERROR, "Fail2\n"); + +fail1: + XenIfaceDebugPrint(ERROR, "Fail1 (%08x)\n", status); + return status; +} + +DECLSPEC_NOINLINE +NTSTATUS +IoctlGnttabGetGrantResult( + __in PXENIFACE_FDO Fdo, + __in PVOID Buffer, + __in ULONG InLen, + __in ULONG OutLen, + __out PULONG_PTR Info + ) +{ + NTSTATUS status; + PXENIFACE_GNTTAB_GET_GRANT_RESULT_IN In = Buffer; + PXENIFACE_GNTTAB_GET_GRANT_RESULT_OUT Out = Buffer; + XENIFACE_CONTEXT_ID Id; + KIRQL Irql; + PIRP Irp; + PXENIFACE_CONTEXT_ID ContextId; + PXENIFACE_GRANT_CONTEXT Context; + + status = STATUS_INVALID_BUFFER_SIZE; + if (InLen != sizeof(XENIFACE_GNTTAB_GET_GRANT_RESULT_IN)) + goto fail1; + + Id.Process = PsGetCurrentProcess(); + Id.RequestId = In->RequestId; + Id.Type = XENIFACE_CONTEXT_GRANT; + + XenIfaceDebugPrint(TRACE, "> Process %p, Id %lu\n", Id.Process, Id.RequestId); + + CsqAcquireLock(&Fdo->IrpQueue, &Irql); + Irp = CsqPeekNextIrp(&Fdo->IrpQueue, NULL, &Id); + + status = STATUS_NOT_FOUND; + if (Irp == NULL) + goto fail2; + + ContextId = Irp->Tail.Overlay.DriverContext[0]; + Context = CONTAINING_RECORD(ContextId, XENIFACE_GRANT_CONTEXT, Id); + + status = STATUS_INVALID_BUFFER_SIZE; + if (OutLen != (sizeof(XENIFACE_GNTTAB_GET_GRANT_RESULT_OUT) + sizeof(ULONG) * Context->NumberPages)) + goto fail3; + + Out->Address = Context->UserVa; + XenIfaceDebugPrint(TRACE, "< Address %p, Irp %p\n", Context->UserVa, Irp); + + for (ULONG Page = 0; Page < Context->NumberPages; Page++) { + Out->References[Page] = XENBUS_GNTTAB(GetReference, + &Fdo->GnttabInterface, + Context->Grants[Page]); + XenIfaceDebugPrint(INFO, "Ref[%lu] = %lu\n", Page, Out->References[Page]); + } + + CsqReleaseLock(&Fdo->IrpQueue, Irql); + *Info = OutLen; + + return STATUS_SUCCESS; + +fail3: + XenIfaceDebugPrint(ERROR, "Fail3\n"); +fail2: + XenIfaceDebugPrint(ERROR, "Fail2\n"); + CsqReleaseLock(&Fdo->IrpQueue, Irql); +fail1: + XenIfaceDebugPrint(ERROR, "Fail1 (%08x)\n", status); + return status; +} + +_IRQL_requires_max_(APC_LEVEL) +VOID +GnttabFreeGrant( + __in PXENIFACE_FDO Fdo, + __inout PXENIFACE_GRANT_CONTEXT Context +) +{ + NTSTATUS status; + ULONG Page; + + ASSERT(KeGetCurrentIrql() <= APC_LEVEL); + + XenIfaceDebugPrint(TRACE, "Context %p\n", Context); + + if (Context->Flags & XENIFACE_GNTTAB_USE_NOTIFY_OFFSET) { + ((PCHAR)Context->KernelVa)[Context->NotifyOffset] = 0; + } + + if (Context->Flags & XENIFACE_GNTTAB_USE_NOTIFY_PORT) { + status = EvtchnNotify(Fdo, Context->NotifyPort, NULL); + + if (!NT_SUCCESS(status)) // non-fatal, we must free memory + XenIfaceDebugPrint(ERROR, "failed to notify port %lu: 0x%x\n", Context->NotifyPort, status); + } + + // unmap from user address space + MmUnmapLockedPages(Context->UserVa, Context->Mdl); + + // stop sharing + for (Page = 0; Page < Context->NumberPages; Page++) { + status = XENBUS_GNTTAB(RevokeForeignAccess, + &Fdo->GnttabInterface, + Fdo->GnttabCache, + FALSE, + Context->Grants[Page]); + + ASSERT(NT_SUCCESS(status)); // failure here is fatal, something must've gone catastrophically wrong + } + + IoFreeMdl(Context->Mdl); + + RtlZeroMemory(Context->KernelVa, Context->NumberPages * PAGE_SIZE); + ExFreePoolWithTag(Context->KernelVa, XENIFACE_POOL_TAG); + + RtlZeroMemory(Context->Grants, Context->NumberPages * sizeof(PXENBUS_GNTTAB_ENTRY)); + ExFreePoolWithTag(Context->Grants, XENIFACE_POOL_TAG); + + RtlZeroMemory(Context, sizeof(XENIFACE_GRANT_CONTEXT)); + ExFreePoolWithTag(Context, XENIFACE_POOL_TAG); +} + +DECLSPEC_NOINLINE +NTSTATUS +IoctlGnttabRevokeForeignAccess( + __in PXENIFACE_FDO Fdo, + __in PVOID Buffer, + __in ULONG InLen, + __in ULONG OutLen + ) +{ + NTSTATUS status; + PXENIFACE_GNTTAB_REVOKE_FOREIGN_ACCESS_IN In = Buffer; + PXENIFACE_GRANT_CONTEXT Context = NULL; + XENIFACE_CONTEXT_ID Id; + PIRP PendingIrp; + PXENIFACE_CONTEXT_ID ContextId; + + status = STATUS_INVALID_BUFFER_SIZE; + if (InLen != sizeof(XENIFACE_GNTTAB_REVOKE_FOREIGN_ACCESS_IN)) + goto fail1; + + Id.Type = XENIFACE_CONTEXT_GRANT; + Id.Process = PsGetCurrentProcess(); + Id.RequestId = In->RequestId; + + XenIfaceDebugPrint(TRACE, "> Process %p, Id %lu\n", Id.Process, Id.RequestId); + + status = STATUS_NOT_FOUND; + PendingIrp = IoCsqRemoveNextIrp(&Fdo->IrpQueue, &Id); + if (PendingIrp == NULL) + goto fail2; + + ContextId = PendingIrp->Tail.Overlay.DriverContext[0]; + Context = CONTAINING_RECORD(ContextId, XENIFACE_GRANT_CONTEXT, Id); + GnttabFreeGrant(Fdo, Context); + + PendingIrp->IoStatus.Status = STATUS_SUCCESS; + PendingIrp->IoStatus.Information = 0; + IoCompleteRequest(PendingIrp, IO_NO_INCREMENT); + + return STATUS_SUCCESS; + +fail2: + XenIfaceDebugPrint(ERROR, "Fail2\n"); +fail1: + XenIfaceDebugPrint(ERROR, "Fail1 (%08x)\n", status); + return status; +} + +DECLSPEC_NOINLINE +NTSTATUS +IoctlGnttabMapForeignPages( + __in PXENIFACE_FDO Fdo, + __in PVOID Buffer, + __in ULONG InLen, + __in ULONG OutLen, + __inout PIRP Irp + ) +{ + NTSTATUS status; + PXENIFACE_GNTTAB_MAP_FOREIGN_PAGES_IN In = Buffer; + PXENIFACE_MAP_CONTEXT Context; + + status = STATUS_INVALID_BUFFER_SIZE; + if (InLen < sizeof(XENIFACE_GNTTAB_MAP_FOREIGN_PAGES_IN) || OutLen != 0) + goto fail1; + + status = STATUS_INVALID_PARAMETER; + if ((In->NumberPages == 0) || (In->NumberPages > 1024 * 1024) || + ((In->Flags & XENIFACE_GNTTAB_USE_NOTIFY_OFFSET) && (In->NotifyOffset >= In->NumberPages * PAGE_SIZE)) + ) + goto fail2; + + status = STATUS_INVALID_BUFFER_SIZE; + if (InLen != sizeof(XENIFACE_GNTTAB_MAP_FOREIGN_PAGES_IN) + sizeof(ULONG) * In->NumberPages) + goto fail3; + + status = STATUS_NO_MEMORY; + Context = ExAllocatePoolWithTag(NonPagedPool, sizeof(XENIFACE_MAP_CONTEXT), XENIFACE_POOL_TAG); + if (Context == NULL) + goto fail4; + + RtlZeroMemory(Context, sizeof(XENIFACE_MAP_CONTEXT)); + Context->Id.Type = XENIFACE_CONTEXT_MAP; + Context->Id.Process = PsGetCurrentProcess(); + Context->Id.RequestId = In->RequestId; + Context->RemoteDomain = In->RemoteDomain; + Context->NumberPages = In->NumberPages; + Context->Flags = In->Flags; + Context->NotifyOffset = In->NotifyOffset; + Context->NotifyPort = In->NotifyPort; + + XenIfaceDebugPrint(TRACE, "> RemoteDomain %d, NumberPages %lu, Flags 0x%x, Offset 0x%x, Port %d, Process %p, Id %lu\n", + Context->RemoteDomain, Context->NumberPages, Context->Flags, Context->NotifyOffset, Context->NotifyPort, + Context->Id.Process, Context->Id.RequestId); + + for (ULONG i = 0; i < In->NumberPages; i++) + XenIfaceDebugPrint(INFO, "> Ref %d\n", In->References[i]); + + status = STATUS_INVALID_PARAMETER; + if (FindGnttabIrp(Fdo, &Context->Id) != NULL) + goto fail5; + + status = XENBUS_GNTTAB(MapForeignPages, + &Fdo->GnttabInterface, + Context->RemoteDomain, + Context->NumberPages, + In->References, + Context->Flags & XENIFACE_GNTTAB_READONLY, + &Context->Address); + + if (!NT_SUCCESS(status)) + goto fail6; + + status = STATUS_NO_MEMORY; + Context->KernelVa = MmMapIoSpace(Context->Address, Context->NumberPages * PAGE_SIZE, MmCached); + if (Context->KernelVa == NULL) + goto fail7; + + status = STATUS_NO_MEMORY; + Context->Mdl = IoAllocateMdl(Context->KernelVa, Context->NumberPages * PAGE_SIZE, FALSE, FALSE, NULL); + if (Context->Mdl == NULL) + goto fail8; + + MmBuildMdlForNonPagedPool(Context->Mdl); + + // map into user mode +#pragma prefast(suppress: 6320) // we want to catch all exceptions + __try { + Context->UserVa = MmMapLockedPagesSpecifyCache(Context->Mdl, UserMode, MmCached, NULL, FALSE, NormalPagePriority); + } + __except (EXCEPTION_EXECUTE_HANDLER) { + status = GetExceptionCode(); + goto fail9; + } + + status = STATUS_UNSUCCESSFUL; + if (Context->UserVa == NULL) + goto fail10; + + XenIfaceDebugPrint(TRACE, "< Context %p, Irp %p, Address %p, KernelVa %p, UserVa %p\n", + Context, Irp, Context->Address, Context->KernelVa, Context->UserVa); + + // Insert the IRP/context into the pending queue. + // This also checks (again) if the request ID is unique. + Irp->Tail.Overlay.DriverContext[0] = &Context->Id; + status = IoCsqInsertIrpEx(&Fdo->IrpQueue, Irp, NULL, &Context->Id); + if (!NT_SUCCESS(status)) + goto fail11; + + return STATUS_PENDING; + +fail11: + XenIfaceDebugPrint(ERROR, "Fail11\n"); + MmUnmapLockedPages(Context->UserVa, Context->Mdl); + +fail10: + XenIfaceDebugPrint(ERROR, "Fail10\n"); + +fail9: + XenIfaceDebugPrint(ERROR, "Fail9\n"); + IoFreeMdl(Context->Mdl); + +fail8: + XenIfaceDebugPrint(ERROR, "Fail8\n"); + MmUnmapIoSpace(Context->KernelVa, Context->NumberPages * PAGE_SIZE); + +fail7: + XenIfaceDebugPrint(ERROR, "Fail7\n"); + ASSERT(NT_SUCCESS(XENBUS_GNTTAB(UnmapForeignPages, + &Fdo->GnttabInterface, + Context->Address + ))); + +fail6: + XenIfaceDebugPrint(ERROR, "Fail6\n"); + +fail5: + XenIfaceDebugPrint(ERROR, "Fail5\n"); + RtlZeroMemory(Context, sizeof(XENIFACE_MAP_CONTEXT)); + ExFreePoolWithTag(Context, XENIFACE_POOL_TAG); + +fail4: + XenIfaceDebugPrint(ERROR, "Fail4\n"); + +fail3: + XenIfaceDebugPrint(ERROR, "Fail3\n"); + +fail2: + XenIfaceDebugPrint(ERROR, "Fail2\n"); + +fail1: + XenIfaceDebugPrint(ERROR, "Fail1 (%08x)\n", status); + return status; +} + +DECLSPEC_NOINLINE +NTSTATUS +IoctlGnttabGetMapResult( + __in PXENIFACE_FDO Fdo, + __in PVOID Buffer, + __in ULONG InLen, + __in ULONG OutLen, + __out PULONG_PTR Info + ) +{ + NTSTATUS status; + PXENIFACE_GNTTAB_GET_MAP_RESULT_IN In = Buffer; + PXENIFACE_GNTTAB_GET_MAP_RESULT_OUT Out = Buffer; + XENIFACE_CONTEXT_ID Id; + KIRQL Irql; + PIRP Irp; + PXENIFACE_MAP_CONTEXT Context; + PXENIFACE_CONTEXT_ID ContextId; + + status = STATUS_INVALID_BUFFER_SIZE; + if (InLen != sizeof(XENIFACE_GNTTAB_GET_MAP_RESULT_IN) || OutLen != sizeof(XENIFACE_GNTTAB_GET_MAP_RESULT_OUT)) + goto fail1; + + Id.Type = XENIFACE_CONTEXT_MAP; + Id.Process = PsGetCurrentProcess(); + Id.RequestId = In->RequestId; + + XenIfaceDebugPrint(TRACE, "> Process %p, Id %lu\n", Id.Process, Id.RequestId); + + CsqAcquireLock(&Fdo->IrpQueue, &Irql); + Irp = CsqPeekNextIrp(&Fdo->IrpQueue, NULL, &Id); + + status = STATUS_NOT_FOUND; + if (Irp == NULL) + goto fail2; + + ContextId = Irp->Tail.Overlay.DriverContext[0]; + Context = CONTAINING_RECORD(ContextId, XENIFACE_MAP_CONTEXT, Id); + + Out->Address = Context->UserVa; + XenIfaceDebugPrint(TRACE, "< Address %p, Irp %p\n", Context->UserVa, Irp); + + CsqReleaseLock(&Fdo->IrpQueue, Irql); + *Info = OutLen; + + return STATUS_SUCCESS; + +fail2: + XenIfaceDebugPrint(ERROR, "Fail2\n"); + CsqReleaseLock(&Fdo->IrpQueue, Irql); +fail1: + XenIfaceDebugPrint(ERROR, "Fail1 (%08x)\n", status); + return status; +} + +_IRQL_requires_max_(APC_LEVEL) +DECLSPEC_NOINLINE +VOID +GnttabFreeMap( + __in PXENIFACE_FDO Fdo, + __inout PXENIFACE_MAP_CONTEXT Context + ) +{ + NTSTATUS status; + + ASSERT(KeGetCurrentIrql() <= APC_LEVEL); + + XenIfaceDebugPrint(TRACE, "Context %p\n", Context); + + if (Context->Flags & XENIFACE_GNTTAB_USE_NOTIFY_OFFSET) { + ((PCHAR)Context->KernelVa)[Context->NotifyOffset] = 0; + } + + if (Context->Flags & XENIFACE_GNTTAB_USE_NOTIFY_PORT) { + status = EvtchnNotify(Fdo, Context->NotifyPort, NULL); + + if (!NT_SUCCESS(status)) // non-fatal, we must free memory + XenIfaceDebugPrint(ERROR, "failed to notify port %lu: 0x%x\n", Context->NotifyPort, status); + } + + // unmap from user address space + MmUnmapLockedPages(Context->UserVa, Context->Mdl); + + IoFreeMdl(Context->Mdl); + + // unmap from system space + MmUnmapIoSpace(Context->KernelVa, Context->NumberPages * PAGE_SIZE); + + // undo mapping + status = XENBUS_GNTTAB(UnmapForeignPages, + &Fdo->GnttabInterface, + Context->Address); + + ASSERT(NT_SUCCESS(status)); + + RtlZeroMemory(Context, sizeof(XENIFACE_MAP_CONTEXT)); + ExFreePoolWithTag(Context, XENIFACE_POOL_TAG); +} + +DECLSPEC_NOINLINE +NTSTATUS +IoctlGnttabUnmapForeignPages( + __in PXENIFACE_FDO Fdo, + __in PVOID Buffer, + __in ULONG InLen, + __in ULONG OutLen + ) +{ + NTSTATUS status; + PXENIFACE_GNTTAB_UNMAP_FOREIGN_PAGES_IN In = Buffer; + PXENIFACE_MAP_CONTEXT Context = NULL; + XENIFACE_CONTEXT_ID Id; + PIRP PendingIrp; + PXENIFACE_CONTEXT_ID ContextId; + + status = STATUS_INVALID_BUFFER_SIZE; + if (InLen != sizeof(XENIFACE_GNTTAB_UNMAP_FOREIGN_PAGES_IN) && OutLen != 0) + goto fail1; + + Id.Type = XENIFACE_CONTEXT_MAP; + Id.Process = PsGetCurrentProcess(); + Id.RequestId = In->RequestId; + + XenIfaceDebugPrint(TRACE, "> Process %p, Id %lu\n", Id.Process, Id.RequestId); + + status = STATUS_NOT_FOUND; + PendingIrp = IoCsqRemoveNextIrp(&Fdo->IrpQueue, &Id); + if (PendingIrp == NULL) + goto fail2; + + ContextId = PendingIrp->Tail.Overlay.DriverContext[0]; + Context = CONTAINING_RECORD(ContextId, XENIFACE_MAP_CONTEXT, Id); + GnttabFreeMap(Fdo, Context); + + PendingIrp->IoStatus.Status = STATUS_SUCCESS; + PendingIrp->IoStatus.Information = 0; + IoCompleteRequest(PendingIrp, IO_NO_INCREMENT); + + return STATUS_SUCCESS; + +fail2: + XenIfaceDebugPrint(ERROR, "Fail2\n"); +fail1: + XenIfaceDebugPrint(ERROR, "Fail1 (%08x)\n", status); + return status; +} diff --git a/src/xeniface/ioctl_store.c b/src/xeniface/ioctl_store.c new file mode 100644 index 0000000..181c706 --- /dev/null +++ b/src/xeniface/ioctl_store.c @@ -0,0 +1,574 @@ +/* Copyright (c) Citrix Systems Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, + * with or without modification, are permitted provided + * that the following conditions are met: + * + * * Redistributions of source code must retain the above + * copyright notice, this list of conditions and the + * following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the + * following disclaimer in the documentation and/or other + * materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "driver.h" +#include "ioctls.h" +#include "..\..\include\xeniface_ioctls.h" +#include "log.h" + +#define XENSTORE_ABS_PATH_MAX 3072 +#define XENSTORE_REL_PATH_MAX 2048 + +static +NTSTATUS +__CaptureUserBuffer( + __in PVOID Buffer, + __in ULONG Length, + __out PVOID *CapturedBuffer + ) +{ + NTSTATUS Status; + PVOID TempBuffer = NULL; + + if (Length == 0) { + *CapturedBuffer = NULL; + return STATUS_SUCCESS; + } + + Status = STATUS_NO_MEMORY; + TempBuffer = ExAllocatePoolWithTag(NonPagedPool, Length, XENIFACE_POOL_TAG); + if (TempBuffer == NULL) + return STATUS_INSUFFICIENT_RESOURCES; + + Status = STATUS_SUCCESS; + +#pragma prefast(suppress: 6320) // we want to catch all exceptions + try { + ProbeForRead(Buffer, Length, 1); + RtlCopyMemory(TempBuffer, Buffer, Length); + } except(EXCEPTION_EXECUTE_HANDLER) { + XenIfaceDebugPrint(ERROR, "Exception while probing/reading buffer at %p, size 0x%lx\n", Buffer, Length); + ExFreePoolWithTag(TempBuffer, XENIFACE_POOL_TAG); + TempBuffer = NULL; + Status = GetExceptionCode(); + } + + *CapturedBuffer = TempBuffer; + + return Status; + } + +static +VOID +__FreeCapturedBuffer( + __in PVOID CapturedBuffer + ) +{ + if (CapturedBuffer != NULL) { + ExFreePoolWithTag(CapturedBuffer, XENIFACE_POOL_TAG); + } +} + +static FORCEINLINE +BOOLEAN +__IsValidStr( + __in PCHAR Str, + __in ULONG Len + ) +{ + for ( ; Len--; ++Str) { + if (*Str == '\0') + return TRUE; + if (!isprint((unsigned char)*Str)) + break; + } + return FALSE; +} + +static FORCEINLINE +ULONG +__MultiSzLen( + __in PCHAR Str, + __out PULONG Count + ) +{ + ULONG Length = 0; + if (Count) *Count = 0; + do { + for ( ; *Str; ++Str, ++Length) ; + ++Str; ++Length; + if (*Count) ++(*Count); + } while (*Str); + return Length; +} + +static FORCEINLINE +VOID +__DisplayMultiSz( + __in PCHAR Caller, + __in PCHAR Str + ) +{ + PCHAR Ptr; + ULONG Idx; + ULONG Len; + + for (Ptr = Str, Idx = 0; *Ptr; ++Idx) { + Len = (ULONG)strlen(Ptr); + XenIfaceDebugPrint(TRACE, "|%s: [%d]=(%d)->\"%s\"\n", Caller, Idx, Len, Ptr); + Ptr += (Len + 1); + } +} + +DECLSPEC_NOINLINE +NTSTATUS +IoctlStoreRead( + __in PXENIFACE_FDO Fdo, + __in PCHAR Buffer, + __in ULONG InLen, + __in ULONG OutLen, + __out PULONG_PTR Info + ) +{ + NTSTATUS status; + PCHAR Value; + ULONG Length; + + status = STATUS_INVALID_BUFFER_SIZE; + if (InLen == 0) + goto fail1; + + status = STATUS_INVALID_PARAMETER; + if (!__IsValidStr(Buffer, InLen)) + goto fail2; + + status = XENBUS_STORE(Read, &Fdo->StoreInterface, NULL, NULL, Buffer, &Value); + if (!NT_SUCCESS(status)) + goto fail3; + + Length = (ULONG)strlen(Value) + 1; + + status = STATUS_BUFFER_OVERFLOW; + if (OutLen == 0) { + XenIfaceDebugPrint(TRACE, "(\"%s\")=(%d)\n", Buffer, Length); + goto done; + } + + status = STATUS_INVALID_PARAMETER; + if (OutLen < Length) + goto fail4; + + XenIfaceDebugPrint(TRACE, "(\"%s\")=(%d)->\"%s\"\n", Buffer, Length, Value); + + RtlCopyMemory(Buffer, Value, Length); + Buffer[Length - 1] = 0; + status = STATUS_SUCCESS; + +done: + *Info = (ULONG_PTR)Length; + XENBUS_STORE(Free, &Fdo->StoreInterface, Value); + return status; + +fail4: + XenIfaceDebugPrint(ERROR, "Fail4 (\"%s\")=(%d < %d)\n", Buffer, OutLen, Length); + XENBUS_STORE(Free, &Fdo->StoreInterface, Value); +fail3: + XenIfaceDebugPrint(ERROR, "Fail3 (\"%s\")\n", Buffer); +fail2: + XenIfaceDebugPrint(ERROR, "Fail2\n"); +fail1: + XenIfaceDebugPrint(ERROR, "Fail1 (%08x)\n", status); + return status; +} + +DECLSPEC_NOINLINE +NTSTATUS +IoctlStoreWrite( + __in PXENIFACE_FDO Fdo, + __in PCHAR Buffer, + __in ULONG InLen, + __in ULONG OutLen + ) +{ + NTSTATUS status; + PCHAR Value; + ULONG Length; + + status = STATUS_INVALID_BUFFER_SIZE; + if (InLen == 0 || OutLen != 0) + goto fail1; + + status = STATUS_INVALID_PARAMETER; + if (!__IsValidStr(Buffer, InLen)) + goto fail2; + + Length = (ULONG)strlen(Buffer) + 1; + Value = Buffer + Length; + + if (!__IsValidStr(Value, InLen - Length)) + goto fail3; + + status = XENBUS_STORE(Printf, &Fdo->StoreInterface, NULL, NULL, Buffer, Value); + if (!NT_SUCCESS(status)) + goto fail4; + + XenIfaceDebugPrint(TRACE, "(\"%s\"=\"%s\")\n", Buffer, Value); + return status; + +fail4: + XenIfaceDebugPrint(ERROR, "Fail4 (\"%s\")\n", Value); +fail3: + XenIfaceDebugPrint(ERROR, "Fail3 (\"%s\")\n", Buffer); +fail2: + XenIfaceDebugPrint(ERROR, "Fail2\n"); +fail1: + XenIfaceDebugPrint(ERROR, "Fail1 (%08x)\n", status); + return status; +} + +DECLSPEC_NOINLINE +NTSTATUS +IoctlStoreDirectory( + __in PXENIFACE_FDO Fdo, + __in PCHAR Buffer, + __in ULONG InLen, + __in ULONG OutLen, + __out PULONG_PTR Info + ) +{ + NTSTATUS status; + PCHAR Value; + ULONG Length; + ULONG Count; + + status = STATUS_INVALID_BUFFER_SIZE; + if (InLen == 0) + goto fail1; + + status = STATUS_INVALID_PARAMETER; + if (!__IsValidStr(Buffer, InLen)) + goto fail2; + + status = XENBUS_STORE(Directory, &Fdo->StoreInterface, NULL, NULL, Buffer, &Value); + if (!NT_SUCCESS(status)) + goto fail3; + + Length = __MultiSzLen(Value, &Count) + 1; + + status = STATUS_BUFFER_OVERFLOW; + if (OutLen == 0) { + XenIfaceDebugPrint(TRACE, "(\"%s\")=(%d)(%d)\n", Buffer, Length, Count); + goto done; + } + + status = STATUS_INVALID_PARAMETER; + if (OutLen < Length) + goto fail4; + + XenIfaceDebugPrint(INFO, "(\"%s\")=(%d)(%d)\n", Buffer, Length, Count); +#if DBG + __DisplayMultiSz(__FUNCTION__, Value); +#endif + + RtlCopyMemory(Buffer, Value, Length); + Buffer[Length - 2] = 0; + Buffer[Length - 1] = 0; + status = STATUS_SUCCESS; + +done: + *Info = (ULONG_PTR)Length; + XENBUS_STORE(Free, &Fdo->StoreInterface, Value); + return status; + +fail4: + XenIfaceDebugPrint(ERROR, "Fail4 (\"%s\")=(%d < %d)\n", Buffer, OutLen, Length); + XENBUS_STORE(Free, &Fdo->StoreInterface, Value); +fail3: + XenIfaceDebugPrint(ERROR, "Fail3 (\"%s\")\n", Buffer); +fail2: + XenIfaceDebugPrint(ERROR, "Fail2\n"); +fail1: + XenIfaceDebugPrint(ERROR, "Fail1 (%08x)\n", status); + return status; +} + +DECLSPEC_NOINLINE +NTSTATUS +IoctlStoreRemove( + __in PXENIFACE_FDO Fdo, + __in PCHAR Buffer, + __in ULONG InLen, + __in ULONG OutLen + ) +{ + NTSTATUS status; + + status = STATUS_INVALID_BUFFER_SIZE; + if (InLen == 0 || OutLen != 0) + goto fail1; + + status = STATUS_INVALID_PARAMETER; + if (!__IsValidStr(Buffer, InLen)) + goto fail2; + + status = XENBUS_STORE(Remove, &Fdo->StoreInterface, NULL, NULL, Buffer); + if (!NT_SUCCESS(status)) + goto fail3; + + XenIfaceDebugPrint(TRACE, "(\"%s\")\n", Buffer); + return status; + +fail3: + XenIfaceDebugPrint(ERROR, "Fail3 (\"%s\")\n", Buffer); +fail2: + XenIfaceDebugPrint(ERROR, "Fail2\n"); +fail1: + XenIfaceDebugPrint(ERROR, "Fail1 (%08x)\n", status); + return status; +} + +DECLSPEC_NOINLINE +NTSTATUS +IoctlStoreSetPermissions( + __in PXENIFACE_FDO Fdo, + __in PVOID Buffer, + __in ULONG InLen, + __in ULONG OutLen + ) +{ + NTSTATUS status; + PXENIFACE_STORE_SET_PERMISSIONS_IN In = Buffer; + ULONG Index; + PCHAR Path; + + status = STATUS_INVALID_BUFFER_SIZE; + if (InLen < sizeof(XENIFACE_STORE_SET_PERMISSIONS_IN) || OutLen != 0) + goto fail1; + + if (InLen < sizeof(XENIFACE_STORE_SET_PERMISSIONS_IN) + In->NumberPermissions * sizeof(XENBUS_STORE_PERMISSION)) + goto fail2; + + status = STATUS_INVALID_PARAMETER; + if (In->PathLength == 0 || In->PathLength > XENSTORE_ABS_PATH_MAX) + goto fail3; + + status = __CaptureUserBuffer(In->Path, In->PathLength, &Path); + if (!NT_SUCCESS(status)) + goto fail4; + + Path[In->PathLength - 1] = 0; + XenIfaceDebugPrint(TRACE, "> Path '%s', NumberPermissions %lu\n", Path, In->NumberPermissions); + + for (Index = 0; Index < In->NumberPermissions; Index++) { + XenIfaceDebugPrint(TRACE, "> %lu: Domain %d, Mask 0x%x\n", Index, In->Permissions[Index].Domain, In->Permissions[Index].Mask); + if ((In->Permissions[Index].Mask & ~XENIFACE_STORE_ALLOWED_PERMISSIONS) != 0) + goto fail5; + } + + status = XENBUS_STORE(PermissionsSet, + &Fdo->StoreInterface, + NULL, // transaction + NULL, // prefix + Path, + In->Permissions, + In->NumberPermissions); + + if (!NT_SUCCESS(status)) + goto fail6; + + __FreeCapturedBuffer(Path); + return status; + +fail6: + XenIfaceDebugPrint(ERROR, "Fail6\n"); +fail5: + XenIfaceDebugPrint(ERROR, "Fail5\n"); + __FreeCapturedBuffer(Path); +fail4: + XenIfaceDebugPrint(ERROR, "Fail4\n"); +fail3: + XenIfaceDebugPrint(ERROR, "Fail3\n"); +fail2: + XenIfaceDebugPrint(ERROR, "Fail2\n"); +fail1: + XenIfaceDebugPrint(ERROR, "Fail1 (%08x)\n", status); + return status; +} + +DECLSPEC_NOINLINE +NTSTATUS +IoctlStoreAddWatch( + __in PXENIFACE_FDO Fdo, + __in PVOID Buffer, + __in ULONG InLen, + __in ULONG OutLen, + __in PFILE_OBJECT FileObject, + __out PULONG_PTR Info + ) +{ + NTSTATUS status; + PXENIFACE_STORE_ADD_WATCH_IN In = Buffer; + PXENIFACE_STORE_ADD_WATCH_OUT Out = Buffer; + PCHAR Path; + PXENIFACE_STORE_CONTEXT Context; + + status = STATUS_INVALID_BUFFER_SIZE; + if (InLen != sizeof(XENIFACE_STORE_ADD_WATCH_IN) || OutLen != sizeof(XENIFACE_STORE_ADD_WATCH_OUT)) + goto fail1; + + status = STATUS_INVALID_PARAMETER; + if (In->PathLength == 0 || In->PathLength > XENSTORE_ABS_PATH_MAX) + goto fail2; + + status = __CaptureUserBuffer(In->Path, In->PathLength, &Path); + if (!NT_SUCCESS(status)) + goto fail3; + + Path[In->PathLength - 1] = 0; + + status = STATUS_NO_MEMORY; + Context = ExAllocatePoolWithTag(NonPagedPool, sizeof(XENIFACE_STORE_CONTEXT), XENIFACE_POOL_TAG); + if (Context == NULL) + goto fail4; + + RtlZeroMemory(Context, sizeof(XENIFACE_STORE_CONTEXT)); + + Context->FileObject = FileObject; + + status = ObReferenceObjectByHandle(In->Event, EVENT_MODIFY_STATE, *ExEventObjectType, UserMode, &Context->Event, NULL); + if (!NT_SUCCESS(status)) + goto fail5; + + XenIfaceDebugPrint(TRACE, "> Path '%s', Event %p, FO %p\n", Path, In->Event, FileObject); + + status = XENBUS_STORE(WatchAdd, + &Fdo->StoreInterface, + NULL, // prefix + Path, + Context->Event, + &Context->Watch); + + if (!NT_SUCCESS(status)) + goto fail6; + + __FreeCapturedBuffer(Path); + + ExInterlockedInsertTailList(&Fdo->StoreWatchList, &Context->Entry, &Fdo->StoreWatchLock); + + XenIfaceDebugPrint(TRACE, "< Context %p, Watch %p\n", Context, Context->Watch); + + Out->Context = Context; + *Info = sizeof(XENIFACE_STORE_ADD_WATCH_OUT); + + return status; + +fail6: + XenIfaceDebugPrint(ERROR, "Fail6\n"); + ObDereferenceObject(Context->Event); +fail5: + XenIfaceDebugPrint(ERROR, "Fail5\n"); + RtlZeroMemory(Context, sizeof(XENIFACE_STORE_CONTEXT)); + ExFreePoolWithTag(Context, XENIFACE_POOL_TAG); +fail4: + XenIfaceDebugPrint(ERROR, "Fail4\n"); + __FreeCapturedBuffer(Path); +fail3: + XenIfaceDebugPrint(ERROR, "Fail3\n"); +fail2: + XenIfaceDebugPrint(ERROR, "Fail2\n"); +fail1: + XenIfaceDebugPrint(ERROR, "Fail1 (%08x)\n", status); + return status; +} + +_IRQL_requires_max_(DISPATCH_LEVEL) +VOID +StoreFreeWatch( + __in PXENIFACE_FDO Fdo, + __inout PXENIFACE_STORE_CONTEXT Context + ) +{ + NTSTATUS status; + + XenIfaceDebugPrint(TRACE, "Context %p, Watch %p, FO %p\n", + Context, Context->Watch, Context->FileObject); + + status = XENBUS_STORE(WatchRemove, + &Fdo->StoreInterface, + Context->Watch); + + ASSERT(NT_SUCCESS(status)); // this is fatal since we'd leave an active watch without cleaning it up + + ObDereferenceObject(Context->Event); + RtlZeroMemory(Context, sizeof(XENIFACE_STORE_CONTEXT)); + ExFreePoolWithTag(Context, XENIFACE_POOL_TAG); +} + +DECLSPEC_NOINLINE +NTSTATUS +IoctlStoreRemoveWatch( + __in PXENIFACE_FDO Fdo, + __in PVOID Buffer, + __in ULONG InLen, + __in ULONG OutLen, + __in PFILE_OBJECT FileObject + ) +{ + NTSTATUS status; + PXENIFACE_STORE_REMOVE_WATCH_IN In = Buffer; + PXENIFACE_STORE_CONTEXT Context = NULL; + KIRQL Irql; + PLIST_ENTRY Node; + + status = STATUS_INVALID_BUFFER_SIZE; + if (InLen != sizeof(XENIFACE_STORE_REMOVE_WATCH_IN) || OutLen != 0) + goto fail1; + + XenIfaceDebugPrint(TRACE, "> Context %p, FO %p\n", In->Context, FileObject); + + KeAcquireSpinLock(&Fdo->StoreWatchLock, &Irql); + Node = Fdo->StoreWatchList.Flink; + while (Node->Flink != Fdo->StoreWatchList.Flink) { + Context = CONTAINING_RECORD(Node, XENIFACE_STORE_CONTEXT, Entry); + + Node = Node->Flink; + if (Context != In->Context || Context->FileObject != FileObject) + continue; + + RemoveEntryList(&Context->Entry); + break; + } + KeReleaseSpinLock(&Fdo->StoreWatchLock, Irql); + + status = STATUS_NOT_FOUND; + if (Context == NULL || Context != In->Context) + goto fail2; + + StoreFreeWatch(Fdo, Context); + + return STATUS_SUCCESS; + +fail2: + XenIfaceDebugPrint(ERROR, "Fail2\n"); +fail1: + XenIfaceDebugPrint(ERROR, "Fail1 (%08x)\n", status); + return status; +} diff --git a/src/xeniface/ioctls.c b/src/xeniface/ioctls.c index 3bef9ea..e20b7dc 100644 --- a/src/xeniface/ioctls.c +++ b/src/xeniface/ioctls.c @@ -35,287 +35,156 @@ #include "..\..\include\xeniface_ioctls.h" #include "log.h" -static FORCEINLINE BOOLEAN -__IsValidStr( - __in PCHAR Str, - __in ULONG Len - ) -{ - for ( ; Len--; ++Str) { - if (*Str == '\0') - return TRUE; - if (!isprint((unsigned char)*Str)) - break; - } - return FALSE; -} -static FORCEINLINE ULONG -__MultiSzLen( - __in PCHAR Str, - __out PULONG Count - ) -{ - ULONG Length = 0; - if (Count) *Count = 0; - do { - for ( ; *Str; ++Str, ++Length) ; - ++Str; ++Length; - if (*Count) ++(*Count); - } while (*Str); - return Length; -} -static FORCEINLINE VOID -__DisplayMultiSz( - __in PCHAR Caller, - __in PCHAR Str - ) -{ - PCHAR Ptr; - ULONG Idx; - ULONG Len; - - for (Ptr = Str, Idx = 0; *Ptr; ++Idx) { - Len = (ULONG)strlen(Ptr); - XenIfaceDebugPrint(INFO, "|%s: [%d]=(%d)->\"%s\"\n", Caller, Idx, Len, Ptr); - Ptr += (Len + 1); - } -} - - -static DECLSPEC_NOINLINE NTSTATUS -IoctlRead( +// Cleanup store watches and event channels, called on file object close. +_IRQL_requires_(PASSIVE_LEVEL) // EvtchnFree calls KeFlushQueuedDpcs +VOID +XenIfaceCleanup( __in PXENIFACE_FDO Fdo, - __in PCHAR Buffer, - __in ULONG InLen, - __in ULONG OutLen, - __out PULONG_PTR Info + __in PFILE_OBJECT FileObject ) { - NTSTATUS status; - PCHAR Value; - ULONG Length; - - status = STATUS_INVALID_BUFFER_SIZE; - if (InLen == 0) - goto fail1; - - status = STATUS_INVALID_PARAMETER; - if (!__IsValidStr(Buffer, InLen)) - goto fail2; - - status = XENBUS_STORE(Read, &Fdo->StoreInterface, NULL, NULL, Buffer, &Value); - if (!NT_SUCCESS(status)) - goto fail3; - - Length = (ULONG)strlen(Value) + 1; - - status = STATUS_BUFFER_OVERFLOW; - if (OutLen == 0) { - XenIfaceDebugPrint(INFO, "|%s: (\"%s\")=(%d)\n", __FUNCTION__, Buffer, Length); - goto done; + PLIST_ENTRY Node; + PXENIFACE_STORE_CONTEXT StoreContext; + PXENIFACE_EVTCHN_CONTEXT EvtchnContext; + KIRQL Irql; + LIST_ENTRY ToFree; + + XenIfaceDebugPrint(TRACE, "FO %p, IRQL %d, Cpu %lu\n", FileObject, KeGetCurrentIrql(), KeGetCurrentProcessorNumber()); + + // store watches + KeAcquireSpinLock(&Fdo->StoreWatchLock, &Irql); + Node = Fdo->StoreWatchList.Flink; + while (Node->Flink != Fdo->StoreWatchList.Flink) { + StoreContext = CONTAINING_RECORD(Node, XENIFACE_STORE_CONTEXT, Entry); + + Node = Node->Flink; + if (StoreContext->FileObject != FileObject) + continue; + + XenIfaceDebugPrint(TRACE, "Store context %p\n", StoreContext); + RemoveEntryList(&StoreContext->Entry); + StoreFreeWatch(Fdo, StoreContext); } + KeReleaseSpinLock(&Fdo->StoreWatchLock, Irql); - status = STATUS_INVALID_PARAMETER; - if (OutLen < Length) - goto fail4; - - XenIfaceDebugPrint(INFO, "|%s: (\"%s\")=(%d)->\"%s\"\n", __FUNCTION__, Buffer, Length, Value); - - RtlCopyMemory(Buffer, Value, Length); - Buffer[Length - 1] = 0; - status = STATUS_SUCCESS; + // event channels + InitializeListHead(&ToFree); + KeAcquireSpinLock(&Fdo->EvtchnLock, &Irql); + Node = Fdo->EvtchnList.Flink; + while (Node->Flink != Fdo->EvtchnList.Flink) { + EvtchnContext = CONTAINING_RECORD(Node, XENIFACE_EVTCHN_CONTEXT, Entry); + + Node = Node->Flink; + if (EvtchnContext->FileObject != FileObject) + continue; + + XenIfaceDebugPrint(TRACE, "Evtchn context %p\n", EvtchnContext); + RemoveEntryList(&EvtchnContext->Entry); + // EvtchnFree requires PASSIVE_LEVEL and we're inside a lock + InsertTailList(&ToFree, &EvtchnContext->Entry); + } + KeReleaseSpinLock(&Fdo->EvtchnLock, Irql); -done: - *Info = (ULONG_PTR)Length; - XENBUS_STORE(Free, &Fdo->StoreInterface, Value); - return status; + Node = ToFree.Flink; + while (Node->Flink != ToFree.Flink) { + EvtchnContext = CONTAINING_RECORD(Node, XENIFACE_EVTCHN_CONTEXT, Entry); + Node = Node->Flink; -fail4: - XenIfaceDebugPrint(ERROR, "|%s: Fail4 (\"%s\")=(%d < %d)\n", __FUNCTION__, Buffer, OutLen, Length); - XENBUS_STORE(Free, &Fdo->StoreInterface, Value); -fail3: - XenIfaceDebugPrint(ERROR, "|%s: Fail3 (\"%s\")\n", __FUNCTION__, Buffer); -fail2: - XenIfaceDebugPrint(ERROR, "|%s: Fail2\n", __FUNCTION__); -fail1: - XenIfaceDebugPrint(ERROR, "|%s: Fail1 (%08x)\n", __FUNCTION__, status); - return status; + RemoveEntryList(&EvtchnContext->Entry); + EvtchnFree(Fdo, EvtchnContext); } - -static DECLSPEC_NOINLINE NTSTATUS -IoctlWrite( - __in PXENIFACE_FDO Fdo, - __in PCHAR Buffer, - __in ULONG InLen, - __in ULONG OutLen - ) -{ - NTSTATUS status; - PCHAR Value; - ULONG Length; - - status = STATUS_INVALID_BUFFER_SIZE; - if (InLen == 0 || OutLen != 0) - goto fail1; - - status = STATUS_INVALID_PARAMETER; - if (!__IsValidStr(Buffer, InLen)) - goto fail2; - - Length = (ULONG)strlen(Buffer) + 1; - Value = Buffer + Length; - - if (!__IsValidStr(Value, InLen - Length)) - goto fail3; - - status = XENBUS_STORE(Printf, &Fdo->StoreInterface, NULL, NULL, Buffer, Value); - if (!NT_SUCCESS(status)) - goto fail4; - - XenIfaceDebugPrint(INFO, "|%s: (\"%s\"=\"%s\")\n", __FUNCTION__, Buffer, Value); - return status; - -fail4: - XenIfaceDebugPrint(ERROR, "|%s: Fail4 (\"%s\")\n", __FUNCTION__, Value); -fail3: - XenIfaceDebugPrint(ERROR, "|%s: Fail3 (\"%s\")\n", __FUNCTION__, Buffer); -fail2: - XenIfaceDebugPrint(ERROR, "|%s: Fail2\n", __FUNCTION__); -fail1: - XenIfaceDebugPrint(ERROR, "|%s: Fail1 (%08x)\n", __FUNCTION__, status); - return status; } -static DECLSPEC_NOINLINE NTSTATUS -IoctlDirectory( +NTSTATUS +XenIfaceIoctl( __in PXENIFACE_FDO Fdo, - __in PCHAR Buffer, - __in ULONG InLen, - __in ULONG OutLen, - __out PULONG_PTR Info + __inout PIRP Irp ) { NTSTATUS status; - PCHAR Value; - ULONG Length; - ULONG Count; - - status = STATUS_INVALID_BUFFER_SIZE; - if (InLen == 0) - goto fail1; - - status = STATUS_INVALID_PARAMETER; - if (!__IsValidStr(Buffer, InLen)) - goto fail2; - - status = XENBUS_STORE(Directory, &Fdo->StoreInterface, NULL, NULL, Buffer, &Value); - if (!NT_SUCCESS(status)) - goto fail3; - - Length = __MultiSzLen(Value, &Count) + 1; + PIO_STACK_LOCATION Stack = IoGetCurrentIrpStackLocation(Irp); + PVOID Buffer = Irp->AssociatedIrp.SystemBuffer; + ULONG InLen = Stack->Parameters.DeviceIoControl.InputBufferLength; + ULONG OutLen = Stack->Parameters.DeviceIoControl.OutputBufferLength; - status = STATUS_BUFFER_OVERFLOW; - if (OutLen == 0) { - XenIfaceDebugPrint(INFO, "|%s: (\"%s\")=(%d)(%d)\n", __FUNCTION__, Buffer, Length, Count); + status = STATUS_DEVICE_NOT_READY; + if (Fdo->InterfacesAcquired == FALSE) goto done; - } - status = STATUS_INVALID_PARAMETER; - if (OutLen < Length) - goto fail4; + switch (Stack->Parameters.DeviceIoControl.IoControlCode) { + // store + case IOCTL_XENIFACE_STORE_READ: + status = IoctlStoreRead(Fdo, (PCHAR)Buffer, InLen, OutLen, &Irp->IoStatus.Information); + break; - XenIfaceDebugPrint(INFO, "|%s: (\"%s\")=(%d)(%d)\n", __FUNCTION__, Buffer, Length, Count); -#if DBG - __DisplayMultiSz(__FUNCTION__, Value); -#endif + case IOCTL_XENIFACE_STORE_WRITE: + status = IoctlStoreWrite(Fdo, (PCHAR)Buffer, InLen, OutLen); + break; - RtlCopyMemory(Buffer, Value, Length); - Buffer[Length - 2] = 0; - Buffer[Length - 1] = 0; - status = STATUS_SUCCESS; + case IOCTL_XENIFACE_STORE_DIRECTORY: + status = IoctlStoreDirectory(Fdo, (PCHAR)Buffer, InLen, OutLen, &Irp->IoStatus.Information); + break; -done: - *Info = (ULONG_PTR)Length; - XENBUS_STORE(Free, &Fdo->StoreInterface, Value); - return status; + case IOCTL_XENIFACE_STORE_REMOVE: + status = IoctlStoreRemove(Fdo, (PCHAR)Buffer, InLen, OutLen); + break; -fail4: - XenIfaceDebugPrint(ERROR, "|%s: Fail4 (\"%s\")=(%d < %d)\n", __FUNCTION__, Buffer, OutLen, Length); - XENBUS_STORE(Free, &Fdo->StoreInterface, Value); -fail3: - XenIfaceDebugPrint(ERROR, "|%s: Fail3 (\"%s\")\n", __FUNCTION__, Buffer); -fail2: - XenIfaceDebugPrint(ERROR, "|%s: Fail2\n", __FUNCTION__); -fail1: - XenIfaceDebugPrint(ERROR, "|%s: Fail1 (%08x)\n", __FUNCTION__, status); - return status; -} + case IOCTL_XENIFACE_STORE_SET_PERMISSIONS: + status = IoctlStoreSetPermissions(Fdo, Buffer, InLen, OutLen); + break; -static DECLSPEC_NOINLINE NTSTATUS -IoctlRemove( - __in PXENIFACE_FDO Fdo, - __in PCHAR Buffer, - __in ULONG InLen, - __in ULONG OutLen - ) -{ - NTSTATUS status; + case IOCTL_XENIFACE_STORE_ADD_WATCH: + status = IoctlStoreAddWatch(Fdo, Buffer, InLen, OutLen, Stack->FileObject, &Irp->IoStatus.Information); + break; - status = STATUS_INVALID_BUFFER_SIZE; - if (InLen == 0 || OutLen != 0) - goto fail1; + case IOCTL_XENIFACE_STORE_REMOVE_WATCH: + status = IoctlStoreRemoveWatch(Fdo, Buffer, InLen, OutLen, Stack->FileObject); + break; - status = STATUS_INVALID_PARAMETER; - if (!__IsValidStr(Buffer, InLen)) - goto fail2; + // evtchn + case IOCTL_XENIFACE_EVTCHN_BIND_UNBOUND: + status = IoctlEvtchnBindUnbound(Fdo, Buffer, InLen, OutLen, Stack->FileObject, &Irp->IoStatus.Information); + break; - status = XENBUS_STORE(Remove, &Fdo->StoreInterface, NULL, NULL, Buffer); - if (!NT_SUCCESS(status)) - goto fail3; + case IOCTL_XENIFACE_EVTCHN_BIND_INTERDOMAIN: + status = IoctlEvtchnBindInterdomain(Fdo, Buffer, InLen, OutLen, Stack->FileObject, &Irp->IoStatus.Information); + break; - XenIfaceDebugPrint(INFO, "|%s: (\"%s\")\n", __FUNCTION__, Buffer); - return status; + case IOCTL_XENIFACE_EVTCHN_CLOSE: + status = IoctlEvtchnClose(Fdo, Buffer, InLen, OutLen, Stack->FileObject); + break; -fail3: - XenIfaceDebugPrint(ERROR, "|%s: Fail3 (\"%s\")\n", __FUNCTION__, Buffer); -fail2: - XenIfaceDebugPrint(ERROR, "|%s: Fail2\n", __FUNCTION__); -fail1: - XenIfaceDebugPrint(ERROR, "|%s: Fail1 (%08x)\n", __FUNCTION__, status); - return status; -} + case IOCTL_XENIFACE_EVTCHN_NOTIFY: + status = IoctlEvtchnNotify(Fdo, Buffer, InLen, OutLen, Stack->FileObject); + break; -NTSTATUS -XenIFaceIoctl( - __in PXENIFACE_FDO Fdo, - __in PIRP Irp - ) -{ - NTSTATUS status; - PIO_STACK_LOCATION Stack = IoGetCurrentIrpStackLocation(Irp); - PVOID Buffer = Irp->AssociatedIrp.SystemBuffer; - ULONG InLen = Stack->Parameters.DeviceIoControl.InputBufferLength; - ULONG OutLen = Stack->Parameters.DeviceIoControl.OutputBufferLength; + case IOCTL_XENIFACE_EVTCHN_UNMASK: + status = IoctlEvtchnUnmask(Fdo, Buffer, InLen, OutLen, Stack->FileObject); + break; - status = STATUS_DEVICE_NOT_READY; - if (Fdo->InterfacesAcquired == FALSE) - goto done; + // gnttab + case IOCTL_XENIFACE_GNTTAB_PERMIT_FOREIGN_ACCESS: + status = IoctlGnttabPermitForeignAccess(Fdo, Buffer, InLen, OutLen, Irp); + break; - switch (Stack->Parameters.DeviceIoControl.IoControlCode) { - case IOCTL_XENIFACE_STORE_READ: - status = IoctlRead(Fdo, (PCHAR)Buffer, InLen, OutLen, &Irp->IoStatus.Information); + case IOCTL_XENIFACE_GNTTAB_GET_GRANT_RESULT: + status = IoctlGnttabGetGrantResult(Fdo, Buffer, InLen, OutLen, &Irp->IoStatus.Information); break; - case IOCTL_XENIFACE_STORE_WRITE: - status = IoctlWrite(Fdo, (PCHAR)Buffer, InLen, OutLen); + case IOCTL_XENIFACE_GNTTAB_REVOKE_FOREIGN_ACCESS: + status = IoctlGnttabRevokeForeignAccess(Fdo, Buffer, InLen, OutLen); break; - case IOCTL_XENIFACE_STORE_DIRECTORY: - status = IoctlDirectory(Fdo, (PCHAR)Buffer, InLen, OutLen, &Irp->IoStatus.Information); + case IOCTL_XENIFACE_GNTTAB_MAP_FOREIGN_PAGES: + status = IoctlGnttabMapForeignPages(Fdo, Buffer, InLen, OutLen, Irp); break; - case IOCTL_XENIFACE_STORE_REMOVE: - status = IoctlRemove(Fdo, (PCHAR)Buffer, InLen, OutLen); + case IOCTL_XENIFACE_GNTTAB_GET_MAP_RESULT: + status = IoctlGnttabGetMapResult(Fdo, Buffer, InLen, OutLen, &Irp->IoStatus.Information); + break; + + case IOCTL_XENIFACE_GNTTAB_UNMAP_FOREIGN_PAGES: + status = IoctlGnttabUnmapForeignPages(Fdo, Buffer, InLen, OutLen); break; default: @@ -327,6 +196,7 @@ done: Irp->IoStatus.Status = status; + if (status != STATUS_PENDING) IoCompleteRequest(Irp, IO_NO_INCREMENT); return status; diff --git a/src/xeniface/ioctls.h b/src/xeniface/ioctls.h index 7ee7801..00e11e4 100644 --- a/src/xeniface/ioctls.h +++ b/src/xeniface/ioctls.h @@ -33,11 +33,322 @@ #define _IOCTLS_H_ #define XENIFACE_KERNEL_MODE +#include "xeniface_ioctls.h" +typedef enum _XENIFACE_CONTEXT_TYPE { + XENIFACE_CONTEXT_GRANT = 1, + XENIFACE_CONTEXT_MAP +} XENIFACE_CONTEXT_TYPE; + +typedef struct _XENIFACE_CONTEXT_ID { + XENIFACE_CONTEXT_TYPE Type; + ULONG RequestId; + PEPROCESS Process; +} XENIFACE_CONTEXT_ID, *PXENIFACE_CONTEXT_ID; + +typedef struct _XENIFACE_STORE_CONTEXT { + LIST_ENTRY Entry; + PXENBUS_STORE_WATCH Watch; + PKEVENT Event; + PVOID FileObject; +} XENIFACE_STORE_CONTEXT, *PXENIFACE_STORE_CONTEXT; + +typedef struct _XENIFACE_EVTCHN_CONTEXT { + LIST_ENTRY Entry; + PXENBUS_EVTCHN_CHANNEL Channel; + ULONG LocalPort; + PKEVENT Event; + PXENIFACE_FDO Fdo; + BOOLEAN Active; + PVOID FileObject; +} XENIFACE_EVTCHN_CONTEXT, *PXENIFACE_EVTCHN_CONTEXT; + +typedef struct _XENIFACE_GRANT_CONTEXT { + XENIFACE_CONTEXT_ID Id; + LIST_ENTRY Entry; + PXENBUS_GNTTAB_ENTRY *Grants; + USHORT RemoteDomain; + ULONG NumberPages; + XENIFACE_GNTTAB_PAGE_FLAGS Flags; + ULONG NotifyOffset; + ULONG NotifyPort; + PVOID KernelVa; + PVOID UserVa; + PMDL Mdl; +} XENIFACE_GRANT_CONTEXT, *PXENIFACE_GRANT_CONTEXT; + +typedef struct _XENIFACE_MAP_CONTEXT { + XENIFACE_CONTEXT_ID Id; + LIST_ENTRY Entry; + USHORT RemoteDomain; + ULONG NumberPages; + XENIFACE_GNTTAB_PAGE_FLAGS Flags; + ULONG NotifyOffset; + ULONG NotifyPort; + PHYSICAL_ADDRESS Address; + PVOID KernelVa; + PVOID UserVa; + PMDL Mdl; +} XENIFACE_MAP_CONTEXT, *PXENIFACE_MAP_CONTEXT; + +NTSTATUS +XenIfaceIoctl( + __in PXENIFACE_FDO Fdo, + __inout PIRP Irp + ); + +_IRQL_requires_(PASSIVE_LEVEL) +VOID +XenIfaceCleanup( + __in PXENIFACE_FDO Fdo, + __in PFILE_OBJECT FileObject + ); + +DECLSPEC_NOINLINE NTSTATUS -XenIFaceIoctl( +IoctlStoreRead( + __in PXENIFACE_FDO Fdo, + __in PCHAR Buffer, + __in ULONG InLen, + __in ULONG OutLen, + __out PULONG_PTR Info + ); + +DECLSPEC_NOINLINE +NTSTATUS +IoctlStoreWrite( + __in PXENIFACE_FDO Fdo, + __in PCHAR Buffer, + __in ULONG InLen, + __in ULONG OutLen + ); + +DECLSPEC_NOINLINE +NTSTATUS +IoctlStoreDirectory( + __in PXENIFACE_FDO Fdo, + __in PCHAR Buffer, + __in ULONG InLen, + __in ULONG OutLen, + __out PULONG_PTR Info + ); + +DECLSPEC_NOINLINE +NTSTATUS +IoctlStoreRemove( + __in PXENIFACE_FDO Fdo, + __in PCHAR Buffer, + __in ULONG InLen, + __in ULONG OutLen + ); + +DECLSPEC_NOINLINE +NTSTATUS +IoctlStoreSetPermissions( + __in PXENIFACE_FDO Fdo, + __in PVOID Buffer, + __in ULONG InLen, + __in ULONG OutLen + ); + +DECLSPEC_NOINLINE +NTSTATUS +IoctlStoreAddWatch( + __in PXENIFACE_FDO Fdo, + __in PVOID Buffer, + __in ULONG InLen, + __in ULONG OutLen, + __in PFILE_OBJECT FileObject, + __out PULONG_PTR Info + ); + +DECLSPEC_NOINLINE +NTSTATUS +IoctlStoreRemoveWatch( + __in PXENIFACE_FDO Fdo, + __in PVOID Buffer, + __in ULONG InLen, + __in ULONG OutLen, + __in PFILE_OBJECT FileObject + ); + +_IRQL_requires_max_(DISPATCH_LEVEL) +VOID +StoreFreeWatch( + __in PXENIFACE_FDO Fdo, + __inout PXENIFACE_STORE_CONTEXT Context + ); + +DECLSPEC_NOINLINE +NTSTATUS +IoctlEvtchnBindUnbound( + __in PXENIFACE_FDO Fdo, + __in PVOID Buffer, + __in ULONG InLen, + __in ULONG OutLen, + __in PFILE_OBJECT FileObject, + __out PULONG_PTR Info + ); + +DECLSPEC_NOINLINE +NTSTATUS +IoctlEvtchnBindInterdomain( + __in PXENIFACE_FDO Fdo, + __in PVOID Buffer, + __in ULONG InLen, + __in ULONG OutLen, + __in PFILE_OBJECT FileObject, + __out PULONG_PTR Info + ); + +DECLSPEC_NOINLINE +NTSTATUS +IoctlEvtchnClose( + __in PXENIFACE_FDO Fdo, + __in PVOID Buffer, + __in ULONG InLen, + __in ULONG OutLen, + __in PFILE_OBJECT FileObject + ); + +DECLSPEC_NOINLINE +NTSTATUS +IoctlEvtchnNotify( + __in PXENIFACE_FDO Fdo, + __in PVOID Buffer, + __in ULONG InLen, + __in ULONG OutLen, + __in PFILE_OBJECT FileObject + ); + +DECLSPEC_NOINLINE +NTSTATUS +IoctlEvtchnUnmask( __in PXENIFACE_FDO Fdo, - __in PIRP Irp + __in PVOID Buffer, + __in ULONG InLen, + __in ULONG OutLen, + __in PFILE_OBJECT FileObject + ); + +_Requires_lock_not_held_(Fdo->EvtchnLock) +DECLSPEC_NOINLINE +NTSTATUS +EvtchnNotify( + __in PXENIFACE_FDO Fdo, + __in ULONG LocalPort, + __in_opt PFILE_OBJECT FileObject + ); + +_Function_class_(KDEFERRED_ROUTINE) +_IRQL_requires_(DISPATCH_LEVEL) +_IRQL_requires_same_ +VOID +EvtchnNotificationDpc( + __in PKDPC Dpc, + __in_opt PVOID Context, + __in_opt PVOID Argument1, + __in_opt PVOID Argument2 + ); + +_IRQL_requires_(PASSIVE_LEVEL) +VOID +EvtchnFree( + __in PXENIFACE_FDO Fdo, + __inout PXENIFACE_EVTCHN_CONTEXT Context + ); + +DECLSPEC_NOINLINE +NTSTATUS +IoctlGnttabPermitForeignAccess( + __in PXENIFACE_FDO Fdo, + __in PVOID Buffer, + __in ULONG InLen, + __in ULONG OutLen, + __inout PIRP Irp + ); + +DECLSPEC_NOINLINE +NTSTATUS +IoctlGnttabGetGrantResult( + __in PXENIFACE_FDO Fdo, + __in PVOID Buffer, + __in ULONG InLen, + __in ULONG OutLen, + __out PULONG_PTR Info + ); + +DECLSPEC_NOINLINE +NTSTATUS +IoctlGnttabRevokeForeignAccess( + __in PXENIFACE_FDO Fdo, + __in PVOID Buffer, + __in ULONG InLen, + __in ULONG OutLen + ); + +DECLSPEC_NOINLINE +NTSTATUS +IoctlGnttabMapForeignPages( + __in PXENIFACE_FDO Fdo, + __in PVOID Buffer, + __in ULONG InLen, + __in ULONG OutLen, + __inout PIRP Irp + ); + +DECLSPEC_NOINLINE +NTSTATUS +IoctlGnttabGetMapResult( + __in PXENIFACE_FDO Fdo, + __in PVOID Buffer, + __in ULONG InLen, + __in ULONG OutLen, + __out PULONG_PTR Info + ); + +DECLSPEC_NOINLINE +NTSTATUS +IoctlGnttabUnmapForeignPages( + __in PXENIFACE_FDO Fdo, + __in PVOID Buffer, + __in ULONG InLen, + __in ULONG OutLen + ); + +_Acquires_exclusive_lock_(((PXENIFACE_FDO)Argument)->GnttabCacheLock) +_IRQL_requires_(DISPATCH_LEVEL) +VOID +GnttabAcquireLock( + __in PVOID Argument + ); + +_Releases_exclusive_lock_(((PXENIFACE_FDO)Argument)->GnttabCacheLock) +_IRQL_requires_(DISPATCH_LEVEL) +VOID +GnttabReleaseLock( + __in PVOID Argument + ); + +_Function_class_(IO_WORKITEM_ROUTINE) +VOID +CompleteGnttabIrp( + __in PDEVICE_OBJECT DeviceObject, + __in_opt PVOID Context + ); + +_IRQL_requires_max_(APC_LEVEL) +VOID +GnttabFreeGrant( + __in PXENIFACE_FDO Fdo, + __inout PXENIFACE_GRANT_CONTEXT Context + ); + +_IRQL_requires_max_(APC_LEVEL) +VOID +GnttabFreeMap( + __in PXENIFACE_FDO Fdo, + __inout PXENIFACE_MAP_CONTEXT Context ); #endif // _IOCTLS_H_ diff --git a/src/xeniface/irp_queue.c b/src/xeniface/irp_queue.c new file mode 100644 index 0000000..c3bf86c --- /dev/null +++ b/src/xeniface/irp_queue.c @@ -0,0 +1,131 @@ +#include "driver.h" +#include "irp_queue.h" +#include "log.h" +#include "ioctls.h" + +// Cancel-safe IRP queue implementation + +NTSTATUS +CsqInsertIrpEx( + _In_ PIO_CSQ Csq, + _In_ PIRP Irp, + _In_ PVOID InsertContext // PXENIFACE_CONTEXT_ID + ) +{ + PXENIFACE_FDO Fdo; + + Fdo = CONTAINING_RECORD(Csq, XENIFACE_FDO, IrpQueue); + + // Fail if a request with the same ID already exists. + if (CsqPeekNextIrp(Csq, NULL, InsertContext) != NULL) + return STATUS_INVALID_PARAMETER; + + InsertTailList(&Fdo->IrpList, &Irp->Tail.Overlay.ListEntry); + return STATUS_SUCCESS; +} + +VOID +CsqRemoveIrp( + _In_ PIO_CSQ Csq, + _In_ PIRP Irp + ) +{ + UNREFERENCED_PARAMETER(Csq); + + RemoveEntryList(&Irp->Tail.Overlay.ListEntry); +} + +PIRP +CsqPeekNextIrp( + _In_ PIO_CSQ Csq, + _In_opt_ PIRP Irp, + _In_opt_ PVOID PeekContext // PXENIFACE_CONTEXT_ID + ) +{ + PXENIFACE_FDO Fdo; + PIRP NextIrp = NULL; + PLIST_ENTRY Head, NextEntry; + PXENIFACE_CONTEXT_ID Id, TargetId; + + Fdo = CONTAINING_RECORD(Csq, XENIFACE_FDO, IrpQueue); + TargetId = PeekContext; + Head = &Fdo->IrpList; + + // If the IRP is NULL, we will start peeking from the list head, + // else we will start from that IRP onwards. This is done under the + // assumption that new IRPs are always inserted at the tail. + + if (Irp == NULL) { + NextEntry = Head->Flink; + } else { + NextEntry = Irp->Tail.Overlay.ListEntry.Flink; + } + + while (NextEntry != Head) { + NextIrp = CONTAINING_RECORD(NextEntry, IRP, Tail.Overlay.ListEntry); + + if (PeekContext) { + Id = NextIrp->Tail.Overlay.DriverContext[0]; + if (Id->RequestId == TargetId->RequestId && Id->Process == TargetId->Process) + break; + } else { + break; + } + NextIrp = NULL; + NextEntry = NextEntry->Flink; + } + + return NextIrp; +} + +_IRQL_raises_(DISPATCH_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_Acquires_lock_(CONTAINING_RECORD(Csq, XENIFACE_FDO, IrpQueue)->IrpQueueLock) +VOID +CsqAcquireLock( + _In_ PIO_CSQ Csq, + _Out_ _At_(*Irql, _Post_ _IRQL_saves_) PKIRQL Irql + ) +{ + PXENIFACE_FDO Fdo; + + Fdo = CONTAINING_RECORD(Csq, XENIFACE_FDO, IrpQueue); + + KeAcquireSpinLock(&Fdo->IrpQueueLock, Irql); +} + +_IRQL_requires_(DISPATCH_LEVEL) +_Releases_lock_(CONTAINING_RECORD(Csq, XENIFACE_FDO, IrpQueue)->IrpQueueLock) +VOID +CsqReleaseLock( + _In_ PIO_CSQ Csq, + _In_ _IRQL_restores_ KIRQL Irql + ) +{ + PXENIFACE_FDO Fdo; + + Fdo = CONTAINING_RECORD(Csq, XENIFACE_FDO, IrpQueue); + + KeReleaseSpinLock(&Fdo->IrpQueueLock, Irql); +} + +_IRQL_requires_max_(DISPATCH_LEVEL) +VOID +CsqCompleteCanceledIrp( + _In_ PIO_CSQ Csq, + _In_ PIRP Irp + ) +{ + PXENIFACE_FDO Fdo = CONTAINING_RECORD(Csq, XENIFACE_FDO, IrpQueue); + PIO_WORKITEM WorkItem; + + XenIfaceDebugPrint(TRACE, "Irp %p, IRQL %d\n", + Irp, KeGetCurrentIrql()); + + // This is not guaranteed to run at PASSIVE_LEVEL, so queue a work item + // to perform actual cleanup/IRP completion. + + WorkItem = IoAllocateWorkItem(Fdo->Dx->DeviceObject); + Irp->Tail.Overlay.DriverContext[1] = WorkItem; // store so the work item can free it + IoQueueWorkItem(WorkItem, CompleteGnttabIrp, DelayedWorkQueue, Irp); +} diff --git a/src/xeniface/irp_queue.h b/src/xeniface/irp_queue.h new file mode 100644 index 0000000..746ee19 --- /dev/null +++ b/src/xeniface/irp_queue.h @@ -0,0 +1,50 @@ +#ifndef _IRP_QUEUE_H_ +#define _IRP_QUEUE_H_ + +#include <ntddk.h> + +NTSTATUS +CsqInsertIrpEx( + _In_ PIO_CSQ Csq, + _In_ PIRP Irp, + _In_ PVOID InsertContext + ); + +VOID +CsqRemoveIrp( + _In_ PIO_CSQ Csq, + _In_ PIRP Irp + ); + +PIRP +CsqPeekNextIrp( + _In_ PIO_CSQ Csq, + _In_opt_ PIRP Irp, + _In_opt_ PVOID PeekContext // PXENIFACE_CONTEXT_ID + ); + +_IRQL_raises_(DISPATCH_LEVEL) +_IRQL_requires_max_(DISPATCH_LEVEL) +_Acquires_lock_(CONTAINING_RECORD(Csq, XENIFACE_FDO, IrpQueue)->IrpQueueLock) +VOID +CsqAcquireLock( + _In_ PIO_CSQ Csq, + _Out_ _At_(*Irql, _Post_ _IRQL_saves_) PKIRQL Irql + ); + +_IRQL_requires_(DISPATCH_LEVEL) +_Releases_lock_(CONTAINING_RECORD(Csq, XENIFACE_FDO, IrpQueue)->IrpQueueLock) +VOID +CsqReleaseLock( + _In_ PIO_CSQ Csq, + _In_ _IRQL_restores_ KIRQL Irql + ); + +_IRQL_requires_max_(DISPATCH_LEVEL) +VOID +CsqCompleteCanceledIrp( + _In_ PIO_CSQ Csq, + _In_ PIRP Irp + ); + +#endif diff --git a/vs2013/xeniface/xeniface.vcxproj b/vs2013/xeniface/xeniface.vcxproj index fea2ad1..14f1338 100644 --- a/vs2013/xeniface/xeniface.vcxproj +++ b/vs2013/xeniface/xeniface.vcxproj @@ -131,6 +131,10 @@ <ClCompile Include="../../src/xeniface/fdo.c" /> <ClCompile Include="../../src/xeniface/registry.c" /> <ClCompile Include="../../src\xeniface/thread.c" /> + <ClCompile Include="..\..\src\xeniface\ioctl_evtchn.c" /> + <ClCompile Include="..\..\src\xeniface\ioctl_gnttab.c" /> + <ClCompile Include="..\..\src\xeniface\ioctl_store.c" /> + <ClCompile Include="..\..\src\xeniface\irp_queue.c" /> </ItemGroup> <ItemGroup> <Mofcomp Include="../../src/xeniface/wmi.mof"> -- 1.8.1.msysgit.1 _______________________________________________ win-pv-devel mailing list win-pv-devel@xxxxxxxxxxxxxxxxxxxx http://lists.xenproject.org/cgi-bin/mailman/listinfo/win-pv-devel
|
Lists.xenproject.org is hosted with RackSpace, monitoring our |