This patch implements new store, evtchn and gnttab IOCTLs.
Handlers are split into separate files for readability.
Signed-off-by: Rafal Wojdyla <omeg@invisiblethingslab.com>
/* Copyright (c) Citrix Systems Inc.
+ * Copyright (c) Rafal Wojdyla <omeg@invisiblethingslab.com>
* All rights reserved.
*
* Redistribution and use in source and binary forms,
#include <stdlib.h>
#include <store_interface.h>
-
+#include <evtchn_interface.h>
+#include <gnttab_interface.h>
#include <suspend_interface.h>
#include "ioctls.h"
#include "wmi.h"
#include "xeniface_ioctls.h"
+#include "irp_queue.h"
#define FDO_POOL 'ODF'
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;
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);
__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");
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;
}
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;
}
break;
case IRP_MJ_DEVICE_CONTROL:
- status = XenIFaceIoctl(Fdo, Irp);
+ status = XenIfaceIoctl(Fdo, Irp);
break;
case IRP_MJ_SYSTEM_CONTROL:
WCHAR Name[MAXNAMELEN * sizeof (WCHAR)];
ULONG Size;
NTSTATUS status;
+ ULONG ProcessorCount;
+ ULONG Index;
#pragma prefast(suppress:28197) // Possibly leaking memory 'FunctionDeviceObject'
status = IoCreateDevice(DriverObject,
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;
status = ThreadCreate(FdoRegistryThreadHandler, Fdo, &Fdo->registryThread);
if (!NT_SUCCESS(status))
- goto fail11;
+ 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 = KeQueryMaximumProcessorCountEx(ALL_PROCESSOR_GROUPS);
+
+ status = STATUS_NO_MEMORY;
+ Fdo->EvtchnDpc = __FdoAllocate(sizeof (KDPC) * ProcessorCount);
+ if (Fdo->EvtchnDpc == NULL)
+ goto fail15;
+
+ for (Index = 0; Index < ProcessorCount; Index++) {
+ PROCESSOR_NUMBER ProcNumber;
+
+ status = KeGetProcessorNumberFromIndex(Index, &ProcNumber);
+ ASSERT(NT_SUCCESS(status));
+
+ KeInitializeDpc(&Fdo->EvtchnDpc[Index], EvtchnNotificationDpc, NULL);
+ status = KeSetTargetProcessorDpcEx(&Fdo->EvtchnDpc[Index], &ProcNumber);
+ ASSERT(NT_SUCCESS(status));
+ }
Info("%p (%s)\n",
FunctionDeviceObject,
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");
)
{
PXENIFACE_DX Dx = Fdo->Dx;
- PDEVICE_OBJECT FunctionDeviceObject = Dx->DeviceObject;
+ PDEVICE_OBJECT FunctionDeviceObject = Dx->DeviceObject;
+ ULONG ProcessorCount;
ASSERT(IsListEmpty(&Dx->ListEntry));
ASSERT3U(Fdo->References, ==, 0);
Dx->Fdo = NULL;
+ ProcessorCount = KeQueryMaximumProcessorCountEx(ALL_PROCESSOR_GROUPS);
+ RtlZeroMemory(Fdo->EvtchnDpc, sizeof (KDPC) * ProcessorCount);
+ __FdoFree(Fdo->EvtchnDpc);
+
+ 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));
#include <ntifs.h>
#include <store_interface.h>
+#include <evtchn_interface.h>
+#include <gnttab_interface.h>
#include <suspend_interface.h>
#include <shared_info_interface.h>
FDO_RESOURCE Resource[RESOURCE_COUNT];
-
XENBUS_STORE_INTERFACE StoreInterface;
-
XENBUS_SUSPEND_INTERFACE SuspendInterface;
-
XENBUS_SHARED_INFO_INTERFACE SharedInfoInterface;
-
+ XENBUS_EVTCHN_INTERFACE EvtchnInterface;
+ XENBUS_GNTTAB_INTERFACE GnttabInterface;
PXENBUS_SUSPEND_CALLBACK SuspendCallbackLate;
- BOOLEAN InterfacesAcquired;
+ BOOLEAN InterfacesAcquired;
- #define MAX_SESSIONS (65536)
+ KSPIN_LOCK StoreWatchLock;
+ LIST_ENTRY StoreWatchList;
+
+ KSPIN_LOCK EvtchnLock;
+ LIST_ENTRY EvtchnList;
+ PKDPC EvtchnDpc;
- int WmiReady;
+ KSPIN_LOCK GnttabCacheLock;
- USHORT Sessions;
- FAST_MUTEX SessionLock;
- LIST_ENTRY SessionHead;
+ IO_CSQ IrpQueue;
+ KSPIN_LOCK IrpQueueLock;
+ LIST_ENTRY IrpList;
- PXENIFACE_THREAD registryThread;
- KEVENT registryWriteEvent;
+ PXENBUS_GNTTAB_CACHE GnttabCache;
+ #define MAX_SESSIONS (65536)
+
+ int WmiReady;
+
+ USHORT Sessions;
+ FAST_MUTEX SessionLock;
+ LIST_ENTRY SessionHead;
- UNICODE_STRING SuggestedInstanceName;
+ PXENIFACE_THREAD registryThread;
+ KEVENT registryWriteEvent;
- UNICODE_STRING InterfaceName;
+ UNICODE_STRING SuggestedInstanceName;
+
+ UNICODE_STRING InterfaceName;
} XENIFACE_FDO, *PXENIFACE_FDO;
IN PIRP Irp
);
-
-
#endif // _XENIFACE_FDO_H
--- /dev/null
+/* Copyright (c) Rafal Wojdyla <omeg@invisiblethingslab.com>
+ * 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 "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 = Argument1;
+
+ UNREFERENCED_PARAMETER(Dpc);
+ UNREFERENCED_PARAMETER(_Context);
+ UNREFERENCED_PARAMETER(Argument2);
+
+ ASSERT(Context);
+
+#if DBG
+ XenIfaceDebugPrint(INFO, "Channel %p, LocalPort %d, Cpu %lu\n",
+ Context->Channel, Context->LocalPort, KeGetCurrentProcessorNumber());
+#endif
+ 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 = Argument;
+ PROCESSOR_NUMBER ProcNumber;
+ ULONG ProcIndex;
+
+ UNREFERENCED_PARAMETER(Interrupt);
+ ASSERT(Context);
+
+ KeGetCurrentProcessorNumberEx(&ProcNumber);
+ ProcIndex = KeGetProcessorIndexFromNumber(&ProcNumber);
+ if (!KeInsertQueueDpc(&Context->Fdo->EvtchnDpc[ProcIndex], Context, NULL)) {
+ XenIfaceDebugPrint(TRACE, "NOT INSERTED: Context %p, Port %lu, FO %p, Cpu %lu\n",
+ Context, Context->LocalPort, Context->FileObject, ProcIndex);
+ }
+
+ 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);
+
+ 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 &&
+ FileObject != Context->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);
+
+ 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);
+
+ 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;
+ 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);
+ status = STATUS_NOT_FOUND;
+ Context = EvtchnFindChannel(Fdo, In->LocalPort, FileObject);
+ if (Context == NULL)
+ goto fail2;
+
+ RemoveEntryList(&Context->Entry);
+ KeReleaseSpinLock(&Fdo->EvtchnLock, Irql);
+ EvtchnFree(Fdo, Context);
+
+ return STATUS_SUCCESS;
+
+fail2:
+ XenIfaceDebugPrint(ERROR, "Fail2\n");
+ KeReleaseSpinLock(&Fdo->EvtchnLock, Irql);
+
+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;
+ 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:
+ XenIfaceDebugPrint(ERROR, "Fail1 (%08x)\n", status);
+ KeReleaseSpinLock(&Fdo->EvtchnLock, Irql);
+ 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;
+ 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:
+ XenIfaceDebugPrint(ERROR, "Fail2\n");
+ KeReleaseSpinLock(&Fdo->EvtchnLock, Irql);
+
+fail1:
+ XenIfaceDebugPrint(ERROR, "Fail1 (%08x)\n", status);
+ return status;
+}
--- /dev/null
+/* Copyright (c) Rafal Wojdyla <omeg@invisiblethingslab.com>
+ * 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 "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;
+ PXENIFACE_GNTTAB_PERMIT_FOREIGN_ACCESS_OUT Out = Irp->UserBuffer;
+ PXENIFACE_GRANT_CONTEXT Context;
+ ULONG Page;
+
+ status = STATUS_INVALID_BUFFER_SIZE;
+ if (InLen != sizeof(XENIFACE_GNTTAB_PERMIT_FOREIGN_ACCESS_IN))
+ goto fail1;
+
+ // This IOCTL uses METHOD_NEITHER so we directly access user memory.
+ status = __CaptureUserBuffer(Buffer, InLen, &In);
+ if (!NT_SUCCESS(status))
+ goto fail2;
+
+ status = STATUS_INVALID_PARAMETER;
+ if (In->NumberPages == 0 ||
+ In->NumberPages > 1024 * 1024) {
+ goto fail3;
+ }
+
+ if ((In->Flags & XENIFACE_GNTTAB_USE_NOTIFY_OFFSET) &&
+ (In->NotifyOffset >= In->NumberPages * PAGE_SIZE)) {
+ goto fail4;
+ }
+
+ status = STATUS_INVALID_BUFFER_SIZE;
+ if (OutLen != (ULONG)FIELD_OFFSET(XENIFACE_GNTTAB_PERMIT_FOREIGN_ACCESS_OUT, References[In->NumberPages]))
+ goto fail5;
+
+ status = STATUS_NO_MEMORY;
+ Context = ExAllocatePoolWithTag(NonPagedPool, sizeof(XENIFACE_GRANT_CONTEXT), XENIFACE_POOL_TAG);
+ if (Context == NULL)
+ goto fail6;
+
+ 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 for this process.
+ // 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 fail7;
+
+ status = STATUS_NO_MEMORY;
+ Context->Grants = ExAllocatePoolWithTag(NonPagedPool, Context->NumberPages * sizeof(PXENBUS_GNTTAB_ENTRY), XENIFACE_POOL_TAG);
+ if (Context->Grants == NULL)
+ goto fail8;
+
+ 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 fail9;
+
+ RtlZeroMemory(Context->KernelVa, Context->NumberPages * PAGE_SIZE);
+ Context->Mdl = IoAllocateMdl(Context->KernelVa, Context->NumberPages * PAGE_SIZE, FALSE, FALSE, NULL);
+ if (Context->Mdl == NULL)
+ goto fail10;
+
+ 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 fail11;
+ }
+
+ // 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 fail12;
+ }
+
+ status = STATUS_UNSUCCESSFUL;
+ if (Context->UserVa == NULL)
+ goto fail13;
+
+ XenIfaceDebugPrint(TRACE, "< Context %p, Irp %p, KernelVa %p, UserVa %p\n",
+ Context, Irp, Context->KernelVa, Context->UserVa);
+
+ // Pass the result to user mode.
+#pragma prefast(suppress: 6320) // we want to catch all exceptions
+ try {
+ ProbeForWrite(Out, OutLen, 1);
+ Out->Address = Context->UserVa;
+
+ for (Page = 0; Page < Context->NumberPages; Page++) {
+ Out->References[Page] = XENBUS_GNTTAB(GetReference,
+ &Fdo->GnttabInterface,
+ Context->Grants[Page]);
+ }
+ } except(EXCEPTION_EXECUTE_HANDLER) {
+ status = GetExceptionCode();
+ XenIfaceDebugPrint(ERROR, "Exception 0x%lx while probing/writing output buffer at %p, size 0x%lx\n", status, Out, OutLen);
+ goto fail14;
+ }
+
+ // Insert the IRP/context into the pending queue.
+ // This also checks (again) if the request ID is unique for the calling process.
+ Irp->Tail.Overlay.DriverContext[0] = &Context->Id;
+ status = IoCsqInsertIrpEx(&Fdo->IrpQueue, Irp, NULL, &Context->Id);
+ if (!NT_SUCCESS(status))
+ goto fail15;
+
+ __FreeCapturedBuffer(In);
+
+ return STATUS_PENDING;
+
+fail15:
+ XenIfaceDebugPrint(ERROR, "Fail15\n");
+
+fail14:
+ XenIfaceDebugPrint(ERROR, "Fail14\n");
+ MmUnmapLockedPages(Context->UserVa, Context->Mdl);
+
+fail13:
+ XenIfaceDebugPrint(ERROR, "Fail13\n");
+
+fail12:
+ XenIfaceDebugPrint(ERROR, "Fail12\n");
+
+fail11:
+ XenIfaceDebugPrint(ERROR, "Fail11: 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);
+
+fail10:
+ XenIfaceDebugPrint(ERROR, "Fail10\n");
+ ExFreePoolWithTag(Context->KernelVa, XENIFACE_POOL_TAG);
+
+fail9:
+ XenIfaceDebugPrint(ERROR, "Fail9\n");
+ ExFreePoolWithTag(Context->Grants, XENIFACE_POOL_TAG);
+
+fail8:
+ XenIfaceDebugPrint(ERROR, "Fail8\n");
+
+fail7:
+ XenIfaceDebugPrint(ERROR, "Fail7\n");
+ RtlZeroMemory(Context, sizeof(XENIFACE_GRANT_CONTEXT));
+ ExFreePoolWithTag(Context, XENIFACE_POOL_TAG);
+
+fail6:
+ XenIfaceDebugPrint(ERROR, "Fail6\n");
+
+fail5:
+ XenIfaceDebugPrint(ERROR, "Fail5\n");
+
+fail4:
+ XenIfaceDebugPrint(ERROR, "Fail4\n");
+
+fail3:
+ XenIfaceDebugPrint(ERROR, "Fail3\n");
+ __FreeCapturedBuffer(In);
+
+fail2:
+ XenIfaceDebugPrint(ERROR, "Fail2\n");
+
+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_GNTTAB_MAP_FOREIGN_PAGES_OUT Out = Irp->UserBuffer;
+ ULONG NumberPages;
+ ULONG PageIndex;
+ PXENIFACE_MAP_CONTEXT Context;
+
+ status = STATUS_INVALID_BUFFER_SIZE;
+ if (InLen < sizeof(XENIFACE_GNTTAB_MAP_FOREIGN_PAGES_IN) ||
+ OutLen != sizeof(XENIFACE_GNTTAB_MAP_FOREIGN_PAGES_OUT)) {
+ goto fail1;
+ }
+
+ // This IOCTL uses METHOD_NEITHER so we directly access user memory.
+
+ // Calculate the expected number of pages based on input buffer size.
+ NumberPages = (InLen - (ULONG)FIELD_OFFSET(XENIFACE_GNTTAB_MAP_FOREIGN_PAGES_IN, References)) / sizeof(In->References[0]);
+
+ status = __CaptureUserBuffer(Buffer, InLen, &In);
+ if (!NT_SUCCESS(status))
+ goto fail2;
+
+ status = STATUS_INVALID_PARAMETER;
+ if (In->NumberPages == 0 ||
+ In->NumberPages > 1024 * 1024 ||
+ In->NumberPages != NumberPages) {
+ goto fail3;
+ }
+
+ if ((In->Flags & XENIFACE_GNTTAB_USE_NOTIFY_OFFSET) &&
+ (In->NotifyOffset >= In->NumberPages * PAGE_SIZE)) {
+ goto fail4;
+ }
+
+ status = STATUS_INVALID_BUFFER_SIZE;
+ if (InLen != (ULONG)FIELD_OFFSET(XENIFACE_GNTTAB_MAP_FOREIGN_PAGES_IN, References[In->NumberPages]))
+ goto fail5;
+
+ status = STATUS_NO_MEMORY;
+ Context = ExAllocatePoolWithTag(NonPagedPool, sizeof(XENIFACE_MAP_CONTEXT), XENIFACE_POOL_TAG);
+ if (Context == NULL)
+ goto fail6;
+
+ 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 (PageIndex = 0; PageIndex < In->NumberPages; PageIndex++)
+ XenIfaceDebugPrint(INFO, "> Ref %d\n", In->References[PageIndex]);
+
+ status = STATUS_INVALID_PARAMETER;
+ if (FindGnttabIrp(Fdo, &Context->Id) != NULL)
+ goto fail7;
+
+ status = XENBUS_GNTTAB(MapForeignPages,
+ &Fdo->GnttabInterface,
+ Context->RemoteDomain,
+ Context->NumberPages,
+ In->References,
+ Context->Flags & XENIFACE_GNTTAB_READONLY,
+ &Context->Address);
+
+ if (!NT_SUCCESS(status))
+ goto fail8;
+
+ status = STATUS_NO_MEMORY;
+ Context->KernelVa = MmMapIoSpace(Context->Address, Context->NumberPages * PAGE_SIZE, MmCached);
+ if (Context->KernelVa == NULL)
+ goto fail9;
+
+ status = STATUS_NO_MEMORY;
+ Context->Mdl = IoAllocateMdl(Context->KernelVa, Context->NumberPages * PAGE_SIZE, FALSE, FALSE, NULL);
+ if (Context->Mdl == NULL)
+ goto fail10;
+
+ 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 fail11;
+ }
+
+ status = STATUS_UNSUCCESSFUL;
+ if (Context->UserVa == NULL)
+ goto fail12;
+
+ XenIfaceDebugPrint(TRACE, "< Context %p, Irp %p, Address %p, KernelVa %p, UserVa %p\n",
+ Context, Irp, Context->Address, Context->KernelVa, Context->UserVa);
+
+ // Pass the result to user mode.
+#pragma prefast(suppress: 6320) // we want to catch all exceptions
+ try {
+ ProbeForWrite(Out, OutLen, 1);
+ Out->Address = Context->UserVa;
+ } except(EXCEPTION_EXECUTE_HANDLER) {
+ status = GetExceptionCode();
+ XenIfaceDebugPrint(ERROR, "Exception 0x%lx while probing/writing output buffer at %p, size 0x%lx\n", status, Out, OutLen);
+ goto fail13;
+ }
+
+ // Insert the IRP/context into the pending queue.
+ // This also checks (again) if the request ID is unique for the calling process.
+ Irp->Tail.Overlay.DriverContext[0] = &Context->Id;
+ status = IoCsqInsertIrpEx(&Fdo->IrpQueue, Irp, NULL, &Context->Id);
+ if (!NT_SUCCESS(status))
+ goto fail14;
+
+ __FreeCapturedBuffer(In);
+
+ return STATUS_PENDING;
+
+fail14:
+ XenIfaceDebugPrint(ERROR, "Fail14\n");
+
+fail13:
+ XenIfaceDebugPrint(ERROR, "Fail13\n");
+ MmUnmapLockedPages(Context->UserVa, Context->Mdl);
+
+fail12:
+ XenIfaceDebugPrint(ERROR, "Fail12\n");
+
+fail11:
+ XenIfaceDebugPrint(ERROR, "Fail11\n");
+ IoFreeMdl(Context->Mdl);
+
+fail10:
+ XenIfaceDebugPrint(ERROR, "Fail10\n");
+ MmUnmapIoSpace(Context->KernelVa, Context->NumberPages * PAGE_SIZE);
+
+fail9:
+ XenIfaceDebugPrint(ERROR, "Fail9\n");
+ ASSERT(NT_SUCCESS(XENBUS_GNTTAB(UnmapForeignPages,
+ &Fdo->GnttabInterface,
+ Context->Address
+ )));
+
+fail8:
+ XenIfaceDebugPrint(ERROR, "Fail8\n");
+
+fail7:
+ XenIfaceDebugPrint(ERROR, "Fail7\n");
+ RtlZeroMemory(Context, sizeof(XENIFACE_MAP_CONTEXT));
+ ExFreePoolWithTag(Context, XENIFACE_POOL_TAG);
+
+fail6:
+ XenIfaceDebugPrint(ERROR, "Fail6\n");
+
+fail5:
+ XenIfaceDebugPrint(ERROR, "Fail5\n");
+
+fail4:
+ XenIfaceDebugPrint(ERROR, "Fail4\n");
+
+fail3:
+ XenIfaceDebugPrint(ERROR, "Fail3\n");
+ __FreeCapturedBuffer(In);
+
+fail2:
+ XenIfaceDebugPrint(ERROR, "Fail2\n");
+
+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;
+}
--- /dev/null
+/* Copyright (c) Citrix Systems Inc.
+ * Copyright (c) Rafal Wojdyla <omeg@invisiblethingslab.com>
+ * 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 "xeniface_ioctls.h"
+#include "log.h"
+
+#define XENSTORE_ABS_PATH_MAX 3072
+#define XENSTORE_REL_PATH_MAX 2048
+
+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;
+}
+
+static
+PXENBUS_STORE_PERMISSION
+__ConvertPermissions(
+ __in ULONG NumberPermissions,
+ __in PXENIFACE_STORE_PERMISSION XenifacePermissions
+)
+{
+ PXENBUS_STORE_PERMISSION XenbusPermissions;
+ ULONG Index;
+
+ if (NumberPermissions > 255)
+ goto fail1;
+
+ XenbusPermissions = ExAllocatePoolWithTag(NonPagedPool, NumberPermissions * sizeof(XENBUS_STORE_PERMISSION), XENIFACE_POOL_TAG);
+ if (XenbusPermissions == NULL)
+ goto fail2;
+
+ // Currently XENIFACE_STORE_PERMISSION is the same as XENBUS_STORE_PERMISSION,
+ // but we convert them here in case something changes in the future.
+ for (Index = 0; Index < NumberPermissions; Index++) {
+ if ((XenifacePermissions[Index].Mask & ~XENIFACE_STORE_ALLOWED_PERMISSIONS) != 0)
+ goto fail3;
+
+ XenbusPermissions[Index].Domain = XenifacePermissions[Index].Domain;
+ XenbusPermissions[Index].Mask = (XENBUS_STORE_PERMISSION_MASK)XenifacePermissions[Index].Mask;
+ }
+
+ return XenbusPermissions;
+
+fail3:
+ XenIfaceDebugPrint(ERROR, "Fail3\n");
+ ExFreePoolWithTag(XenbusPermissions, XENIFACE_POOL_TAG);
+
+fail2:
+ XenIfaceDebugPrint(ERROR, "Fail2\n");
+
+fail1:
+ XenIfaceDebugPrint(ERROR, "Fail1\n");
+ return NULL;
+}
+
+static
+VOID
+__FreePermissions(
+ __in PXENBUS_STORE_PERMISSION Permissions
+ )
+{
+ ExFreePoolWithTag(Permissions, XENIFACE_POOL_TAG);
+}
+
+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;
+ PXENBUS_STORE_PERMISSION Permissions;
+ ULONG Index;
+ PCHAR Path;
+
+ status = STATUS_INVALID_BUFFER_SIZE;
+ if (InLen < sizeof(XENIFACE_STORE_SET_PERMISSIONS_IN) ||
+ OutLen != 0) {
+ goto fail1;
+ }
+
+ if (InLen != (ULONG)FIELD_OFFSET(XENIFACE_STORE_SET_PERMISSIONS_IN, Permissions[In->NumberPermissions]))
+ goto fail2;
+
+ status = STATUS_INVALID_PARAMETER;
+ if (In->PathLength == 0 ||
+ In->PathLength > XENSTORE_ABS_PATH_MAX) {
+ goto fail3;
+ }
+
+ Permissions = __ConvertPermissions(In->NumberPermissions, In->Permissions);
+ if (Permissions == NULL)
+ goto fail4;
+
+ status = __CaptureUserBuffer(In->Path, In->PathLength, &Path);
+ if (!NT_SUCCESS(status))
+ goto fail5;
+
+ 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, Permissions[Index].Domain, Permissions[Index].Mask);
+ }
+
+ status = XENBUS_STORE(PermissionsSet,
+ &Fdo->StoreInterface,
+ NULL, // transaction
+ NULL, // prefix
+ Path,
+ Permissions,
+ In->NumberPermissions);
+
+ if (!NT_SUCCESS(status))
+ goto fail6;
+
+ __FreeCapturedBuffer(Path);
+ return status;
+
+fail6:
+ XenIfaceDebugPrint(ERROR, "Fail6\n");
+ __FreeCapturedBuffer(Path);
+
+fail5:
+ XenIfaceDebugPrint(ERROR, "Fail5\n");
+ __FreePermissions(Permissions);
+
+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;
+}
/* Copyright (c) Citrix Systems Inc.
+ * Copyright (c) Rafal Wojdyla <omeg@invisiblethingslab.com>
* All rights reserved.
*
* Redistribution and use in source and binary forms,
* SUCH DAMAGE.
*/
-
#include "driver.h"
#include "ioctls.h"
-#include "..\..\include\xeniface_ioctls.h"
+#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(
- __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(INFO, "|%s: (\"%s\")=(%d)\n", __FUNCTION__, Buffer, Length);
- goto done;
- }
-
- 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;
-
-done:
- *Info = (ULONG_PTR)Length;
- XENBUS_STORE(Free, &Fdo->StoreInterface, Value);
- return status;
-
-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;
-}
-
-static DECLSPEC_NOINLINE NTSTATUS
-IoctlWrite(
- __in PXENIFACE_FDO Fdo,
- __in PCHAR Buffer,
- __in ULONG InLen,
- __in ULONG OutLen
+NTSTATUS
+__CaptureUserBuffer(
+ __in PVOID Buffer,
+ __in ULONG Length,
+ __out PVOID *CapturedBuffer
)
{
- NTSTATUS status;
- PCHAR Value;
- ULONG Length;
-
- status = STATUS_INVALID_BUFFER_SIZE;
- if (InLen == 0 || OutLen != 0)
- goto fail1;
+ NTSTATUS Status;
+ PVOID TempBuffer = NULL;
- 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;
+ if (Length == 0) {
+ *CapturedBuffer = NULL;
+ return STATUS_SUCCESS;
+ }
- status = XENBUS_STORE(Printf, &Fdo->StoreInterface, NULL, NULL, Buffer, Value);
- if (!NT_SUCCESS(status))
- goto fail4;
+ 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();
+ }
- XenIfaceDebugPrint(INFO, "|%s: (\"%s\"=\"%s\")\n", __FUNCTION__, Buffer, Value);
- return status;
+ *CapturedBuffer = TempBuffer;
-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;
+ return Status;
}
-static DECLSPEC_NOINLINE NTSTATUS
-IoctlDirectory(
- __in PXENIFACE_FDO Fdo,
- __in PCHAR Buffer,
- __in ULONG InLen,
- __in ULONG OutLen,
- __out PULONG_PTR Info
+VOID
+__FreeCapturedBuffer(
+ __in PVOID CapturedBuffer
)
{
- 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(INFO, "|%s: (\"%s\")=(%d)(%d)\n", __FUNCTION__, Buffer, Length, Count);
- goto done;
- }
-
- status = STATUS_INVALID_PARAMETER;
- if (OutLen < Length)
- goto fail4;
-
- XenIfaceDebugPrint(INFO, "|%s: (\"%s\")=(%d)(%d)\n", __FUNCTION__, 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, "|%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;
+ if (CapturedBuffer != NULL) {
+ ExFreePoolWithTag(CapturedBuffer, XENIFACE_POOL_TAG);
+ }
}
-static DECLSPEC_NOINLINE NTSTATUS
-IoctlRemove(
- __in PXENIFACE_FDO Fdo,
- __in PCHAR Buffer,
- __in ULONG InLen,
- __in ULONG OutLen
+// 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 PFILE_OBJECT FileObject
)
{
- 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;
+ 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);
+
+ // 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);
- XenIfaceDebugPrint(INFO, "|%s: (\"%s\")\n", __FUNCTION__, Buffer);
- return status;
+ Node = ToFree.Flink;
+ while (Node->Flink != ToFree.Flink) {
+ EvtchnContext = CONTAINING_RECORD(Node, XENIFACE_EVTCHN_CONTEXT, Entry);
+ Node = Node->Flink;
-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);
+ }
}
NTSTATUS
-XenIFaceIoctl(
- __in PXENIFACE_FDO Fdo,
- __in PIRP Irp
+XenIfaceIoctl(
+ __in PXENIFACE_FDO Fdo,
+ __inout PIRP Irp
)
{
NTSTATUS status;
goto done;
switch (Stack->Parameters.DeviceIoControl.IoControlCode) {
+ // store
case IOCTL_XENIFACE_STORE_READ:
- status = IoctlRead(Fdo, (PCHAR)Buffer, InLen, OutLen, &Irp->IoStatus.Information);
+ status = IoctlStoreRead(Fdo, (PCHAR)Buffer, InLen, OutLen, &Irp->IoStatus.Information);
break;
case IOCTL_XENIFACE_STORE_WRITE:
- status = IoctlWrite(Fdo, (PCHAR)Buffer, InLen, OutLen);
+ status = IoctlStoreWrite(Fdo, (PCHAR)Buffer, InLen, OutLen);
break;
case IOCTL_XENIFACE_STORE_DIRECTORY:
- status = IoctlDirectory(Fdo, (PCHAR)Buffer, InLen, OutLen, &Irp->IoStatus.Information);
+ status = IoctlStoreDirectory(Fdo, (PCHAR)Buffer, InLen, OutLen, &Irp->IoStatus.Information);
break;
case IOCTL_XENIFACE_STORE_REMOVE:
- status = IoctlRemove(Fdo, (PCHAR)Buffer, InLen, OutLen);
+ status = IoctlStoreRemove(Fdo, (PCHAR)Buffer, InLen, OutLen);
+ break;
+
+ case IOCTL_XENIFACE_STORE_SET_PERMISSIONS:
+ status = IoctlStoreSetPermissions(Fdo, Buffer, InLen, OutLen);
+ break;
+
+ case IOCTL_XENIFACE_STORE_ADD_WATCH:
+ status = IoctlStoreAddWatch(Fdo, Buffer, InLen, OutLen, Stack->FileObject, &Irp->IoStatus.Information);
+ break;
+
+ case IOCTL_XENIFACE_STORE_REMOVE_WATCH:
+ status = IoctlStoreRemoveWatch(Fdo, Buffer, InLen, OutLen, Stack->FileObject);
+ break;
+
+ // evtchn
+ case IOCTL_XENIFACE_EVTCHN_BIND_UNBOUND:
+ status = IoctlEvtchnBindUnbound(Fdo, Buffer, InLen, OutLen, Stack->FileObject, &Irp->IoStatus.Information);
+ break;
+
+ case IOCTL_XENIFACE_EVTCHN_BIND_INTERDOMAIN:
+ status = IoctlEvtchnBindInterdomain(Fdo, Buffer, InLen, OutLen, Stack->FileObject, &Irp->IoStatus.Information);
+ break;
+
+ case IOCTL_XENIFACE_EVTCHN_CLOSE:
+ status = IoctlEvtchnClose(Fdo, Buffer, InLen, OutLen, Stack->FileObject);
+ break;
+
+ case IOCTL_XENIFACE_EVTCHN_NOTIFY:
+ status = IoctlEvtchnNotify(Fdo, Buffer, InLen, OutLen, Stack->FileObject);
+ break;
+
+ case IOCTL_XENIFACE_EVTCHN_UNMASK:
+ status = IoctlEvtchnUnmask(Fdo, Buffer, InLen, OutLen, Stack->FileObject);
+ break;
+
+ // gnttab
+ case IOCTL_XENIFACE_GNTTAB_PERMIT_FOREIGN_ACCESS: // this is a METHOD_NEITHER IOCTL
+ status = IoctlGnttabPermitForeignAccess(Fdo, Stack->Parameters.DeviceIoControl.Type3InputBuffer, InLen, OutLen, Irp);
+ break;
+
+ case IOCTL_XENIFACE_GNTTAB_REVOKE_FOREIGN_ACCESS:
+ status = IoctlGnttabRevokeForeignAccess(Fdo, Buffer, InLen, OutLen);
+ break;
+
+ case IOCTL_XENIFACE_GNTTAB_MAP_FOREIGN_PAGES: // this is a METHOD_NEITHER IOCTL
+ status = IoctlGnttabMapForeignPages(Fdo, Stack->Parameters.DeviceIoControl.Type3InputBuffer, InLen, OutLen, Irp);
+ break;
+
+ case IOCTL_XENIFACE_GNTTAB_UNMAP_FOREIGN_PAGES:
+ status = IoctlGnttabUnmapForeignPages(Fdo, Buffer, InLen, OutLen);
break;
default:
Irp->IoStatus.Status = status;
- IoCompleteRequest(Irp, IO_NO_INCREMENT);
+ if (status != STATUS_PENDING)
+ IoCompleteRequest(Irp, IO_NO_INCREMENT);
return status;
}
/* Copyright (c) Citrix Systems Inc.
+ * Copyright (c) Rafal Wojdyla <omeg@invisiblethingslab.com>
* All rights reserved.
*
* Redistribution and use in source and binary forms,
#ifndef _IOCTLS_H_
#define _IOCTLS_H_
+#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;
+ 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
+__CaptureUserBuffer(
+ __in PVOID Buffer,
+ __in ULONG Length,
+ __out PVOID *CapturedBuffer
+ );
+
+VOID
+__FreeCapturedBuffer(
+ __in PVOID CapturedBuffer
+ );
+
+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
+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
-XenIFaceIoctl(
- __in PXENIFACE_FDO Fdo,
- __in PIRP Irp
+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 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_
--- /dev/null
+/* Copyright (c) Rafal Wojdyla <omeg@invisiblethingslab.com>
+ * 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 "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);
+}
--- /dev/null
+/* Copyright (c) Rafal Wojdyla <omeg@invisiblethingslab.com>
+ * 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.
+ */
+
+#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
<FilesToPackage Include="@(Inf->'%(CopyOutput)')" Condition="'@(Inf)'!=''" />
</ItemGroup>
<ItemGroup>
- <ClCompile Include="../../src/xeniface/ioctls.c" />
- <ClCompile Include="../../src/xeniface/wmi.c" />
- <ClCompile Include="../../src/xeniface/driver.c" />
- <ClCompile Include="../../src/xeniface/fdo.c" />
- <ClCompile Include="../../src/xeniface/registry.c" />
- <ClCompile Include="../../src\xeniface/thread.c" />
+ <ClCompile Include="..\..\src\xeniface\ioctls.c" />
+ <ClCompile Include="..\..\src\xeniface\wmi.c" />
+ <ClCompile Include="..\..\src\xeniface\driver.c" />
+ <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">
<ItemGroup>
<Inf Include="..\xeniface.inf" />
</ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="..\..\src\xeniface\assert.h" />
+ <ClInclude Include="..\..\src\xeniface\driver.h" />
+ <ClInclude Include="..\..\src\xeniface\fdo.h" />
+ <ClInclude Include="..\..\src\xeniface\ioctls.h" />
+ <ClInclude Include="..\..\src\xeniface\irp_queue.h" />
+ <ClInclude Include="..\..\src\xeniface\log.h" />
+ <ClInclude Include="..\..\src\xeniface\mutex.h" />
+ <ClInclude Include="..\..\src\xeniface\names.h" />
+ <ClInclude Include="..\..\src\xeniface\registry.h" />
+ <ClInclude Include="..\..\src\xeniface\thread.h" />
+ <ClInclude Include="..\..\src\xeniface\types.h" />
+ <ClInclude Include="..\..\src\xeniface\wmi.h" />
+ </ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
</Project>
<FilesToPackage Include="@(Inf->'%(CopyOutput)')" Condition="'@(Inf)'!=''" />
</ItemGroup>
<ItemGroup>
- <ClCompile Include="../../src/xeniface/ioctls.c" />
- <ClCompile Include="../../src/xeniface/wmi.c" />
- <ClCompile Include="../../src/xeniface/driver.c" />
- <ClCompile Include="../../src/xeniface/fdo.c" />
- <ClCompile Include="../../src/xeniface/registry.c" />
- <ClCompile Include="../../src\xeniface/thread.c" />
+ <ClCompile Include="..\..\src\xeniface\ioctls.c" />
+ <ClCompile Include="..\..\src\xeniface\wmi.c" />
+ <ClCompile Include="..\..\src\xeniface\driver.c" />
+ <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">
<ItemGroup>
<Inf Include="..\xeniface.inf" />
</ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="..\..\src\xeniface\assert.h" />
+ <ClInclude Include="..\..\src\xeniface\driver.h" />
+ <ClInclude Include="..\..\src\xeniface\fdo.h" />
+ <ClInclude Include="..\..\src\xeniface\ioctls.h" />
+ <ClInclude Include="..\..\src\xeniface\irp_queue.h" />
+ <ClInclude Include="..\..\src\xeniface\log.h" />
+ <ClInclude Include="..\..\src\xeniface\mutex.h" />
+ <ClInclude Include="..\..\src\xeniface\names.h" />
+ <ClInclude Include="..\..\src\xeniface\registry.h" />
+ <ClInclude Include="..\..\src\xeniface\thread.h" />
+ <ClInclude Include="..\..\src\xeniface\types.h" />
+ <ClInclude Include="..\..\src\xeniface\wmi.h" />
+ </ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
</Project>