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

[win-pv-devel] [PATCH 3/3] Add XenDisk device upper filter



XenDisk intercepts and translates IOCTL_STORAGE_MANAGE_DATA_SET_ATTRIBUTES
into SCSIOP_UNMAP SRBs. Storport will pass on these SCSIOP_UNMAP requests
to XenVbd, which will create the appropriate BLKIF_OP_DISCARD requests.
XenDisk is only neccessary because Storport does not do the translation
to SCSIOP_UNMAP SRBs.

Signed-off-by: Owen smith <owen.smith@xxxxxxxxxx>
---
 build.py                            |    1 +
 src/xendisk/assert.h                |  220 ++++
 src/xendisk/debug.h                 |   94 ++
 src/xendisk/driver.c                |  203 ++++
 src/xendisk/driver.h                |   68 ++
 src/xendisk/fdo.c                   | 1863 ++++++++++++++++++++++++++++++
 src/xendisk/fdo.h                   |   78 ++
 src/xendisk/mutex.h                 |  113 ++
 src/xendisk/pdo.c                   | 2126 +++++++++++++++++++++++++++++++++++
 src/xendisk/pdo.h                   |   90 ++
 src/xendisk/thread.c                |  225 ++++
 src/xendisk/thread.h                |   74 ++
 src/xendisk/types.h                 |   53 +
 src/xendisk/util.h                  |  222 ++++
 src/xendisk/xendisk.rc              |   56 +
 src/xenvbd.inf                      |   18 +
 vs2012/package/package.vcxproj      |    5 +-
 vs2012/xendisk/xendisk.vcxproj      |   84 ++
 vs2012/xendisk/xendisk.vcxproj.user |    8 +
 vs2012/xenvbd.sln                   |   30 +-
 vs2013/package/package.vcxproj      |    5 +-
 vs2013/xendisk/xendisk.user         |    8 +
 vs2013/xendisk/xendisk.vcxproj      |  112 ++
 vs2013/xenvbd.sln                   |   27 +
 24 files changed, 5780 insertions(+), 3 deletions(-)
 create mode 100644 src/xendisk/assert.h
 create mode 100644 src/xendisk/debug.h
 create mode 100644 src/xendisk/driver.c
 create mode 100644 src/xendisk/driver.h
 create mode 100644 src/xendisk/fdo.c
 create mode 100644 src/xendisk/fdo.h
 create mode 100644 src/xendisk/mutex.h
 create mode 100644 src/xendisk/pdo.c
 create mode 100644 src/xendisk/pdo.h
 create mode 100644 src/xendisk/thread.c
 create mode 100644 src/xendisk/thread.h
 create mode 100644 src/xendisk/types.h
 create mode 100644 src/xendisk/util.h
 create mode 100644 src/xendisk/xendisk.rc
 create mode 100644 vs2012/xendisk/xendisk.vcxproj
 create mode 100644 vs2012/xendisk/xendisk.vcxproj.user
 create mode 100644 vs2013/xendisk/xendisk.user
 create mode 100644 vs2013/xendisk/xendisk.vcxproj

diff --git a/build.py b/build.py
index 26261ad..3a71d0c 100644
--- a/build.py
+++ b/build.py
@@ -368,6 +368,7 @@ if __name__ == '__main__':
 
     if len(sys.argv) <= 2 or sdv[sys.argv[2]]:
         run_sdv('xencrsh', driver, vs)
+        run_sdv('xendisk', driver, vs)
         run_sdv('xenvbd', driver, vs)
 
     archive(driver + '\\source.tgz', manifest().splitlines(), tgz=True)
