[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[RFC PATCH 5/6] Add gnttab IOCTLs that allow sharing already existing memory



IOCTL_XENIFACE_GNTTAB_PERMIT_FOREIGN_ACCESS shares pages that are
newly allocated by the driver. This commit adds new IOCTLs:
IOCTL_XENIFACE_GNTTAB_PERMIT_FOREIGN_ACCESS_V2,
IOCTL_XENIFACE_GNTTAB_REVOKE_FOREIGN_ACCESS_V2,
IOCTL_XENIFACE_GNTTAB_MAP_FOREIGN_PAGES_V2
and IOCTL_XENIFACE_GNTTAB_UNMAP_FOREIGN_PAGES_V2.

IOCTL_XENIFACE_GNTTAB_PERMIT_FOREIGN_ACCESS_V2 allows specifying
user-mode address of a memory region that will be shared
(the region is locked for the duration).

All _V2 IOCTLs use user-mode address of the shared region
as a unique (per-process) request identifier. This differs from
the existing IOCTLs that use client-supplied RequestId. _V2 IOCTLs
should be considered as preferable since they don't require
the client to manage IDs.

Signed-off-by: Rafał Wojdyła <omeg@xxxxxxxxxxxxxxxxxxxxxx>
---
 include/xeniface_ioctls.h   |  94 ++++-
 src/xeniface/ioctl_gnttab.c | 711 ++++++++++++++++++++++--------------
 src/xeniface/ioctls.c       |  25 +-
 src/xeniface/ioctls.h       |  75 ++--
 src/xeniface/irp_queue.c    |  27 +-
 src/xeniface/irp_queue.h    |   2 +-
 6 files changed, 596 insertions(+), 338 deletions(-)

diff --git a/include/xeniface_ioctls.h b/include/xeniface_ioctls.h
index 1a8bcb5..67deedf 100644
--- a/include/xeniface_ioctls.h
+++ b/include/xeniface_ioctls.h
@@ -245,8 +245,10 @@ typedef enum _XENIFACE_GNTTAB_PAGE_FLAGS {
 } XENIFACE_GNTTAB_PAGE_FLAGS;
 
 /*! \brief Grant permission to access local memory pages to a foreign domain
+    \deprecated Use the _V2 IOCTLs that don't need RequestId input instead
     \note This IOCTL must be asynchronous. The driver doesn't complete the 
request
           until the grant is explicitly revoked or the calling thread 
terminates.
+          Granted memory is allocated by the driver.
 
     Input: XENIFACE_GNTTAB_PERMIT_FOREIGN_ACCESS_IN
 
@@ -271,7 +273,35 @@ typedef struct _XENIFACE_GNTTAB_PERMIT_FOREIGN_ACCESS_OUT {
     ULONG References[ANYSIZE_ARRAY]; /*!< An array of Xen-assigned references 
for each granted page */
 } XENIFACE_GNTTAB_PERMIT_FOREIGN_ACCESS_OUT, 
*PXENIFACE_GNTTAB_PERMIT_FOREIGN_ACCESS_OUT;
 
-/*! \brief Revoke a foreign domain access to previously granted memory region
+/*! \brief Grant permission to access local memory pages to a foreign domain
+    \note This IOCTL must be asynchronous. The driver doesn't complete the 
request
+          until the grant is explicitly revoked or the calling thread 
terminates.
+          Granted memory may be already mapped or allocated by the driver.
+
+    Input: XENIFACE_GNTTAB_PERMIT_FOREIGN_ACCESS_IN_V2
+
+    Output: XENIFACE_GNTTAB_PERMIT_FOREIGN_ACCESS_OUT_V2
+*/
+#define IOCTL_XENIFACE_GNTTAB_PERMIT_FOREIGN_ACCESS_V2 \
+    CTL_CODE(FILE_DEVICE_UNKNOWN, 0x824, METHOD_NEITHER, FILE_ANY_ACCESS)
+
+/*! \brief Input for IOCTL_XENIFACE_GNTTAB_PERMIT_FOREIGN_ACCESS_V2 */
+typedef struct _XENIFACE_GNTTAB_PERMIT_FOREIGN_ACCESS_IN_V2 {
+    USHORT                     RemoteDomain; /*!< Remote domain that is being 
granted access */
+    PVOID                      Address;      /*!< Address of the granted 
memory region, allocated by the driver if NULL */
+    ULONG                      NumberPages;  /*!< Number of 4k pages to grant 
access to */
+    XENIFACE_GNTTAB_PAGE_FLAGS Flags;        /*!< Additional flags */
+    ULONG                      NotifyOffset; /*!< Offset of a byte in the 
granted region that will be set to 0 when the grant is revoked */
+    ULONG                      NotifyPort;   /*!< Local port number of an open 
event channel that will be notified when the grant is revoked */
+} XENIFACE_GNTTAB_PERMIT_FOREIGN_ACCESS_IN_V2, 
*PXENIFACE_GNTTAB_PERMIT_FOREIGN_ACCESS_IN_V2;
+
+/*! \brief Output for IOCTL_XENIFACE_GNTTAB_PERMIT_FOREIGN_ACCESS_V2 */
+typedef struct _XENIFACE_GNTTAB_PERMIT_FOREIGN_ACCESS_OUT \
+    XENIFACE_GNTTAB_PERMIT_FOREIGN_ACCESS_OUT_V2, 
*PXENIFACE_GNTTAB_PERMIT_FOREIGN_ACCESS_OUT_V2;
+
+/*! \brief Revoke a foreign domain access to a memory region
+           previously granted by IOCTL_XENIFACE_GNTTAB_PERMIT_FOREIGN_ACCESS
+    \deprecated Use the _V2 IOCTLs that don't need RequestId input instead
 
     Input: XENIFACE_GNTTAB_REVOKE_FOREIGN_ACCESS_IN
 
@@ -285,7 +315,23 @@ typedef struct _XENIFACE_GNTTAB_REVOKE_FOREIGN_ACCESS_IN {
     ULONG RequestId; /*! Request ID used in the corresponding 
IOCTL_XENIFACE_GNTTAB_PERMIT_FOREIGN_ACCESS call */
 } XENIFACE_GNTTAB_REVOKE_FOREIGN_ACCESS_IN, 
*PXENIFACE_GNTTAB_REVOKE_FOREIGN_ACCESS_IN;
 
+/*! \brief Revoke a foreign domain access to a memory region
+           previously granted by IOCTL_XENIFACE_GNTTAB_PERMIT_FOREIGN_ACCESS_V2
+
+    Input: XENIFACE_GNTTAB_REVOKE_FOREIGN_ACCESS_IN_V2
+
+    Output: None
+*/
+#define IOCTL_XENIFACE_GNTTAB_REVOKE_FOREIGN_ACCESS_V2 \
+    CTL_CODE(FILE_DEVICE_UNKNOWN, 0x825, METHOD_BUFFERED, FILE_ANY_ACCESS)
+
+/*! \brief Input for IOCTL_XENIFACE_GNTTAB_REVOKE_FOREIGN_ACCESS_V2 */
+typedef struct _XENIFACE_GNTTAB_REVOKE_FOREIGN_ACCESS_IN_V2 {
+    PVOID Address; /*!< User-mode address of the shared memory region */
+} XENIFACE_GNTTAB_REVOKE_FOREIGN_ACCESS_IN_V2, * 
PXENIFACE_GNTTAB_REVOKE_FOREIGN_ACCESS_IN_V2;
+
 /*! \brief Map a foreign memory region into the current address space
+    \deprecated Use the _V2 IOCTLs that don't need RequestId input instead
     \note This IOCTL must be asynchronous. The driver doesn't complete the 
request
           until the memory is explicitly unmapped or the calling thread 
terminates.
 
@@ -309,10 +355,37 @@ typedef struct _XENIFACE_GNTTAB_MAP_FOREIGN_PAGES_IN {
 
 /*! \brief Output for IOCTL_XENIFACE_GNTTAB_MAP_FOREIGN_PAGES */
 typedef struct _XENIFACE_GNTTAB_MAP_FOREIGN_PAGES_OUT {
-    PVOID Address; /*!< User-mode address of the mapped memory region */
+    PVOID Address; /*!< User-mode address of the shared memory region */
 } XENIFACE_GNTTAB_MAP_FOREIGN_PAGES_OUT, 