diff --git a/src/xendisk/assert.h b/src/xendisk/assert.h
new file mode 100644
index 0000000..cd571bd
--- /dev/null
+++ b/src/xendisk/assert.h
@@ -0,0 +1,220 @@
+/* Copyright (c) Citrix Systems Inc.
+ * All rights reserved.
+ * 
+ * Redistribution and use in source and binary forms, 
+ * with or without modification, are permitted provided 
+ * that the following conditions are met:
+ * 
+ * *   Redistributions of source code must retain the above 
+ *     copyright notice, this list of conditions and the 
+ *     following disclaimer.
+ * *   Redistributions in binary form must reproduce the above 
+ *     copyright notice, this list of conditions and the 
+ *     following disclaimer in the documentation and/or other 
+ *     materials provided with the distribution.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND 
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, 
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR 
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 
+ * SUCH DAMAGE.
+ */ 
+
+#ifndef _XENDISK_ASSERT_H
+#define _XENDISK_ASSERT_H
+
+#include <ntddk.h>
+
+#include "debug.h"
+
+static FORCEINLINE VOID
+__BugCheck(
+    __in  ULONG       Code,
+    __in_opt ULONG_PTR   Parameter1,
+    __in_opt ULONG_PTR   Parameter2,
+    __in_opt ULONG_PTR   Parameter3,
+    __in_opt ULONG_PTR   Parameter4
+    )
+{
+#pragma prefast(suppress:28159)
+    KeBugCheckEx(Code,
+                 Parameter1,
+                 Parameter2,
+                 Parameter3,
+                 Parameter4);
+}
+
+#define ASSERTION_FAILURE   0x0000DEAD
+
+
+#define BUG(_TEXT)                                              \
+        do {                                                    \
+            const CHAR  *_Text = (_TEXT);                       \
+            const CHAR  *_File = __FILE__;                      \
+            ULONG       _Line = __LINE__;                       \
+                                                                \
+            Error("BUG: " _TEXT "\n");                          \
+            __BugCheck(ASSERTION_FAILURE,                       \
+                       (ULONG_PTR)_Text,                        \
+                       (ULONG_PTR)_File,                        \
+                       (ULONG_PTR)_Line,                        \
+                       0);                                      \
+        } while (FALSE)
+
+#define BUG_MSG(_TEXT1, _TEXT2)                                 \
+        do {                                                    \
+            const CHAR  *_Text1 = (_TEXT1);                     \
+            const CHAR  *_Text2 = (_TEXT2);                     \
+            const CHAR  *_File = __FILE__;                      \
+            ULONG       _Line = __LINE__;                       \
+                                                                \
+            Error("BUG: " _TEXT1 " %s\n", _Text2);              \
+            __BugCheck(ASSERTION_FAILURE,                       \
+                       (ULONG_PTR)_Text1,                       \
+                       (ULONG_PTR)_File,                        \
+                       (ULONG_PTR)_Line,                        \
+                       (ULONG_PTR)_Text2);                      \
+        } while (FALSE)
+
+#define BUG_ON(_EXP)                           \
+        if (_EXP) BUG(#_EXP)
+
+#define BUG_ON_MSG(_EXP, _TEXT)                \
+        if (_EXP) BUG_MSG(#_EXP, _TEXT)
+
+#if DBG
+
+#define __NT_ASSERT(_EXP)                                       \
+        ((!(_EXP)) ?                                            \
+        (Error("ASSERTION FAILED: " #_EXP "\n"),                \
+         __annotation(L"Debug", L"AssertFail", L#_EXP),         \
+         DbgRaiseAssertionFailure(), FALSE) :                   \
+        TRUE)
+
+#define __NT_ASSERT_MSG(_EXP, _TEXT)                            \
+        ((!(_EXP)) ?                                            \
+        (Error("ASSERTION FAILED: " #_EXP " " #_TEXT "\n"),     \
+         __annotation(L"Debug", L"AssertFail", L#_EXP),         \
+         DbgRaiseAssertionFailure(), FALSE) :                   \
+        TRUE)
+
+#define __ASSERT(_EXP)              __NT_ASSERT(_EXP)
+#define __ASSERT_MSG(_EXP, _TEXT)   __NT_ASSERT_MSG(_EXP, _TEXT)
+
+#else   // DBG
+
+#define __ASSERT(_EXP)              BUG_ON(!(_EXP))
+#define __ASSERT_MSG(_EXP, _TEXT)   BUG_ON_MSG(!(_EXP), _TEXT)
+
+#endif  // DBG
+
+#undef  ASSERT
+
+#define ASSERT(_EXP)                    \
+        do {                            \
+            __ASSERT(_EXP);             \
+            __analysis_assume(_EXP);    \
+        } while (FALSE)
+
+#define ASSERT_MSG(_EXP, _TEXT)         \
+        do {                            \
+            __ASSERT_MSG(_EXP, _TEXT);  \
+            __analysis_assume(_EXP);    \
+        } while (FALSE)
+
+#define ASSERT3U(_X, _OP, _Y)                       \
+        do {                                        \
+            ULONGLONG   _Lval = (ULONGLONG)(_X);    \
+            ULONGLONG   _Rval = (ULONGLONG)(_Y);    \
+            if (!(_Lval _OP _Rval)) {               \
+                Error("%s = %llu\n", #_X, _Lval);   \
+                Error("%s = %llu\n", #_Y, _Rval);   \
+                ASSERT(_X _OP _Y);                  \
+            }                                       \
+        } while (FALSE)
+
+#define ASSERT3S(_X, _OP, _Y)                       \
+        do {                                        \
+            LONGLONG    _Lval = (LONGLONG)(_X);     \
+            LONGLONG    _Rval = (LONGLONG)(_Y);     \
+            if (!(_Lval _OP _Rval)) {               \
+                Error("%s = %lld\n", #_X, _Lval);   \
+                Error("%s = %lld\n", #_Y, _Rval);   \
+                ASSERT(_X _OP _Y);                  \
+            }                                       \
+        } while (FALSE)
+
+#define ASSERT3P(_X, _OP, _Y)                       \
+        do {                                        \
+            PVOID   _Lval = (PVOID)(_X);            \
+            PVOID   _Rval = (PVOID)(_Y);            \
+            if (!(_Lval _OP _Rval)) {               \
+                Error("%s = %p\n", #_X, _Lval);     \
+                Error("%s = %p\n", #_Y, _Rval);     \
+                ASSERT(_X _OP _Y);                  \
+            }                                       \
+        } while (FALSE)
+
+#define ASSERTREFCOUNT(_X, _OP, _Y, _Z)             \
+        do {                                        \
+            LONG    _L = (LONG)(_X);                \
+            LONG    _R = (LONG)(_Y);                \
+            if (!(_L _OP _R)) {                     \
+                Error("%s:%s = %d\n", (_Z), #_X, _L); \
+                Error("%s:%s = %d\n", (_Z), #_Y, _R); \
+                ASSERT_MSG(_X _OP _Y, (_Z));        \
+            }                                       \
+        } while (FALSE)
+
+#ifndef TEST_MEMORY
+#define TEST_MEMORY DBG
+#endif
+
+#if TEST_MEMORY
+
+__checkReturn
+static __inline BOOLEAN
+_IsZeroMemory(
+    __in const PCHAR Caller,
+    __in const PCHAR Name,
+    __in PVOID       Buffer,
+    __in ULONG       Length
+    )
+{
+    ULONG           Offset;
+
+    Offset = 0;
+    while (Offset < Length) {
+        if (*((PUCHAR)Buffer + Offset) != 0) {
+            Error("%s: non-zero byte in %s (0x%p+0x%x)\n", Caller, Name, 
Buffer, Offset);
+            return FALSE;
+        }
+        Offset++;
+    }
+
+    return TRUE;
+}
+
+#define IsZeroMemory(_Buffer, _Length) \
+        _IsZeroMemory(__FUNCTION__, #_Buffer, (_Buffer), (_Length))
+
+#else   // TEST_MEMORY
+
+#define IsZeroMemory(_Buffer, _Length)  TRUE
+
+#endif  // TEST_MEMORY
+
+#define IMPLY(_X, _Y)   (!(_X) || (_Y))
+#define EQUIV(_X, _Y)   (IMPLY((_X), (_Y)) && IMPLY((_Y), (_X)))
+
+#endif  // _XENDISK_ASSERT_H
+
diff --git a/src/xendisk/debug.h b/src/xendisk/debug.h
new file mode 100644
index 0000000..f592f86
--- /dev/null
+++ b/src/xendisk/debug.h
@@ -0,0 +1,94 @@
+/* Copyright (c) Citrix Systems Inc.
+ * All rights reserved.
+ * 
+ * Redistribution and use in source and binary forms, 
+ * with or without modification, are permitted provided 
+ * that the following conditions are met:
+ * 
+ * *   Redistributions of source code must retain the above 
+ *     copyright notice, this list of conditions and the 
+ *     following disclaimer.
+ * *   Redistributions in binary form must reproduce the above 
+ *     copyright notice, this list of conditions and the 
+ *     following disclaimer in the documentation and/or other 
+ *     materials provided with the distribution.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND 
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, 
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR 
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 
+ * SUCH DAMAGE.
+ */ 
+
+#ifndef _DEBUG_H
+#define _DEBUG_H
+
+#include <ntddk.h>
+#include <stdarg.h>
+
+#ifdef  _SDV_
+#define __MODULE__ ""
+#endif
+
+// DEBUG_FILTER_MASKs
+// Set these to see relevant output
+// ERROR        0x00000001
+// WARNING      0x00000002
+// TRACE        0x00000004
+// INFO         0x00000008
+
+#pragma warning(disable:4127)   // conditional expression is constant
+
+//
+// Debug Output and Logging
+//
+static __inline VOID
+__DebugMessage(
+    __in    ULONG       Level,
+    __in __nullterminated const CHAR  *Prefix,
+    __in __nullterminated const CHAR  *Format,
+    ...
+    )
+{
+    va_list         Arguments;
+
+    va_start(Arguments, Format);
+
+#pragma prefast(suppress:6001) // Using uninitialized memory
+    vDbgPrintExWithPrefix(Prefix,
+                          DPFLTR_IHVDRIVER_ID,
+                          Level,
+                          Format,
+                          Arguments);
+    va_end(Arguments);
+}
+
+#define Error(...)  \
+        __DebugMessage(DPFLTR_ERROR_LEVEL, __MODULE__ "|" __FUNCTION__ ":", 
__VA_ARGS__)
+
+#define Warning(...)  \
+        __DebugMessage(DPFLTR_WARNING_LEVEL, __MODULE__ "|" __FUNCTION__ ":", 
__VA_ARGS__)
+
+#if DBG
+#define Trace(...)  \
+        __DebugMessage(DPFLTR_TRACE_LEVEL, __MODULE__ "|" __FUNCTION__ ":", 
__VA_ARGS__)
+#else   // DBG
+#define Trace(...) \
+        (VOID)(__VA_ARGS__)
+#endif  // DBG
+
+#define Verbose(...) \
+        __DebugMessage(DPFLTR_INFO_LEVEL, __MODULE__ "|" __FUNCTION__ ":", 
__VA_ARGS__)
+
+#include "assert.h"
+
+#endif  // _DEBUG_H
diff --git a/src/xendisk/driver.c b/src/xendisk/driver.c
new file mode 100644
index 0000000..743fc61
--- /dev/null
+++ b/src/xendisk/driver.c
@@ -0,0 +1,203 @@
+/* Copyright (c) Citrix Systems Inc.
+ * All rights reserved.
+ * 
+ * Redistribution and use in source and binary forms, 
+ * with or without modification, are permitted provided 
+ * that the following conditions are met:
+ * 
+ * *   Redistributions of source code must retain the above 
+ *     copyright notice, this list of conditions and the 
+ *     following disclaimer.
+ * *   Redistributions in binary form must reproduce the above 
+ *     copyright notice, this list of conditions and the 
+ *     following disclaimer in the documentation and/or other 
+ *     materials provided with the distribution.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND 
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, 
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR 
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 
+ * SUCH DAMAGE.
+ */ 
+
+#include <ntddk.h>
+#include "driver.h"
+#include "util.h"
+#include "debug.h"
+#include "assert.h"
+#include <version.h>
+
+extern PULONG       InitSafeBootMode;
+
+typedef struct _XENDISK_DRIVER {
+    PDRIVER_OBJECT              DriverObject;
+} XENDISK_DRIVER, *PXENDISK_DRIVER;
+
+static XENDISK_DRIVER   Driver;
+
+static FORCEINLINE VOID
+__DriverSetDriverObject(
+    IN  PDRIVER_OBJECT  DriverObject
+    )
+{
+    Driver.DriverObject = DriverObject;
+}
+
+static FORCEINLINE PDRIVER_OBJECT
+__DriverGetDriverObject(
+    VOID
+    )
+{
+    return Driver.DriverObject;
+}
+
+PDRIVER_OBJECT
+DriverGetDriverObject(
+    VOID
+    )
+{
+    return __DriverGetDriverObject();
+}
+
+DRIVER_UNLOAD   DriverUnload;
+
+VOID
+DriverUnload(
+    IN  PDRIVER_OBJECT  DriverObject
+    )
+{
+    ASSERT3P(DriverObject, ==, __DriverGetDriverObject());
+
+    Trace("====>\n");
+
+    __DriverSetDriverObject(NULL);
+
+    ASSERT(IsZeroMemory(&Driver, sizeof (XENDISK_DRIVER)));
+
+    Trace("<====\n");
+}
+
+DRIVER_ADD_DEVICE   AddDevice;
+
+NTSTATUS
+#pragma prefast(suppress:28152) // Does not clear DO_DEVICE_INITIALIZING
+AddDevice(
+    IN  PDRIVER_OBJECT  DriverObject,
+    IN  PDEVICE_OBJECT  PhysicalDeviceObject
+    )
+{
+    NTSTATUS            status;
+
+    ASSERT3P(DriverObject, ==, __DriverGetDriverObject());
+
+    status = FdoCreate(PhysicalDeviceObject);
+    if (!NT_SUCCESS(status))
+        goto fail1;
+
+    return STATUS_SUCCESS;
+
+fail1:
+    Error("fail1 (%08x)\n", status);
+
+    return status;
+}
+
+DRIVER_DISPATCH Dispatch;
+
+NTSTATUS 
+Dispatch(
+    IN PDEVICE_OBJECT   DeviceObject,
+    IN PIRP             Irp
+    )
+{
+    PXENDISK_DX         Dx;
+    NTSTATUS            status;
+
+    Dx = (PXENDISK_DX)DeviceObject->DeviceExtension;
+    ASSERT3P(Dx->DeviceObject, ==, DeviceObject);
+
+    if (Dx->DevicePnpState == Deleted) {
+        status = STATUS_NO_SUCH_DEVICE;
+
+        Irp->IoStatus.Status = status;
+        IoCompleteRequest(Irp, IO_NO_INCREMENT);
+        goto done;
+    }
+
+    status = STATUS_NOT_SUPPORTED;
+    switch (Dx->Type) {
+    case PHYSICAL_DEVICE_OBJECT: {
+        PXENDISK_PDO    Pdo = Dx->Pdo;
+
+        status = PdoDispatch(Pdo, Irp);
+        break;
+    }
+    case FUNCTION_DEVICE_OBJECT: {
+        PXENDISK_FDO    Fdo = Dx->Fdo;
+
+        status = FdoDispatch(Fdo, Irp);
+        break;
+    }
+    default:
+        ASSERT(FALSE);
+        break;
+    }
+
+done:
+    return status;
+}
+
+DRIVER_INITIALIZE   DriverEntry;
+
+NTSTATUS
+DriverEntry(
+    IN  PDRIVER_OBJECT  DriverObject,
+    IN  PUNICODE_STRING RegistryPath
+    )
+{
+    ULONG               Index;
+
+    ASSERT3P(__DriverGetDriverObject(), ==, NULL);
+    UNREFERENCED_PARAMETER(RegistryPath);
+
+    ExInitializeDriverRuntime(DrvRtPoolNxOptIn);
+
+    Trace("====>\n");
+
+    __DriverSetDriverObject(DriverObject);
+
+    DriverObject->DriverUnload = DriverUnload;
+
+    if (*InitSafeBootMode > 0)
+        goto done;
+
+    Verbose("XENDISK %d.%d.%d (%d) (%02d.%02d.%04d)\n",
+            MAJOR_VERSION,
+            MINOR_VERSION,
+            MICRO_VERSION,
+            BUILD_NUMBER,
+            DAY,
+            MONTH,
+            YEAR);
+
+    DriverObject->DriverExtension->AddDevice = AddDevice;
+
+    for (Index = 0; Index <= IRP_MJ_MAXIMUM_FUNCTION; Index++) {
+#pragma prefast(suppress:28169) // No __drv_dispatchType annotation
+#pragma prefast(suppress:28168) // No matching __drv_dispatchType annotation 
for IRP_MJ_CREATE
+        DriverObject->MajorFunction[Index] = Dispatch;
+    }
+
+done:
+    Trace("<====\n");
+    return STATUS_SUCCESS;
+}
diff --git a/src/xendisk/driver.h b/src/xendisk/driver.h
new file mode 100644
index 0000000..b47e21b
--- /dev/null
+++ b/src/xendisk/driver.h
@@ -0,0 +1,68 @@
+/* Copyright (c) Citrix Systems Inc.
+ * All rights reserved.
+ * 
+ * Redistribution and use in source and binary forms, 
+ * with or without modification, are permitted provided 
+ * that the following conditions are met:
+ * 
+ * *   Redistributions of source code must retain the above 
+ *     copyright notice, this list of conditions and the 
+ *     following disclaimer.
+ * *   Redistributions in binary form must reproduce the above 
+ *     copyright notice, this list of conditions and the 
+ *     following disclaimer in the documentation and/or other 
+ *     materials provided with the distribution.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND 
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, 
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR 
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 
+ * SUCH DAMAGE.
+ */ 
+
+#ifndef _XENDISK_DRIVER_H
+#define _XENDISK_DRIVER_H
+
+#include "fdo.h"
+#include "pdo.h"
+
+extern PDRIVER_OBJECT
+DriverGetDriverObject(
+    VOID
+    );
+
+#pragma warning(push)
+#pragma warning(disable:4201) // nonstandard extension used : nameless 
struct/union
+
+typedef struct _XENDISK_DX {
+    PDEVICE_OBJECT      DeviceObject;
+    DEVICE_OBJECT_TYPE  Type;
+
+    DEVICE_PNP_STATE    DevicePnpState;
+    DEVICE_PNP_STATE    PreviousDevicePnpState;
+
+    SYSTEM_POWER_STATE  SystemPowerState;
+    DEVICE_POWER_STATE  DevicePowerState;
+
+    IO_REMOVE_LOCK      RemoveLock;
+
+    LIST_ENTRY          ListEntry;
+
+    union {
+        PXENDISK_FDO    Fdo;
+        PXENDISK_PDO    Pdo;
+    };
+} XENDISK_DX, *PXENDISK_DX;
+
+#pragma warning(pop)
+
+#endif // _XENDISK_DRIVER_H
diff --git a/src/xendisk/fdo.c b/src/xendisk/fdo.c
new file mode 100644
index 0000000..5209ca6
--- /dev/null
+++ b/src/xendisk/fdo.c
@@ -0,0 +1,1863 @@
+/* Copyright (c) Citrix Systems Inc.
+ * All rights reserved.
+ * 
+ * Redistribution and use in source and binary forms, 
+ * with or without modification, are permitted provided 
+ * that the following conditions are met:
+ * 
+ * *   Redistributions of source code must retain the above 
+ *     copyright notice, this list of conditions and the 
+ *     following disclaimer.
+ * *   Redistributions in binary form must reproduce the above 
+ *     copyright notice, this list of conditions and the 
+ *     following disclaimer in the documentation and/or other 
+ *     materials provided with the distribution.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND 
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, 
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR 
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 
+ * SUCH DAMAGE.
+ */ 
+
+#define INITGUID 1
+
+#include <ntddk.h>
+#include <wdmguid.h>
+#include <ntstrsafe.h>
+#include <stdlib.h>
+#include <names.h>
+
+#include "driver.h"
+#include "fdo.h"
+#include "pdo.h"
+#include "thread.h"
+#include "mutex.h"
+#include "debug.h"
+#include "assert.h"
+#include "util.h"
+
+#define FDO_TAG 'ODF'
+
+struct _XENDISK_FDO {
+    PXENDISK_DX                     Dx;
+    PDEVICE_OBJECT                  LowerDeviceObject;
+    PDEVICE_OBJECT                  PhysicalDeviceObject;
+
+    PXENDISK_THREAD                 SystemPowerThread;
+    PIRP                            SystemPowerIrp;
+    PXENDISK_THREAD                 DevicePowerThread;
+    PIRP                            DevicePowerIrp;
+
+    MUTEX                           Mutex;
+    ULONG                           References;
+};
+
+static FORCEINLINE PVOID
+__FdoAllocate(
+    IN  ULONG   Length
+    )
+{
+    return __AllocateNonPagedPoolWithTag(__FUNCTION__, __LINE__, Length, 
FDO_TAG);
+}
+
+static FORCEINLINE VOID
+__FdoFree(
+    IN  PVOID   Buffer
+    )
+{
+    __FreePoolWithTag(Buffer, FDO_TAG);
+}
+
+static FORCEINLINE VOID
+__FdoSetDevicePnpState(
+    IN  PXENDISK_FDO        Fdo,
+    IN  DEVICE_PNP_STATE    State
+    )
+{
+    PXENDISK_DX             Dx = Fdo->Dx;
+
+    // We can never transition out of the deleted state
+    ASSERT(Dx->DevicePnpState != Deleted || State == Deleted);
+
+    Dx->PreviousDevicePnpState = Dx->DevicePnpState;
+    Dx->DevicePnpState = State;
+}
+
+static FORCEINLINE VOID
+__FdoRestoreDevicePnpState(
+    IN  PXENDISK_FDO        Fdo,
+    IN  DEVICE_PNP_STATE    State
+    )
+{
+    PXENDISK_DX             Dx = Fdo->Dx;
+
+    if (Dx->DevicePnpState == State)
+        Dx->DevicePnpState = Dx->PreviousDevicePnpState;
+}
+
+static FORCEINLINE DEVICE_PNP_STATE
+__FdoGetDevicePnpState(
+    IN  PXENDISK_FDO    Fdo
+    )
+{
+    PXENDISK_DX         Dx = Fdo->Dx;
+
+    return Dx->DevicePnpState;
+}
+
+static FORCEINLINE VOID
+__FdoSetDevicePowerState(
+    IN  PXENDISK_FDO        Fdo,
+    IN  DEVICE_POWER_STATE  State
+    )
+{
+    PXENDISK_DX             Dx = Fdo->Dx;
+
+    Dx->DevicePowerState = State;
+}
+
+static FORCEINLINE DEVICE_POWER_STATE
+__FdoGetDevicePowerState(
+    IN  PXENDISK_FDO    Fdo
+    )
+{
+    PXENDISK_DX         Dx = Fdo->Dx;
+
+    return Dx->DevicePowerState;
+}
+
+static FORCEINLINE VOID
+__FdoSetSystemPowerState(
+    IN  PXENDISK_FDO        Fdo,
+    IN  SYSTEM_POWER_STATE  State
+    )
+{
+    PXENDISK_DX              Dx = Fdo->Dx;
+
+    Dx->SystemPowerState = State;
+}
+
+static FORCEINLINE SYSTEM_POWER_STATE
+__FdoGetSystemPowerState(
+    IN  PXENDISK_FDO    Fdo
+    )
+{
+    PXENDISK_DX         Dx = Fdo->Dx;
+
+    return Dx->SystemPowerState;
+}
+
+static FORCEINLINE PDEVICE_OBJECT
+__FdoGetPhysicalDeviceObject(
+    IN  PXENDISK_FDO    Fdo
+    )
+{
+    return Fdo->PhysicalDeviceObject;
+}
+
+VOID
+FdoAddPhysicalDeviceObject(
+    IN  PXENDISK_FDO    Fdo,
+    IN  PDEVICE_OBJECT  DeviceObject
+    )
+{
+    PXENDISK_DX         Dx;
+
+    Dx = (PXENDISK_DX)DeviceObject->DeviceExtension;
+    ASSERT3U(Dx->Type, ==, PHYSICAL_DEVICE_OBJECT);
+
+    InsertTailList(&Fdo->Dx->ListEntry, &Dx->ListEntry);
+    ASSERT3U(Fdo->References, !=, 0);
+    Fdo->References++;
+}
+
+VOID
+FdoRemovePhysicalDeviceObject(
+    IN  PXENDISK_FDO    Fdo,
+    IN  PDEVICE_OBJECT  DeviceObject
+    )
+{
+    PXENDISK_DX         Dx;
+
+    Dx = (PXENDISK_DX)DeviceObject->DeviceExtension;
+    ASSERT3U(Dx->Type, ==, PHYSICAL_DEVICE_OBJECT);
+
+    RemoveEntryList(&Dx->ListEntry);
+    ASSERT3U(Fdo->References, !=, 0);
+    --Fdo->References;
+}
+
+static FORCEINLINE VOID
+__FdoAcquireMutex(
+    IN  PXENDISK_FDO     Fdo
+    )
+{
+    AcquireMutex(&Fdo->Mutex);
+}
+
+VOID
+FdoAcquireMutex(
+    IN  PXENDISK_FDO     Fdo
+    )
+{
+    __FdoAcquireMutex(Fdo);
+}
+
+static FORCEINLINE VOID
+__FdoReleaseMutex(
+    IN  PXENDISK_FDO     Fdo
+    )
+{
+    ReleaseMutex(&Fdo->Mutex);
+}
+
+VOID
+FdoReleaseMutex(
+    IN  PXENDISK_FDO     Fdo
+    )
+{
+    __FdoReleaseMutex(Fdo);
+
+    if (Fdo->References == 0)
+        FdoDestroy(Fdo);
+}
+
+static FORCEINLINE VOID
+__FdoEnumerate(
+    IN  PXENDISK_FDO        Fdo,
+    IN  PDEVICE_RELATIONS   Relations
+    )
+{
+    PDEVICE_OBJECT          *PhysicalDeviceObject;
+    ULONG                   Count;
+    PLIST_ENTRY             ListEntry;
+    ULONG                   Index;
+    NTSTATUS                status;
+
+    Count = Relations->Count;
+    ASSERT(Count != 0);
+
+    PhysicalDeviceObject = __FdoAllocate(sizeof (PDEVICE_OBJECT) * Count);
+
+    status = STATUS_NO_MEMORY;
+    if (PhysicalDeviceObject == NULL)
+        goto fail1;
+
+    RtlCopyMemory(PhysicalDeviceObject,
+                  Relations->Objects,
+                  sizeof (PDEVICE_OBJECT) * Count);
+
+    AcquireMutex(&Fdo->Mutex);
+
+    // Remove any PDOs that do not appear in the device list
+    ListEntry = Fdo->Dx->ListEntry.Flink;
+    while (ListEntry != &Fdo->Dx->ListEntry) {
+        PLIST_ENTRY     Next = ListEntry->Flink;
+        PXENDISK_DX     Dx = CONTAINING_RECORD(ListEntry, XENDISK_DX, 
ListEntry);
+        PXENDISK_PDO    Pdo = Dx->Pdo;
+        BOOLEAN         Missing;
+
+        Missing = TRUE;
+        for (Index = 0; Index < Count; Index++) {
+            if (PdoGetPhysicalDeviceObject(Pdo) == 
PhysicalDeviceObject[Index]) {
+                Missing = FALSE;
+#pragma prefast(suppress:6387)  // PhysicalDeviceObject[Index] could be NULL
+                ObDereferenceObject(PhysicalDeviceObject[Index]);
+                PhysicalDeviceObject[Index] = NULL; // avoid duplication
+                break;
+            }
+        }
+
+        if (Missing && !PdoIsMissing(Pdo)) {
+            if (PdoGetDevicePnpState(Pdo) == Present) {
+                PdoSetDevicePnpState(Pdo, Deleted);
+                PdoDestroy(Pdo);
+            } else {
+                PdoSetMissing(Pdo, "device disappeared");
+            }
+        }
+
+        ListEntry = Next;
+    }
+
+    // Walk the list and create PDO filters for any new devices
+    for (Index = 0; Index < Count; Index++) {
+#pragma warning(suppress:6385)  // Reading invalid data from 
'PhysicalDeviceObject'
+        if (PhysicalDeviceObject[Index] != NULL) {
+            (VOID) PdoCreate(Fdo,
+                             PhysicalDeviceObject[Index]);
+            ObDereferenceObject(PhysicalDeviceObject[Index]);
+        }
+    }
+    
+    ReleaseMutex(&Fdo->Mutex);
+
+    __FdoFree(PhysicalDeviceObject);
+    return;
+
+fail1:
+    Error("fail1 (%08x)\n", status);
+}
+
+__drv_functionClass(IO_COMPLETION_ROUTINE)
+__drv_sameIRQL
+static NTSTATUS
+__FdoForwardIrpSynchronously(
+    IN  PDEVICE_OBJECT  DeviceObject,
+    IN  PIRP            Irp,
+    IN  PVOID           Context
+    )
+{
+    PKEVENT             Event = Context;
+
+    UNREFERENCED_PARAMETER(DeviceObject);
+    UNREFERENCED_PARAMETER(Irp);
+
+    KeSetEvent(Event, IO_NO_INCREMENT, FALSE);
+
+    return STATUS_MORE_PROCESSING_REQUIRED;
+}
+
+static NTSTATUS
+FdoForwardIrpSynchronously(
+    IN  PXENDISK_FDO    Fdo,
+    IN  PIRP            Irp
+    )
+{
+    KEVENT              Event;
+    NTSTATUS            status;
+
+    ASSERT3U(KeGetCurrentIrql(), ==, PASSIVE_LEVEL);
+
+    KeInitializeEvent(&Event, NotificationEvent, FALSE);
+
+    IoCopyCurrentIrpStackLocationToNext(Irp);
+    IoSetCompletionRoutine(Irp,
+                           __FdoForwardIrpSynchronously,
+                           &Event,
+                           TRUE,
+                           TRUE,
+                           TRUE);
+
+    status = IoCallDriver(Fdo->LowerDeviceObject, Irp);
+    if (status == STATUS_PENDING) {
+        (VOID) KeWaitForSingleObject(&Event,
+                                     Executive,
+                                     KernelMode,
+                                     FALSE,
+                                     NULL);
+        status = Irp->IoStatus.Status;
+    } else {
+        ASSERT3U(status, ==, Irp->IoStatus.Status);
+    }
+
+    return status;
+}
+
+static DECLSPEC_NOINLINE NTSTATUS
+FdoStartDevice(
+    IN  PXENDISK_FDO    Fdo,
+    IN  PIRP            Irp
+    )
+{
+    POWER_STATE         PowerState;
+    NTSTATUS            status;
+
+    status = IoAcquireRemoveLock(&Fdo->Dx->RemoveLock, Irp);
+    if (!NT_SUCCESS(status))
+        goto fail1;
+
+    status = FdoForwardIrpSynchronously(Fdo, Irp);
+    if (!NT_SUCCESS(status))
+        goto fail2;
+
+    __FdoSetSystemPowerState(Fdo, PowerSystemWorking);
+    __FdoSetDevicePowerState(Fdo, PowerDeviceD0);
+
+    PowerState.DeviceState = PowerDeviceD0;
+    PoSetPowerState(Fdo->Dx->DeviceObject,
+                    DevicePowerState,
+                    PowerState);
+
+    __FdoSetDevicePnpState(Fdo, Started);
+
+    IoReleaseRemoveLock(&Fdo->Dx->RemoveLock, Irp);
+
+    IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+    return status;
+
+fail2:
+    Error("fail2\n");
+
+    IoReleaseRemoveLock(&Fdo->Dx->RemoveLock, Irp);
+
+fail1:
+    Error("fail1 (%08x)\n", status);
+
+    Irp->IoStatus.Status = status;
+    IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+    return status;
+}
+
+__drv_functionClass(IO_COMPLETION_ROUTINE)
+__drv_sameIRQL
+static NTSTATUS
+__FdoQueryStopDevice(
+    IN  PDEVICE_OBJECT  DeviceObject,
+    IN  PIRP            Irp,
+    IN  PVOID           Context
+    )
+{
+    PXENDISK_FDO        Fdo = Context;
+
+    UNREFERENCED_PARAMETER(DeviceObject);
+
+    if (Irp->PendingReturned)
+        IoMarkIrpPending(Irp);
+
+    IoReleaseRemoveLock(&Fdo->Dx->RemoveLock, Irp);
+
+    return STATUS_SUCCESS;
+}
+
+static DECLSPEC_NOINLINE NTSTATUS
+FdoQueryStopDevice(
+    IN  PXENDISK_FDO    Fdo,
+    IN  PIRP            Irp
+    )
+{
+    NTSTATUS            status;
+
+    status = IoAcquireRemoveLock(&Fdo->Dx->RemoveLock, Irp);
+    if (!NT_SUCCESS(status))
+        goto fail1;
+
+    __FdoSetDevicePnpState(Fdo, StopPending);
+    Irp->IoStatus.Status = STATUS_SUCCESS;
+
+    IoCopyCurrentIrpStackLocationToNext(Irp);
+    IoSetCompletionRoutine(Irp,
+                           __FdoQueryStopDevice,
+                           Fdo,
+                           TRUE,
+                           TRUE,
+                           TRUE);
+
+    status = IoCallDriver(Fdo->LowerDeviceObject, Irp);
+
+    return status;
+
+fail1:
+    Irp->IoStatus.Status = status;
+    IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+    return status;
+}
+
+__drv_functionClass(IO_COMPLETION_ROUTINE)
+__drv_sameIRQL
+static NTSTATUS
+__FdoCancelStopDevice(
+    IN  PDEVICE_OBJECT  DeviceObject,
+    IN  PIRP            Irp,
+    IN  PVOID           Context
+    )
+{
+    PXENDISK_FDO        Fdo = Context;
+
+    UNREFERENCED_PARAMETER(DeviceObject);
+
+    if (Irp->PendingReturned)
+        IoMarkIrpPending(Irp);
+
+    IoReleaseRemoveLock(&Fdo->Dx->RemoveLock, Irp);
+
+    return STATUS_SUCCESS;
+}
+
+static DECLSPEC_NOINLINE NTSTATUS
+FdoCancelStopDevice(
+    IN  PXENDISK_FDO    Fdo,
+    IN  PIRP            Irp
+    )
+{
+    NTSTATUS            status;
+
+    status = IoAcquireRemoveLock(&Fdo->Dx->RemoveLock, Irp);
+    if (!NT_SUCCESS(status))
+        goto fail1;
+
+    Irp->IoStatus.Status = STATUS_SUCCESS;
+
+    __FdoRestoreDevicePnpState(Fdo, StopPending);
+
+    IoCopyCurrentIrpStackLocationToNext(Irp);
+    IoSetCompletionRoutine(Irp,
+                           __FdoCancelStopDevice,
+                           Fdo,
+                           TRUE,
+                           TRUE,
+                           TRUE);
+
+    status = IoCallDriver(Fdo->LowerDeviceObject, Irp);
+
+    return status;
+
+fail1:
+    Irp->IoStatus.Status = status;
+    IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+    return status;
+}
+
+__drv_functionClass(IO_COMPLETION_ROUTINE)
+__drv_sameIRQL
+static NTSTATUS
+__FdoStopDevice(
+    IN  PDEVICE_OBJECT  DeviceObject,
+    IN  PIRP            Irp,
+    IN  PVOID           Context
+    )
+{
+    PXENDISK_FDO        Fdo = Context;
+
+    UNREFERENCED_PARAMETER(DeviceObject);
+
+    if (Irp->PendingReturned)
+        IoMarkIrpPending(Irp);
+
+    IoReleaseRemoveLock(&Fdo->Dx->RemoveLock, Irp);
+
+    return STATUS_SUCCESS;
+}
+
+static DECLSPEC_NOINLINE NTSTATUS
+FdoStopDevice(
+    IN  PXENDISK_FDO    Fdo,
+    IN  PIRP            Irp
+    )
+{
+    POWER_STATE         PowerState;
+    NTSTATUS            status;
+
+    status = IoAcquireRemoveLock(&Fdo->Dx->RemoveLock, Irp);
+    if (!NT_SUCCESS(status))
+        goto fail1;
+
+    if (__FdoGetDevicePowerState(Fdo) != PowerDeviceD0)
+        goto done;
+
+    PowerState.DeviceState = PowerDeviceD3;
+    PoSetPowerState(Fdo->Dx->DeviceObject,
+                    DevicePowerState,
+                    PowerState);
+
+    __FdoSetDevicePowerState(Fdo, PowerDeviceD3);
+    __FdoSetSystemPowerState(Fdo, PowerSystemShutdown);
+
+done:
+    __FdoSetDevicePnpState(Fdo, Stopped);
+    Irp->IoStatus.Status = STATUS_SUCCESS;
+
+    IoCopyCurrentIrpStackLocationToNext(Irp);
+    IoSetCompletionRoutine(Irp,
+                           __FdoStopDevice,
+                           Fdo,
+                           TRUE,
+                           TRUE,
+                           TRUE);
+
+    status = IoCallDriver(Fdo->LowerDeviceObject, Irp);
+
+    return status;
+
+fail1:
+    Irp->IoStatus.Status = status;
+    IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+    return status;
+}
+
+__drv_functionClass(IO_COMPLETION_ROUTINE)
+__drv_sameIRQL
+static NTSTATUS
+__FdoQueryRemoveDevice(
+    IN  PDEVICE_OBJECT  DeviceObject,
+    IN  PIRP            Irp,
+    IN  PVOID           Context
+    )
+{
+    PXENDISK_FDO        Fdo = Context;
+
+    UNREFERENCED_PARAMETER(DeviceObject);
+
+    if (Irp->PendingReturned)
+        IoMarkIrpPending(Irp);
+
+    IoReleaseRemoveLock(&Fdo->Dx->RemoveLock, Irp);
+
+    return STATUS_SUCCESS;
+}
+
+static DECLSPEC_NOINLINE NTSTATUS
+FdoQueryRemoveDevice(
+    IN  PXENDISK_FDO    Fdo,
+    IN  PIRP            Irp
+    )
+{
+    NTSTATUS            status;
+
+    status = IoAcquireRemoveLock(&Fdo->Dx->RemoveLock, Irp);
+    if (!NT_SUCCESS(status))
+        goto fail1;
+
+    __FdoSetDevicePnpState(Fdo, RemovePending);
+    Irp->IoStatus.Status = STATUS_SUCCESS;
+
+    IoCopyCurrentIrpStackLocationToNext(Irp);
+    IoSetCompletionRoutine(Irp,
+                           __FdoQueryRemoveDevice,
+                           Fdo,
+                           TRUE,
+                           TRUE,
+                           TRUE);
+
+    status = IoCallDriver(Fdo->LowerDeviceObject, Irp);
+
+    return status;
+
+fail1:
+    Irp->IoStatus.Status = status;
+    IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+    return status;
+}
+
+__drv_functionClass(IO_COMPLETION_ROUTINE)
+__drv_sameIRQL
+static NTSTATUS
+__FdoCancelRemoveDevice(
+    IN  PDEVICE_OBJECT  DeviceObject,
+    IN  PIRP            Irp,
+    IN  PVOID           Context
+    )
+{
+    PXENDISK_FDO        Fdo = Context;
+
+    UNREFERENCED_PARAMETER(DeviceObject);
+
+    if (Irp->PendingReturned)
+        IoMarkIrpPending(Irp);
+
+    IoReleaseRemoveLock(&Fdo->Dx->RemoveLock, Irp);
+
+    return STATUS_SUCCESS;
+}
+
+static DECLSPEC_NOINLINE NTSTATUS
+FdoCancelRemoveDevice(
+    IN  PXENDISK_FDO    Fdo,
+    IN  PIRP            Irp
+    )
+{
+    NTSTATUS            status;
+
+    status = IoAcquireRemoveLock(&Fdo->Dx->RemoveLock, Irp);
+    if (!NT_SUCCESS(status))
+        goto fail1;
+
+    __FdoRestoreDevicePnpState(Fdo, RemovePending);
+    Irp->IoStatus.Status = STATUS_SUCCESS;
+
+    IoCopyCurrentIrpStackLocationToNext(Irp);
+    IoSetCompletionRoutine(Irp,
+                           __FdoCancelRemoveDevice,
+                           Fdo,
+                           TRUE,
+                           TRUE,
+                           TRUE);
+
+    status = IoCallDriver(Fdo->LowerDeviceObject, Irp);
+
+    return status;
+
+fail1:
+    Irp->IoStatus.Status = status;
+    IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+    return status;
+}
+
+__drv_functionClass(IO_COMPLETION_ROUTINE)
+__drv_sameIRQL
+static NTSTATUS
+__FdoSurpriseRemoval(
+    IN  PDEVICE_OBJECT  DeviceObject,
+    IN  PIRP            Irp,
+    IN  PVOID           Context
+    )
+{
+    PXENDISK_FDO        Fdo = Context;
+
+    UNREFERENCED_PARAMETER(DeviceObject);
+
+    if (Irp->PendingReturned)
+        IoMarkIrpPending(Irp);
+
+    IoReleaseRemoveLock(&Fdo->Dx->RemoveLock, Irp);
+
+    return STATUS_SUCCESS;
+}
+
+static DECLSPEC_NOINLINE NTSTATUS
+FdoSurpriseRemoval(
+    IN  PXENDISK_FDO    Fdo,
+    IN  PIRP            Irp
+    )
+{
+    NTSTATUS            status;
+
+    status = IoAcquireRemoveLock(&Fdo->Dx->RemoveLock, Irp);
+    if (!NT_SUCCESS(status))
+        goto fail1;
+
+    __FdoSetDevicePnpState(Fdo, SurpriseRemovePending);
+    Irp->IoStatus.Status = STATUS_SUCCESS;
+
+    IoCopyCurrentIrpStackLocationToNext(Irp);
+    IoSetCompletionRoutine(Irp,
+                           __FdoSurpriseRemoval,
+                           Fdo,
+                           TRUE,
+                           TRUE,
+                           TRUE);
+
+    status = IoCallDriver(Fdo->LowerDeviceObject, Irp);
+
+    return status;
+
+fail1:
+    Irp->IoStatus.Status = status;
+    IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+    return status;
+}
+
+static DECLSPEC_NOINLINE NTSTATUS
+FdoRemoveDevice(
+    IN  PXENDISK_FDO    Fdo,
+    IN  PIRP            Irp
+    )
+{
+    POWER_STATE         PowerState;
+    NTSTATUS            status;
+
+    status = IoAcquireRemoveLock(&Fdo->Dx->RemoveLock, Irp);
+    if (!NT_SUCCESS(status))
+        goto fail1;
+
+    if (__FdoGetDevicePowerState(Fdo) != PowerDeviceD0)
+        goto done;
+
+    PowerState.DeviceState = PowerDeviceD3;
+    PoSetPowerState(Fdo->Dx->DeviceObject,
+                    DevicePowerState,
+                    PowerState);
+
+    __FdoSetDevicePowerState(Fdo, PowerDeviceD3);
+    __FdoSetSystemPowerState(Fdo, PowerSystemShutdown);
+
+done:
+    __FdoSetDevicePnpState(Fdo, Deleted);
+
+    IoReleaseRemoveLockAndWait(&Fdo->Dx->RemoveLock, Irp);
+
+    status = FdoForwardIrpSynchronously(Fdo, Irp);
+    IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+    AcquireMutex(&Fdo->Mutex);
+    ASSERT3U(Fdo->References, !=, 0);
+    --Fdo->References;
+    ReleaseMutex(&Fdo->Mutex);
+
+    if (Fdo->References == 0)
+        FdoDestroy(Fdo);
+
+    return status;
+
+fail1:
+    Irp->IoStatus.Status = status;
+    IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+    return status;
+}
+
+__drv_functionClass(IO_COMPLETION_ROUTINE)
+__drv_sameIRQL
+static NTSTATUS
+__FdoQueryDeviceRelations(
+    IN  PDEVICE_OBJECT  DeviceObject,
+    IN  PIRP            Irp,
+    IN  PVOID           Context
+    )
+{
+    PKEVENT             Event = Context;
+
+    UNREFERENCED_PARAMETER(DeviceObject);
+    UNREFERENCED_PARAMETER(Irp);
+
+    KeSetEvent(Event, IO_NO_INCREMENT, FALSE);
+
+    return STATUS_MORE_PROCESSING_REQUIRED;
+}
+
+static DECLSPEC_NOINLINE NTSTATUS
+FdoQueryDeviceRelations(
+    IN  PXENDISK_FDO    Fdo,
+    IN  PIRP            Irp
+    )
+{
+    KEVENT              Event;
+    PIO_STACK_LOCATION  StackLocation;
+    ULONG               Size;
+    PDEVICE_RELATIONS   Relations;
+    PLIST_ENTRY         ListEntry;
+    ULONG               Count;
+    NTSTATUS            status;
+
+    status = IoAcquireRemoveLock(&Fdo->Dx->RemoveLock, Irp);
+    if (!NT_SUCCESS(status))
+        goto fail1;
+
+    KeInitializeEvent(&Event, NotificationEvent, FALSE);
+
+    IoCopyCurrentIrpStackLocationToNext(Irp);
+    IoSetCompletionRoutine(Irp,
+                           __FdoQueryDeviceRelations,
+                           &Event,
+                           TRUE,
+                           TRUE,
+                           TRUE);
+
+    status = IoCallDriver(Fdo->LowerDeviceObject, Irp);
+    if (status == STATUS_PENDING) {
+        (VOID) KeWaitForSingleObject(&Event,
+                                     Executive,
+                                     KernelMode,
+                                     FALSE,
+                                     NULL);
+        status = Irp->IoStatus.Status;
+    } else {
+        ASSERT3U(status, ==, Irp->IoStatus.Status);
+    }
+
+    if (!NT_SUCCESS(status))
+        goto fail2;
+
+    StackLocation = IoGetCurrentIrpStackLocation(Irp);
+    if (StackLocation->Parameters.QueryDeviceRelations.Type != BusRelations)
+        goto done;
+
+    Relations = (PDEVICE_RELATIONS)Irp->IoStatus.Information;
+
+    if (Relations->Count != 0)
+        __FdoEnumerate(Fdo, Relations);
+
+    ExFreePool(Relations);
+
+    AcquireMutex(&Fdo->Mutex);
+
+    Count = 0;
+    for (ListEntry = Fdo->Dx->ListEntry.Flink;
+         ListEntry != &Fdo->Dx->ListEntry;
+         ListEntry = ListEntry->Flink)
+        Count++;
+
+    Size = FIELD_OFFSET(DEVICE_RELATIONS, Objects) + (sizeof (DEVICE_OBJECT) * 
__min(Count, 1));
+
+    Relations = ExAllocatePoolWithTag(PagedPool, Size, 'TLIF');
+
+    status = STATUS_NO_MEMORY;
+    if (Relations == NULL)
+        goto fail3;
+
+    RtlZeroMemory(Relations, Size);
+
+    for (ListEntry = Fdo->Dx->ListEntry.Flink;
+         ListEntry != &Fdo->Dx->ListEntry;
+         ListEntry = ListEntry->Flink) {
+        PXENDISK_DX     Dx = CONTAINING_RECORD(ListEntry, XENDISK_DX, 
ListEntry);
+        PXENDISK_PDO    Pdo = Dx->Pdo;
+
+        ASSERT3U(Dx->Type, ==, PHYSICAL_DEVICE_OBJECT);
+
+        if (PdoGetDevicePnpState(Pdo) == Present)
+            PdoSetDevicePnpState(Pdo, Enumerated);
+
+        ObReferenceObject(PdoGetPhysicalDeviceObject(Pdo));
+        Relations->Objects[Relations->Count++] = 
PdoGetPhysicalDeviceObject(Pdo);
+    }
+
+    ASSERT3U(Relations->Count, ==, Count);
+
+    Trace("%d PDO(s)\n", Relations->Count);
+
+    ReleaseMutex(&Fdo->Mutex);
+
+    Irp->IoStatus.Information = (ULONG_PTR)Relations;
+    status = STATUS_SUCCESS;
+
+done:
+    IoReleaseRemoveLock(&Fdo->Dx->RemoveLock, Irp);
+
+    Irp->IoStatus.Status = status;
+    IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+    return status;
+
+fail3:
+    ReleaseMutex(&Fdo->Mutex);
+
+fail2:
+    IoReleaseRemoveLock(&Fdo->Dx->RemoveLock, Irp);
+
+fail1:
+    Irp->IoStatus.Status = status;
+    IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+    return status;
+}
+
+__drv_functionClass(IO_COMPLETION_ROUTINE)
+__drv_sameIRQL
+static NTSTATUS
+__FdoDispatchPnp(
+    IN  PDEVICE_OBJECT  DeviceObject,
+    IN  PIRP            Irp,
+    IN  PVOID           Context
+    )
+{
+    PXENDISK_FDO        Fdo = Context;
+
+    UNREFERENCED_PARAMETER(DeviceObject);
+
+    if (Irp->PendingReturned)
+        IoMarkIrpPending(Irp);
+
+    IoReleaseRemoveLock(&Fdo->Dx->RemoveLock, Irp);
+    return STATUS_SUCCESS;
+}
+
+static DECLSPEC_NOINLINE NTSTATUS
+FdoDispatchPnp(
+    IN  PXENDISK_FDO    Fdo,
+    IN  PIRP            Irp
+    )
+{
+    PIO_STACK_LOCATION  StackLocation;
+    UCHAR               MinorFunction;
+    NTSTATUS            status;
+
+    StackLocation = IoGetCurrentIrpStackLocation(Irp);
+    MinorFunction = StackLocation->MinorFunction;
+
+    switch (StackLocation->MinorFunction) {
+    case IRP_MN_START_DEVICE:
+        status = FdoStartDevice(Fdo, Irp);
+        break;
+
+    case IRP_MN_QUERY_STOP_DEVICE:
+        status = FdoQueryStopDevice(Fdo, Irp);
+        break;
+
+    case IRP_MN_CANCEL_STOP_DEVICE:
+        status = FdoCancelStopDevice(Fdo, Irp);
+        break;
+
+    case IRP_MN_STOP_DEVICE:
+        status = FdoStopDevice(Fdo, Irp);
+        break;
+
+    case IRP_MN_QUERY_REMOVE_DEVICE:
+        status = FdoQueryRemoveDevice(Fdo, Irp);
+        break;
+
+    case IRP_MN_SURPRISE_REMOVAL:
+        status = FdoSurpriseRemoval(Fdo, Irp);
+        break;
+
+    case IRP_MN_REMOVE_DEVICE:
+        status = FdoRemoveDevice(Fdo, Irp);
+        break;
+
+    case IRP_MN_CANCEL_REMOVE_DEVICE:
+        status = FdoCancelRemoveDevice(Fdo, Irp);
+        break;
+
+    case IRP_MN_QUERY_DEVICE_RELATIONS:
+        status = FdoQueryDeviceRelations(Fdo, Irp);
+        break;
+
+    default:
+        status = IoAcquireRemoveLock(&Fdo->Dx->RemoveLock, Irp);
+        if (!NT_SUCCESS(status))
+            goto fail1;
+
+        IoCopyCurrentIrpStackLocationToNext(Irp);
+        IoSetCompletionRoutine(Irp,
+                               __FdoDispatchPnp,
+                               Fdo,
+                               TRUE,
+                               TRUE,
+                               TRUE);
+
+        status = IoCallDriver(Fdo->LowerDeviceObject, Irp);
+        break;
+    }
+
+    return status;
+
+fail1:
+    Error("fail1 (%08x)\n", status);
+
+    Irp->IoStatus.Status = status;
+    IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+    return status;
+}
+
+static FORCEINLINE NTSTATUS
+__FdoSetDevicePowerUp(
+    IN  PXENDISK_FDO    Fdo,
+    IN  PIRP            Irp
+    )
+{
+    PIO_STACK_LOCATION  StackLocation;
+    DEVICE_POWER_STATE  DeviceState;
+    NTSTATUS            status;
+
+    StackLocation = IoGetCurrentIrpStackLocation(Irp);
+    DeviceState = StackLocation->Parameters.Power.State.DeviceState;
+
+    ASSERT3U(DeviceState, <,  __FdoGetDevicePowerState(Fdo));
+
+    status = FdoForwardIrpSynchronously(Fdo, Irp);
+    if (!NT_SUCCESS(status))
+        goto done;
+
+    Verbose("%p: %s -> %s\n",
+         Fdo->Dx->DeviceObject,
+         PowerDeviceStateName(__FdoGetDevicePowerState(Fdo)),
+         PowerDeviceStateName(DeviceState));
+
+    __FdoSetDevicePowerState(Fdo, DeviceState);
+
+done:
+    IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+    return status;
+}
+
+static FORCEINLINE NTSTATUS
+__FdoSetDevicePowerDown(
+    IN  PXENDISK_FDO    Fdo,
+    IN  PIRP            Irp
+    )
+{
+    PIO_STACK_LOCATION  StackLocation;
+    DEVICE_POWER_STATE  DeviceState;
+    NTSTATUS            status;
+
+    StackLocation = IoGetCurrentIrpStackLocation(Irp);
+    DeviceState = StackLocation->Parameters.Power.State.DeviceState;
+
+    ASSERT3U(DeviceState, >,  __FdoGetDevicePowerState(Fdo));
+
+    Verbose("%p: %s -> %s\n",
+         Fdo->Dx->DeviceObject,
+         PowerDeviceStateName(__FdoGetDevicePowerState(Fdo)),
+         PowerDeviceStateName(DeviceState));
+
+    __FdoSetDevicePowerState(Fdo, DeviceState);
+
+    status = FdoForwardIrpSynchronously(Fdo, Irp);
+    IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+    return status;
+}
+
+static FORCEINLINE NTSTATUS
+__FdoSetDevicePower(
+    IN  PXENDISK_FDO    Fdo,
+    IN  PIRP            Irp
+    )
+{
+    PIO_STACK_LOCATION  StackLocation;
+    DEVICE_POWER_STATE  DeviceState;
+    POWER_ACTION        PowerAction;
+    NTSTATUS            status;
+
+    StackLocation = IoGetCurrentIrpStackLocation(Irp);
+    DeviceState = StackLocation->Parameters.Power.State.DeviceState;
+    PowerAction = StackLocation->Parameters.Power.ShutdownType;
+
+    Trace("====> (%s:%s)\n",
+          PowerDeviceStateName(DeviceState), 
+          PowerActionName(PowerAction));
+
+    if (DeviceState == __FdoGetDevicePowerState(Fdo)) {
+        status = FdoForwardIrpSynchronously(Fdo, Irp);
+        IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+        goto done;
+    }
+
+    status = (DeviceState < __FdoGetDevicePowerState(Fdo)) ?
+             __FdoSetDevicePowerUp(Fdo, Irp) :
+             __FdoSetDevicePowerDown(Fdo, Irp);
+
+done:
+    Trace("<==== (%s:%s)(%08x)\n",
+          PowerDeviceStateName(DeviceState), 
+          PowerActionName(PowerAction),
+          status);
+    return status;
+}
+
+static FORCEINLINE NTSTATUS
+__FdoSetSystemPowerUp(
+    IN  PXENDISK_FDO    Fdo,
+    IN  PIRP            Irp
+    )
+{
+    PIO_STACK_LOCATION  StackLocation;
+    SYSTEM_POWER_STATE  SystemState;
+    NTSTATUS            status;
+
+    StackLocation = IoGetCurrentIrpStackLocation(Irp);
+    SystemState = StackLocation->Parameters.Power.State.SystemState;
+
+    ASSERT3U(SystemState, <,  __FdoGetSystemPowerState(Fdo));
+
+    status = FdoForwardIrpSynchronously(Fdo, Irp);
+    if (!NT_SUCCESS(status))
+        goto done;
+
+    Verbose("%p: %s -> %s\n",
+         Fdo->Dx->DeviceObject,
+         PowerSystemStateName(__FdoGetSystemPowerState(Fdo)),
+         PowerSystemStateName(SystemState));
+
+    __FdoSetSystemPowerState(Fdo, SystemState);
+
+done:
+    IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+    return status;
+}
+
+static FORCEINLINE NTSTATUS
+__FdoSetSystemPowerDown(
+    IN  PXENDISK_FDO    Fdo,
+    IN  PIRP            Irp
+    )
+{
+    PIO_STACK_LOCATION  StackLocation;
+    SYSTEM_POWER_STATE  SystemState;
+    NTSTATUS            status;
+
+    StackLocation = IoGetCurrentIrpStackLocation(Irp);
+    SystemState = StackLocation->Parameters.Power.State.SystemState;
+
+    ASSERT3U(SystemState, >,  __FdoGetSystemPowerState(Fdo));
+
+    Verbose("%p: %s -> %s\n",
+         Fdo->Dx->DeviceObject,
+         PowerSystemStateName(__FdoGetSystemPowerState(Fdo)),
+         PowerSystemStateName(SystemState));
+
+    __FdoSetSystemPowerState(Fdo, SystemState);
+
+    status = FdoForwardIrpSynchronously(Fdo, Irp);
+    IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+    return status;
+}
+
+static FORCEINLINE NTSTATUS
+__FdoSetSystemPower(
+    IN  PXENDISK_FDO    Fdo,
+    IN  PIRP            Irp
+    )
+{
+    PIO_STACK_LOCATION  StackLocation;
+    SYSTEM_POWER_STATE  SystemState;
+    POWER_ACTION        PowerAction;
+    NTSTATUS            status;
+
+    StackLocation = IoGetCurrentIrpStackLocation(Irp);
+    SystemState = StackLocation->Parameters.Power.State.SystemState;
+    PowerAction = StackLocation->Parameters.Power.ShutdownType;
+
+    Trace("====> (%s:%s)\n",
+          PowerSystemStateName(SystemState), 
+          PowerActionName(PowerAction));
+
+    if (SystemState == __FdoGetSystemPowerState(Fdo)) {
+        status = FdoForwardIrpSynchronously(Fdo, Irp);
+        IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+        goto done;
+    }
+
+    status = (SystemState < __FdoGetSystemPowerState(Fdo)) ?
+             __FdoSetSystemPowerUp(Fdo, Irp) :
+             __FdoSetSystemPowerDown(Fdo, Irp);
+
+done:
+    Trace("<==== (%s:%s)(%08x)\n",
+          PowerSystemStateName(SystemState), 
+          PowerActionName(PowerAction),
+          status);
+    return status;
+}
+
+static FORCEINLINE NTSTATUS
+__FdoQueryDevicePowerUp(
+    IN  PXENDISK_FDO    Fdo,
+    IN  PIRP            Irp
+    )
+{
+    PIO_STACK_LOCATION  StackLocation;
+    DEVICE_POWER_STATE  DeviceState;
+    NTSTATUS            status;
+
+    StackLocation = IoGetCurrentIrpStackLocation(Irp);
+    DeviceState = StackLocation->Parameters.Power.State.DeviceState;
+
+    ASSERT3U(DeviceState, <,  __FdoGetDevicePowerState(Fdo));
+
+    status = FdoForwardIrpSynchronously(Fdo, Irp);
+
+    IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+    return status;
+}
+
+static FORCEINLINE NTSTATUS
+__FdoQueryDevicePowerDown(
+    IN  PXENDISK_FDO    Fdo,
+    IN  PIRP            Irp
+    )
+{
+    PIO_STACK_LOCATION  StackLocation;
+    DEVICE_POWER_STATE  DeviceState;
+    NTSTATUS            status;
+
+    StackLocation = IoGetCurrentIrpStackLocation(Irp);
+    DeviceState = StackLocation->Parameters.Power.State.DeviceState;
+
+    ASSERT3U(DeviceState, >,  __FdoGetDevicePowerState(Fdo));
+
+    status = FdoForwardIrpSynchronously(Fdo, Irp);
+    IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+    return status;
+}
+
+static FORCEINLINE NTSTATUS
+__FdoQueryDevicePower(
+    IN  PXENDISK_FDO    Fdo,
+    IN  PIRP            Irp
+    )
+{
+    PIO_STACK_LOCATION  StackLocation;
+    DEVICE_POWER_STATE  DeviceState;
+    POWER_ACTION        PowerAction;
+    NTSTATUS            status;
+
+    StackLocation = IoGetCurrentIrpStackLocation(Irp);
+    DeviceState = StackLocation->Parameters.Power.State.DeviceState;
+    PowerAction = StackLocation->Parameters.Power.ShutdownType;
+
+    Trace("====> (%s:%s)\n",
+          PowerDeviceStateName(DeviceState), 
+          PowerActionName(PowerAction));
+
+    if (DeviceState == __FdoGetDevicePowerState(Fdo)) {
+        status = FdoForwardIrpSynchronously(Fdo, Irp);
+        IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+        goto done;
+    }
+
+    status = (DeviceState < __FdoGetDevicePowerState(Fdo)) ?
+             __FdoQueryDevicePowerUp(Fdo, Irp) :
+             __FdoQueryDevicePowerDown(Fdo, Irp);
+
+done:
+    Trace("<==== (%s:%s)(%08x)\n",
+          PowerDeviceStateName(DeviceState), 
+          PowerActionName(PowerAction),
+          status);
+    return status;
+}
+
+static FORCEINLINE NTSTATUS
+__FdoQuerySystemPowerUp(
+    IN  PXENDISK_FDO     Fdo,
+    IN  PIRP            Irp
+    )
+{
+    PIO_STACK_LOCATION  StackLocation;
+    SYSTEM_POWER_STATE  SystemState;
+    NTSTATUS            status;
+
+    StackLocation = IoGetCurrentIrpStackLocation(Irp);
+    SystemState = StackLocation->Parameters.Power.State.SystemState;
+
+    ASSERT3U(SystemState, <,  __FdoGetSystemPowerState(Fdo));
+
+    status = FdoForwardIrpSynchronously(Fdo, Irp);
+
+    IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+    return status;
+}
+
+static FORCEINLINE NTSTATUS
+__FdoQuerySystemPowerDown(
+    IN  PXENDISK_FDO    Fdo,
+    IN  PIRP            Irp
+    )
+{
+    PIO_STACK_LOCATION  StackLocation;
+    SYSTEM_POWER_STATE  SystemState;
+    NTSTATUS            status;
+
+    StackLocation = IoGetCurrentIrpStackLocation(Irp);
+    SystemState = StackLocation->Parameters.Power.State.SystemState;
+
+    ASSERT3U(SystemState, >,  __FdoGetSystemPowerState(Fdo));
+
+    status = FdoForwardIrpSynchronously(Fdo, Irp);
+    IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+    return status;
+}
+
+static FORCEINLINE NTSTATUS
+__FdoQuerySystemPower(
+    IN  PXENDISK_FDO    Fdo,
+    IN  PIRP            Irp
+    )
+{
+    PIO_STACK_LOCATION  StackLocation;
+    SYSTEM_POWER_STATE  SystemState;
+    POWER_ACTION        PowerAction;
+    NTSTATUS            status;
+
+    StackLocation = IoGetCurrentIrpStackLocation(Irp);
+    SystemState = StackLocation->Parameters.Power.State.SystemState;
+    PowerAction = StackLocation->Parameters.Power.ShutdownType;
+
+    Trace("====> (%s:%s)\n",
+          PowerSystemStateName(SystemState), 
+          PowerActionName(PowerAction));
+
+    if (SystemState == __FdoGetSystemPowerState(Fdo)) {
+        status = FdoForwardIrpSynchronously(Fdo, Irp);
+        IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+        goto done;
+    }
+
+    status = (SystemState < __FdoGetSystemPowerState(Fdo)) ?
+             __FdoQuerySystemPowerUp(Fdo, Irp) :
+             __FdoQuerySystemPowerDown(Fdo, Irp);
+
+done:
+    Trace("<==== (%s:%s)(%08x)\n",
+          PowerSystemStateName(SystemState), 
+          PowerActionName(PowerAction),
+          status);
+
+    return status;
+}
+
+static NTSTATUS
+FdoDevicePower(
+    IN  PXENDISK_THREAD Self,
+    IN  PVOID           Context
+    )
+{
+    PXENDISK_FDO        Fdo = Context;
+    PKEVENT             Event;
+
+    Event = ThreadGetEvent(Self);
+
+    for (;;) {
+        PIRP                Irp;
+        PIO_STACK_LOCATION  StackLocation;
+        UCHAR               MinorFunction;
+
+        if (Fdo->DevicePowerIrp == NULL) {
+            (VOID) KeWaitForSingleObject(Event,
+                                         Executive,
+                                         KernelMode,
+                                         FALSE,
+                                         NULL);
+            KeClearEvent(Event);
+        }
+
+        if (ThreadIsAlerted(Self))
+            break;
+
+        Irp = Fdo->DevicePowerIrp;
+
+        if (Irp == NULL)
+            continue;
+
+        Fdo->DevicePowerIrp = NULL;
+        KeMemoryBarrier();
+
+        StackLocation = IoGetCurrentIrpStackLocation(Irp);
+        MinorFunction = StackLocation->MinorFunction;
+
+        switch (StackLocation->MinorFunction) {
+        case IRP_MN_SET_POWER:
+            (VOID) __FdoSetDevicePower(Fdo, Irp);
+            break;
+
+        case IRP_MN_QUERY_POWER:
+            (VOID) __FdoQueryDevicePower(Fdo, Irp);
+            break;
+
+        default:
+            ASSERT(FALSE);
+            break;
+        }
+
+        IoReleaseRemoveLock(&Fdo->Dx->RemoveLock, Irp);
+    }
+
+    return STATUS_SUCCESS;
+}
+
+static NTSTATUS
+FdoSystemPower(
+    IN  PXENDISK_THREAD Self,
+    IN  PVOID           Context
+    )
+{
+    PXENDISK_FDO        Fdo = Context;
+    PKEVENT             Event;
+
+    Event = ThreadGetEvent(Self);
+
+    for (;;) {
+        PIRP                Irp;
+        PIO_STACK_LOCATION  StackLocation;
+        UCHAR               MinorFunction;
+
+        if (Fdo->SystemPowerIrp == NULL) {
+            (VOID) KeWaitForSingleObject(Event,
+                                         Executive,
+                                         KernelMode,
+                                         FALSE,
+                                         NULL);
+            KeClearEvent(Event);
+        }
+
+        if (ThreadIsAlerted(Self))
+            break;
+
+        Irp = Fdo->SystemPowerIrp;
+
+        if (Irp == NULL)
+            continue;
+
+        Fdo->SystemPowerIrp = NULL;
+        KeMemoryBarrier();
+
+        StackLocation = IoGetCurrentIrpStackLocation(Irp);
+        MinorFunction = StackLocation->MinorFunction;
+
+        switch (StackLocation->MinorFunction) {
+        case IRP_MN_SET_POWER:
+            (VOID) __FdoSetSystemPower(Fdo, Irp);
+            break;
+
+        case IRP_MN_QUERY_POWER:
+            (VOID) __FdoQuerySystemPower(Fdo, Irp);
+            break;
+
+        default:
+            ASSERT(FALSE);
+            break;
+        }
+
+        IoReleaseRemoveLock(&Fdo->Dx->RemoveLock, Irp);
+    }
+
+    return STATUS_SUCCESS;
+}
+
+__drv_functionClass(IO_COMPLETION_ROUTINE)
+__drv_sameIRQL
+static NTSTATUS
+__FdoDispatchPower(
+    IN  PDEVICE_OBJECT  DeviceObject,
+    IN  PIRP            Irp,
+    IN  PVOID           Context
+    )
+{
+    PXENDISK_FDO        Fdo = Context;
+
+    UNREFERENCED_PARAMETER(DeviceObject);
+
+    if (Irp->PendingReturned)
+        IoMarkIrpPending(Irp);
+
+    IoReleaseRemoveLock(&Fdo->Dx->RemoveLock, Irp);
+    return STATUS_SUCCESS;
+}
+
+static DECLSPEC_NOINLINE NTSTATUS
+FdoDispatchPower(
+    IN  PXENDISK_FDO    Fdo,
+    IN  PIRP            Irp
+    )
+{
+    PIO_STACK_LOCATION  StackLocation;
+    UCHAR               MinorFunction;
+    POWER_STATE_TYPE    PowerType;
+    NTSTATUS            status;
+
+    status = IoAcquireRemoveLock(&Fdo->Dx->RemoveLock, Irp);
+    if (!NT_SUCCESS(status))
+        goto fail1;
+
+    StackLocation = IoGetCurrentIrpStackLocation(Irp);
+    MinorFunction = StackLocation->MinorFunction;
+
+    if (MinorFunction != IRP_MN_QUERY_POWER &&
+        MinorFunction != IRP_MN_SET_POWER) {
+        IoCopyCurrentIrpStackLocationToNext(Irp);
+        IoSetCompletionRoutine(Irp,
+                               __FdoDispatchPower,
+                               Fdo,
+                               TRUE,
+                               TRUE,
+                               TRUE);
+
+        status = IoCallDriver(Fdo->LowerDeviceObject, Irp);
+
+        goto done;
+    }
+
+    PowerType = StackLocation->Parameters.Power.Type;
+
+    Trace("====> (%02x:%s)\n",
+          MinorFunction, 
+          PowerMinorFunctionName(MinorFunction)); 
+
+    switch (PowerType) {
+    case DevicePowerState:
+        IoMarkIrpPending(Irp);
+
+        ASSERT3P(Fdo->DevicePowerIrp, ==, NULL);
+        Fdo->DevicePowerIrp = Irp;
+        KeMemoryBarrier();
+
+        ThreadWake(Fdo->DevicePowerThread);
+
+        status = STATUS_PENDING;
+        break;
+
+    case SystemPowerState:
+        IoMarkIrpPending(Irp);
+
+        ASSERT3P(Fdo->SystemPowerIrp, ==, NULL);
+        Fdo->SystemPowerIrp = Irp;
+        KeMemoryBarrier();
+
+        ThreadWake(Fdo->SystemPowerThread);
+
+        status = STATUS_PENDING;
+        break;
+
+    default:
+        IoCopyCurrentIrpStackLocationToNext(Irp);
+        IoSetCompletionRoutine(Irp,
+                               __FdoDispatchPower,
+                               Fdo,
+                               TRUE,
+                               TRUE,
+                               TRUE);
+
+        status = IoCallDriver(Fdo->LowerDeviceObject, Irp);
+        break;
+    }
+
+    Trace("<==== (%02x:%s) (%08x)\n",
+          MinorFunction, 
+          PowerMinorFunctionName(MinorFunction),
+          status);
+
+done:
+    return status;
+
+fail1:
+    Error("fail1 (%08x)\n", status);
+
+    Irp->IoStatus.Status = status;
+    IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+    return status;
+}
+
+__drv_functionClass(IO_COMPLETION_ROUTINE)
+__drv_sameIRQL
+static NTSTATUS
+__FdoDispatchDefault(
+    IN  PDEVICE_OBJECT  DeviceObject,
+    IN  PIRP            Irp,
+    IN  PVOID           Context
+    )
+{
+    PXENDISK_FDO        Fdo = Context;
+
+    UNREFERENCED_PARAMETER(DeviceObject);
+
+    if (Irp->PendingReturned)
+        IoMarkIrpPending(Irp);
+
+    IoReleaseRemoveLock(&Fdo->Dx->RemoveLock, Irp);
+
+    return STATUS_SUCCESS;
+}
+
+static DECLSPEC_NOINLINE NTSTATUS
+FdoDispatchDefault(
+    IN  PXENDISK_FDO    Fdo,
+    IN  PIRP            Irp
+    )
+{
+    NTSTATUS            status;
+
+    status = IoAcquireRemoveLock(&Fdo->Dx->RemoveLock, Irp);
+    if (!NT_SUCCESS(status))
+        goto fail1;
+
+    IoCopyCurrentIrpStackLocationToNext(Irp);
+    IoSetCompletionRoutine(Irp,
+                           __FdoDispatchDefault,
+                           Fdo,
+                           TRUE,
+                           TRUE,
+                           TRUE);
+
+    status = IoCallDriver(Fdo->LowerDeviceObject, Irp);
+
+    return status;
+
+fail1:
+    Error("fail1 (%08x)\n", status);
+
+    Irp->IoStatus.Status = status;
+    IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+    return status;
+}
+
+NTSTATUS
+FdoDispatch(
+    IN  PXENDISK_FDO    Fdo,
+    IN  PIRP            Irp
+    )
+{
+    PIO_STACK_LOCATION  StackLocation;
+    NTSTATUS            status;
+
+    StackLocation = IoGetCurrentIrpStackLocation(Irp);
+
+    switch (StackLocation->MajorFunction) {
+    case IRP_MJ_PNP:
+        status = FdoDispatchPnp(Fdo, Irp);
+        break;
+
+    case IRP_MJ_POWER:
+        status = FdoDispatchPower(Fdo, Irp);
+        break;
+
+    default:
+        status = FdoDispatchDefault(Fdo, Irp);
+        break;
+    }
+
+    return status;
+}
+
+NTSTATUS
+FdoCreate(
+    IN  PDEVICE_OBJECT  PhysicalDeviceObject
+    )
+{
+    PDEVICE_OBJECT      LowerDeviceObject;
+    ULONG               DeviceType;
+    PDEVICE_OBJECT      FilterDeviceObject;
+    PXENDISK_DX         Dx;
+    PXENDISK_FDO        Fdo;
+    NTSTATUS            status;
+
+    LowerDeviceObject = IoGetAttachedDeviceReference(PhysicalDeviceObject);
+    DeviceType = LowerDeviceObject->DeviceType;
+    ObDereferenceObject(LowerDeviceObject);
+
+#pragma prefast(suppress:28197) // Possibly leaking memory 'FilterDeviceObject'
+    status = IoCreateDevice(DriverGetDriverObject(),
+                            sizeof (XENDISK_DX),
+                            NULL,
+                            DeviceType,
+                            FILE_DEVICE_SECURE_OPEN,
+                            FALSE,
+                            &FilterDeviceObject);
+    if (!NT_SUCCESS(status))
+        goto fail1;
+
+    Dx = (PXENDISK_DX)FilterDeviceObject->DeviceExtension;
+    RtlZeroMemory(Dx, sizeof (XENDISK_DX));
+
+    Dx->Type = FUNCTION_DEVICE_OBJECT;
+    Dx->DeviceObject = FilterDeviceObject;
+    Dx->DevicePnpState = Added;
+    Dx->SystemPowerState = PowerSystemShutdown;
+    Dx->DevicePowerState = PowerDeviceD3;
+
+    IoInitializeRemoveLock(&Dx->RemoveLock, FDO_TAG, 0, 0);
+
+    Fdo = __FdoAllocate(sizeof (XENDISK_FDO));
+
+    status = STATUS_NO_MEMORY;
+    if (Fdo == NULL)
+        goto fail2;
+
+    LowerDeviceObject = IoAttachDeviceToDeviceStack(FilterDeviceObject,
+                                                    PhysicalDeviceObject);
+
+    status = STATUS_UNSUCCESSFUL;
+    if (LowerDeviceObject == NULL)
+        goto fail3;
+
+    Fdo->Dx = Dx;
+    Fdo->PhysicalDeviceObject = PhysicalDeviceObject;
+    Fdo->LowerDeviceObject = LowerDeviceObject;
+
+    status = ThreadCreate(FdoSystemPower, Fdo, &Fdo->SystemPowerThread);
+    if (!NT_SUCCESS(status))
+        goto fail4;
+
+    status = ThreadCreate(FdoDevicePower, Fdo, &Fdo->DevicePowerThread);
+    if (!NT_SUCCESS(status))
+        goto fail5;
+
+    InitializeMutex(&Fdo->Mutex);
+    InitializeListHead(&Dx->ListEntry);
+    Fdo->References = 1;
+
+    Verbose("%p\n", FilterDeviceObject);
+
+    Dx->Fdo = Fdo;
+
+#pragma prefast(suppress:28182)  // Dereferencing NULL pointer
+    FilterDeviceObject->DeviceType = LowerDeviceObject->DeviceType;
+    FilterDeviceObject->Characteristics = LowerDeviceObject->Characteristics;
+
+    FilterDeviceObject->Flags |= LowerDeviceObject->Flags;
+    FilterDeviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
+
+    return STATUS_SUCCESS;
+
+fail5:
+    Error("fail5\n");
+
+    ThreadAlert(Fdo->SystemPowerThread);
+    ThreadJoin(Fdo->SystemPowerThread);
+    Fdo->SystemPowerThread = NULL;
+
+fail4:
+    Error("fail4\n");
+
+    Fdo->PhysicalDeviceObject = NULL;
+    Fdo->LowerDeviceObject = NULL;
+    Fdo->Dx = NULL;
+
+    IoDetachDevice(LowerDeviceObject);
+
+fail3:
+    Error("fail3\n");
+
+    ASSERT(IsZeroMemory(Fdo, sizeof (XENDISK_FDO)));
+    __FdoFree(Fdo);
+
+fail2:
+    Error("fail2\n");
+
+    IoDeleteDevice(FilterDeviceObject);
+
+fail1:
+    Error("fail1 (%08x)\n", status);
+
+    return status;
+}
+
+VOID
+FdoDestroy(
+    IN  PXENDISK_FDO    Fdo
+    )
+{
+    PDEVICE_OBJECT      LowerDeviceObject = Fdo->LowerDeviceObject;
+    PXENDISK_DX         Dx = Fdo->Dx;
+    PDEVICE_OBJECT      FilterDeviceObject = Dx->DeviceObject;
+
+    ASSERT(IsListEmpty(&Dx->ListEntry));
+    ASSERT3U(Fdo->References, ==, 0);
+    ASSERT3U(__FdoGetDevicePnpState(Fdo), ==, Deleted);
+
+    Dx->Fdo = NULL;
+
+    RtlZeroMemory(&Fdo->Mutex, sizeof (MUTEX));
+
+    ThreadAlert(Fdo->DevicePowerThread);
+    ThreadJoin(Fdo->DevicePowerThread);
+    Fdo->DevicePowerThread = NULL;
+
+    ThreadAlert(Fdo->SystemPowerThread);
+    ThreadJoin(Fdo->SystemPowerThread);
+    Fdo->SystemPowerThread = NULL;
+
+    Fdo->LowerDeviceObject = NULL;
+    Fdo->PhysicalDeviceObject = NULL;
+    Fdo->Dx = NULL;
+
+    IoDetachDevice(LowerDeviceObject);
+
+    ASSERT(IsZeroMemory(Fdo, sizeof (XENDISK_FDO)));
+    __FdoFree(Fdo);
+
+    IoDeleteDevice(FilterDeviceObject);
+}
+
diff --git a/src/xendisk/fdo.h b/src/xendisk/fdo.h
new file mode 100644
index 0000000..8aa0529
--- /dev/null
+++ b/src/xendisk/fdo.h
@@ -0,0 +1,78 @@
+/* Copyright (c) Citrix Systems Inc.
+ * All rights reserved.
+ * 
+ * Redistribution and use in source and binary forms, 
+ * with or without modification, are permitted provided 
+ * that the following conditions are met:
+ * 
+ * *   Redistributions of source code must retain the above 
+ *     copyright notice, this list of conditions and the 
+ *     following disclaimer.
+ * *   Redistributions in binary form must reproduce the above 
+ *     copyright notice, this list of conditions and the 
+ *     following disclaimer in the documentation and/or other 
+ *     materials provided with the distribution.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND 
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, 
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR 
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 
+ * SUCH DAMAGE.
+ */ 
+
+#ifndef _XENDISK_FDO_H
+#define _XENDISK_FDO_H
+
+#include <ntddk.h>
+#include "types.h"
+
+typedef struct _XENDISK_FDO XENDISK_FDO, *PXENDISK_FDO;
+
+extern VOID
+FdoAddPhysicalDeviceObject(
+    IN  PXENDISK_FDO    Fdo,
+    IN  PDEVICE_OBJECT  DeviceObject
+    );
+
+extern VOID
+FdoRemovePhysicalDeviceObject(
+    IN  PXENDISK_FDO    Fdo,
+    IN  PDEVICE_OBJECT  DeviceObject
+    );
+
+extern VOID
+FdoAcquireMutex(
+    IN  PXENDISK_FDO     Fdo
+    );
+
+extern VOID
+FdoReleaseMutex(
+    IN  PXENDISK_FDO     Fdo
+    );
+
+extern NTSTATUS
+FdoDispatch(
+    IN  PXENDISK_FDO    Fdo,
+    IN  PIRP            Irp
+    );
+
+extern NTSTATUS
+FdoCreate(
+    IN  PDEVICE_OBJECT  PhysicalDeviceObject
+    );
+
+extern VOID
+FdoDestroy(
+    IN  PXENDISK_FDO    Fdo
+    );
+
+#endif // _XENDISK_FDO_H
diff --git a/src/xendisk/mutex.h b/src/xendisk/mutex.h
new file mode 100644
index 0000000..bcc2414
--- /dev/null
+++ b/src/xendisk/mutex.h
@@ -0,0 +1,113 @@
+/* Copyright (c) Citrix Systems Inc.
+ * All rights reserved.
+ * 
+ * Redistribution and use in source and binary forms, 
+ * with or without modification, are permitted provided 
+ * that the following conditions are met:
+ * 
+ * *   Redistributions of source code must retain the above 
+ *     copyright notice, this list of conditions and the 
+ *     following disclaimer.
+ * *   Redistributions in binary form must reproduce the above 
+ *     copyright notice, this list of conditions and the 
+ *     following disclaimer in the documentation and/or other 
+ *     materials provided with the distribution.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND 
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, 
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR 
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 
+ * SUCH DAMAGE.
+ */
+
+#ifndef _XENDISK_MUTEX_H
+#define _XENDISK_MUTEX_H
+
+#include <ntddk.h>
+
+#include "assert.h"
+
+typedef struct _MUTEX {
+    PKTHREAD    Owner;
+    KEVENT      Event;
+} MUTEX, *PMUTEX;
+
+static FORCEINLINE VOID
+InitializeMutex(
+    IN  PMUTEX  Mutex
+    )
+{
+    RtlZeroMemory(Mutex, sizeof (MUTEX));
+
+    KeInitializeEvent(&Mutex->Event, SynchronizationEvent, TRUE);
+}
+
+static FORCEINLINE BOOLEAN
+__drv_maxIRQL(PASSIVE_LEVEL)
+TryAcquireMutex(
+    IN  PMUTEX      Mutex
+    )
+{
+    LARGE_INTEGER   Timeout;
+    NTSTATUS        status;
+
+    Timeout.QuadPart = 0;
+
+    status = KeWaitForSingleObject(&Mutex->Event,
+                                   Executive,
+                                   KernelMode,
+                                   FALSE,
+                                   &Timeout);
+    if (status == STATUS_TIMEOUT)
+        return FALSE;
+
+    ASSERT(NT_SUCCESS(status));
+
+    ASSERT3P(Mutex->Owner, ==, NULL);
+    Mutex->Owner = KeGetCurrentThread();
+
+    return TRUE;
+}
+
+static FORCEINLINE VOID
+__drv_maxIRQL(PASSIVE_LEVEL)
+AcquireMutex(
+    IN  PMUTEX  Mutex
+    )
+{
+    NTSTATUS    status;
+
+    status = KeWaitForSingleObject(&Mutex->Event,
+                                   Executive,
+                                   KernelMode,
+                                   FALSE,
+                                   NULL);
+
+    ASSERT(NT_SUCCESS(status));
+
+    ASSERT3P(Mutex->Owner, ==, NULL);
+    Mutex->Owner = KeGetCurrentThread();
+}
+
+static FORCEINLINE VOID
+__drv_maxIRQL(PASSIVE_LEVEL)
+ReleaseMutex(
+    IN  PMUTEX  Mutex
+    )
+{
+    ASSERT3P(Mutex->Owner, ==, KeGetCurrentThread());
+    Mutex->Owner = NULL;
+
+    KeSetEvent(&Mutex->Event, IO_NO_INCREMENT, FALSE);
+}
+
+#endif  // _XENDISK_MUTEX_H
diff --git a/src/xendisk/pdo.c b/src/xendisk/pdo.c
new file mode 100644
index 0000000..c815a1e
--- /dev/null
+++ b/src/xendisk/pdo.c
@@ -0,0 +1,2126 @@
+/* Copyright (c) Citrix Systems Inc.
+ * All rights reserved.
+ * 
+ * Redistribution and use in source and binary forms, 
+ * with or without modification, are permitted provided 
+ * that the following conditions are met:
+ * 
+ * *   Redistributions of source code must retain the above 
+ *     copyright notice, this list of conditions and the 
+ *     following disclaimer.
+ * *   Redistributions in binary form must reproduce the above 
+ *     copyright notice, this list of conditions and the 
+ *     following disclaimer in the documentation and/or other 
+ *     materials provided with the distribution.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND 
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, 
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR 
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 
+ * SUCH DAMAGE.
+ */ 
+
+#define INITGUID 1
+
+#include <ntddk.h>
+#include <wdmguid.h>
+#include <ntstrsafe.h>
+#include <stdlib.h>
+#include <storport.h>
+#include <Ntddstor.h>
+#include <Ntddscsi.h>
+#include <names.h>
+
+#include "fdo.h"
+#include "pdo.h"
+#include "driver.h"
+#include "thread.h"
+#include "debug.h"
+#include "assert.h"
+#include "util.h"
+
+#define PDO_TAG 'ODP'
+
+struct _XENDISK_PDO {
+    PXENDISK_DX                 Dx;
+    PDEVICE_OBJECT              LowerDeviceObject;
+    PDEVICE_OBJECT              PhysicalDeviceObject;
+
+    PXENDISK_THREAD             SystemPowerThread;
+    PIRP                        SystemPowerIrp;
+    PXENDISK_THREAD             DevicePowerThread;
+    PIRP                        DevicePowerIrp;
+
+    PXENDISK_FDO                Fdo;
+    BOOLEAN                     Missing;
+    const CHAR                  *Reason;
+
+    ULONG                       SectorSize;
+};
+
+static FORCEINLINE PVOID
+__PdoAllocate(
+    IN  ULONG   Length
+    )
+{
+    return __AllocateNonPagedPoolWithTag(__FUNCTION__, __LINE__, Length, 
PDO_TAG);
+}
+
+static FORCEINLINE VOID
+__PdoFree(
+    IN  PVOID   Buffer
+    )
+{
+    __FreePoolWithTag(Buffer, PDO_TAG);
+}
+
+static FORCEINLINE VOID
+__PdoSetDevicePnpState(
+    IN  PXENDISK_PDO        Pdo,
+    IN  DEVICE_PNP_STATE    State
+    )
+{
+    PXENDISK_DX             Dx = Pdo->Dx;
+
+    // We can never transition out of the deleted state
+    ASSERT(Dx->DevicePnpState != Deleted || State == Deleted);
+
+    Dx->PreviousDevicePnpState = Dx->DevicePnpState;
+    Dx->DevicePnpState = State;
+}
+
+VOID
+PdoSetDevicePnpState(
+    IN  PXENDISK_PDO        Pdo,
+    IN  DEVICE_PNP_STATE    State
+    )
+{
+    __PdoSetDevicePnpState(Pdo, State);
+}
+
+static FORCEINLINE VOID
+__PdoRestoreDevicePnpState(
+    IN  PXENDISK_PDO        Pdo,
+    IN  DEVICE_PNP_STATE    State
+    )
+{
+    PXENDISK_DX             Dx = Pdo->Dx;
+
+    if (Dx->DevicePnpState == State)
+        Dx->DevicePnpState = Dx->PreviousDevicePnpState;
+}
+
+static FORCEINLINE DEVICE_PNP_STATE
+__PdoGetDevicePnpState(
+    IN  PXENDISK_PDO    Pdo
+    )
+{
+    PXENDISK_DX         Dx = Pdo->Dx;
+
+    return Dx->DevicePnpState;
+}
+
+DEVICE_PNP_STATE
+PdoGetDevicePnpState(
+    IN  PXENDISK_PDO    Pdo
+    )
+{
+    return __PdoGetDevicePnpState(Pdo);
+}
+
+static FORCEINLINE VOID
+__PdoSetDevicePowerState(
+    IN  PXENDISK_PDO        Pdo,
+    IN  DEVICE_POWER_STATE  State
+    )
+{
+    PXENDISK_DX             Dx = Pdo->Dx;
+
+    Dx->DevicePowerState = State;
+}
+
+static FORCEINLINE DEVICE_POWER_STATE
+__PdoGetDevicePowerState(
+    IN  PXENDISK_PDO    Pdo
+    )
+{
+    PXENDISK_DX         Dx = Pdo->Dx;
+
+    return Dx->DevicePowerState;
+}
+
+static FORCEINLINE VOID
+__PdoSetSystemPowerState(
+    IN  PXENDISK_PDO        Pdo,
+    IN  SYSTEM_POWER_STATE  State
+    )
+{
+    PXENDISK_DX             Dx = Pdo->Dx;
+
+    Dx->SystemPowerState = State;
+}
+
+static FORCEINLINE SYSTEM_POWER_STATE
+__PdoGetSystemPowerState(
+    IN  PXENDISK_PDO    Pdo
+    )
+{
+    PXENDISK_DX         Dx = Pdo->Dx;
+
+    return Dx->SystemPowerState;
+}
+
+PDEVICE_OBJECT
+PdoGetPhysicalDeviceObject(
+    IN  PXENDISK_PDO    Pdo
+    )
+{
+    return Pdo->PhysicalDeviceObject;
+}
+
+static FORCEINLINE VOID
+__PdoSetMissing(
+    IN  PXENDISK_PDO    Pdo,
+    IN  const CHAR      *Reason
+    )
+{
+    Pdo->Reason = Reason;
+    Pdo->Missing = TRUE;
+}
+
+VOID
+PdoSetMissing(
+    IN  PXENDISK_PDO    Pdo,
+    IN  const CHAR      *Reason
+    )
+{
+    __PdoSetMissing(Pdo, Reason);
+}
+
+static FORCEINLINE BOOLEAN
+__PdoIsMissing(
+    IN  PXENDISK_PDO    Pdo
+    )
+{
+    return Pdo->Missing;
+}
+
+BOOLEAN
+PdoIsMissing(
+    IN  PXENDISK_PDO    Pdo
+    )
+{
+    return __PdoIsMissing(Pdo);
+}
+
+static FORCEINLINE VOID
+__PdoLink(
+    IN  PXENDISK_PDO    Pdo,
+    IN  PXENDISK_FDO    Fdo
+    )
+{
+    Pdo->Fdo = Fdo;
+    FdoAddPhysicalDeviceObject(Fdo, Pdo->Dx->DeviceObject);
+}
+
+static FORCEINLINE VOID
+__PdoUnlink(
+    IN  PXENDISK_PDO    Pdo
+    )
+{
+    PXENDISK_FDO        Fdo = Pdo->Fdo;
+
+    ASSERT(Fdo != NULL);
+
+    FdoRemovePhysicalDeviceObject(Fdo, Pdo->Dx->DeviceObject);
+
+    Pdo->Fdo = NULL;
+}
+
+static FORCEINLINE PXENDISK_FDO
+__PdoGetFdo(
+    IN  PXENDISK_PDO Pdo
+    )
+{
+    return Pdo->Fdo;
+}
+
+__drv_functionClass(IO_COMPLETION_ROUTINE)
+__drv_sameIRQL
+static NTSTATUS
+__PdoForwardIrpSynchronously(
+    IN  PDEVICE_OBJECT  DeviceObject,
+    IN  PIRP            Irp,
+    IN  PVOID           Context
+    )
+{
+    PKEVENT             Event = Context;
+
+    UNREFERENCED_PARAMETER(DeviceObject);
+    UNREFERENCED_PARAMETER(Irp);
+
+    KeSetEvent(Event, IO_NO_INCREMENT, FALSE);
+
+    return STATUS_MORE_PROCESSING_REQUIRED;
+}
+
+static NTSTATUS
+PdoForwardIrpSynchronously(
+    IN  PXENDISK_PDO    Pdo,
+    IN  PIRP            Irp
+    )
+{
+    KEVENT              Event;
+    NTSTATUS            status;
+
+    ASSERT3U(KeGetCurrentIrql(), ==, PASSIVE_LEVEL);
+
+    KeInitializeEvent(&Event, NotificationEvent, FALSE);
+
+    IoCopyCurrentIrpStackLocationToNext(Irp);
+    IoSetCompletionRoutine(Irp,
+                           __PdoForwardIrpSynchronously,
+                           &Event,
+                           TRUE,
+                           TRUE,
+                           TRUE);
+
+    status = IoCallDriver(Pdo->LowerDeviceObject, Irp);
+    if (status == STATUS_PENDING) {
+        (VOID) KeWaitForSingleObject(&Event,
+                                     Executive,
+                                     KernelMode,
+                                     FALSE,
+                                     NULL);
+        status = Irp->IoStatus.Status;
+    } else {
+        ASSERT3U(status, ==, Irp->IoStatus.Status);
+    }
+
+    return status;
+}
+
+__drv_functionClass(IO_COMPLETION_ROUTINE)
+__drv_sameIRQL
+static NTSTATUS
+__PdoForwardIrpAndForget(
+    IN  PDEVICE_OBJECT  DeviceObject,
+    IN  PIRP            Irp,
+    IN  PVOID           Context
+    )
+{
+    PXENDISK_PDO        Pdo = Context;
+
+    UNREFERENCED_PARAMETER(DeviceObject);
+
+    if (Irp->PendingReturned)
+        IoMarkIrpPending(Irp);
+
+    IoReleaseRemoveLock(&Pdo->Dx->RemoveLock, Irp);
+
+    return STATUS_SUCCESS;
+}
+
+static NTSTATUS
+PdoForwardIrpAndForget(
+    IN  PXENDISK_PDO    Pdo,
+    IN  PIRP            Irp
+    )
+{
+    IoCopyCurrentIrpStackLocationToNext(Irp);
+    IoSetCompletionRoutine(Irp,
+                            __PdoForwardIrpAndForget,
+                            Pdo,
+                            TRUE,
+                            TRUE,
+                            TRUE);
+
+    return IoCallDriver(Pdo->LowerDeviceObject, Irp);
+}
+
+static NTSTATUS
+PdoCompleteIrp(
+    IN  PXENDISK_PDO    Pdo,
+    IN  PIRP            Irp,
+    IN  NTSTATUS        Status
+    )
+{
+    Irp->IoStatus.Status = Status;
+    IoReleaseRemoveLock(&Pdo->Dx->RemoveLock, Irp);
+    IoCompleteRequest(Irp, IO_NO_INCREMENT);
+    return Status;
+}
+
+__drv_functionClass(IO_COMPLETION_ROUTINE)
+__drv_sameIRQL
+static NTSTATUS
+__PdoQueryProperty(
+    IN  PDEVICE_OBJECT  DeviceObject,
+    IN  PIRP            Irp,
+    IN  PVOID           Context
+    )
+{
+    PXENDISK_PDO        Pdo = Context;
+    PSTORAGE_ACCESS_ALIGNMENT_DESCRIPTOR    Descriptor = Irp->UserBuffer;
+
+    UNREFERENCED_PARAMETER(DeviceObject);
+
+    if (Irp->PendingReturned)
+        IoMarkIrpPending(Irp);
+
+    if (!NT_SUCCESS(Irp->IoStatus.Status))
+        goto done;
+
+    Descriptor = Irp->UserBuffer;
+    Pdo->SectorSize = Descriptor->BytesPerLogicalSector;
+    Verbose("%p : %u bytes per sector\n", Pdo->Dx->DeviceObject, 
Pdo->SectorSize);
+
+done:
+    IoReleaseRemoveLock(&Pdo->Dx->RemoveLock, Irp);
+
+    return STATUS_SUCCESS;
+}
+
+static DECLSPEC_NOINLINE NTSTATUS
+PdoQueryProperty(
+    IN  PXENDISK_PDO    Pdo,
+    IN  PIRP            Irp
+    )
+{
+    PSTORAGE_PROPERTY_QUERY Query;
+    PDEVICE_TRIM_DESCRIPTOR Trim;
+    NTSTATUS                status;
+
+    Query = Irp->AssociatedIrp.SystemBuffer;
+
+    switch (Query->PropertyId) {
+    case StorageAccessAlignmentProperty:
+        IoCopyCurrentIrpStackLocationToNext(Irp);
+        IoSetCompletionRoutine(Irp,
+                                __PdoQueryProperty,
+                                Pdo,
+                                TRUE,
+                                TRUE,
+                                TRUE);
+
+        status = IoCallDriver(Pdo->LowerDeviceObject, Irp);
+        break;
+
+    case StorageDeviceTrimProperty:
+        Trim = Irp->AssociatedIrp.SystemBuffer;
+
+        Trim->Version = 0;
+        Trim->Size = sizeof(DEVICE_TRIM_DESCRIPTOR);
+        Trim->TrimEnabled = TRUE;
+
+        Irp->IoStatus.Information = (ULONG_PTR)sizeof(DEVICE_TRIM_DESCRIPTOR);
+        status = PdoCompleteIrp(Pdo, Irp, STATUS_SUCCESS);
+        break;
+
+    default:
+        status = PdoForwardIrpAndForget(Pdo, Irp);
+        break;
+    }
+
+    return status;
+}
+
+__drv_functionClass(IO_COMPLETION_ROUTINE)
+__drv_sameIRQL
+static NTSTATUS
+__PdoSendAwaitSrb(
+    IN  PDEVICE_OBJECT          DeviceObject,
+    IN  PIRP                    Irp,
+    IN  PVOID                   Context
+    )
+{
+    UNREFERENCED_PARAMETER(DeviceObject);
+    UNREFERENCED_PARAMETER(Context);
+
+    *(Irp->UserIosb) = Irp->IoStatus;
+
+    if (Irp->MdlAddress) {
+        MmUnlockPages(Irp->MdlAddress);
+        IoFreeMdl(Irp->MdlAddress);
+    }
+
+    KeSetEvent(Irp->UserEvent, IO_NO_INCREMENT, FALSE);
+
+    IoFreeIrp(Irp);
+    return STATUS_MORE_PROCESSING_REQUIRED;
+}
+
+static NTSTATUS
+PdoSendAwaitSrb(
+    IN  PXENDISK_PDO            Pdo,
+    IN  PSCSI_REQUEST_BLOCK     Srb
+    )
+{
+    PIRP                        Irp;
+    IO_STATUS_BLOCK             IoStatus;
+    KEVENT                      Event;
+    PIO_STACK_LOCATION          Stack;
+    NTSTATUS                    status;
+
+    KeInitializeEvent(&Event, NotificationEvent, FALSE);
+
+    status = STATUS_NO_MEMORY;
+    Irp = IoAllocateIrp((CCHAR)(Pdo->LowerDeviceObject->StackSize + 1), FALSE);
+    if (Irp == NULL)
+        goto fail1;
+
+    Stack = IoGetNextIrpStackLocation(Irp);
+    Stack->MajorFunction = IRP_MJ_SCSI;
+    Stack->Parameters.Scsi.Srb = Srb;
+
+    IoSetCompletionRoutine(Irp,
+                            __PdoSendAwaitSrb,
+                            Srb,
+                            TRUE,
+                            TRUE,
+                            TRUE);
+    Irp->UserIosb = &IoStatus;
+    Irp->UserEvent = &Event;
+
+    Irp->MdlAddress = IoAllocateMdl(Srb->DataBuffer,
+                                    Srb->DataTransferLength,
+                                    FALSE,
+                                    FALSE,
+                                    Irp);
+    if (Irp->MdlAddress == NULL)
+        goto fail2;
+
+#pragma warning(disable:6320)
+    try {
+        MmProbeAndLockPages(Irp->MdlAddress, KernelMode, IoReadAccess);
+    } except (EXCEPTION_EXECUTE_HANDLER) {
+        status = GetExceptionCode();
+
+        goto fail3;
+    }
+#pragma warning(default:6320)
+    
+    Srb->OriginalRequest = Irp;
+
+    status = IoCallDriver(Pdo->LowerDeviceObject, Irp);
+    if (status == STATUS_PENDING) {
+        (VOID) KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, 
NULL);
+        status = IoStatus.Status;
+    }
+
+    return status;
+
+fail3:
+    Error("fail3\n");
+
+    IoFreeMdl(Irp->MdlAddress);
+
+fail2:
+    Error("fail2\n");
+
+    IoFreeIrp(Irp);
+
+fail1:
+    Error("fail1 (%08x)\n", status);
+
+    return status;
+}
+
+static FORCEINLINE VOID
+__Set8Bytes(
+    IN  PUCHAR                  Bytes,
+    IN  ULONGLONG               Value
+    )
+{
+    Bytes[0] = (UCHAR)(Value >> 56);
+    Bytes[1] = (UCHAR)(Value >> 48);
+    Bytes[2] = (UCHAR)(Value >> 40);
+    Bytes[3] = (UCHAR)(Value >> 32);
+    Bytes[4] = (UCHAR)(Value >> 24);
+    Bytes[5] = (UCHAR)(Value >> 16);
+    Bytes[6] = (UCHAR)(Value >> 8);
+    Bytes[7] = (UCHAR)(Value);
+}
+
+static FORCEINLINE VOID
+__Set4Bytes(
+    IN  PUCHAR                  Bytes,
+    IN  ULONG                   Value
+    )
+{
+    Bytes[0] = (UCHAR)(Value >> 24);
+    Bytes[1] = (UCHAR)(Value >> 16);
+    Bytes[2] = (UCHAR)(Value >> 8);
+    Bytes[3] = (UCHAR)(Value);
+}
+
+static FORCEINLINE VOID
+__Set2Bytes(
+    IN  PUCHAR                  Bytes,
+    IN  USHORT                  Value
+    )
+{
+    Bytes[0] = (UCHAR)(Value << 8);
+    Bytes[1] = (UCHAR)(Value);
+}
+
+static FORCEINLINE VOID
+__SetUnmap(
+    IN  PUNMAP_LIST_HEADER      Unmap,
+    IN  ULONG                   Length,
+    IN  PDEVICE_DATA_SET_RANGE  Ranges,
+    IN  ULONG                   Count,
+    IN  ULONG                   SectorSize
+    )
+{
+    ULONG       Index;
+
+    __Set2Bytes(Unmap->DataLength, (USHORT)Length);
+    __Set2Bytes(Unmap->BlockDescrDataLength, 
(USHORT)sizeof(UNMAP_BLOCK_DESCRIPTOR));
+
+    for (Index = 0; Index < Count; ++Index) {
+        Trace("TRIM[%x] %x @ %llx\n", 
+                        Index,
+                        (ULONG)(Ranges[Index].LengthInBytes / SectorSize),
+                        (ULONG64)(Ranges[Index].StartingOffset / SectorSize));
+
+        __Set8Bytes(Unmap->Descriptors[Index].StartingLba,
+                        (ULONG64)(Ranges[Index].StartingOffset / SectorSize));
+        __Set4Bytes(Unmap->Descriptors[Index].LbaCount,
+                        (ULONG)(Ranges[Index].LengthInBytes / SectorSize));
+    }
+}
+
+static NTSTATUS
+PdoSendTrimSynchronous(
+    IN  PXENDISK_PDO            Pdo,
+    IN  PDEVICE_DATA_SET_RANGE  Ranges,
+    IN  ULONG                   Count
+    )
+{
+    SCSI_REQUEST_BLOCK          Srb;
+    PCDB                        Cdb;
+    PUNMAP_LIST_HEADER          Unmap;
+    ULONG                       Length;
+    NTSTATUS                    status;
+
+    Length = sizeof(UNMAP_LIST_HEADER) + Count * 
sizeof(UNMAP_BLOCK_DESCRIPTOR);
+
+    status = STATUS_NO_MEMORY;
+    Unmap = __PdoAllocate(Length);
+    if (Unmap == NULL)
+        goto fail1;
+
+    RtlZeroMemory(&Srb, sizeof(SCSI_REQUEST_BLOCK));
+    Srb.Length = sizeof(SCSI_REQUEST_BLOCK);
+    Srb.SrbFlags = SRB_FLAGS_BYPASS_FROZEN_QUEUE | SRB_FLAGS_NO_QUEUE_FREEZE;
+    Srb.Function = SRB_FUNCTION_EXECUTE_SCSI;
+    Srb.DataBuffer = Unmap;
+    Srb.DataTransferLength = Length;
+    Srb.TimeOutValue = (ULONG)-1;
+    Srb.CdbLength = 10;
+
+    Cdb = (PCDB)&Srb.Cdb[0];
+    Cdb->UNMAP.OperationCode = SCSIOP_UNMAP;
+    __Set2Bytes(Cdb->UNMAP.AllocationLength, (USHORT)Length);
+
+    __SetUnmap(Unmap, Length, Ranges, Count, Pdo->SectorSize);
+
+    status = PdoSendAwaitSrb(Pdo, &Srb);
+    if (!NT_SUCCESS(status))
+        goto fail2;
+
+    __PdoFree(Unmap);
+    return status;
+
+fail2:
+    Error("fail2\n");
+
+    __PdoFree(Unmap);
+
+fail1:
+    Error("fail1 (%08x)\n", status);
+
+    return status;
+}
+
+static DECLSPEC_NOINLINE NTSTATUS
+PdoManageDataSetAttributes(
+    IN  PXENDISK_PDO    Pdo,
+    IN  PIRP            Irp
+    )
+{
+    PDEVICE_MANAGE_DATA_SET_ATTRIBUTES  Attributes;
+    PDEVICE_DATA_SET_RANGE              Ranges;
+    ULONG                               NumRanges;
+    NTSTATUS                            status;
+
+    Attributes = Irp->AssociatedIrp.SystemBuffer;
+
+    switch (Attributes->Action) {
+    case DeviceDsmAction_Trim:
+        Ranges = (PDEVICE_DATA_SET_RANGE)((PUCHAR)Attributes + 
Attributes->DataSetRangesOffset);
+        NumRanges = Attributes->DataSetRangesLength / 
sizeof(DEVICE_DATA_SET_RANGE);
+
+        status = PdoSendTrimSynchronous(Pdo, Ranges, NumRanges);
+
+        status = PdoCompleteIrp(Pdo, Irp, status);
+        break;
+
+    default:
+        status = PdoForwardIrpAndForget(Pdo, Irp);
+        break;
+    }
+
+    return status;
+}
+
+static DECLSPEC_NOINLINE NTSTATUS
+PdoDispatchControl(
+    IN  PXENDISK_PDO    Pdo,
+    IN  PIRP            Irp
+    )
+{
+    PIO_STACK_LOCATION  StackLocation;
+    ULONG               ControlCode;
+    NTSTATUS            status;
+
+    status = IoAcquireRemoveLock(&Pdo->Dx->RemoveLock, Irp);
+    if (!NT_SUCCESS(status))
+        goto fail1;
+
+    StackLocation = IoGetCurrentIrpStackLocation(Irp);
+    ControlCode = StackLocation->Parameters.DeviceIoControl.IoControlCode;
+
+    switch (ControlCode) {
+    case IOCTL_STORAGE_QUERY_PROPERTY:
+        status = PdoQueryProperty(Pdo, Irp);
+        break;
+
+    case IOCTL_STORAGE_MANAGE_DATA_SET_ATTRIBUTES:
+        status = PdoManageDataSetAttributes(Pdo, Irp);
+        break;
+
+    default:
+        status = PdoForwardIrpAndForget(Pdo, Irp);
+        break;
+    }
+
+    return status;
+
+fail1:
+    Error("fail1 (%08x)\n", status);
+
+    Irp->IoStatus.Status = status;
+    IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+    return status;
+}
+
+static DECLSPEC_NOINLINE NTSTATUS
+PdoStartDevice(
+    IN  PXENDISK_PDO    Pdo,
+    IN  PIRP            Irp
+    )
+{
+    POWER_STATE         PowerState;
+    NTSTATUS            status;
+
+    status = IoAcquireRemoveLock(&Pdo->Dx->RemoveLock, Irp);
+    if (!NT_SUCCESS(status))
+        goto fail1;
+
+    status = PdoForwardIrpSynchronously(Pdo, Irp);
+    if (!NT_SUCCESS(status))
+        goto fail2;
+
+    __PdoSetSystemPowerState(Pdo, PowerSystemWorking);
+    __PdoSetDevicePowerState(Pdo, PowerDeviceD0);
+
+    PowerState.DeviceState = PowerDeviceD0;
+    PoSetPowerState(Pdo->Dx->DeviceObject,
+                    DevicePowerState,
+                    PowerState);
+
+    __PdoSetDevicePnpState(Pdo, Started);
+
+    IoReleaseRemoveLock(&Pdo->Dx->RemoveLock, Irp);
+
+    Irp->IoStatus.Status = STATUS_SUCCESS;
+    IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+    return STATUS_SUCCESS;
+
+fail2:
+    IoReleaseRemoveLock(&Pdo->Dx->RemoveLock, Irp);
+
+fail1:
+    Irp->IoStatus.Status = status;
+    IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+    return status;
+}
+
+__drv_functionClass(IO_COMPLETION_ROUTINE)
+__drv_sameIRQL
+static NTSTATUS
+__PdoQueryStopDevice(
+    IN  PDEVICE_OBJECT  DeviceObject,
+    IN  PIRP            Irp,
+    IN  PVOID           Context
+    )
+{
+    PXENDISK_PDO        Pdo = Context;
+
+    UNREFERENCED_PARAMETER(DeviceObject);
+
+    if (Irp->PendingReturned)
+        IoMarkIrpPending(Irp);
+
+    IoReleaseRemoveLock(&Pdo->Dx->RemoveLock, Irp);
+
+    return STATUS_SUCCESS;
+}
+
+static DECLSPEC_NOINLINE NTSTATUS
+PdoQueryStopDevice(
+    IN  PXENDISK_PDO    Pdo,
+    IN  PIRP            Irp
+    )
+{
+    NTSTATUS            status;
+
+    status = IoAcquireRemoveLock(&Pdo->Dx->RemoveLock, Irp);
+    if (!NT_SUCCESS(status))
+        goto fail1;
+
+    __PdoSetDevicePnpState(Pdo, StopPending);
+    Irp->IoStatus.Status = STATUS_SUCCESS;
+
+    IoCopyCurrentIrpStackLocationToNext(Irp);
+    IoSetCompletionRoutine(Irp,
+                           __PdoQueryStopDevice,
+                           Pdo,
+                           TRUE,
+                           TRUE,
+                           TRUE);
+
+    status = IoCallDriver(Pdo->LowerDeviceObject, Irp);
+
+    return status;
+
+fail1:
+    Irp->IoStatus.Status = status;
+    IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+    return status;
+}
+
+__drv_functionClass(IO_COMPLETION_ROUTINE)
+__drv_sameIRQL
+static NTSTATUS
+__PdoCancelStopDevice(
+    IN  PDEVICE_OBJECT  DeviceObject,
+    IN  PIRP            Irp,
+    IN  PVOID           Context
+    )
+{
+    PXENDISK_PDO        Pdo = Context;
+
+    UNREFERENCED_PARAMETER(DeviceObject);
+
+    if (Irp->PendingReturned)
+        IoMarkIrpPending(Irp);
+
+    IoReleaseRemoveLock(&Pdo->Dx->RemoveLock, Irp);
+
+    return STATUS_SUCCESS;
+}
+
+static DECLSPEC_NOINLINE NTSTATUS
+PdoCancelStopDevice(
+    IN  PXENDISK_PDO    Pdo,
+    IN  PIRP            Irp
+    )
+{
+    NTSTATUS            status;
+
+    status = IoAcquireRemoveLock(&Pdo->Dx->RemoveLock, Irp);
+    if (!NT_SUCCESS(status))
+        goto fail1;
+
+    Irp->IoStatus.Status = STATUS_SUCCESS;
+
+    __PdoRestoreDevicePnpState(Pdo, StopPending);
+
+    IoCopyCurrentIrpStackLocationToNext(Irp);
+    IoSetCompletionRoutine(Irp,
+                           __PdoCancelStopDevice,
+                           Pdo,
+                           TRUE,
+                           TRUE,
+                           TRUE);
+
+    status = IoCallDriver(Pdo->LowerDeviceObject, Irp);
+
+    return status;
+
+fail1:
+    Irp->IoStatus.Status = status;
+    IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+    return status;
+}
+
+__drv_functionClass(IO_COMPLETION_ROUTINE)
+__drv_sameIRQL
+static NTSTATUS
+__PdoStopDevice(
+    IN  PDEVICE_OBJECT  DeviceObject,
+    IN  PIRP            Irp,
+    IN  PVOID           Context
+    )
+{
+    PXENDISK_PDO        Pdo = Context;
+
+    UNREFERENCED_PARAMETER(DeviceObject);
+
+    if (Irp->PendingReturned)
+        IoMarkIrpPending(Irp);
+
+    IoReleaseRemoveLock(&Pdo->Dx->RemoveLock, Irp);
+
+    return STATUS_SUCCESS;
+}
+
+static DECLSPEC_NOINLINE NTSTATUS
+PdoStopDevice(
+    IN  PXENDISK_PDO    Pdo,
+    IN  PIRP            Irp
+    )
+{
+    POWER_STATE         PowerState;
+    NTSTATUS            status;
+
+    status = IoAcquireRemoveLock(&Pdo->Dx->RemoveLock, Irp);
+    if (!NT_SUCCESS(status))
+        goto fail1;
+
+    if (__PdoGetDevicePowerState(Pdo) != PowerDeviceD0)
+        goto done;
+
+    __PdoSetDevicePowerState(Pdo, PowerDeviceD3);
+    __PdoSetSystemPowerState(Pdo, PowerSystemShutdown);
+
+    PowerState.DeviceState = PowerDeviceD3;
+    PoSetPowerState(Pdo->Dx->DeviceObject,
+                    DevicePowerState,
+                    PowerState);
+
+done:
+    __PdoSetDevicePnpState(Pdo, Stopped);
+    Irp->IoStatus.Status = STATUS_SUCCESS;
+
+    IoCopyCurrentIrpStackLocationToNext(Irp);
+    IoSetCompletionRoutine(Irp,
+                           __PdoStopDevice,
+                           Pdo,
+                           TRUE,
+                           TRUE,
+                           TRUE);
+
+    status = IoCallDriver(Pdo->LowerDeviceObject, Irp);
+
+    return status;
+
+fail1:
+    Irp->IoStatus.Status = status;
+    IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+    return status;
+}
+
+__drv_functionClass(IO_COMPLETION_ROUTINE)
+__drv_sameIRQL
+static NTSTATUS
+__PdoQueryRemoveDevice(
+    IN  PDEVICE_OBJECT  DeviceObject,
+    IN  PIRP            Irp,
+    IN  PVOID           Context
+    )
+{
+    PXENDISK_PDO        Pdo = Context;
+
+    UNREFERENCED_PARAMETER(DeviceObject);
+
+    if (Irp->PendingReturned)
+        IoMarkIrpPending(Irp);
+
+    IoReleaseRemoveLock(&Pdo->Dx->RemoveLock, Irp);
+
+    return STATUS_SUCCESS;
+}
+
+static DECLSPEC_NOINLINE NTSTATUS
+PdoQueryRemoveDevice(
+    IN  PXENDISK_PDO    Pdo,
+    IN  PIRP            Irp
+    )
+{
+    NTSTATUS            status;
+
+    status = IoAcquireRemoveLock(&Pdo->Dx->RemoveLock, Irp);
+    if (!NT_SUCCESS(status))
+        goto fail1;
+
+    __PdoSetDevicePnpState(Pdo, RemovePending);
+    Irp->IoStatus.Status = STATUS_SUCCESS;
+
+    IoCopyCurrentIrpStackLocationToNext(Irp);
+    IoSetCompletionRoutine(Irp,
+                           __PdoQueryRemoveDevice,
+                           Pdo,
+                           TRUE,
+                           TRUE,
+                           TRUE);
+
+    status = IoCallDriver(Pdo->LowerDeviceObject, Irp);
+
+    return status;
+
+fail1:
+    Irp->IoStatus.Status = status;
+    IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+    return status;
+}
+
+__drv_functionClass(IO_COMPLETION_ROUTINE)
+__drv_sameIRQL
+static NTSTATUS
+__PdoCancelRemoveDevice(
+    IN  PDEVICE_OBJECT  DeviceObject,
+    IN  PIRP            Irp,
+    IN  PVOID           Context
+    )
+{
+    PXENDISK_PDO        Pdo = Context;
+
+    UNREFERENCED_PARAMETER(DeviceObject);
+
+    if (Irp->PendingReturned)
+        IoMarkIrpPending(Irp);
+
+    IoReleaseRemoveLock(&Pdo->Dx->RemoveLock, Irp);
+
+    return STATUS_SUCCESS;
+}
+
+static DECLSPEC_NOINLINE NTSTATUS
+PdoCancelRemoveDevice(
+    IN  PXENDISK_PDO    Pdo,
+    IN  PIRP            Irp
+    )
+{
+    NTSTATUS            status;
+
+    status = IoAcquireRemoveLock(&Pdo->Dx->RemoveLock, Irp);
+    if (!NT_SUCCESS(status))
+        goto fail1;
+
+    __PdoRestoreDevicePnpState(Pdo, RemovePending);
+    Irp->IoStatus.Status = STATUS_SUCCESS;
+
+    IoCopyCurrentIrpStackLocationToNext(Irp);
+    IoSetCompletionRoutine(Irp,
+                           __PdoCancelRemoveDevice,
+                           Pdo,
+                           TRUE,
+                           TRUE,
+                           TRUE);
+
+    status = IoCallDriver(Pdo->LowerDeviceObject, Irp);
+
+    return status;
+
+fail1:
+    Irp->IoStatus.Status = status;
+    IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+    return status;
+}
+
+__drv_functionClass(IO_COMPLETION_ROUTINE)
+__drv_sameIRQL
+static NTSTATUS
+__PdoSurpriseRemoval(
+    IN  PDEVICE_OBJECT  DeviceObject,
+    IN  PIRP            Irp,
+    IN  PVOID           Context
+    )
+{
+    PXENDISK_PDO        Pdo = Context;
+
+    UNREFERENCED_PARAMETER(DeviceObject);
+
+    if (Irp->PendingReturned)
+        IoMarkIrpPending(Irp);
+
+    IoReleaseRemoveLock(&Pdo->Dx->RemoveLock, Irp);
+
+    return STATUS_SUCCESS;
+}
+
+static DECLSPEC_NOINLINE NTSTATUS
+PdoSurpriseRemoval(
+    IN  PXENDISK_PDO    Pdo,
+    IN  PIRP            Irp
+    )
+{
+    NTSTATUS            status;
+
+    status = IoAcquireRemoveLock(&Pdo->Dx->RemoveLock, Irp);
+    if (!NT_SUCCESS(status))
+        goto fail1;
+
+    __PdoSetDevicePnpState(Pdo, SurpriseRemovePending);
+    Irp->IoStatus.Status = STATUS_SUCCESS;
+
+    IoCopyCurrentIrpStackLocationToNext(Irp);
+    IoSetCompletionRoutine(Irp,
+                           __PdoSurpriseRemoval,
+                           Pdo,
+                           TRUE,
+                           TRUE,
+                           TRUE);
+
+    status = IoCallDriver(Pdo->LowerDeviceObject, Irp);
+
+    return status;
+
+fail1:
+    Irp->IoStatus.Status = status;
+    IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+    return status;
+}
+
+static DECLSPEC_NOINLINE NTSTATUS
+PdoRemoveDevice(
+    IN  PXENDISK_PDO    Pdo,
+    IN  PIRP            Irp
+    )
+{
+    PXENDISK_FDO        Fdo = __PdoGetFdo(Pdo);
+    POWER_STATE         PowerState;
+    NTSTATUS            status;
+
+    status = IoAcquireRemoveLock(&Pdo->Dx->RemoveLock, Irp);
+    if (!NT_SUCCESS(status))
+        goto fail1;
+
+    if (__PdoGetDevicePowerState(Pdo) != PowerDeviceD0)
+        goto done;
+
+    __PdoSetDevicePowerState(Pdo, PowerDeviceD3);
+    __PdoSetSystemPowerState(Pdo, PowerSystemShutdown);
+
+    PowerState.DeviceState = PowerDeviceD3;
+    PoSetPowerState(Pdo->Dx->DeviceObject,
+                    DevicePowerState,
+                    PowerState);
+
+done:
+    if (__PdoIsMissing(Pdo)) {
+        __PdoSetDevicePnpState(Pdo, Deleted);
+        IoReleaseRemoveLockAndWait(&Pdo->Dx->RemoveLock, Irp);
+    } else {
+        __PdoSetDevicePnpState(Pdo, Enumerated);
+        IoReleaseRemoveLock(&Pdo->Dx->RemoveLock, Irp);
+    }
+
+    status = PdoForwardIrpSynchronously(Pdo, Irp);
+    IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+    if (__PdoIsMissing(Pdo)) {
+        FdoAcquireMutex(Fdo);
+        PdoDestroy(Pdo);
+        FdoReleaseMutex(Fdo);
+    }
+
+    return status;
+
+fail1:
+    Irp->IoStatus.Status = status;
+    IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+    return status;
+}
+
+static DECLSPEC_NOINLINE NTSTATUS
+PdoEject(
+    IN  PXENDISK_PDO    Pdo,
+    IN  PIRP            Irp
+    )
+{
+    PXENDISK_FDO        Fdo = __PdoGetFdo(Pdo);
+    NTSTATUS            status;
+
+    __PdoSetMissing(Pdo, "Ejected");
+    __PdoSetDevicePnpState(Pdo, Deleted);
+
+    status = PdoForwardIrpSynchronously(Pdo, Irp);
+    IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+    FdoAcquireMutex(Fdo);
+    PdoDestroy(Pdo);
+    FdoReleaseMutex(Fdo);
+
+    return status;
+}
+
+__drv_functionClass(IO_COMPLETION_ROUTINE)
+__drv_sameIRQL
+static NTSTATUS
+__PdoDispatchPnp(
+    IN  PDEVICE_OBJECT  DeviceObject,
+    IN  PIRP            Irp,
+    IN  PVOID           Context
+    )
+{
+    PXENDISK_PDO        Pdo = Context;
+
+    UNREFERENCED_PARAMETER(DeviceObject);
+
+    if (Irp->PendingReturned)
+        IoMarkIrpPending(Irp);
+
+    IoReleaseRemoveLock(&Pdo->Dx->RemoveLock, Irp);
+    return STATUS_SUCCESS;
+}
+
+static DECLSPEC_NOINLINE NTSTATUS
+PdoDispatchPnp(
+    IN  PXENDISK_PDO    Pdo,
+    IN  PIRP            Irp
+    )
+{
+    PIO_STACK_LOCATION  StackLocation;
+    UCHAR               MinorFunction;
+    NTSTATUS            status;
+
+    StackLocation = IoGetCurrentIrpStackLocation(Irp);
+    MinorFunction = StackLocation->MinorFunction;
+
+    switch (StackLocation->MinorFunction) {
+    case IRP_MN_START_DEVICE:
+        status = PdoStartDevice(Pdo, Irp);
+        break;
+
+    case IRP_MN_QUERY_STOP_DEVICE:
+        status = PdoQueryStopDevice(Pdo, Irp);
+        break;
+
+    case IRP_MN_CANCEL_STOP_DEVICE:
+        status = PdoCancelStopDevice(Pdo, Irp);
+        break;
+
+    case IRP_MN_STOP_DEVICE:
+        status = PdoStopDevice(Pdo, Irp);
+        break;
+
+    case IRP_MN_QUERY_REMOVE_DEVICE:
+        status = PdoQueryRemoveDevice(Pdo, Irp);
+        break;
+
+    case IRP_MN_SURPRISE_REMOVAL:
+        status = PdoSurpriseRemoval(Pdo, Irp);
+        break;
+
+    case IRP_MN_REMOVE_DEVICE:
+        status = PdoRemoveDevice(Pdo, Irp);
+        break;
+
+    case IRP_MN_CANCEL_REMOVE_DEVICE:
+        status = PdoCancelRemoveDevice(Pdo, Irp);
+        break;
+
+    case IRP_MN_EJECT:
+        status = PdoEject(Pdo, Irp);
+        break;
+
+    default:
+        status = IoAcquireRemoveLock(&Pdo->Dx->RemoveLock, Irp);
+        if (!NT_SUCCESS(status))
+            goto fail1;
+
+        IoCopyCurrentIrpStackLocationToNext(Irp);
+        IoSetCompletionRoutine(Irp,
+                               __PdoDispatchPnp,
+                               Pdo,
+                               TRUE,
+                               TRUE,
+                               TRUE);
+
+        status = IoCallDriver(Pdo->LowerDeviceObject, Irp);
+        break;
+    }
+
+    return status;
+
+fail1:
+    Error("fail1 (%08x)\n", status);
+
+    Irp->IoStatus.Status = status;
+    IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+    return status;
+}
+
+static FORCEINLINE NTSTATUS
+__PdoSetDevicePowerUp(
+    IN  PXENDISK_PDO    Pdo,
+    IN  PIRP            Irp
+    )
+{
+    PIO_STACK_LOCATION  StackLocation;
+    DEVICE_POWER_STATE  DeviceState;
+    NTSTATUS            status;
+
+    StackLocation = IoGetCurrentIrpStackLocation(Irp);
+    DeviceState = StackLocation->Parameters.Power.State.DeviceState;
+
+    ASSERT3U(DeviceState, <,  __PdoGetDevicePowerState(Pdo));
+
+    status = PdoForwardIrpSynchronously(Pdo, Irp);
+    if (!NT_SUCCESS(status))
+        goto done;
+
+    Verbose("%p: %s -> %s\n",
+         Pdo->Dx->DeviceObject,
+         PowerDeviceStateName(__PdoGetDevicePowerState(Pdo)),
+         PowerDeviceStateName(DeviceState));
+
+    __PdoSetDevicePowerState(Pdo, DeviceState);
+
+done:
+    Irp->IoStatus.Status = status;
+    IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+    return status;
+}
+
+static FORCEINLINE NTSTATUS
+__PdoSetDevicePowerDown(
+    IN  PXENDISK_PDO    Pdo,
+    IN  PIRP            Irp
+    )
+{
+    PIO_STACK_LOCATION  StackLocation;
+    DEVICE_POWER_STATE  DeviceState;
+    NTSTATUS            status;
+
+    StackLocation = IoGetCurrentIrpStackLocation(Irp);
+    DeviceState = StackLocation->Parameters.Power.State.DeviceState;
+
+    ASSERT3U(DeviceState, >,  __PdoGetDevicePowerState(Pdo));
+
+    Verbose("%p: %s -> %s\n",
+         Pdo->Dx->DeviceObject,
+         PowerDeviceStateName(__PdoGetDevicePowerState(Pdo)),
+         PowerDeviceStateName(DeviceState));
+
+    __PdoSetDevicePowerState(Pdo, DeviceState);
+
+    status = PdoForwardIrpSynchronously(Pdo, Irp);
+    IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+    return status;
+}
+
+static FORCEINLINE NTSTATUS
+__PdoSetDevicePower(
+    IN  PXENDISK_PDO    Pdo,
+    IN  PIRP            Irp
+    )
+{
+    PIO_STACK_LOCATION  StackLocation;
+    DEVICE_POWER_STATE  DeviceState;
+    POWER_ACTION        PowerAction;
+    NTSTATUS            status;
+
+    StackLocation = IoGetCurrentIrpStackLocation(Irp);
+    DeviceState = StackLocation->Parameters.Power.State.DeviceState;
+    PowerAction = StackLocation->Parameters.Power.ShutdownType;
+
+    Trace("====> (%s:%s)\n",
+          PowerDeviceStateName(DeviceState), 
+          PowerActionName(PowerAction));
+
+    if (DeviceState == __PdoGetDevicePowerState(Pdo)) {
+        status = PdoForwardIrpSynchronously(Pdo, Irp);
+        IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+        goto done;
+    }
+
+    status = (DeviceState < __PdoGetDevicePowerState(Pdo)) ?
+             __PdoSetDevicePowerUp(Pdo, Irp) :
+             __PdoSetDevicePowerDown(Pdo, Irp);
+
+done:
+    Trace("<==== (%s:%s)(%08x)\n",
+          PowerDeviceStateName(DeviceState), 
+          PowerActionName(PowerAction),
+          status);
+    return status;
+}
+
+static FORCEINLINE NTSTATUS
+__PdoSetSystemPowerUp(
+    IN  PXENDISK_PDO    Pdo,
+    IN  PIRP            Irp
+    )
+{
+    PIO_STACK_LOCATION  StackLocation;
+    SYSTEM_POWER_STATE  SystemState;
+    NTSTATUS            status;
+
+    StackLocation = IoGetCurrentIrpStackLocation(Irp);
+    SystemState = StackLocation->Parameters.Power.State.SystemState;
+
+    ASSERT3U(SystemState, <,  __PdoGetSystemPowerState(Pdo));
+
+    status = PdoForwardIrpSynchronously(Pdo, Irp);
+    if (!NT_SUCCESS(status))
+        goto done;
+
+    Verbose("%p: %s -> %s\n",
+         Pdo->Dx->DeviceObject,
+         PowerSystemStateName(__PdoGetSystemPowerState(Pdo)),
+         PowerSystemStateName(SystemState));
+
+    __PdoSetSystemPowerState(Pdo, SystemState);
+
+done:
+    Irp->IoStatus.Status = status;
+    IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+    return status;
+}
+
+static FORCEINLINE NTSTATUS
+__PdoSetSystemPowerDown(
+    IN  PXENDISK_PDO     Pdo,
+    IN  PIRP            Irp
+    )
+{
+    PIO_STACK_LOCATION  StackLocation;
+    SYSTEM_POWER_STATE  SystemState;
+    NTSTATUS            status;
+
+    StackLocation = IoGetCurrentIrpStackLocation(Irp);
+    SystemState = StackLocation->Parameters.Power.State.SystemState;
+
+    ASSERT3U(SystemState, >,  __PdoGetSystemPowerState(Pdo));
+
+    Verbose("%p: %s -> %s\n",
+         Pdo->Dx->DeviceObject,
+         PowerSystemStateName(__PdoGetSystemPowerState(Pdo)),
+         PowerSystemStateName(SystemState));
+
+    __PdoSetSystemPowerState(Pdo, SystemState);
+
+    status = PdoForwardIrpSynchronously(Pdo, Irp);
+    IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+    return status;
+}
+
+static FORCEINLINE NTSTATUS
+__PdoSetSystemPower(
+    IN  PXENDISK_PDO    Pdo,
+    IN  PIRP            Irp
+    )
+{
+    PIO_STACK_LOCATION  StackLocation;
+    SYSTEM_POWER_STATE  SystemState;
+    POWER_ACTION        PowerAction;
+    NTSTATUS            status;
+
+    StackLocation = IoGetCurrentIrpStackLocation(Irp);
+    SystemState = StackLocation->Parameters.Power.State.SystemState;
+    PowerAction = StackLocation->Parameters.Power.ShutdownType;
+
+    Trace("====> (%s:%s)\n",
+          PowerSystemStateName(SystemState), 
+          PowerActionName(PowerAction));
+
+    if (SystemState == __PdoGetSystemPowerState(Pdo)) {
+        status = PdoForwardIrpSynchronously(Pdo, Irp);
+        IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+        goto done;
+    }
+
+    status = (SystemState < __PdoGetSystemPowerState(Pdo)) ?
+             __PdoSetSystemPowerUp(Pdo, Irp) :
+             __PdoSetSystemPowerDown(Pdo, Irp);
+
+done:
+    Trace("<==== (%s:%s)(%08x)\n",
+          PowerSystemStateName(SystemState), 
+          PowerActionName(PowerAction),
+          status);
+    return status;
+}
+
+static FORCEINLINE NTSTATUS
+__PdoQueryDevicePowerUp(
+    IN  PXENDISK_PDO    Pdo,
+    IN  PIRP            Irp
+    )
+{
+    PIO_STACK_LOCATION  StackLocation;
+    DEVICE_POWER_STATE  DeviceState;
+    NTSTATUS            status;
+
+    StackLocation = IoGetCurrentIrpStackLocation(Irp);
+    DeviceState = StackLocation->Parameters.Power.State.DeviceState;
+
+    ASSERT3U(DeviceState, <,  __PdoGetDevicePowerState(Pdo));
+
+    status = PdoForwardIrpSynchronously(Pdo, Irp);
+
+    Irp->IoStatus.Status = status;
+    IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+    return status;
+}
+
+static FORCEINLINE NTSTATUS
+__PdoQueryDevicePowerDown(
+    IN  PXENDISK_PDO    Pdo,
+    IN  PIRP            Irp
+    )
+{
+    PIO_STACK_LOCATION  StackLocation;
+    DEVICE_POWER_STATE  DeviceState;
+    NTSTATUS            status;
+
+    StackLocation = IoGetCurrentIrpStackLocation(Irp);
+    DeviceState = StackLocation->Parameters.Power.State.DeviceState;
+
+    ASSERT3U(DeviceState, >,  __PdoGetDevicePowerState(Pdo));
+
+    status = PdoForwardIrpSynchronously(Pdo, Irp);
+    IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+    return status;
+}
+
+static FORCEINLINE NTSTATUS
+__PdoQueryDevicePower(
+    IN  PXENDISK_PDO    Pdo,
+    IN  PIRP            Irp
+    )
+{
+    PIO_STACK_LOCATION  StackLocation;
+    DEVICE_POWER_STATE  DeviceState;
+    POWER_ACTION        PowerAction;
+    NTSTATUS            status;
+
+    StackLocation = IoGetCurrentIrpStackLocation(Irp);
+    DeviceState = StackLocation->Parameters.Power.State.DeviceState;
+    PowerAction = StackLocation->Parameters.Power.ShutdownType;
+
+    Trace("====> (%s:%s)\n",
+          PowerDeviceStateName(DeviceState), 
+          PowerActionName(PowerAction));
+
+    if (DeviceState == __PdoGetDevicePowerState(Pdo)) {
+        status = PdoForwardIrpSynchronously(Pdo, Irp);
+        IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+        goto done;
+    }
+
+    status = (DeviceState < __PdoGetDevicePowerState(Pdo)) ?
+             __PdoQueryDevicePowerUp(Pdo, Irp) :
+             __PdoQueryDevicePowerDown(Pdo, Irp);
+
+done:
+    Trace("<==== (%s:%s)(%08x)\n",
+          PowerDeviceStateName(DeviceState), 
+          PowerActionName(PowerAction),
+          status);
+    return status;
+}
+
+static FORCEINLINE NTSTATUS
+__PdoQuerySystemPowerUp(
+    IN  PXENDISK_PDO     Pdo,
+    IN  PIRP            Irp
+    )
+{
+    PIO_STACK_LOCATION  StackLocation;
+    SYSTEM_POWER_STATE  SystemState;
+    NTSTATUS            status;
+
+    StackLocation = IoGetCurrentIrpStackLocation(Irp);
+    SystemState = StackLocation->Parameters.Power.State.SystemState;
+
+    ASSERT3U(SystemState, <,  __PdoGetSystemPowerState(Pdo));
+
+    status = PdoForwardIrpSynchronously(Pdo, Irp);
+
+    Irp->IoStatus.Status = status;
+    IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+    return status;
+}
+
+static FORCEINLINE NTSTATUS
+__PdoQuerySystemPowerDown(
+    IN  PXENDISK_PDO    Pdo,
+    IN  PIRP            Irp
+    )
+{
+    PIO_STACK_LOCATION  StackLocation;
+    SYSTEM_POWER_STATE  SystemState;
+    NTSTATUS            status;
+
+    StackLocation = IoGetCurrentIrpStackLocation(Irp);
+    SystemState = StackLocation->Parameters.Power.State.SystemState;
+
+    ASSERT3U(SystemState, >,  __PdoGetSystemPowerState(Pdo));
+
+    status = PdoForwardIrpSynchronously(Pdo, Irp);
+    IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+    return status;
+}
+
+static FORCEINLINE NTSTATUS
+__PdoQuerySystemPower(
+    IN  PXENDISK_PDO    Pdo,
+    IN  PIRP            Irp
+    )
+{
+    PIO_STACK_LOCATION  StackLocation;
+    SYSTEM_POWER_STATE  SystemState;
+    POWER_ACTION        PowerAction;
+    NTSTATUS            status;
+
+    StackLocation = IoGetCurrentIrpStackLocation(Irp);
+    SystemState = StackLocation->Parameters.Power.State.SystemState;
+    PowerAction = StackLocation->Parameters.Power.ShutdownType;
+
+    Trace("====> (%s:%s)\n",
+          PowerSystemStateName(SystemState), 
+          PowerActionName(PowerAction));
+
+    if (SystemState == __PdoGetSystemPowerState(Pdo)) {
+        status = PdoForwardIrpSynchronously(Pdo, Irp);
+        IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+        goto done;
+    }
+
+    status = (SystemState < __PdoGetSystemPowerState(Pdo)) ?
+             __PdoQuerySystemPowerUp(Pdo, Irp) :
+             __PdoQuerySystemPowerDown(Pdo, Irp);
+
+done:
+    Trace("<==== (%s:%s)(%08x)\n",
+          PowerSystemStateName(SystemState), 
+          PowerActionName(PowerAction),
+          status);
+
+    return status;
+}
+
+static NTSTATUS
+PdoDevicePower(
+    IN  PXENDISK_THREAD Self,
+    IN  PVOID           Context
+    )
+{
+    PXENDISK_PDO        Pdo = Context;
+    PKEVENT             Event;
+
+    Event = ThreadGetEvent(Self);
+
+    for (;;) {
+        PIRP                Irp;
+        PIO_STACK_LOCATION  StackLocation;
+        UCHAR               MinorFunction;
+
+        if (Pdo->DevicePowerIrp == NULL) {
+            (VOID) KeWaitForSingleObject(Event,
+                                         Executive,
+                                         KernelMode,
+                                         FALSE,
+                                         NULL);
+            KeClearEvent(Event);
+        }
+
+        if (ThreadIsAlerted(Self))
+            break;
+
+        Irp = Pdo->DevicePowerIrp;
+
+        if (Irp == NULL)
+            continue;
+
+        Pdo->DevicePowerIrp = NULL;
+        KeMemoryBarrier();
+
+        StackLocation = IoGetCurrentIrpStackLocation(Irp);
+        MinorFunction = StackLocation->MinorFunction;
+
+        switch (StackLocation->MinorFunction) {
+        case IRP_MN_SET_POWER:
+            (VOID) __PdoSetDevicePower(Pdo, Irp);
+            break;
+
+        case IRP_MN_QUERY_POWER:
+            (VOID) __PdoQueryDevicePower(Pdo, Irp);
+            break;
+
+        default:
+            ASSERT(FALSE);
+            break;
+        }
+
+        IoReleaseRemoveLock(&Pdo->Dx->RemoveLock, Irp);
+    }
+
+    return STATUS_SUCCESS;
+}
+
+static NTSTATUS
+PdoSystemPower(
+    IN  PXENDISK_THREAD Self,
+    IN  PVOID           Context
+    )
+{
+    PXENDISK_PDO        Pdo = Context;
+    PKEVENT             Event;
+
+    Event = ThreadGetEvent(Self);
+
+    for (;;) {
+        PIRP                Irp;
+        PIO_STACK_LOCATION  StackLocation;
+        UCHAR               MinorFunction;
+
+        if (Pdo->SystemPowerIrp == NULL) {
+            (VOID) KeWaitForSingleObject(Event,
+                                         Executive,
+                                         KernelMode,
+                                         FALSE,
+                                         NULL);
+            KeClearEvent(Event);
+        }
+
+        if (ThreadIsAlerted(Self))
+            break;
+
+        Irp = Pdo->SystemPowerIrp;
+
+        if (Irp == NULL)
+            continue;
+
+        Pdo->SystemPowerIrp = NULL;
+        KeMemoryBarrier();
+
+        StackLocation = IoGetCurrentIrpStackLocation(Irp);
+        MinorFunction = StackLocation->MinorFunction;
+
+        switch (StackLocation->MinorFunction) {
+        case IRP_MN_SET_POWER:
+            (VOID) __PdoSetSystemPower(Pdo, Irp);
+            break;
+
+        case IRP_MN_QUERY_POWER:
+            (VOID) __PdoQuerySystemPower(Pdo, Irp);
+            break;
+
+        default:
+            ASSERT(FALSE);
+            break;
+        }
+
+        IoReleaseRemoveLock(&Pdo->Dx->RemoveLock, Irp);
+    }
+
+    return STATUS_SUCCESS;
+}
+
+__drv_functionClass(IO_COMPLETION_ROUTINE)
+__drv_sameIRQL
+static NTSTATUS
+__PdoDispatchPower(
+    IN  PDEVICE_OBJECT  DeviceObject,
+    IN  PIRP            Irp,
+    IN  PVOID           Context
+    )
+{
+    PXENDISK_PDO        Pdo = Context;
+
+    UNREFERENCED_PARAMETER(DeviceObject);
+
+    if (Irp->PendingReturned)
+        IoMarkIrpPending(Irp);
+
+    IoReleaseRemoveLock(&Pdo->Dx->RemoveLock, Irp);
+    return STATUS_SUCCESS;
+}
+
+static DECLSPEC_NOINLINE NTSTATUS
+PdoDispatchPower(
+    IN  PXENDISK_PDO    Pdo,
+    IN  PIRP            Irp
+    )
+{
+    PIO_STACK_LOCATION  StackLocation;
+    UCHAR               MinorFunction;
+    POWER_STATE_TYPE    PowerType;
+    NTSTATUS            status;
+
+    status = IoAcquireRemoveLock(&Pdo->Dx->RemoveLock, Irp);
+    if (!NT_SUCCESS(status))
+        goto fail1;
+
+    StackLocation = IoGetCurrentIrpStackLocation(Irp);
+    MinorFunction = StackLocation->MinorFunction;
+
+    if (MinorFunction != IRP_MN_QUERY_POWER &&
+        MinorFunction != IRP_MN_SET_POWER) {
+        IoCopyCurrentIrpStackLocationToNext(Irp);
+        IoSetCompletionRoutine(Irp,
+                               __PdoDispatchPower,
+                               Pdo,
+                               TRUE,
+                               TRUE,
+                               TRUE);
+
+        status = IoCallDriver(Pdo->LowerDeviceObject, Irp);
+
+        goto done;
+    }
+
+    PowerType = StackLocation->Parameters.Power.Type;
+
+    Trace("====> (%02x:%s)\n",
+          MinorFunction, 
+          PowerMinorFunctionName(MinorFunction)); 
+
+    switch (PowerType) {
+    case DevicePowerState:
+        IoMarkIrpPending(Irp);
+
+        ASSERT3P(Pdo->DevicePowerIrp, ==, NULL);
+        Pdo->DevicePowerIrp = Irp;
+        KeMemoryBarrier();
+
+        ThreadWake(Pdo->DevicePowerThread);
+
+        status = STATUS_PENDING;
+        break;
+
+    case SystemPowerState:
+        IoMarkIrpPending(Irp);
+
+        ASSERT3P(Pdo->SystemPowerIrp, ==, NULL);
+        Pdo->SystemPowerIrp = Irp;
+        KeMemoryBarrier();
+
+        ThreadWake(Pdo->SystemPowerThread);
+
+        status = STATUS_PENDING;
+        break;
+
+    default:
+        IoCopyCurrentIrpStackLocationToNext(Irp);
+        IoSetCompletionRoutine(Irp,
+                               __PdoDispatchPower,
+                               Pdo,
+                               TRUE,
+                               TRUE,
+                               TRUE);
+
+        status = IoCallDriver(Pdo->LowerDeviceObject, Irp);
+        break;
+    }
+
+    Trace("<==== (%02x:%s) (%08x)\n",
+          MinorFunction, 
+          PowerMinorFunctionName(MinorFunction),
+          status);
+
+done:
+    return status;
+
+fail1:
+    Error("fail1 (%08x)\n", status);
+
+    Irp->IoStatus.Status = status;
+    IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+    return status;
+}
+
+__drv_functionClass(IO_COMPLETION_ROUTINE)
+__drv_sameIRQL
+static NTSTATUS
+__PdoDispatchDefault(
+    IN  PDEVICE_OBJECT  DeviceObject,
+    IN  PIRP            Irp,
+    IN  PVOID           Context
+    )
+{
+    PXENDISK_PDO        Pdo = Context;
+
+    UNREFERENCED_PARAMETER(DeviceObject);
+
+    if (Irp->PendingReturned)
+        IoMarkIrpPending(Irp);
+
+    IoReleaseRemoveLock(&Pdo->Dx->RemoveLock, Irp);
+
+    return STATUS_SUCCESS;
+}
+
+static DECLSPEC_NOINLINE NTSTATUS
+PdoDispatchDefault(
+    IN  PXENDISK_PDO    Pdo,
+    IN  PIRP            Irp
+    )
+{
+    NTSTATUS            status;
+
+    status = IoAcquireRemoveLock(&Pdo->Dx->RemoveLock, Irp);
+    if (!NT_SUCCESS(status))
+        goto fail1;
+
+    IoCopyCurrentIrpStackLocationToNext(Irp);
+    IoSetCompletionRoutine(Irp,
+                           __PdoDispatchDefault,
+                           Pdo,
+                           TRUE,
+                           TRUE,
+                           TRUE);
+
+    status = IoCallDriver(Pdo->LowerDeviceObject, Irp);
+
+    return status;
+
+fail1:
+    Error("fail1 (%08x)\n", status);
+
+    Irp->IoStatus.Status = status;
+    IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+    return status;
+}
+
+NTSTATUS
+PdoDispatch(
+    IN  PXENDISK_PDO    Pdo,
+    IN  PIRP            Irp
+    )
+{
+    PIO_STACK_LOCATION  StackLocation;
+    NTSTATUS            status;
+
+    StackLocation = IoGetCurrentIrpStackLocation(Irp);
+
+    switch (StackLocation->MajorFunction) {
+    case IRP_MJ_DEVICE_CONTROL:
+        status = PdoDispatchControl(Pdo, Irp);
+        break;
+
+    case IRP_MJ_PNP:
+        status = PdoDispatchPnp(Pdo, Irp);
+        break;
+
+    case IRP_MJ_POWER:
+        status = PdoDispatchPower(Pdo, Irp);
+        break;
+
+    default:
+        status = PdoDispatchDefault(Pdo, Irp);
+        break;
+    }
+
+    return status;
+}
+
+NTSTATUS
+PdoCreate(
+    PXENDISK_FDO                    Fdo,
+    PDEVICE_OBJECT                  PhysicalDeviceObject
+    )
+{
+    PDEVICE_OBJECT                  LowerDeviceObject;
+    ULONG                           DeviceType;
+    PDEVICE_OBJECT                  FilterDeviceObject;
+    PXENDISK_DX                     Dx;
+    PXENDISK_PDO                    Pdo;
+    NTSTATUS                        status;
+
+    LowerDeviceObject = IoGetAttachedDeviceReference(PhysicalDeviceObject);
+    DeviceType = LowerDeviceObject->DeviceType;
+    ObDereferenceObject(LowerDeviceObject);
+
+#pragma prefast(suppress:28197) // Possibly leaking memory 
'PhysicalDeviceObject'
+    status = IoCreateDevice(DriverGetDriverObject(),
+                            sizeof(XENDISK_DX),
+                            NULL,
+                            DeviceType,
+                            FILE_DEVICE_SECURE_OPEN,
+                            FALSE,
+                            &FilterDeviceObject);
+    if (!NT_SUCCESS(status))
+        goto fail1;
+
+    Dx = (PXENDISK_DX)FilterDeviceObject->DeviceExtension;
+    RtlZeroMemory(Dx, sizeof (XENDISK_DX));
+
+    Dx->Type = PHYSICAL_DEVICE_OBJECT;
+    Dx->DeviceObject = FilterDeviceObject;
+    Dx->DevicePnpState = Present;
+    Dx->SystemPowerState = PowerSystemShutdown;
+    Dx->DevicePowerState = PowerDeviceD3;
+
+    IoInitializeRemoveLock(&Dx->RemoveLock, PDO_TAG, 0, 0);
+
+    Pdo = __PdoAllocate(sizeof (XENDISK_PDO));
+
+    status = STATUS_NO_MEMORY;
+    if (Pdo == NULL)
+        goto fail2;
+
+    LowerDeviceObject = IoAttachDeviceToDeviceStack(FilterDeviceObject,
+                                                    PhysicalDeviceObject);
+
+    status = STATUS_UNSUCCESSFUL;
+    if (LowerDeviceObject == NULL)
+        goto fail3;
+
+    Pdo->Dx = Dx;
+    Pdo->PhysicalDeviceObject = PhysicalDeviceObject;
+    Pdo->LowerDeviceObject = LowerDeviceObject;
+    Pdo->SectorSize = 512;
+
+    status = ThreadCreate(PdoSystemPower, Pdo, &Pdo->SystemPowerThread);
+    if (!NT_SUCCESS(status))
+        goto fail4;
+
+    status = ThreadCreate(PdoDevicePower, Pdo, &Pdo->DevicePowerThread);
+    if (!NT_SUCCESS(status))
+        goto fail5;
+
+    Verbose("%p\n", FilterDeviceObject);
+
+    Dx->Pdo = Pdo;
+
+#pragma prefast(suppress:28182) // Dereferencing NULL pointer
+    FilterDeviceObject->DeviceType = LowerDeviceObject->DeviceType;
+    FilterDeviceObject->Characteristics = LowerDeviceObject->Characteristics;
+
+    FilterDeviceObject->Flags |= LowerDeviceObject->Flags;
+    FilterDeviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
+
+    __PdoLink(Pdo, Fdo);
+
+    return STATUS_SUCCESS;
+
+fail5:
+    Error("fail5\n");
+
+    ThreadAlert(Pdo->SystemPowerThread);
+    ThreadJoin(Pdo->SystemPowerThread);
+    Pdo->SystemPowerThread = NULL;
+
+fail4:
+    Error("fail4\n");
+
+    Pdo->PhysicalDeviceObject = NULL;
+    Pdo->LowerDeviceObject = NULL;
+    Pdo->Dx = NULL;
+
+    IoDetachDevice(LowerDeviceObject);
+
+fail3:
+    Error("fail3\n");
+
+    ASSERT(IsZeroMemory(Pdo, sizeof (XENDISK_PDO)));
+    __PdoFree(Pdo);
+
+fail2:
+    Error("fail2\n");
+
+    IoDeleteDevice(FilterDeviceObject);
+
+fail1:
+    Error("fail1 (%08x)\n", status);
+
+    return status;
+}
+
+VOID
+PdoDestroy(
+    IN  PXENDISK_PDO    Pdo
+    )
+{
+    PDEVICE_OBJECT      LowerDeviceObject = Pdo->LowerDeviceObject;
+    PXENDISK_DX         Dx = Pdo->Dx;
+    PDEVICE_OBJECT      FilterDeviceObject = Dx->DeviceObject;
+
+    ASSERT3U(__PdoGetDevicePnpState(Pdo), ==, Deleted);
+
+    ASSERT(__PdoIsMissing(Pdo));
+    Pdo->Missing = FALSE;
+
+    __PdoUnlink(Pdo);
+
+    Verbose("%p (%s)\n",
+         FilterDeviceObject,
+         Pdo->Reason);
+    Pdo->Reason = NULL;
+
+    Dx->Pdo = NULL;
+
+    ThreadAlert(Pdo->DevicePowerThread);
+    ThreadJoin(Pdo->DevicePowerThread);
+    Pdo->DevicePowerThread = NULL;
+
+    ThreadAlert(Pdo->SystemPowerThread);
+    ThreadJoin(Pdo->SystemPowerThread);
+    Pdo->SystemPowerThread = NULL;
+
+    Pdo->SectorSize = 0;
+    Pdo->PhysicalDeviceObject = NULL;
+    Pdo->LowerDeviceObject = NULL;
+    Pdo->Dx = NULL;
+
+    IoDetachDevice(LowerDeviceObject);
+
+    ASSERT(IsZeroMemory(Pdo, sizeof (XENDISK_PDO)));
+    __PdoFree(Pdo);
+
+    IoDeleteDevice(FilterDeviceObject);
+}
+
diff --git a/src/xendisk/pdo.h b/src/xendisk/pdo.h
new file mode 100644
index 0000000..5e2e5d6
--- /dev/null
+++ b/src/xendisk/pdo.h
@@ -0,0 +1,90 @@
+/* Copyright (c) Citrix Systems Inc.
+ * All rights reserved.
+ * 
+ * Redistribution and use in source and binary forms, 
+ * with or without modification, are permitted provided 
+ * that the following conditions are met:
+ * 
+ * *   Redistributions of source code must retain the above 
+ *     copyright notice, this list of conditions and the 
+ *     following disclaimer.
+ * *   Redistributions in binary form must reproduce the above 
+ *     copyright notice, this list of conditions and the 
+ *     following disclaimer in the documentation and/or other 
+ *     materials provided with the distribution.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND 
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, 
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR 
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 
+ * SUCH DAMAGE.
+ */ 
+
+#ifndef _XENDISK_PDO_H
+#define _XENDISK_PDO_H
+
+#include <ntddk.h>
+#include "types.h"
+#include "fdo.h"
+
+typedef struct _XENDISK_PDO XENDISK_PDO, *PXENDISK_PDO;
+
+extern VOID
+PdoSetDevicePnpState(
+    IN  PXENDISK_PDO        Pdo,
+    IN  DEVICE_PNP_STATE    State
+    );
+
+extern DEVICE_PNP_STATE
+PdoGetDevicePnpState(
+    IN  PXENDISK_PDO    Pdo
+    );
+
+extern PDEVICE_OBJECT
+PdoGetPhysicalDeviceObject(
+    IN  PXENDISK_PDO    Pdo
+    );
+
+extern BOOLEAN
+PdoIsMissing(
+    IN  PXENDISK_PDO    Pdo
+    );
+
+extern VOID
+PdoSetMissing(
+    IN  PXENDISK_PDO    Pdo,
+    IN  const CHAR      *Reason
+    );
+
+extern BOOLEAN
+PdoIsMasked(
+    IN  PXENDISK_PDO    Pdo
+    );
+
+extern NTSTATUS
+PdoCreate(
+    IN  PXENDISK_FDO    Fdo,
+    IN  PDEVICE_OBJECT  PhysicalDeviceObject
+    );
+
+extern VOID
+PdoDestroy(
+    IN  PXENDISK_PDO    Pdo
+    );
+
+extern NTSTATUS
+PdoDispatch(
+    IN  PXENDISK_PDO    Pdo,
+    IN  PIRP            Irp
+    );
+
+#endif // _XENDISK_PDO_H
diff --git a/src/xendisk/thread.c b/src/xendisk/thread.c
new file mode 100644
index 0000000..92e2b94
--- /dev/null
+++ b/src/xendisk/thread.c
@@ -0,0 +1,225 @@
+/* Copyright (c) Citrix Systems Inc.
+ * All rights reserved.
+ * 
+ * Redistribution and use in source and binary forms, 
+ * with or without modification, are permitted provided 
+ * that the following conditions are met:
+ * 
+ * *   Redistributions of source code must retain the above 
+ *     copyright notice, this list of conditions and the 
+ *     following disclaimer.
+ * *   Redistributions in binary form must reproduce the above 
+ *     copyright notice, this list of conditions and the 
+ *     following disclaimer in the documentation and/or other 
+ *     materials provided with the distribution.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND 
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, 
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR 
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 
+ * SUCH DAMAGE.
+ */
+
+#include <ntddk.h>
+
+#include "thread.h"
+#include "debug.h"
+#include "assert.h"
+#include "util.h"
+
+#define THREAD_POOL 'ERHT'
+
+struct _XENDISK_THREAD {
+    XENDISK_THREAD_FUNCTION Function;
+    PVOID                   Context;
+    KEVENT                  Event;
+    BOOLEAN                 Alerted;
+    LONG                    References;
+    PKTHREAD                Thread;
+};
+
+static FORCEINLINE PVOID
+__ThreadAllocate(
+    IN  ULONG   Length
+    )
+{
+    return __AllocateNonPagedPoolWithTag(__FUNCTION__, __LINE__, Length, 
THREAD_POOL);
+}
+
+static FORCEINLINE VOID
+__ThreadFree(
+    IN  PVOID   Buffer
+    )
+{
+    __FreePoolWithTag(Buffer, THREAD_POOL);
+}
+
+static FORCEINLINE VOID
+__ThreadWake(
+    IN  PXENDISK_THREAD Thread
+    )
+{
+    KeSetEvent(&Thread->Event, IO_NO_INCREMENT, FALSE);
+}
+
+VOID
+ThreadWake(
+    IN  PXENDISK_THREAD Thread
+    )
+{
+    __ThreadWake(Thread);
+}
+
+static FORCEINLINE VOID
+__ThreadAlert(
+    IN  PXENDISK_THREAD Thread
+    )
+{
+    Thread->Alerted = TRUE;
+    __ThreadWake(Thread);
+}
+
+VOID
+ThreadAlert(
+    IN  PXENDISK_THREAD Thread
+    )
+{
+    __ThreadAlert(Thread);
+}
+
+KSTART_ROUTINE  ThreadFunction;
+
+VOID
+ThreadFunction(
+    IN  PVOID       Argument
+    )
+{
+    PXENDISK_THREAD Self = Argument;
+    NTSTATUS        status;
+
+    status = Self->Function(Self, Self->Context);
+
+    if (InterlockedDecrement(&Self->References) == 0)
+        __ThreadFree(Self);
+
+    PsTerminateSystemThread(status);
+    // NOT REACHED
+}
+
+NTSTATUS
+ThreadCreate(
+    IN  XENDISK_THREAD_FUNCTION Function,
+    IN  PVOID                   Context,
+    OUT PXENDISK_THREAD         *Thread
+    )
+{
+    HANDLE                      Handle;
+    NTSTATUS                    status;
+
+    ASSERT3U(KeGetCurrentIrql(), ==, PASSIVE_LEVEL);
+
+    (*Thread) = __ThreadAllocate(sizeof (XENDISK_THREAD));
+
+    status = STATUS_NO_MEMORY;
+    if (*Thread == NULL)
+        goto fail1;
+
+    (*Thread)->Function = Function;
+    (*Thread)->Context = Context;
+    (*Thread)->Alerted = FALSE;
+    (*Thread)->References = 2; // One for us, one for the thread function
+
+    KeInitializeEvent(&(*Thread)->Event, NotificationEvent, FALSE);
+
+    status = PsCreateSystemThread(&Handle,
+                                  STANDARD_RIGHTS_ALL | SPECIFIC_RIGHTS_ALL,
+                                  NULL,
+                                  NULL,
+                                  NULL,
+                                  ThreadFunction,
+                                  *Thread);
+    if (!NT_SUCCESS(status)) {
+        --(*Thread)->References;    // Fake thread function termination
+        goto fail2;
+    }
+
+    status = ObReferenceObjectByHandle(Handle,
+                                       SYNCHRONIZE,
+                                       *PsThreadType,
+                                       KernelMode,
+                                       &(*Thread)->Thread,
+                                       NULL);
+    if (!NT_SUCCESS(status))
+        goto fail3;
+
+    ZwClose(Handle);
+
+    return STATUS_SUCCESS;
+
+fail3:
+    Error("fail3\n");
+
+    __ThreadAlert(*Thread);
+    ZwClose(Handle);
+
+fail2:
+    Error("fail2\n");
+
+    if (InterlockedDecrement(&(*Thread)->References) == 0)
+        __ThreadFree(*Thread);
+
+    *Thread = NULL;
+
+fail1:
+    Error("fail1 (%08x)\n", status);
+
+    return status;
+}
+
+PKEVENT
+ThreadGetEvent(
+    IN  PXENDISK_THREAD Thread
+    )
+{
+    return &Thread->Event;
+}
+
+BOOLEAN
+ThreadIsAlerted(
+    IN  PXENDISK_THREAD Thread
+    )
+{
+    return Thread->Alerted;
+}
+
+VOID
+ThreadJoin(
+    IN  PXENDISK_THREAD Thread
+    )
+{
+    LONG                References;
+
+    ASSERT3U(KeGetCurrentIrql(), ==, PASSIVE_LEVEL);
+    ASSERT3P(KeGetCurrentThread(), !=, Thread->Thread);
+
+    (VOID) KeWaitForSingleObject(Thread->Thread,
+                                 Executive,
+                                 KernelMode,
+                                 FALSE,
+                                 NULL);
+
+    References = InterlockedDecrement(&Thread->References);
+    ASSERT3U(References, ==, 0);
+
+    __ThreadFree(Thread);
+}
+
diff --git a/src/xendisk/thread.h b/src/xendisk/thread.h
new file mode 100644
index 0000000..63b4979
--- /dev/null
+++ b/src/xendisk/thread.h
@@ -0,0 +1,74 @@
+/* Copyright (c) Citrix Systems Inc.
+ * All rights reserved.
+ * 
+ * Redistribution and use in source and binary forms, 
+ * with or without modification, are permitted provided 
+ * that the following conditions are met:
+ * 
+ * *   Redistributions of source code must retain the above 
+ *     copyright notice, this list of conditions and the 
+ *     following disclaimer.
+ * *   Redistributions in binary form must reproduce the above 
+ *     copyright notice, this list of conditions and the 
+ *     following disclaimer in the documentation and/or other 
+ *     materials provided with the distribution.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND 
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, 
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR 
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 
+ * SUCH DAMAGE.
+ */
+
+#ifndef _XENDISK_THREAD_H
+#define _XENDISK_THREAD_H
+
+#include <ntddk.h>
+
+typedef struct _XENDISK_THREAD XENDISK_THREAD, *PXENDISK_THREAD;
+
+typedef NTSTATUS (*XENDISK_THREAD_FUNCTION)(PXENDISK_THREAD, PVOID);
+
+extern NTSTATUS
+ThreadCreate(
+    IN  XENDISK_THREAD_FUNCTION Function,
+    IN  PVOID                   Context,
+    OUT PXENDISK_THREAD         *Thread
+    );
+
+extern PKEVENT
+ThreadGetEvent(
+    IN  PXENDISK_THREAD Self
+    );
+
+extern BOOLEAN
+ThreadIsAlerted(
+    IN  PXENDISK_THREAD Self
+    );
+
+extern VOID
+ThreadWake(
+    IN  PXENDISK_THREAD Thread
+    );
+
+extern VOID
+ThreadAlert(
+    IN  PXENDISK_THREAD Thread
+    );
+
+extern VOID
+ThreadJoin(
+    IN  PXENDISK_THREAD Thread
+    );
+
+#endif  // _XENDISK_THREAD_H
+
diff --git a/src/xendisk/types.h b/src/xendisk/types.h
new file mode 100644
index 0000000..b2b4218
--- /dev/null
+++ b/src/xendisk/types.h
@@ -0,0 +1,53 @@
+/* Copyright (c) Citrix Systems Inc.
+ * All rights reserved.
+ * 
+ * Redistribution and use in source and binary forms, 
+ * with or without modification, are permitted provided 
+ * that the following conditions are met:
+ * 
+ * *   Redistributions of source code must retain the above 
+ *     copyright notice, this list of conditions and the 
+ *     following disclaimer.
+ * *   Redistributions in binary form must reproduce the above 
+ *     copyright notice, this list of conditions and the 
+ *     following disclaimer in the documentation and/or other 
+ *     materials provided with the distribution.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND 
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, 
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR 
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 
+ * SUCH DAMAGE.
+ */
+
+#ifndef _XENDISK_TYPES_H
+#define _XENDISK_TYPES_H
+
+typedef enum _DEVICE_OBJECT_TYPE {
+    PHYSICAL_DEVICE_OBJECT = 'ODP',
+    FUNCTION_DEVICE_OBJECT = 'ODF'
+} DEVICE_OBJECT_TYPE, *PDEVICE_OBJECT_TYPE;
+
+typedef enum _DEVICE_PNP_STATE {
+    Invalid = 0,
+    Present,        // PDO only
+    Enumerated,     // PDO only
+    Added,          // FDO only
+    Started,
+    StopPending,
+    Stopped,
+    RemovePending,
+    SurpriseRemovePending,
+    Deleted
+} DEVICE_PNP_STATE, *PDEVICE_PNP_STATE;
+
+#endif  // _XENDISK_TYPES_H
diff --git a/src/xendisk/util.h b/src/xendisk/util.h
new file mode 100644
index 0000000..e7adc22
--- /dev/null
+++ b/src/xendisk/util.h
@@ -0,0 +1,222 @@
+/* Copyright (c) Citrix Systems Inc.
+ * All rights reserved.
+ * 
+ * Redistribution and use in source and binary forms, 
+ * with or without modification, are permitted provided 
+ * that the following conditions are met:
+ * 
+ * *   Redistributions of source code must retain the above 
+ *     copyright notice, this list of conditions and the 
+ *     following disclaimer.
+ * *   Redistributions in binary form must reproduce the above 
+ *     copyright notice, this list of conditions and the 
+ *     following disclaimer in the documentation and/or other 
+ *     materials provided with the distribution.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND 
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, 
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR 
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 
+ * SUCH DAMAGE.
+ */ 
+
+#ifndef _UTIL_H
+#define _UTIL_H
+
+#include <ntddk.h>
+
+#include "assert.h"
+
+typedef struct _NON_PAGED_BUFFER_HEADER {
+    SIZE_T  Length;
+    ULONG   Tag;
+} NON_PAGED_BUFFER_HEADER, *PNON_PAGED_BUFFER_HEADER;
+
+typedef struct _NON_PAGED_BUFFER_TRAILER {
+    ULONG   Tag;
+} NON_PAGED_BUFFER_TRAILER, *PNON_PAGED_BUFFER_TRAILER;
+
+static FORCEINLINE PVOID
+__AllocateNonPagedPoolWithTag(
+    IN  PCHAR                   Caller,
+    IN  ULONG                   Line,
+    IN  SIZE_T                  Length,
+    IN  ULONG                   Tag
+    )
+{
+    PUCHAR                      Buffer;
+    PNON_PAGED_BUFFER_HEADER    Header;
+    PNON_PAGED_BUFFER_TRAILER   Trailer;
+
+    ASSERT3S(Length, !=, 0);
+
+    Buffer = (PUCHAR)ExAllocatePoolWithTag(NonPagedPool,
+                                   sizeof (NON_PAGED_BUFFER_HEADER) +
+                                   Length +
+                                   sizeof (NON_PAGED_BUFFER_TRAILER),
+                                   Tag);
+    if (Buffer == NULL) {
+        Warning("%s:%u : AllocFailed %d bytes, %08x tag\n", Caller, Line, 
Length, Tag);
+        goto done;
+    }
+
+    RtlZeroMemory(Buffer, 
+                  sizeof (NON_PAGED_BUFFER_HEADER) +
+                  Length +
+                  sizeof (NON_PAGED_BUFFER_TRAILER));
+
+    Header = (PNON_PAGED_BUFFER_HEADER)Buffer;
+    Header->Length = Length;
+    Header->Tag = Tag;
+
+    Buffer += sizeof (NON_PAGED_BUFFER_HEADER);
+
+    Trailer = (PNON_PAGED_BUFFER_TRAILER)(Buffer + Length);
+    Trailer->Tag = Tag;
+
+done:
+    return Buffer;
+}
+
+static FORCEINLINE VOID
+__FreePoolWithTag(
+    IN  PVOID                   _Buffer,
+    IN  ULONG                   Tag
+    )
+{
+    PUCHAR                      Buffer = (PUCHAR)_Buffer;
+    SIZE_T                      Length;
+    PNON_PAGED_BUFFER_HEADER    Header;
+    PNON_PAGED_BUFFER_TRAILER   Trailer;
+
+    ASSERT3P(Buffer, !=, NULL);
+
+    Buffer -= sizeof (NON_PAGED_BUFFER_HEADER);
+
+    Header = (PNON_PAGED_BUFFER_HEADER)Buffer;
+    ASSERT3U(Tag, ==, Header->Tag);
+    Length = Header->Length;
+
+    Buffer += sizeof (NON_PAGED_BUFFER_HEADER);
+
+    Trailer = (PNON_PAGED_BUFFER_TRAILER)(Buffer + Length);
+    ASSERT3U(Tag, ==, Trailer->Tag);
+
+    Buffer -= sizeof (NON_PAGED_BUFFER_HEADER);
+
+    RtlFillMemory(Buffer, 
+                  sizeof (NON_PAGED_BUFFER_HEADER) +
+                  Length +
+                  sizeof (NON_PAGED_BUFFER_TRAILER),
+                  0xAA);
+
+    ExFreePoolWithTag(Buffer, Tag);
+}
+
+static FORCEINLINE PMDL
+__AllocPagesForMdl(
+    IN  SIZE_T          Size
+    )
+{
+    PMDL                Mdl;
+    PHYSICAL_ADDRESS    LowAddr;
+    PHYSICAL_ADDRESS    HighAddr;
+    PHYSICAL_ADDRESS    SkipBytes;
+
+    SkipBytes.QuadPart = 0ull;
+    HighAddr.QuadPart = ~0ull;
+
+    // try > 4GB
+    LowAddr.QuadPart = 0x100000000ull;
+    Mdl = MmAllocatePagesForMdlEx(LowAddr, HighAddr, SkipBytes, Size, 
MmCached, 0);
+    if (Mdl) {
+        if (MmGetMdlByteCount(Mdl) == Size) {
+            goto done;
+        }
+        MmFreePagesFromMdl(Mdl);
+        ExFreePool(Mdl);
+        Mdl = NULL;
+    }
+
+    // try > 2GB
+    LowAddr.QuadPart = 0x80000000ull;
+    Mdl = MmAllocatePagesForMdlEx(LowAddr, HighAddr, SkipBytes, Size, 
MmCached, 0);
+    if (Mdl) {
+        if (MmGetMdlByteCount(Mdl) == Size) {
+            goto done;
+        }
+        MmFreePagesFromMdl(Mdl);
+        ExFreePool(Mdl);
+        Mdl = NULL;
+    }
+
+    // try anywhere
+    LowAddr.QuadPart = 0ull;
+    Mdl = MmAllocatePagesForMdlEx(LowAddr, HighAddr, SkipBytes, Size, 
MmCached, 0);
+    // Mdl byte count gets checked again after this returns
+
+done:
+    return Mdl;
+}
+static FORCEINLINE PVOID
+___AllocPages(
+    IN  PCHAR           Caller, 
+    IN  ULONG           Line,
+    IN  SIZE_T          Size,
+    OUT PMDL*           Mdl
+    )
+{
+    PVOID               Buffer;
+
+    *Mdl = __AllocPagesForMdl(Size);
+    if (*Mdl == NULL) {
+        Warning("%s:%u : MmAllocatePagesForMdlEx Failed %d bytes\n", Caller, 
Line, Size);
+        goto fail1;
+    }
+
+    if (MmGetMdlByteCount(*Mdl) != Size) {
+        Warning("%s:%u : %d bytes != %d bytes requested\n", Caller, Line, 
MmGetMdlByteCount(*Mdl), Size);
+        goto fail2;
+    }
+
+    Buffer = MmMapLockedPagesSpecifyCache(*Mdl, KernelMode, MmCached, NULL, 
FALSE, NormalPagePriority);
+    if (Buffer == NULL) {
+        Warning("%s:%u : MmMapLockedPagesSpecifyCache Failed %d bytes\n", 
Caller, Line, Size);
+        goto fail3;
+    }
+
+    return Buffer;
+
+fail3:
+fail2:
+    MmFreePagesFromMdl(*Mdl);
+    ExFreePool(*Mdl);
+fail1:
+    *Mdl = NULL;
+    return NULL;
+}
+#define __AllocPages(Size, Mdl) ___AllocPages(__FUNCTION__, __LINE__, Size, 
Mdl)
+
+static FORCEINLINE VOID
+__FreePages(
+    IN  PVOID           Buffer,
+    IN  PMDL            Mdl
+    )
+{
+    if (Buffer && Mdl) {
+        MmUnmapLockedPages(Buffer, Mdl);
+        MmFreePagesFromMdl(Mdl);
+        ExFreePool(Mdl);
+    }
+}
+
+#endif  // _UTIL_H
diff --git a/src/xendisk/xendisk.rc b/src/xendisk/xendisk.rc
new file mode 100644
index 0000000..7ca8f60
--- /dev/null
+++ b/src/xendisk/xendisk.rc
@@ -0,0 +1,56 @@
+/* Copyright (c) Citrix Systems Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms,
+ * with or without modification, are permitted provided
+ * that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer in the documentation and/or other
+ * materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <windows.h>
+#include <ntverp.h>
+
+
+#undef VER_COMPANYNAME_STR
+#undef VER_PRODUCTNAME_STR
+#undef VER_PRODUCTVERSION
+#undef VER_PRODUCTVERSION_STR
+
+#include <version.h>
+
+#define VER_COMPANYNAME_STR            COMPANY_NAME_STR
+#define VER_LEGALCOPYRIGHT_STR                 "Copyright (c) Citrix Systems 
Inc."
+
+#define VER_PRODUCTNAME_STR            "XENDISK"
+#define VER_PRODUCTVERSION                     
MAJOR_VERSION,MINOR_VERSION,MICRO_VERSION,BUILD_NUMBER
+#define VER_PRODUCTVERSION_STR                 MAJOR_VERSION_STR "." 
MINOR_VERSION_STR "." MICRO_VERSION_STR "." BUILD_NUMBER_STR
+
+#define VER_INTERNALNAME_STR           "XENDISK.SYS"
+#define VER_FILEDESCRIPTION_STR        "XENDISK"
+
+#define VER_FILETYPE                           VFT_DRV
+#define VER_FILESUBTYPE                        VFT2_DRV_SYSTEM
+
+#include <common.ver>
\ No newline at end of file
diff --git a/src/xenvbd.inf b/src/xenvbd.inf
index bc8627b..20bd38a 100644
--- a/src/xenvbd.inf
+++ b/src/xenvbd.inf
@@ -45,6 +45,7 @@ CoInst_CopyFiles=11
 
 [SourceDisksFiles]
 xenvbd.sys=0,,
+xendisk.sys=0,,
 xencrsh.sys=0,,
 xenvbd_coinst.dll=0,,
 
@@ -59,12 +60,20 @@ xenvbd_coinst.dll=0,,
 [XenVbd_Inst] 
 CopyFiles=XenVbd_Copyfiles
 
+[XenVbd_Inst.HW]
+AddReg=XenVbd_AddReg
+
+[XenVbd_AddReg]
+HKR,,"UpperFilters",0x00010000,"xendisk"
+
 [XenVbd_Copyfiles]
 xenvbd.sys
+xendisk.sys
 xencrsh.sys
 
 [XenVbd_Inst.Services] 
 AddService=xenvbd,2,XenVbd_Service,
+AddService=xendisk,,XenDisk_Service,
 
 [XenVbd_Service] 
 DisplayName=%XenVbdDesc%
@@ -75,6 +84,14 @@ ServiceBinary=%12%\xenvbd.sys
 LoadOrderGroup="Scsi Miniport"
 AddReg=XenVbd_Parameters
 
+[XenDisk_Service]
+DisplayName=%XenDiskDesc%
+ServiceType=%SERVICE_KERNEL_DRIVER%
+StartType=%SERVICE_BOOT_START%
+ErrorControl=%SERVICE_ERROR_NORMAL%
+ServiceBinary=%12%\xendisk.sys
+LoadOrderGroup="Scsi Miniport"
+
 [XenVbd_Parameters] 
 HKR,"Parameters",,0x00000010
 HKR,"Parameters","BusType",0x00010001,0x00000001 
@@ -95,6 +112,7 @@ 
HKR,,CoInstallers32,0x00010000,"xenvbd_coinst_@MAJOR_VERSION@_@MINOR_VERSION@_@M
 Company = "@COMPANY_NAME@"
 DiskDesc = "@PRODUCT_NAME@ PV Storage Host Adapter Package" 
 XenVbdDesc= "@PRODUCT_NAME@ PV Storage Host Adapter"
+XenDiskDesc= "@PRODUCT_NAME@ PV Storage Filter"
 
 SERVICE_BOOT_START = 0x0 
 SERVICE_SYSTEM_START = 0x1 
diff --git a/vs2012/package/package.vcxproj b/vs2012/package/package.vcxproj
index 5e5fcbc..e029d13 100644
--- a/vs2012/package/package.vcxproj
+++ b/vs2012/package/package.vcxproj
@@ -48,6 +48,9 @@
                <ProjectReference Include="..\xencrsh\xencrsh.vcxproj">
                        
<Project>{58f5bc43-b92e-4a2b-975d-0066eab29092}</Project>
                </ProjectReference>
+               <ProjectReference Include="..\xendisk\xendisk.vcxproj">
+                       
<Project>{d7411b2c-2c43-434d-9f56-e10a3d2f5bad}</Project>
+               </ProjectReference>
                <ProjectReference Include="..\xenvbd\xenvbd.vcxproj">
                        
<Project>{ef236371-3145-41b1-99c9-82b33e353f17}</Project>
                </ProjectReference>
@@ -59,4 +62,4 @@
        <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
        <ImportGroup Label="ExtensionTargets">
        </ImportGroup>
-</Project>
\ No newline at end of file
+</Project>
diff --git a/vs2012/xendisk/xendisk.vcxproj b/vs2012/xendisk/xendisk.vcxproj
new file mode 100644
index 0000000..67a6ad7
--- /dev/null
+++ b/vs2012/xendisk/xendisk.vcxproj
@@ -0,0 +1,84 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="4.0" 
xmlns="http://schemas.microsoft.com/developer/msbuild/2003";>
+       <Import Project="..\configs.props" />
+
+       <PropertyGroup Label="Globals">
+               <Configuration>Windows Vista Debug</Configuration>
+               <Platform Condition="'$(Platform)' == ''">Win32</Platform> 
+               <DebuggerFlavor>DbgengKernelDebugger</DebuggerFlavor> 
+       </PropertyGroup>
+       <PropertyGroup Label="ProeprtySheets">
+               <PlatformToolset>WindowsKernelModeDriver8.0</PlatformToolset>
+               <ConfigurationType>Driver</ConfigurationType>
+               <DriverType>WDM</DriverType>
+       </PropertyGroup>
+
+       <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+       
+       <PropertyGroup Label="Globals">
+               
<ProjectGuid>{D7411B2C-2C43-434D-9F56-E10A3D2F5BAD}</ProjectGuid>
+       </PropertyGroup>
+       
+       <Import Project="..\targets.props" />
+       <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> 
+       
+       <PropertyGroup>
+               
<IncludePath>$(ProjectDir)..\..\include;$(IncludePath)</IncludePath>
+               <RunCodeAnalysis>true</RunCodeAnalysis>
+               <EnableInf2cat>false</EnableInf2cat>
+               
<IntDir>..\$(ProjectName)\$(ConfigurationName)\$(Platform)\</IntDir>
+               <OutDir>..\$(ConfigurationName)\$(Platform)\</OutDir>
+       </PropertyGroup>
+
+       <ItemDefinitionGroup>
+               <ClCompile>
+                       <BufferSecurityCheck>false</BufferSecurityCheck>
+                       
<PreprocessorDefinitions>__MODULE__="XENDISK";%(PreprocessorDefinitions)</PreprocessorDefinitions>
+                       <WarningLevel>EnableAllWarnings</WarningLevel>
+                       
<DisableSpecificWarnings>4548;4711;4820;4668;4255;6001;6054;28196;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+                       
<MultiProcessorCompilation>true</MultiProcessorCompilation>
+                       <EnablePREfast>true</EnablePREfast>
+               </ClCompile>
+               <Link>
+                       
<AdditionalDependencies>$(DDK_LIB_PATH)/libcntpr.lib;%(AdditionalDependencies)</AdditionalDependencies>
+               </Link>
+               <Inf>
+                       <SpecifyArchitecture>true</SpecifyArchitecture>
+                       
<SpecifyDriverVerDirectiveVersion>true</SpecifyDriverVerDirectiveVersion>
+                       
<TimeStamp>$(MAJOR_VERSION).$(MINOR_VERSION).$(MICRO_VERSION).$(BUILD_NUMBER)</TimeStamp>
+                       <EnableVerbose>true</EnableVerbose>
+               </Inf>
+       </ItemDefinitionGroup>
+       <ItemDefinitionGroup Condition="'$(Platform)'=='Win32'">
+               <ClCompile>
+                       
<PreprocessorDefinitions>__i386__;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+               </ClCompile>
+               <Inf>
+                       <Architecture>x86</Architecture>
+               </Inf>
+       </ItemDefinitionGroup>
+       <ItemDefinitionGroup Condition="'$(Platform)'=='x64'">
+               <ClCompile>
+                       
<PreprocessorDefinitions>__x86_64__;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+               </ClCompile>
+               <Inf>
+                       <Architecture>amd64</Architecture>
+               </Inf>
+       </ItemDefinitionGroup>
+
+       <ItemGroup>
+               <FilesToPackage Include="$(TargetPath)" />
+               <FilesToPackage Include="$(OutDir)$(TargetName).pdb" />
+               <FilesToPackage Include="@(Inf->'%(CopyOutput)')" 
Condition="'@(Inf)'!=''" />
+       </ItemGroup>
+       <ItemGroup>
+               <ClCompile Include="../../src/xendisk/driver.c" />
+               <ClCompile Include="../../src/xendisk/fdo.c" />
+               <ClCompile Include="../../src/xendisk/pdo.c" />
+               <ClCompile Include="../../src/xendisk/thread.c" />
+       </ItemGroup>
+       <ItemGroup>
+               <ResourceCompile Include="..\..\src\xendisk\xendisk.rc" />
+       </ItemGroup>
+       <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+</Project>
\ No newline at end of file
diff --git a/vs2012/xendisk/xendisk.vcxproj.user 
b/vs2012/xendisk/xendisk.vcxproj.user
new file mode 100644
index 0000000..4ecdd46
--- /dev/null
+++ b/vs2012/xendisk/xendisk.vcxproj.user
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" 
xmlns="http://schemas.microsoft.com/developer/msbuild/2003";>
+       <PropertyGroup>
+               <SignMode>TestSign</SignMode>
+               <TestCertificate>..\..\src\xenvbd.pfx</TestCertificate>
+               
<TimeStampServer>http://timestamp.verisign.com/scripts/timstamp.dll</TimeStampServer>
+       </PropertyGroup>
+</Project>
diff --git a/vs2012/xenvbd.sln b/vs2012/xenvbd.sln
index 86fb1a5..71c2780 100644
--- a/vs2012/xenvbd.sln
+++ b/vs2012/xenvbd.sln
@@ -8,7 +8,10 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "xenvbd", 
"xenvbd\xenvbd.vcx
 EndProject
 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "xencrsh", 
"xencrsh\xencrsh.vcxproj", "{58F5BC43-B92E-4A2B-975D-0066EAB29092}"
 EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "xendisk", 
"xendisk\xendisk.vcxproj", "{D7411B2C-2C43-434D-9F56-E10A3D2F5BAD}" 
+EndProject
 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "xenvbd_coinst", 
"coinst\xenvbd_coinst.vcxproj", "{50C08437-C1F3-4349-BF6A-7B55A06BF999}"
+EndProject
        ProjectSection(ProjectDependencies) = postProject
                {58F5BC43-B92E-4A2B-975D-0066EAB29092} = 
{58F5BC43-B92E-4A2B-975D-0066EAB29092}
        EndProjectSection
@@ -16,6 +19,7 @@ EndProject
 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "package", 
"package\package.vcxproj", "{AB8DAED3-9D70-4907-99A3-C643F1FC1972}"
        ProjectSection(ProjectDependencies) = postProject
                {50C08437-C1F3-4349-BF6A-7B55A06BF999} = 
{50C08437-C1F3-4349-BF6A-7B55A06BF999}
+               {D7411B2C-2C43-434D-9F56-E10A3D2F5BAD} = 
{D7411B2C-2C43-434D-9F56-E10A3D2F5BAD}
                {58F5BC43-B92E-4A2B-975D-0066EAB29092} = 
{58F5BC43-B92E-4A2B-975D-0066EAB29092}
         {EF236371-3145-41B1-99C9-82B33E353F17} = 
{EF236371-3145-41B1-99C9-82B33E353F17}
        EndProjectSection
@@ -36,8 +40,32 @@ Global
                Windows Vista Release|x64 = Windows Vista Release|x64
        EndGlobalSection
        GlobalSection(ProjectConfigurationPlatforms) = postSolution
+               {D7411B2C-2C43-434D-9F56-E10A3D2F5BAD}.Windows 7 
Debug|Win32.ActiveCfg = Windows 7 Debug|Win32
+               {D7411B2C-2C43-434D-9F56-E10A3D2F5BAD}.Windows 7 
Debug|Win32.Build.0 = Windows 7 Debug|Win32
+               {D7411B2C-2C43-434D-9F56-E10A3D2F5BAD}.Windows 7 
Debug|x64.ActiveCfg = Windows 7 Debug|x64 
+               {D7411B2C-2C43-434D-9F56-E10A3D2F5BAD}.Windows 7 
Debug|x64.Build.0 = Windows 7 Debug|x64
+               {D7411B2C-2C43-434D-9F56-E10A3D2F5BAD}.Windows 7 
Release|Win32.ActiveCfg = Windows 7 Release|Win32
+               {D7411B2C-2C43-434D-9F56-E10A3D2F5BAD}.Windows 7 
Release|Win32.Build.0 = Windows 7 Release|Win32
+               {D7411B2C-2C43-434D-9F56-E10A3D2F5BAD}.Windows 7 
Release|x64.ActiveCfg = Windows 7 Release|x64
+               {D7411B2C-2C43-434D-9F56-E10A3D2F5BAD}.Windows 7 
Release|x64.Build.0 = Windows 7 Release|x64 
+               {D7411B2C-2C43-434D-9F56-E10A3D2F5BAD}.Windows 8 
Debug|Win32.ActiveCfg = Windows 8 Debug|Win32
+               {D7411B2C-2C43-434D-9F56-E10A3D2F5BAD}.Windows 8 
Debug|Win32.Build.0 = Windows 8 Debug|Win32 
+               {D7411B2C-2C43-434D-9F56-E10A3D2F5BAD}.Windows 8 
Debug|x64.ActiveCfg = Windows 8 Debug|x64 
+               {D7411B2C-2C43-434D-9F56-E10A3D2F5BAD}.Windows 8 
Debug|x64.Build.0 = Windows 8 Debug|x64 
+               {D7411B2C-2C43-434D-9F56-E10A3D2F5BAD}.Windows 8 
Release|Win32.ActiveCfg = Windows 8 Release|Win32
+               {D7411B2C-2C43-434D-9F56-E10A3D2F5BAD}.Windows 8 
Release|Win32.Build.0 = Windows 8 Release|Win32 
+               {D7411B2C-2C43-434D-9F56-E10A3D2F5BAD}.Windows 8 
Release|x64.ActiveCfg = Windows 8 Release|x64 
+               {D7411B2C-2C43-434D-9F56-E10A3D2F5BAD}.Windows 8 
Release|x64.Build.0 = Windows 8 Release|x64 
+               {D7411B2C-2C43-434D-9F56-E10A3D2F5BAD}.Windows Vista 
Debug|Win32.ActiveCfg = Windows Vista Debug|Win32 
+               {D7411B2C-2C43-434D-9F56-E10A3D2F5BAD}.Windows Vista 
Debug|Win32.Build.0 = Windows Vista Debug|Win32 
+               {D7411B2C-2C43-434D-9F56-E10A3D2F5BAD}.Windows Vista 
Debug|x64.ActiveCfg = Windows Vista Debug|x64 
+               {D7411B2C-2C43-434D-9F56-E10A3D2F5BAD}.Windows Vista 
Debug|x64.Build.0 = Windows Vista Debug|x64 
+               {D7411B2C-2C43-434D-9F56-E10A3D2F5BAD}.Windows Vista 
Release|Win32.ActiveCfg = Windows Vista Release|Win32 
+               {D7411B2C-2C43-434D-9F56-E10A3D2F5BAD}.Windows Vista 
Release|Win32.Build.0 = Windows Vista Release|Win32 
+               {D7411B2C-2C43-434D-9F56-E10A3D2F5BAD}.Windows Vista 
Release|x64.ActiveCfg = Windows Vista Release|x64 
+               {D7411B2C-2C43-434D-9F56-E10A3D2F5BAD}.Windows Vista 
Release|x64.Build.0 = Windows Vista Release|x64    
+               {EF236371-3145-41B1-99C9-82B33E353F17}.Windows 7 
Debug|Win32.Build.0 = Windows 7 Debug|Win32 
                {EF236371-3145-41B1-99C9-82B33E353F17}.Windows 7 
Debug|Win32.ActiveCfg = Windows 7 Debug|Win32
-               {EF236371-3145-41B1-99C9-82B33E353F17}.Windows 7 
Debug|Win32.Build.0 = Windows 7 Debug|Win32
                {EF236371-3145-41B1-99C9-82B33E353F17}.Windows 7 
Debug|x64.ActiveCfg = Windows 7 Debug|x64
                {EF236371-3145-41B1-99C9-82B33E353F17}.Windows 7 
Debug|x64.Build.0 = Windows 7 Debug|x64
                {EF236371-3145-41B1-99C9-82B33E353F17}.Windows 7 
Release|Win32.ActiveCfg = Windows 7 Release|Win32
diff --git a/vs2013/package/package.vcxproj b/vs2013/package/package.vcxproj
index 5d0de4d..5001c61 100644
--- a/vs2013/package/package.vcxproj
+++ b/vs2013/package/package.vcxproj
@@ -77,6 +77,9 @@
     <ProjectReference Include="..\xencrsh\xencrsh.vcxproj">
       <Project>{58f5bc43-b92e-4a2b-975d-0066eab29092}</Project>
     </ProjectReference>
+    <ProjectReference Include="..\xendisk\xendisk.vcxproj">
+      <Project>{d7411b2c-2c43-434d-9f56-e10a3d2f5bad}</Project>
+    </ProjectReference>
     <ProjectReference Include="..\xenvbd\xenvbd.vcxproj">
       <Project>{ef236371-3145-41b1-99c9-82b33e353f17}</Project>
     </ProjectReference>
@@ -88,4 +91,4 @@
   <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
   <ImportGroup Label="ExtensionTargets">
   </ImportGroup>
-</Project>
\ No newline at end of file
+</Project>
diff --git a/vs2013/xendisk/xendisk.user b/vs2013/xendisk/xendisk.user
new file mode 100644
index 0000000..c4b84e8
--- /dev/null
+++ b/vs2013/xendisk/xendisk.user
@@ -0,0 +1,8 @@
+ï<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" 
xmlns="http://schemas.microsoft.com/developer/msbuild/2003";>
+  <PropertyGroup>
+    <SignMode>TestSign</SignMode>
+    <TestCertificate>..\..\src\xenvbd.pfx</TestCertificate>
+    
<TimeStampServer>http://timestamp.verisign.com/scripts/timstamp.dll</TimeStampServer>
+  </PropertyGroup>
+</Project>
diff --git a/vs2013/xendisk/xendisk.vcxproj b/vs2013/xendisk/xendisk.vcxproj
new file mode 100644
index 0000000..cdf3ad8
--- /dev/null
+++ b/vs2013/xendisk/xendisk.vcxproj
@@ -0,0 +1,112 @@
+ï<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="4.0" 
xmlns="http://schemas.microsoft.com/developer/msbuild/2003";>
+  <Import Project="..\configs.props" />
+  <PropertyGroup Label="Globals">
+    <Configuration>Windows Vista Debug</Configuration>
+    <Platform Condition="'$(Platform)' == ''">Win32</Platform>
+    <DebuggerFlavor>DbgengKernelDebugger</DebuggerFlavor>
+  </PropertyGroup>
+  <PropertyGroup Label="ProeprtySheets">
+    <ConfigurationType>Driver</ConfigurationType>
+    <DriverType>WDM</DriverType>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+  <PropertyGroup Label="Configuration" 
Condition="'$(Configuration)|$(Platform)'=='Windows 8 Release|Win32'">
+    <PlatformToolset>WindowsKernelModeDriver8.1</PlatformToolset>
+  </PropertyGroup>
+  <PropertyGroup Label="Configuration" 
Condition="'$(Configuration)|$(Platform)'=='Windows Vista Debug|Win32'">
+    <PlatformToolset>WindowsKernelModeDriver8.1</PlatformToolset>
+  </PropertyGroup>
+  <PropertyGroup Label="Configuration" 
Condition="'$(Configuration)|$(Platform)'=='Windows Vista Release|Win32'">
+    <PlatformToolset>WindowsKernelModeDriver8.1</PlatformToolset>
+  </PropertyGroup>
+  <PropertyGroup Label="Configuration" 
Condition="'$(Configuration)|$(Platform)'=='Windows 7 Release|Win32'">
+    <PlatformToolset>WindowsKernelModeDriver8.1</PlatformToolset>
+  </PropertyGroup>
+  <PropertyGroup Label="Configuration" 
Condition="'$(Configuration)|$(Platform)'=='Windows 7 Debug|Win32'">
+    <PlatformToolset>WindowsKernelModeDriver8.1</PlatformToolset>
+  </PropertyGroup>
+  <PropertyGroup Label="Configuration" 
Condition="'$(Configuration)|$(Platform)'=='Windows 8 Debug|Win32'">
+    <PlatformToolset>WindowsKernelModeDriver8.1</PlatformToolset>
+  </PropertyGroup>
+  <PropertyGroup Label="Configuration" 
Condition="'$(Configuration)|$(Platform)'=='Windows 8 Release|x64'">
+    <PlatformToolset>WindowsKernelModeDriver8.1</PlatformToolset>
+  </PropertyGroup>
+  <PropertyGroup Label="Configuration" 
Condition="'$(Configuration)|$(Platform)'=='Windows Vista Debug|x64'">
+    <PlatformToolset>WindowsKernelModeDriver8.1</PlatformToolset>
+  </PropertyGroup>
+  <PropertyGroup Label="Configuration" 
Condition="'$(Configuration)|$(Platform)'=='Windows Vista Release|x64'">
+    <PlatformToolset>WindowsKernelModeDriver8.1</PlatformToolset>
+  </PropertyGroup>
+  <PropertyGroup Label="Configuration" 
Condition="'$(Configuration)|$(Platform)'=='Windows 7 Release|x64'">
+    <PlatformToolset>WindowsKernelModeDriver8.1</PlatformToolset>
+  </PropertyGroup>
+  <PropertyGroup Label="Configuration" 
Condition="'$(Configuration)|$(Platform)'=='Windows 7 Debug|x64'">
+    <PlatformToolset>WindowsKernelModeDriver8.1</PlatformToolset>
+  </PropertyGroup>
+  <PropertyGroup Label="Configuration" 
Condition="'$(Configuration)|$(Platform)'=='Windows 8 Debug|x64'">
+    <PlatformToolset>WindowsKernelModeDriver8.1</PlatformToolset>
+  </PropertyGroup>
+  <PropertyGroup Label="Globals">
+    <ProjectGuid>{D7411B2C-2C43-434D-9F56-E10A3D2F5BAD}</ProjectGuid>
+  </PropertyGroup>
+  <Import Project="..\targets.props" />
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+  <PropertyGroup>
+    <IncludePath>$(ProjectDir)..\..\include;$(IncludePath)</IncludePath>
+    <RunCodeAnalysis>true</RunCodeAnalysis>
+    <EnableInf2cat>false</EnableInf2cat>
+    <IntDir>..\$(ProjectName)\$(ConfigurationName)\$(Platform)\</IntDir>
+    <OutDir>..\$(ConfigurationName)\$(Platform)\</OutDir>
+  </PropertyGroup>
+  <ItemDefinitionGroup>
+    <ClCompile>
+      <BufferSecurityCheck>false</BufferSecurityCheck>
+      
<PreprocessorDefinitions>__MODULE__="XENDISK";%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <WarningLevel>EnableAllWarnings</WarningLevel>
+      
<DisableSpecificWarnings>4548;4711;4820;4668;4255;6001;6054;28196;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+      <MultiProcessorCompilation>true</MultiProcessorCompilation>
+      <EnablePREfast>true</EnablePREfast>
+    </ClCompile>
+    <Link>
+      
<AdditionalDependencies>$(DDK_LIB_PATH)/libcntpr.lib;%(AdditionalDependencies)</AdditionalDependencies>
+    </Link>
+    <Inf>
+      <SpecifyArchitecture>true</SpecifyArchitecture>
+      <SpecifyDriverVerDirectiveVersion>true</SpecifyDriverVerDirectiveVersion>
+      
<TimeStamp>$(MAJOR_VERSION).$(MINOR_VERSION).$(MICRO_VERSION).$(BUILD_NUMBER)</TimeStamp>
+      <EnableVerbose>true</EnableVerbose>
+    </Inf>
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Platform)'=='Win32'">
+    <ClCompile>
+      
<PreprocessorDefinitions>__i386__;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+    </ClCompile>
+    <Inf>
+      <Architecture>x86</Architecture>
+    </Inf>
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Platform)'=='x64'">
+    <ClCompile>
+      
<PreprocessorDefinitions>__x86_64__;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+    </ClCompile>
+    <Inf>
+      <Architecture>amd64</Architecture>
+    </Inf>
+  </ItemDefinitionGroup>
+  <ItemGroup>
+    <FilesToPackage Include="$(TargetPath)" />
+    <FilesToPackage Include="$(OutDir)$(TargetName).pdb" />
+    <FilesToPackage Include="@(Inf->'%(CopyOutput)')" Condition="'@(Inf)'!=''" 
/>
+  </ItemGroup>
+  <ItemGroup>
+    <ClCompile Include="../../src/xendisk/driver.c" />
+    <ClCompile Include="../../src/xendisk/fdo.c" />
+    <ClCompile Include="../../src/xendisk/pdo.c" />
+    <ClCompile Include="../../src/xendisk/thread.c" />
+  </ItemGroup>
+  <ItemGroup>
+    <ResourceCompile Include="..\..\src\xendisk\xendisk.rc" />
+  </ItemGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+</Project>
\ No newline at end of file
diff --git a/vs2013/xenvbd.sln b/vs2013/xenvbd.sln
index 13128c5..60d9513 100644
--- a/vs2013/xenvbd.sln
+++ b/vs2013/xenvbd.sln
@@ -8,6 +8,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "xenvbd", 
"xenvbd\xenvbd.vcx
 EndProject
 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "xencrsh", 
"xencrsh\xencrsh.vcxproj", "{58F5BC43-B92E-4A2B-975D-0066EAB29092}"
 EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "xendisk", 
"xendisk\xendisk.vcxproj", "{D7411B2C-2C43-434D-9F56-E10A3D2F5BAD}"
+EndProject
 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "xenvbd_coinst", 
"coinst\xenvbd_coinst.vcxproj", "{50C08437-C1F3-4349-BF6A-7B55A06BF999}"
     ProjectSection(ProjectDependencies) = postProject
                {58F5BC43-B92E-4A2B-975D-0066EAB29092} = 
{58F5BC43-B92E-4A2B-975D-0066EAB29092}
@@ -16,6 +18,7 @@ EndProject
 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "package", 
"package\package.vcxproj", "{AB8DAED3-9D70-4907-99A3-C643F1FC1972}"
     ProjectSection(ProjectDependencies) = postProject
                {50C08437-C1F3-4349-BF6A-7B55A06BF999} = 
{50C08437-C1F3-4349-BF6A-7B55A06BF999}
+               {D7411B2C-2C43-434D-9F56-E10A3D2F5BAD} = 
{D7411B2C-2C43-434D-9F56-E10A3D2F5BAD}
                {58F5BC43-B92E-4A2B-975D-0066EAB29092} = 
{58F5BC43-B92E-4A2B-975D-0066EAB29092}
         {EF236371-3145-41B1-99C9-82B33E353F17} = 
{EF236371-3145-41B1-99C9-82B33E353F17}
        EndProjectSection
@@ -36,6 +39,30 @@ Global
                Windows Vista Release|x64 = Windows Vista Release|x64
        EndGlobalSection
        GlobalSection(ProjectConfigurationPlatforms) = postSolution
+               {D7411B2C-2C43-434D-9F56-E10A3D2F5BAD}.Windows 7 
Debug|Win32.ActiveCfg = Windows 7 Debug|Win32
+               {D7411B2C-2C43-434D-9F56-E10A3D2F5BAD}.Windows 7 
Debug|Win32.Build.0 = Windows 7 Debug|Win32
+               {D7411B2C-2C43-434D-9F56-E10A3D2F5BAD}.Windows 7 
Debug|x64.ActiveCfg = Windows 7 Debug|x64
+               {D7411B2C-2C43-434D-9F56-E10A3D2F5BAD}.Windows 7 
Debug|x64.Build.0 = Windows 7 Debug|x64
+               {D7411B2C-2C43-434D-9F56-E10A3D2F5BAD}.Windows 7 
Release|Win32.ActiveCfg = Windows 7 Release|Win32
+               {D7411B2C-2C43-434D-9F56-E10A3D2F5BAD}.Windows 7 
Release|Win32.Build.0 = Windows 7 Release|Win32
+               {D7411B2C-2C43-434D-9F56-E10A3D2F5BAD}.Windows 7 
Release|x64.ActiveCfg = Windows 7 Release|x64
+               {D7411B2C-2C43-434D-9F56-E10A3D2F5BAD}.Windows 7 
Release|x64.Build.0 = Windows 7 Release|x64
+               {D7411B2C-2C43-434D-9F56-E10A3D2F5BAD}.Windows 8 
Debug|Win32.ActiveCfg = Windows 8 Debug|Win32
+               {D7411B2C-2C43-434D-9F56-E10A3D2F5BAD}.Windows 8 
Debug|Win32.Build.0 = Windows 8 Debug|Win32
+               {D7411B2C-2C43-434D-9F56-E10A3D2F5BAD}.Windows 8 
Debug|x64.ActiveCfg = Windows 8 Debug|x64
+               {D7411B2C-2C43-434D-9F56-E10A3D2F5BAD}.Windows 8 
Debug|x64.Build.0 = Windows 8 Debug|x64
+               {D7411B2C-2C43-434D-9F56-E10A3D2F5BAD}.Windows 8 
Release|Win32.ActiveCfg = Windows 8 Release|Win32
+               {D7411B2C-2C43-434D-9F56-E10A3D2F5BAD}.Windows 8 
Release|Win32.Build.0 = Windows 8 Release|Win32
+               {D7411B2C-2C43-434D-9F56-E10A3D2F5BAD}.Windows 8 
Release|x64.ActiveCfg = Windows 8 Release|x64
+               {D7411B2C-2C43-434D-9F56-E10A3D2F5BAD}.Windows 8 
Release|x64.Build.0 = Windows 8 Release|x64
+               {D7411B2C-2C43-434D-9F56-E10A3D2F5BAD}.Windows Vista 
Debug|Win32.ActiveCfg = Windows Vista Debug|Win32
+               {D7411B2C-2C43-434D-9F56-E10A3D2F5BAD}.Windows Vista 
Debug|Win32.Build.0 = Windows Vista Debug|Win32
+               {D7411B2C-2C43-434D-9F56-E10A3D2F5BAD}.Windows Vista 
Debug|x64.ActiveCfg = Windows Vista Debug|x64
+               {D7411B2C-2C43-434D-9F56-E10A3D2F5BAD}.Windows Vista 
Debug|x64.Build.0 = Windows Vista Debug|x64
+               {D7411B2C-2C43-434D-9F56-E10A3D2F5BAD}.Windows Vista 
Release|Win32.ActiveCfg = Windows Vista Release|Win32
+               {D7411B2C-2C43-434D-9F56-E10A3D2F5BAD}.Windows Vista 
Release|Win32.Build.0 = Windows Vista Release|Win32
+               {D7411B2C-2C43-434D-9F56-E10A3D2F5BAD}.Windows Vista 
Release|x64.ActiveCfg = Windows Vista Release|x64
+               {D7411B2C-2C43-434D-9F56-E10A3D2F5BAD}.Windows Vista 
Release|x64.Build.0 = Windows Vista Release|x64
                {EF236371-3145-41B1-99C9-82B33E353F17}.Windows 7 
Debug|Win32.ActiveCfg = Windows 7 Debug|Win32
                {EF236371-3145-41B1-99C9-82B33E353F17}.Windows 7 
Debug|Win32.Build.0 = Windows 7 Debug|Win32
                {EF236371-3145-41B1-99C9-82B33E353F17}.Windows 7 
Debug|x64.ActiveCfg = Windows 7 Debug|x64
-- 
2.1.0


_______________________________________________
win-pv-devel mailing list
win-pv-devel@xxxxxxxxxxxxxxxxxxxx
http://lists.xenproject.org/cgi-bin/mailman/listinfo/win-pv-devel

 


Rackspace

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