*PXENIFACE_GNTTAB_MAP_FOREIGN_PAGES_OUT;
 
-/*! \brief Unmap a foreign memory region from the current address space
+/*! \brief Map a foreign memory region into the current address space
+    \note This IOCTL must be asynchronous. The driver doesn't complete the 
request
+          until the memory is explicitly unmapped or the calling thread 
terminates.
+
+    Input: XENIFACE_GNTTAB_MAP_FOREIGN_PAGES_IN_V2
+
+    Output: XENIFACE_GNTTAB_MAP_FOREIGN_PAGES_OUT_V2
+*/
+#define IOCTL_XENIFACE_GNTTAB_MAP_FOREIGN_PAGES_V2 \
+    CTL_CODE(FILE_DEVICE_UNKNOWN, 0x826, METHOD_NEITHER, FILE_ANY_ACCESS)
+
+/*! \brief Input for IOCTL_XENIFACE_GNTTAB_MAP_FOREIGN_PAGES_V2 */
+typedef struct _XENIFACE_GNTTAB_MAP_FOREIGN_PAGES_IN_V2 {
+    USHORT                     RemoteDomain;              /*!< Remote domain 
that has granted access to the pages */
+    ULONG                      NumberPages;               /*!< Number of 4k 
pages to map */
+    XENIFACE_GNTTAB_PAGE_FLAGS Flags;                     /*!< Additional 
flags */
+    ULONG                      NotifyOffset;              /*!< Offset of a 
byte in the mapped region that will be set to 0 when the region is unmapped */
+    ULONG                      NotifyPort;                /*!< Local port 
number of an open event channel that will be notified when the region is 
unmapped */
+    ULONG                      References[ANYSIZE_ARRAY]; /*!< An array of 
Xen-assigned references for each granted page */
+} XENIFACE_GNTTAB_MAP_FOREIGN_PAGES_IN_V2, 
*PXENIFACE_GNTTAB_MAP_FOREIGN_PAGES_IN_V2;
+
+/*! \brief Output for IOCTL_XENIFACE_GNTTAB_MAP_FOREIGN_PAGES_V2 */
+typedef struct _XENIFACE_GNTTAB_MAP_FOREIGN_PAGES_OUT \
+    XENIFACE_GNTTAB_MAP_FOREIGN_PAGES_OUT_V2, 
*PXENIFACE_GNTTAB_MAP_FOREIGN_PAGES_OUT_V2;
+
+/*! \brief Unmap a foreign memory region mapped by 
IOCTL_XENIFACE_GNTTAB_MAP_FOREIGN_PAGES
+           from the current address space
+    \deprecated Use the _V2 IOCTLs that don't need RequestId input instead
 
     Input: XENIFACE_GNTTAB_UNMAP_FOREIGN_PAGES_IN
 
@@ -326,6 +399,21 @@ typedef struct _XENIFACE_GNTTAB_UNMAP_FOREIGN_PAGES_IN {
     ULONG RequestId; /*! Request ID used in the corresponding 
IOCTL_XENIFACE_GNTTAB_MAP_FOREIGN_PAGES call */
 } XENIFACE_GNTTAB_UNMAP_FOREIGN_PAGES_IN, 
*PXENIFACE_GNTTAB_UNMAP_FOREIGN_PAGES_IN;
 
+/*! \brief Unmap a foreign memory region mapped by 
IOCTL_XENIFACE_GNTTAB_MAP_FOREIGN_PAGES_V2
+           from the current address space
+
+    Input: XENIFACE_GNTTAB_UNMAP_FOREIGN_PAGES_IN_V2
+
+    Output: None
+*/
+#define IOCTL_XENIFACE_GNTTAB_UNMAP_FOREIGN_PAGES_V2 \
+    CTL_CODE(FILE_DEVICE_UNKNOWN, 0x827, METHOD_BUFFERED, FILE_ANY_ACCESS)
+
+/*! \brief Input for IOCTL_XENIFACE_GNTTAB_UNMAP_FOREIGN_PAGES_V2 */
+typedef struct _XENIFACE_GNTTAB_UNMAP_FOREIGN_PAGES_IN_V2 {
+    PVOID Address; /*!< User-mode address of the shared memory region */
+} XENIFACE_GNTTAB_UNMAP_FOREIGN_PAGES_IN_V2, 
*PXENIFACE_GNTTAB_UNMAP_FOREIGN_PAGES_IN_V2;
+
 /*! \brief Gets the current suspend count.
 
     Input: None
diff --git a/src/xeniface/ioctl_gnttab.c b/src/xeniface/ioctl_gnttab.c
index b6b7327..c496f1b 100644
--- a/src/xeniface/ioctl_gnttab.c
+++ b/src/xeniface/ioctl_gnttab.c
@@ -47,35 +47,35 @@ CompleteGnttabIrp(
     PXENIFACE_DX Dx = (PXENIFACE_DX)DeviceObject->DeviceExtension;
     PXENIFACE_FDO Fdo = Dx->Fdo;
     PIRP Irp = Context;
-    PXENIFACE_CONTEXT_ID Id;
+    PXENIFACE_GNTTAB_CONTEXT GnttabContext;
     PIO_WORKITEM WorkItem;
     KAPC_STATE ApcState;
     BOOLEAN ChangeProcess;
 
     ASSERT(Context != NULL);
 
-    Id = Irp->Tail.Overlay.DriverContext[0];
+    GnttabContext = 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;
+    ChangeProcess = PsGetCurrentProcess() != GnttabContext->Process;
     if (ChangeProcess) {
-        Trace("Changing process from %p to %p\n", PsGetCurrentProcess(), 
Id->Process);
-        KeStackAttachProcess(Id->Process, &ApcState);
+        Trace("Changing process from %p to %p\n", PsGetCurrentProcess(), 
GnttabContext->Process);
+        KeStackAttachProcess(GnttabContext->Process, &ApcState);
     }
 
     Trace("Irp %p, Process %p, Id %lu, Type %d, IRQL %d\n",
-          Irp, Id->Process, Id->RequestId, Id->Type, KeGetCurrentIrql());
+          Irp, GnttabContext->Process, GnttabContext->RequestId, 
GnttabContext->Type, KeGetCurrentIrql());
 
-    switch (Id->Type) {
+    switch (GnttabContext->Type) {
 
-    case XENIFACE_CONTEXT_GRANT:
-        GnttabFreeGrant(Fdo, CONTAINING_RECORD(Id, XENIFACE_GRANT_CONTEXT, 
Id));
+    case XENIFACE_GNTTAB_CONTEXT_GRANT:
+        GnttabFreeGrant(Fdo, GnttabContext);
         break;
 
-    case XENIFACE_CONTEXT_MAP:
-        GnttabFreeMap(Fdo, CONTAINING_RECORD(Id, XENIFACE_MAP_CONTEXT, Id));
+    case XENIFACE_GNTTAB_CONTEXT_MAP:
+        GnttabFreeMap(Fdo, GnttabContext);
         break;
 
     default:
@@ -125,18 +125,194 @@ static
 PIRP
 FindGnttabIrp(
     __in  PXENIFACE_FDO Fdo,
-    __in  PXENIFACE_CONTEXT_ID Id
+    __in  PXENIFACE_GNTTAB_CONTEXT Context
     )
 {
     KIRQL Irql;
     PIRP Irp;
 
     CsqAcquireLock(&Fdo->IrpQueue, &Irql);
-    Irp = CsqPeekNextIrp(&Fdo->IrpQueue, NULL, Id);
+    Irp = CsqPeekNextIrp(&Fdo->IrpQueue, NULL, Context);
     CsqReleaseLock(&Fdo->IrpQueue, Irql);
     return Irp;
 }
 
+// Undo (possibly partially done) sharing, free/clear associated context 
fields.
+// Does not trigger notifications if the flags are set.
+static
+void
+GnttabStopSharing(
+    __in     PXENIFACE_FDO             Fdo,
+    __inout  PXENIFACE_GNTTAB_CONTEXT  Context,
+    __in     ULONG                     NumberPages
+)
+{
+    if (Context->Grants != NULL) {
+        for (ULONG Page = 0; Page < NumberPages; Page++) {
+            ASSERT(NT_SUCCESS(XENBUS_GNTTAB(RevokeForeignAccess,
+                                            &Fdo->GnttabInterface,
+                                            Fdo->GnttabCache,
+                                            FALSE,
+                                            Context->Grants[Page])));
+        }
+
+        RtlZeroMemory(Context->Grants, Context->NumberPages * 
sizeof(Context->Grants[0]));
+        __FreePoolWithTag(Context->Grants, XENIFACE_POOL_TAG);
+        Context->Grants = NULL;
+    }
+
+    if (Context->Mdl != NULL) {
+        if (Context->KernelVa != NULL) {
+            // driver-allocated memory
+            MmUnmapLockedPages(Context->UserVa, Context->Mdl);
+        } else {
+            // user-supplied memory
+            try {
+                MmUnlockPages(Context->Mdl);
+            } except(EXCEPTION_EXECUTE_HANDLER) {
+                Error("Failed to unlock user pages: 0x%x\n", 
GetExceptionCode());
+                // this shouldn't happen and will BSOD the system when the 
user process exits with locked pages
+            }
+        }
+
+        IoFreeMdl(Context->Mdl);
+        Context->Mdl = NULL;
+    }
+
+    if (Context->KernelVa != NULL) {
+        __FreePoolWithTag(Context->KernelVa, XENIFACE_POOL_TAG);
+        Context->KernelVa = NULL;
+    }
+}
+
+static
+NTSTATUS
+GnttabPermitForeignAccess(
+    __in     PXENIFACE_FDO             Fdo,
+    __inout  PXENIFACE_GNTTAB_CONTEXT  Context
+    )
+{
+    NTSTATUS Status;
+    ULONG Page = 0;
+    size_t GrantsSize = 0;
+    ULONG SharedSize = 0;
+
+    Trace("> RemoteDomain %d, UserVa %p, NumberPages %lu, Flags 0x%x, Offset 
0x%x, Port %d, Process %p, Id %lu\n",
+          Context->RemoteDomain, Context->UserVa, Context->NumberPages, 
Context->Flags,
+          Context->NotifyOffset, Context->NotifyPort, Context->Process, 
Context->RequestId);
+
+    // Check if the request ID/address 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) != NULL)
+        goto fail1;
+
+    GrantsSize = Context->NumberPages * sizeof(PXENBUS_GNTTAB_ENTRY);
+    SharedSize = Context->NumberPages * PAGE_SIZE;
+    Status = STATUS_NO_MEMORY;
+    Context->Grants = __AllocatePoolWithTag(NonPagedPool, GrantsSize, 
XENIFACE_POOL_TAG);
+    if (Context->Grants == NULL)
+        goto fail2;
+
+    if (Context->UserVa == NULL) {
+        // sharing driver-allocated pages
+        Status = STATUS_NO_MEMORY;
+        Context->KernelVa = __AllocatePoolWithTag(NonPagedPool, SharedSize, 
XENIFACE_POOL_TAG);
+        if (Context->KernelVa == NULL)
+            goto fail3;
+
+        Context->Mdl = IoAllocateMdl(Context->KernelVa, SharedSize, FALSE, 
FALSE, NULL);
+        if (Context->Mdl == NULL)
+            goto fail4;
+
+        MmBuildMdlForNonPagedPool(Context->Mdl);
+        ASSERT(MmGetMdlByteCount(Context->Mdl) == SharedSize);
+    } else {
+        // sharing existing memory
+        Context->KernelVa = NULL;
+        Context->Mdl = IoAllocateMdl(Context->UserVa, SharedSize, FALSE, 
FALSE, NULL);
+        if (Context->Mdl == NULL)
+            goto fail4;
+
+        try {
+            MmProbeAndLockPages(Context->Mdl,
+                                UserMode,
+                                (Context->Flags & XENIFACE_GNTTAB_READONLY) != 
0 ? IoReadAccess : IoWriteAccess);
+        } except(EXCEPTION_EXECUTE_HANDLER) {
+            Status = GetExceptionCode();
+            Error("Failed to lock user pages: 0x%x\n", Status);
+            Page = 0;
+            goto fail5;
+        }
+    }
+
+    // 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]));
+#if DBG
+        Info("Grants[%lu] = %p\n", Page, Context->Grants[Page]);
+#endif
+        if (!NT_SUCCESS(Status))
+            goto fail5;
+    }
+
+    if (Context->KernelVa != NULL) {
+        // map driver-allocated memory 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 fail6;
+        }
+    }
+
+    Trace("< Context %p, KernelVa %p, UserVa %p\n",
+          Context, Context->KernelVa, Context->UserVa);
+
+    return STATUS_SUCCESS;
+
+fail6:
+    Error("Fail6\n");
+
+fail5:
+    Error("Fail5\n");
+
+fail4:
+    Error("Fail4\n");
+
+fail3:
+    Error("Fail3\n");
+
+fail2:
+    Error("Fail2\n");
+
+fail1:
+    Error("Fail1\n");
+    GnttabStopSharing(Fdo, Context, Page);
+
+    if (Context != NULL) {
+        RtlZeroMemory(Context, sizeof(*Context));
+        __FreePoolWithTag(Context, XENIFACE_POOL_TAG);
+    }
+
+    return Status;
+}
+
 DECLSPEC_NOINLINE
 NTSTATUS
 IoctlGnttabPermitForeignAccess(
@@ -147,123 +323,91 @@ IoctlGnttabPermitForeignAccess(
     __inout  PIRP           Irp
     )
 {
-    NTSTATUS status;
-    PXENIFACE_GNTTAB_PERMIT_FOREIGN_ACCESS_IN In;
+    NTSTATUS Status;
+    PXENIFACE_GNTTAB_PERMIT_FOREIGN_ACCESS_IN In1 = NULL;
+    PXENIFACE_GNTTAB_PERMIT_FOREIGN_ACCESS_IN_V2 In = NULL;
+    // XENIFACE_GNTTAB_PERMIT_FOREIGN_ACCESS_OUT_V2 is the same as 
XENIFACE_GNTTAB_PERMIT_FOREIGN_ACCESS_OUT
     PXENIFACE_GNTTAB_PERMIT_FOREIGN_ACCESS_OUT Out = Irp->UserBuffer;
-    PXENIFACE_GRANT_CONTEXT Context;
+    PXENIFACE_GNTTAB_CONTEXT Context;
     ULONG Page;
+    ULONG ControlCode = 
IoGetCurrentIrpStackLocation(Irp)->Parameters.DeviceIoControl.IoControlCode;
 
-    status = STATUS_INVALID_BUFFER_SIZE;
-    if (InLen != sizeof(XENIFACE_GNTTAB_PERMIT_FOREIGN_ACCESS_IN))
+    Status = STATUS_INVALID_BUFFER_SIZE;
+    if ((InLen != sizeof(XENIFACE_GNTTAB_PERMIT_FOREIGN_ACCESS_IN) && 
ControlCode == IOCTL_XENIFACE_GNTTAB_PERMIT_FOREIGN_ACCESS)
+        || (InLen != sizeof(XENIFACE_GNTTAB_PERMIT_FOREIGN_ACCESS_IN_V2) && 
ControlCode == IOCTL_XENIFACE_GNTTAB_PERMIT_FOREIGN_ACCESS_V2))
         goto fail1;
 
     // This IOCTL uses METHOD_NEITHER so we directly access user memory.
-    status = __CaptureUserBuffer(Buffer, InLen, &In);
-    if (!NT_SUCCESS(status))
-        goto fail2;
+    if (ControlCode == IOCTL_XENIFACE_GNTTAB_PERMIT_FOREIGN_ACCESS) {
+        // legacy IOCTL, convert the input to v2
+        Status = __CaptureUserBuffer(Buffer, InLen, &In1);
+        if (!NT_SUCCESS(Status))
+            goto fail2;
+
+        Status = STATUS_NO_MEMORY;
+        In = __AllocatePoolWithTag(NonPagedPool, sizeof(*In), 
XENIFACE_POOL_TAG);
+        if (!In)
+            goto fail3;
+
+        In->RemoteDomain = In1->RemoteDomain;
+        In->Address = NULL;
+        In->NumberPages = In->NumberPages;
+        In->Flags = In1->Flags;
+        In->NotifyOffset = In1->NotifyOffset;
+        In->NotifyPort = In1->NotifyPort;
+    } else {
+        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;
+    Status = STATUS_INVALID_PARAMETER;
+    if (In->NumberPages == 0 || In->NumberPages > 1024 * 1024) {
+        goto fail4;
     }
 
     if ((In->Flags & XENIFACE_GNTTAB_USE_NOTIFY_OFFSET) &&
         (In->NotifyOffset >= In->NumberPages * PAGE_SIZE)) {
-        goto fail4;
+        goto fail5;
     }
 
-    status = STATUS_INVALID_BUFFER_SIZE;
+    Status = STATUS_INVALID_BUFFER_SIZE;
     if (OutLen != 
(ULONG)FIELD_OFFSET(XENIFACE_GNTTAB_PERMIT_FOREIGN_ACCESS_OUT, 
References[In->NumberPages]))
-        goto fail5;
+        goto fail6;
 
-    status = STATUS_NO_MEMORY;
-    Context = __AllocatePoolWithTag(NonPagedPool, 
sizeof(XENIFACE_GRANT_CONTEXT), XENIFACE_POOL_TAG);
+    Status = STATUS_NO_MEMORY;
+    Context = __AllocatePoolWithTag(NonPagedPool, 
sizeof(XENIFACE_GNTTAB_CONTEXT), XENIFACE_POOL_TAG);
     if (Context == NULL)
-        goto fail6;
+        goto fail7;
 
-    RtlZeroMemory(Context, sizeof(XENIFACE_GRANT_CONTEXT));
-    Context->Id.Type = XENIFACE_CONTEXT_GRANT;
-    Context->Id.Process = PsGetCurrentProcess();
-    Context->Id.RequestId = In->RequestId;
+    Context->Type = XENIFACE_GNTTAB_CONTEXT_GRANT;
+    Context->Process = PsGetCurrentProcess();
     Context->RemoteDomain = In->RemoteDomain;
+    Context->UserVa = In->Address;
     Context->NumberPages = In->NumberPages;
     Context->Flags = In->Flags;
     Context->NotifyOffset = In->NotifyOffset;
     Context->NotifyPort = In->NotifyPort;
 
-    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 = __AllocatePoolWithTag(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 = __AllocatePoolWithTag(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)
-        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;
+    __FreeCapturedBuffer(In);
+    In = NULL;
+
+    if (ControlCode == IOCTL_XENIFACE_GNTTAB_PERMIT_FOREIGN_ACCESS) {
+        Context->UseRequestId = TRUE;
+        Context->RequestId = In1->RequestId;
+        __FreeCapturedBuffer(In1);
+        In1 = NULL;
+    } else {
+        Context->UseRequestId = FALSE;
+        Context->RequestId = 0;
     }
 
-    status = STATUS_UNSUCCESSFUL;
-    if (Context->UserVa == NULL)
-        goto fail13;
+    Status = GnttabPermitForeignAccess(Fdo, Context);
+    if (!NT_SUCCESS(Status))
+        goto fail8;
 
     Trace("< Context %p, Irp %p, KernelVa %p, UserVa %p\n",
-                       Context, Irp, Context->KernelVa, Context->UserVa);
+          Context, Irp, Context->KernelVa, Context->UserVa);
 
     // Pass the result to user mode.
 #pragma prefast(suppress: 6320) // we want to catch all exceptions
@@ -277,64 +421,34 @@ IoctlGnttabPermitForeignAccess(
                                                   Context->Grants[Page]);
         }
     } except(EXCEPTION_EXECUTE_HANDLER) {
-        status = GetExceptionCode();
-        Error("Exception 0x%lx while probing/writing output buffer at %p, size 
0x%lx\n", status, Out, OutLen);
-        goto fail14;
+        Status = GetExceptionCode();
+        Error("Exception 0x%lx while probing/writing output buffer at %p, size 
0x%lx\n", Status, Out, OutLen);
+        goto fail9;
     }
 
     // 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);
+    // This also checks (again) if the request ID/address is unique for the 
calling process.
+    Irp->Tail.Overlay.DriverContext[0] = Context;
+    Status = IoCsqInsertIrpEx(&Fdo->IrpQueue, Irp, NULL, Context);
+    if (!NT_SUCCESS(Status))
+        goto fail10;
 
     return STATUS_PENDING;
 
-fail15:
-    Error("Fail15\n");
-
-fail14:
-    Error("Fail14\n");
-    MmUnmapLockedPages(Context->UserVa, Context->Mdl);
-
-fail13:
-    Error("Fail13\n");
-
-fail12:
-    Error("Fail12\n");
-
-fail11:
-    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:
     Error("Fail10\n");
-    __FreePoolWithTag(Context->KernelVa, XENIFACE_POOL_TAG);
 
 fail9:
     Error("Fail9\n");
-    __FreePoolWithTag(Context->Grants, XENIFACE_POOL_TAG);
+    GnttabStopSharing(Fdo, Context, Context->NumberPages);
 
 fail8:
     Error("Fail8\n");
+    RtlZeroMemory(Context, sizeof(*Context));
+    __FreePoolWithTag(Context, XENIFACE_POOL_TAG);
 
 fail7:
     Error("Fail7\n");
-    RtlZeroMemory(Context, sizeof(XENIFACE_GRANT_CONTEXT));
-    __FreePoolWithTag(Context, XENIFACE_POOL_TAG);
 
 fail6:
     Error("Fail6\n");
@@ -344,33 +458,34 @@ fail5:
 
 fail4:
     Error("Fail4\n");
+    __FreeCapturedBuffer(In);
 
 fail3:
     Error("Fail3\n");
-    __FreeCapturedBuffer(In);
+    __FreeCapturedBuffer(In1); // NULL-safe
 
 fail2:
     Error("Fail2\n");
 
 fail1:
-    Error("Fail1 (%08x)\n", status);
-    return status;
+    Error("Fail1 (%08x)\n", Status);
+    return Status;
 }
 
 _IRQL_requires_max_(APC_LEVEL)
 VOID
 GnttabFreeGrant(
-    __in     PXENIFACE_FDO            Fdo,
-    __inout  PXENIFACE_GRANT_CONTEXT  Context
+    __in     PXENIFACE_FDO             Fdo,
+    __inout  PXENIFACE_GNTTAB_CONTEXT  Context
 )
 {
     NTSTATUS status;
-    ULONG Page;
-
-    ASSERT(KeGetCurrentIrql() <= APC_LEVEL);
 
     Trace("Context %p\n", Context);
 
+    ASSERT(Context->Type == XENIFACE_GNTTAB_CONTEXT_GRANT);
+    ASSERT(KeGetCurrentIrql() <= APC_LEVEL);
+
     if (Context->Flags & XENIFACE_GNTTAB_USE_NOTIFY_OFFSET) {
         ((PCHAR)Context->KernelVa)[Context->NotifyOffset] = 0;
     }
@@ -382,67 +497,56 @@ GnttabFreeGrant(
             Error("failed to notify port %lu: 0x%x\n", Context->NotifyPort, 
status);
     }
 
-    // unmap from user address space
-    MmUnmapLockedPages(Context->UserVa, Context->Mdl);
+    GnttabStopSharing(Fdo, Context, Context->NumberPages);
 
-    // 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);
-    __FreePoolWithTag(Context->KernelVa, XENIFACE_POOL_TAG);
-
-    RtlZeroMemory(Context->Grants, Context->NumberPages * 
sizeof(PXENBUS_GNTTAB_ENTRY));
-    __FreePoolWithTag(Context->Grants, XENIFACE_POOL_TAG);
-
-    RtlZeroMemory(Context, sizeof(XENIFACE_GRANT_CONTEXT));
+    RtlZeroMemory(Context, sizeof(*Context));
     __FreePoolWithTag(Context, XENIFACE_POOL_TAG);
 }
 
 DECLSPEC_NOINLINE
 NTSTATUS
 IoctlGnttabRevokeForeignAccess(
-    __in  PXENIFACE_FDO     Fdo,
-    __in  PVOID             Buffer,
-    __in  ULONG             InLen,
-    __in  ULONG             OutLen
+    __in  PXENIFACE_FDO  Fdo,
+    __in  PVOID          Buffer,
+    __in  ULONG          InLen,
+    __in  ULONG          OutLen,
+    __in  ULONG          ControlCode
     )
 {
-    NTSTATUS status;
-    PXENIFACE_GNTTAB_REVOKE_FOREIGN_ACCESS_IN In = Buffer;
-    PXENIFACE_GRANT_CONTEXT Context = NULL;
-    XENIFACE_CONTEXT_ID Id;
+    NTSTATUS Status;
+    XENIFACE_GNTTAB_CONTEXT SeekContext;
     PIRP PendingIrp;
-    PXENIFACE_CONTEXT_ID ContextId;
+    PXENIFACE_GNTTAB_CONTEXT Context = NULL;
 
     UNREFERENCED_PARAMETER(OutLen);
 
-    status = STATUS_INVALID_BUFFER_SIZE;
-    if (InLen != sizeof(XENIFACE_GNTTAB_REVOKE_FOREIGN_ACCESS_IN))
-        goto fail1;
+    Status = STATUS_INVALID_BUFFER_SIZE;
 
-    Id.Type = XENIFACE_CONTEXT_GRANT;
-    Id.Process = PsGetCurrentProcess();
-    Id.RequestId = In->RequestId;
+    SeekContext.Type = XENIFACE_GNTTAB_CONTEXT_GRANT;
+    SeekContext.Process = PsGetCurrentProcess();
 
-    Trace("> Process %p, Id %lu\n", Id.Process, Id.RequestId);
+    if (ControlCode == IOCTL_XENIFACE_GNTTAB_REVOKE_FOREIGN_ACCESS) {
+        if (InLen != sizeof(XENIFACE_GNTTAB_REVOKE_FOREIGN_ACCESS_IN))
+            goto fail1;
 
-    status = STATUS_NOT_FOUND;
-    PendingIrp = IoCsqRemoveNextIrp(&Fdo->IrpQueue, &Id);
+        SeekContext.UseRequestId = TRUE;
+        SeekContext.RequestId = 
((PXENIFACE_GNTTAB_REVOKE_FOREIGN_ACCESS_IN)Buffer)->RequestId;
+        Trace("> Process %p, Id %lu\n", SeekContext.Process, 
SeekContext.RequestId);
+    } else {
+        if (InLen != sizeof(XENIFACE_GNTTAB_REVOKE_FOREIGN_ACCESS_IN_V2))
+            goto fail1;
+
+        SeekContext.UseRequestId = FALSE;
+        SeekContext.UserVa = 
((PXENIFACE_GNTTAB_REVOKE_FOREIGN_ACCESS_IN_V2)Buffer)->Address;
+        Trace("> Process %p, Address %p\n", SeekContext.Process, 
SeekContext.UserVa);
+    }
+
+    Status = STATUS_NOT_FOUND;
+    PendingIrp = IoCsqRemoveNextIrp(&Fdo->IrpQueue, &SeekContext);
     if (PendingIrp == NULL)
         goto fail2;
 
-    ContextId = PendingIrp->Tail.Overlay.DriverContext[0];
-    Context = CONTAINING_RECORD(ContextId, XENIFACE_GRANT_CONTEXT, Id);
+    Context = PendingIrp->Tail.Overlay.DriverContext[0];
     GnttabFreeGrant(Fdo, Context);
 
     PendingIrp->IoStatus.Status = STATUS_SUCCESS;
@@ -455,67 +559,103 @@ fail2:
     Error("Fail2\n");
 
 fail1:
-    Error("Fail1 (%08x)\n", status);
-    return status;
+    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
+    __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_IN In1 = NULL;
+    PXENIFACE_GNTTAB_MAP_FOREIGN_PAGES_IN_V2 In = NULL;
+    // XENIFACE_GNTTAB_MAP_FOREIGN_PAGES_OUT_V2 is the same as 
XENIFACE_GNTTAB_MAP_FOREIGN_PAGES_OUT
     PXENIFACE_GNTTAB_MAP_FOREIGN_PAGES_OUT Out = Irp->UserBuffer;
     ULONG NumberPages;
-    ULONG PageIndex;
-    PXENIFACE_MAP_CONTEXT Context;
+    PXENIFACE_GNTTAB_CONTEXT Context;
+    ULONG ControlCode = 
IoGetCurrentIrpStackLocation(Irp)->Parameters.DeviceIoControl.IoControlCode;
 
-    status = STATUS_INVALID_BUFFER_SIZE;
-    if (InLen < sizeof(XENIFACE_GNTTAB_MAP_FOREIGN_PAGES_IN) ||
-        OutLen != sizeof(XENIFACE_GNTTAB_MAP_FOREIGN_PAGES_OUT)) {
+    ASSERT(ControlCode == IOCTL_XENIFACE_GNTTAB_MAP_FOREIGN_PAGES || 
ControlCode == IOCTL_XENIFACE_GNTTAB_MAP_FOREIGN_PAGES_V2);
+
+    status = STATUS_NO_MEMORY;
+    Context = __AllocatePoolWithTag(NonPagedPool, sizeof(*Context), 
XENIFACE_POOL_TAG);
+    if (Context == NULL)
         goto fail1;
-    }
 
-    // This IOCTL uses METHOD_NEITHER so we directly access user memory.
+    Context->Type = XENIFACE_GNTTAB_CONTEXT_MAP;
+    Context->Process = PsGetCurrentProcess();
 
-    // 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]);
+    // This IOCTL uses METHOD_NEITHER so we directly access user memory.
+    if (ControlCode == IOCTL_XENIFACE_GNTTAB_MAP_FOREIGN_PAGES) {
+        status = STATUS_INVALID_BUFFER_SIZE;
+        if (InLen < sizeof(XENIFACE_GNTTAB_MAP_FOREIGN_PAGES_IN)
+            || OutLen != sizeof(XENIFACE_GNTTAB_MAP_FOREIGN_PAGES_OUT))
+            goto fail2;
+
+        In1 = Buffer;
+        NumberPages = (InLen - 
(ULONG)FIELD_OFFSET(XENIFACE_GNTTAB_MAP_FOREIGN_PAGES_IN, References)) / 
sizeof(ULONG);
+        status = __CaptureUserBuffer(Buffer, InLen, &In1);
+        if (!NT_SUCCESS(status))
+            goto fail3;
+
+        Context->UseRequestId = TRUE;
+        Context->RequestId = In1->RequestId;
+
+        // legacy IOCTL, convert the input to v2
+        status = STATUS_NO_MEMORY;
+        In = __AllocatePoolWithTag(NonPagedPool, sizeof(*In), 
XENIFACE_POOL_TAG);
+        if (In == NULL)
+            goto fail4;
+
+        In->RemoteDomain = In1->RemoteDomain;
+        In->NumberPages = In1->NumberPages;
+        In->NotifyOffset = In1->NotifyOffset;
+        In->NotifyPort = In1->NotifyPort;
+        In->Flags = In1->Flags;
+        memcpy(&In->References, &In1->References, NumberPages * sizeof(ULONG));
+
+        __FreeCapturedBuffer(In1);
+        In1 = NULL;
+    } else {
+        status = STATUS_INVALID_BUFFER_SIZE;
+        if (InLen < sizeof(XENIFACE_GNTTAB_MAP_FOREIGN_PAGES_IN_V2)
+            || OutLen != sizeof(XENIFACE_GNTTAB_MAP_FOREIGN_PAGES_OUT_V2))
+            goto fail2;
+
+        In = Buffer;
+        NumberPages = (InLen - 
(ULONG)FIELD_OFFSET(XENIFACE_GNTTAB_MAP_FOREIGN_PAGES_IN_V2, References)) / 
sizeof(ULONG);
+        status = __CaptureUserBuffer(Buffer, InLen, &In);
+        if (!NT_SUCCESS(status))
+            goto fail3;
 
-    status = __CaptureUserBuffer(Buffer, InLen, &In);
-    if (!NT_SUCCESS(status))
-        goto fail2;
+        Context->UseRequestId = FALSE;
+        Context->RequestId = 0;
+    }
 
+    // At this point we only access In.
     status = STATUS_INVALID_PARAMETER;
     if (In->NumberPages == 0 ||
         In->NumberPages > 1024 * 1024 ||
         In->NumberPages != NumberPages) {
-        goto fail3;
+        goto fail5;
     }
 
     if ((In->Flags & XENIFACE_GNTTAB_USE_NOTIFY_OFFSET) &&
         (In->NotifyOffset >= In->NumberPages * PAGE_SIZE)) {
-        goto fail4;
+        goto fail6;
     }
 
     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 = __AllocatePoolWithTag(NonPagedPool, 
sizeof(XENIFACE_MAP_CONTEXT), XENIFACE_POOL_TAG);
-    if (Context == NULL)
-        goto fail6;
+    if (InLen != (ULONG)FIELD_OFFSET(XENIFACE_GNTTAB_MAP_FOREIGN_PAGES_IN_V2, 
References[In->NumberPages]))
+        goto fail7;
 
-    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;
@@ -523,15 +663,17 @@ IoctlGnttabMapForeignPages(
     Context->NotifyPort = In->NotifyPort;
 
     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);
+          Context->RemoteDomain, Context->NumberPages, Context->Flags, 
Context->NotifyOffset, Context->NotifyPort,
+          Context->Process, Context->RequestId);
 
-    for (PageIndex = 0; PageIndex < In->NumberPages; PageIndex++)
+#if DBG
+    for (ULONG PageIndex = 0; PageIndex < In->NumberPages; PageIndex++)
         Info("> Ref %d\n", In->References[PageIndex]);
+#endif
 
     status = STATUS_INVALID_PARAMETER;
-    if (FindGnttabIrp(Fdo, &Context->Id) != NULL)
-        goto fail7;
+    if (FindGnttabIrp(Fdo, Context) != NULL)
+        goto fail8;
 
     status = XENBUS_GNTTAB(MapForeignPages,
                            &Fdo->GnttabInterface,
@@ -542,41 +684,36 @@ IoctlGnttabMapForeignPages(
                            &Context->Address);
 
     if (!NT_SUCCESS(status))
-        goto fail8;
+        goto fail9;
 
-    status = STATUS_NO_MEMORY;
+    status = STATUS_INSUFFICIENT_VIRTUAL_ADDR_RESOURCES;
     Context->KernelVa = MmMapIoSpace(Context->Address, Context->NumberPages * 
PAGE_SIZE, MmCached);
     if (Context->KernelVa == NULL)
-        goto fail9;
+        goto fail10;
 
     status = STATUS_NO_MEMORY;
     Context->Mdl = IoAllocateMdl(Context->KernelVa, Context->NumberPages * 
PAGE_SIZE, FALSE, FALSE, NULL);
     if (Context->Mdl == NULL)
-        goto fail10;
+        goto fail11;
 
     MmBuildMdlForNonPagedPool(Context->Mdl);
 
     // map into user mode
 #pragma prefast(suppress: 6320) // we want to catch all exceptions
-    __try {
+    try {
         Context->UserVa = MmMapLockedPagesSpecifyCache(Context->Mdl,
                                                        UserMode,
                                                        MmCached,
                                                        NULL,
                                                        FALSE,
                                                        NormalPagePriority);
-    }
-    __except (EXCEPTION_EXECUTE_HANDLER) {
+    } except (EXCEPTION_EXECUTE_HANDLER) {
         status = GetExceptionCode();
-        goto fail11;
-    }
-
-    status = STATUS_UNSUCCESSFUL;
-    if (Context->UserVa == NULL)
         goto fail12;
+    }
 
     Trace("< Context %p, Irp %p, Address %p, KernelVa %p, UserVa %p\n",
-                       Context, Irp, Context->Address, Context->KernelVa, 
Context->UserVa);
+          Context, Irp, Context->Address, Context->KernelVa, Context->UserVa);
 
     // Pass the result to user mode.
 #pragma prefast(suppress: 6320) // we want to catch all exceptions
@@ -591,8 +728,8 @@ IoctlGnttabMapForeignPages(
 
     // 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);
+    Irp->Tail.Overlay.DriverContext[0] = Context;
+    status = IoCsqInsertIrpEx(&Fdo->IrpQueue, Irp, NULL, Context);
     if (!NT_SUCCESS(status))
         goto fail14;
 
@@ -609,45 +746,46 @@ fail13:
 
 fail12:
     Error("Fail12\n");
+    IoFreeMdl(Context->Mdl);
 
 fail11:
     Error("Fail11\n");
-    IoFreeMdl(Context->Mdl);
+    MmUnmapIoSpace(Context->KernelVa, Context->NumberPages * PAGE_SIZE);
 
 fail10:
     Error("Fail10\n");
-    MmUnmapIoSpace(Context->KernelVa, Context->NumberPages * PAGE_SIZE);
-
-fail9:
-    Error("Fail9\n");
     ASSERT(NT_SUCCESS(XENBUS_GNTTAB(UnmapForeignPages,
                                     &Fdo->GnttabInterface,
                                     Context->Address
                                     )));
 
+fail9:
+    Error("Fail9\n");
+
 fail8:
     Error("Fail8\n");
 
 fail7:
     Error("Fail7\n");
-    RtlZeroMemory(Context, sizeof(XENIFACE_MAP_CONTEXT));
-    __FreePoolWithTag(Context, XENIFACE_POOL_TAG);
 
 fail6:
     Error("Fail6\n");
 
 fail5:
     Error("Fail5\n");
+    __FreeCapturedBuffer(In);
 
 fail4:
     Error("Fail4\n");
+    __FreeCapturedBuffer(In1);
 
 fail3:
     Error("Fail3\n");
-    __FreeCapturedBuffer(In);
 
 fail2:
     Error("Fail2\n");
+    RtlZeroMemory(Context, sizeof(*Context));
+    __FreePoolWithTag(Context, XENIFACE_POOL_TAG);
 
 fail1:
     Error("Fail1 (%08x)\n", status);
@@ -658,12 +796,13 @@ _IRQL_requires_max_(APC_LEVEL)
 DECLSPEC_NOINLINE
 VOID
 GnttabFreeMap(
-    __in     PXENIFACE_FDO            Fdo,
-    __inout  PXENIFACE_MAP_CONTEXT    Context
+    __in     PXENIFACE_FDO             Fdo,
+    __inout  PXENIFACE_GNTTAB_CONTEXT  Context
     )
 {
     NTSTATUS status;
 
+    ASSERT(Context->Type == XENIFACE_GNTTAB_CONTEXT_MAP);
     ASSERT(KeGetCurrentIrql() <= APC_LEVEL);
 
     Trace("Context %p\n", Context);
@@ -694,45 +833,61 @@ GnttabFreeMap(
 
     ASSERT(NT_SUCCESS(status));
 
-    RtlZeroMemory(Context, sizeof(XENIFACE_MAP_CONTEXT));
+    RtlZeroMemory(Context, sizeof(*Context));
     __FreePoolWithTag(Context, XENIFACE_POOL_TAG);
 }
 
 DECLSPEC_NOINLINE
 NTSTATUS
 IoctlGnttabUnmapForeignPages(
-    __in  PXENIFACE_FDO     Fdo,
-    __in  PVOID             Buffer,
-    __in  ULONG             InLen,
-    __in  ULONG             OutLen
+    __in  PXENIFACE_FDO  Fdo,
+    __in  PVOID          Buffer,
+    __in  ULONG          InLen,
+    __in  ULONG          OutLen,
+    __in  ULONG          ControlCode
     )
 {
     NTSTATUS status;
-    PXENIFACE_GNTTAB_UNMAP_FOREIGN_PAGES_IN In = Buffer;
-    PXENIFACE_MAP_CONTEXT Context = NULL;
-    XENIFACE_CONTEXT_ID Id;
+    XENIFACE_GNTTAB_CONTEXT SeekContext;
+    PXENIFACE_GNTTAB_CONTEXT Context;
     PIRP PendingIrp;
-    PXENIFACE_CONTEXT_ID ContextId;
 
-    status = STATUS_INVALID_BUFFER_SIZE;
-    if (InLen != sizeof(XENIFACE_GNTTAB_UNMAP_FOREIGN_PAGES_IN) ||
-        OutLen != 0) {
-        goto fail1;
-    }
+    ASSERT(ControlCode == IOCTL_XENIFACE_GNTTAB_UNMAP_FOREIGN_PAGES
+        || ControlCode == IOCTL_XENIFACE_GNTTAB_UNMAP_FOREIGN_PAGES_V2);
+
+    SeekContext.Type = XENIFACE_GNTTAB_CONTEXT_MAP;
+    SeekContext.Process = PsGetCurrentProcess();
+
+    if (ControlCode == IOCTL_XENIFACE_GNTTAB_UNMAP_FOREIGN_PAGES) {
+        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;
+        PXENIFACE_GNTTAB_UNMAP_FOREIGN_PAGES_IN In = Buffer;
+        SeekContext.UseRequestId = TRUE;
+        SeekContext.RequestId = In->RequestId;
 
-    Trace("> Process %p, Id %lu\n", Id.Process, Id.RequestId);
+        Trace("> Process %p, Id %lu\n", SeekContext.Process, 
SeekContext.RequestId);
+    } else {
+        status = STATUS_INVALID_BUFFER_SIZE;
+        if (InLen != sizeof(XENIFACE_GNTTAB_UNMAP_FOREIGN_PAGES_IN_V2) || 
OutLen != 0) {
+            goto fail1;
+        }
+
+        PXENIFACE_GNTTAB_UNMAP_FOREIGN_PAGES_IN_V2 In = Buffer;
+        SeekContext.UseRequestId = FALSE;
+        SeekContext.UserVa = In->Address;
+
+        Trace("> Process %p, UserVa %p\n", SeekContext.Process, 
SeekContext.UserVa);
+    }
 
     status = STATUS_NOT_FOUND;
-    PendingIrp = IoCsqRemoveNextIrp(&Fdo->IrpQueue, &Id);
+    PendingIrp = IoCsqRemoveNextIrp(&Fdo->IrpQueue, &SeekContext);
     if (PendingIrp == NULL)
         goto fail2;
 
-    ContextId = PendingIrp->Tail.Overlay.DriverContext[0];
-    Context = CONTAINING_RECORD(ContextId, XENIFACE_MAP_CONTEXT, Id);
+    Context = PendingIrp->Tail.Overlay.DriverContext[0];
     GnttabFreeMap(Fdo, Context);
 
     PendingIrp->IoStatus.Status = STATUS_SUCCESS;
diff --git a/src/xeniface/ioctls.c b/src/xeniface/ioctls.c
index 479f0f6..6282e77 100644
--- a/src/xeniface/ioctls.c
+++ b/src/xeniface/ioctls.c
@@ -151,7 +151,7 @@ fail1:
 _IRQL_requires_(PASSIVE_LEVEL) // EvtchnFree calls KeFlushQueuedDpcs
 VOID
 XenIfaceCleanup(
-    __in  PXENIFACE_FDO Fdo,
+    __in      PXENIFACE_FDO Fdo,
     __in_opt  PFILE_OBJECT  FileObject
     )
 {
@@ -247,12 +247,13 @@ XenIfaceIoctl(
     PVOID               Buffer = Irp->AssociatedIrp.SystemBuffer;
     ULONG               InLen = 
Stack->Parameters.DeviceIoControl.InputBufferLength;
     ULONG               OutLen = 
Stack->Parameters.DeviceIoControl.OutputBufferLength;
+    ULONG               ControlCode = 
Stack->Parameters.DeviceIoControl.IoControlCode;
 
     status = STATUS_DEVICE_NOT_READY;
     if (Fdo->InterfacesAcquired == FALSE)
         goto done;
 
-    switch (Stack->Parameters.DeviceIoControl.IoControlCode) {
+    switch (ControlCode) {
         // store
     case IOCTL_XENIFACE_STORE_READ:
         status = IoctlStoreRead(Fdo, (PCHAR)Buffer, InLen, OutLen, 
&Irp->IoStatus.Information);
@@ -308,16 +309,32 @@ XenIfaceIoctl(
         status = IoctlGnttabPermitForeignAccess(Fdo, 
Stack->Parameters.DeviceIoControl.Type3InputBuffer, InLen, OutLen, Irp);
         break;
 
+    case IOCTL_XENIFACE_GNTTAB_PERMIT_FOREIGN_ACCESS_V2: // 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);
+        status = IoctlGnttabRevokeForeignAccess(Fdo, Buffer, InLen, OutLen, 
ControlCode);
+        break;
+
+    case IOCTL_XENIFACE_GNTTAB_REVOKE_FOREIGN_ACCESS_V2:
+        status = IoctlGnttabRevokeForeignAccess(Fdo, Buffer, InLen, OutLen, 
ControlCode);
         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_MAP_FOREIGN_PAGES_V2: // 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);
+        status = IoctlGnttabUnmapForeignPages(Fdo, Buffer, InLen, OutLen, 
ControlCode);
+        break;
+
+    case IOCTL_XENIFACE_GNTTAB_UNMAP_FOREIGN_PAGES_V2:
+        status = IoctlGnttabUnmapForeignPages(Fdo, Buffer, InLen, OutLen, 
ControlCode);
         break;
 
         // suspend
diff --git a/src/xeniface/ioctls.h b/src/xeniface/ioctls.h
index 09946fd..b4c8de2 100644
--- a/src/xeniface/ioctls.h
+++ b/src/xeniface/ioctls.h
@@ -36,17 +36,6 @@
 
 #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;
     PCHAR                  Path;
@@ -72,33 +61,33 @@ typedef struct _XENIFACE_SUSPEND_CONTEXT {
     PVOID                   FileObject;
 } XENIFACE_SUSPEND_CONTEXT, *PXENIFACE_SUSPEND_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;
+typedef enum _XENIFACE_GNTTAB_CONTEXT_TYPE {
+    XENIFACE_GNTTAB_CONTEXT_GRANT = 1,
+    XENIFACE_GNTTAB_CONTEXT_MAP
+} XENIFACE_GNTTAB_CONTEXT_TYPE;
+
+#pragma warning(push)
+#pragma warning(disable:4201) // nonstandard extension used: nameless 
struct/union
+typedef struct _XENIFACE_GNTTAB_CONTEXT {
+    LIST_ENTRY                   Entry;
+    XENIFACE_GNTTAB_CONTEXT_TYPE Type;
+    BOOLEAN                      UseRequestId; // true for legacy IOCTLs
+    ULONG                        RequestId;
+    PEPROCESS                    Process;
+    USHORT                       RemoteDomain;
+    ULONG                        NumberPages;
+    XENIFACE_GNTTAB_PAGE_FLAGS   Flags;
+    ULONG                        NotifyOffset;
+    ULONG                        NotifyPort;
+    union {
+        PXENBUS_GNTTAB_ENTRY     *Grants; // permit
+        PHYSICAL_ADDRESS         Address; // map
+    };
+    PVOID                        KernelVa;
+    PVOID                        UserVa;
+    PMDL                         Mdl;
+} XENIFACE_GNTTAB_CONTEXT, *PXENIFACE_GNTTAB_CONTEXT;
+#pragma warning(pop)
 
 NTSTATUS
 __CaptureUserBuffer(
@@ -295,7 +284,8 @@ IoctlGnttabRevokeForeignAccess(
     __in  PXENIFACE_FDO     Fdo,
     __in  PVOID             Buffer,
     __in  ULONG             InLen,
-    __in  ULONG             OutLen
+    __in  ULONG             OutLen,
+    __in  ULONG             ControlCode
     );
 
 DECLSPEC_NOINLINE
@@ -314,7 +304,8 @@ IoctlGnttabUnmapForeignPages(
     __in  PXENIFACE_FDO     Fdo,
     __in  PVOID             Buffer,
     __in  ULONG             InLen,
-    __in  ULONG             OutLen
+    __in  ULONG             OutLen,
+    __in  ULONG             ControlCode
     );
 
 _Acquires_exclusive_lock_(((PXENIFACE_FDO)Argument)->GnttabCacheLock)
@@ -342,14 +333,14 @@ _IRQL_requires_max_(APC_LEVEL)
 VOID
 GnttabFreeGrant(
     __in     PXENIFACE_FDO Fdo,
-    __inout  PXENIFACE_GRANT_CONTEXT Context
+    __inout  PXENIFACE_GNTTAB_CONTEXT Context
     );
 
 _IRQL_requires_max_(APC_LEVEL)
 VOID
 GnttabFreeMap(
     __in     PXENIFACE_FDO Fdo,
-    __inout  PXENIFACE_MAP_CONTEXT Context
+    __inout  PXENIFACE_GNTTAB_CONTEXT Context
     );
 
 NTSTATUS
diff --git a/src/xeniface/irp_queue.c b/src/xeniface/irp_queue.c
index 37191bc..dcc5ba0 100644
--- a/src/xeniface/irp_queue.c
+++ b/src/xeniface/irp_queue.c
@@ -40,14 +40,14 @@ NTSTATUS
 CsqInsertIrpEx(
     _In_  PIO_CSQ Csq,
     _In_  PIRP    Irp,
-    _In_  PVOID   InsertContext // PXENIFACE_CONTEXT_ID
+    _In_  PVOID   InsertContext // PXENIFACE_GNTTAB_CONTEXT
     )
 {
     PXENIFACE_FDO Fdo;
 
     Fdo = CONTAINING_RECORD(Csq, XENIFACE_FDO, IrpQueue);
 
-    // Fail if a request with the same ID already exists.
+    // Fail if a request with the same ID/address already exists.
     if (CsqPeekNextIrp(Csq, NULL, InsertContext) != NULL)
         return STATUS_INVALID_PARAMETER;
 
@@ -70,16 +70,16 @@ PIRP
 CsqPeekNextIrp(
     _In_      PIO_CSQ Csq,
     _In_opt_  PIRP    Irp,
-    _In_opt_  PVOID   PeekContext // PXENIFACE_CONTEXT_ID
+    _In_opt_  PVOID   PeekContext // PXENIFACE_GNTTAB_CONTEXT
     )
 {
-    PXENIFACE_FDO        Fdo;
-    PIRP                 NextIrp = NULL;
-    PLIST_ENTRY          Head, NextEntry;
-    PXENIFACE_CONTEXT_ID Id, TargetId;
+    PXENIFACE_FDO            Fdo;
+    PIRP                     NextIrp = NULL;
+    PLIST_ENTRY              Head, NextEntry;
+    PXENIFACE_GNTTAB_CONTEXT Context, TargetContext;
 
     Fdo = CONTAINING_RECORD(Csq, XENIFACE_FDO, IrpQueue);
-    TargetId = PeekContext;
+    TargetContext = PeekContext;
     Head = &Fdo->IrpList;
 
     // If the IRP is NULL, we will start peeking from the list head,
@@ -96,8 +96,15 @@ CsqPeekNextIrp(
         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)
+            Context = NextIrp->Tail.Overlay.DriverContext[0];
+
+            BOOL Match = TargetContext->Type == Context->Type;
+            if (TargetContext->UseRequestId)
+                Match = Match && (TargetContext->RequestId == 
Context->RequestId);
+            else
+                Match = Match && (TargetContext->UserVa == Context->UserVa);
+
+            if (Match)
                 break;
         } else {
             break;
diff --git a/src/xeniface/irp_queue.h b/src/xeniface/irp_queue.h
index 6b0e28f..04b9877 100644
--- a/src/xeniface/irp_queue.h
+++ b/src/xeniface/irp_queue.h
@@ -51,7 +51,7 @@ PIRP
 CsqPeekNextIrp(
     _In_      PIO_CSQ Csq,
     _In_opt_  PIRP    Irp,
-    _In_opt_  PVOID   PeekContext // PXENIFACE_CONTEXT_ID
+    _In_opt_  PVOID   PeekContext // PXENIFACE_GNTTAB_CONTEXT
     );
 
 _IRQL_raises_(DISPATCH_LEVEL)
-- 
2.40.1.windows.1




 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.