]> xenbits.xensource.com Git - pvdrivers/win/xenvif.git/commitdiff
[CP-4696] Git repository created for xenvif
authorBen Chalmers <Ben.Chalmers@citrix.com>
Tue, 28 May 2013 13:23:21 +0000 (14:23 +0100)
committerBen Chalmers <Ben.Chalmers@citrix.com>
Tue, 28 May 2013 13:23:21 +0000 (14:23 +0100)
76 files changed:
LICENSE [new file with mode: 0644]
build.py [new file with mode: 0644]
clean.py [new file with mode: 0644]
include/debug_interface.h [new file with mode: 0644]
include/emulated_interface.h [new file with mode: 0644]
include/ethernet.h [new file with mode: 0644]
include/evtchn_interface.h [new file with mode: 0644]
include/get_xen_headers.sh [new file with mode: 0644]
include/gnttab_interface.h [new file with mode: 0644]
include/ipx.h [new file with mode: 0644]
include/llc.h [new file with mode: 0644]
include/store_interface.h [new file with mode: 0644]
include/suspend_interface.h [new file with mode: 0644]
include/tcpip.h [new file with mode: 0644]
include/util.h [new file with mode: 0644]
include/vif_interface.h [new file with mode: 0644]
include/xen-types.h [new file with mode: 0644]
include/xen-version.h [new file with mode: 0644]
include/xen-warnings.h [new file with mode: 0644]
include/xen.h [new file with mode: 0644]
include/xen/arch-x86/xen-x86_32.h [new file with mode: 0644]
include/xen/arch-x86/xen-x86_64.h [new file with mode: 0644]
include/xen/arch-x86/xen.h [new file with mode: 0644]
include/xen/grant_table.h [new file with mode: 0644]
include/xen/io/netif.h [new file with mode: 0644]
include/xen/io/ring.h [new file with mode: 0644]
include/xen/io/xenbus.h [new file with mode: 0644]
include/xen/xen-compat.h [new file with mode: 0644]
include/xen/xen.h [new file with mode: 0644]
kdfiles.py [new file with mode: 0644]
proj/msbuild.bat [new file with mode: 0644]
proj/package/package.vcxproj [new file with mode: 0644]
proj/package/package.vcxproj.user [new file with mode: 0644]
proj/xenvif.sln [new file with mode: 0644]
proj/xenvif/xenvif.vcxproj [new file with mode: 0644]
proj/xenvif/xenvif.vcxproj.user [new file with mode: 0644]
proj/xenvif_coinst/xenvif_coinst.vcxproj [new file with mode: 0644]
proj/xenvif_coinst/xenvif_coinst.vcxproj.user [new file with mode: 0644]
src/coinst/coinst.c [new file with mode: 0644]
src/coinst/xenvif_coinst.def [new file with mode: 0644]
src/xenvif.inf [new file with mode: 0644]
src/xenvif/assert.h [new file with mode: 0644]
src/xenvif/checksum.c [new file with mode: 0644]
src/xenvif/checksum.h [new file with mode: 0644]
src/xenvif/driver.c [new file with mode: 0644]
src/xenvif/driver.h [new file with mode: 0644]
src/xenvif/fdo.c [new file with mode: 0644]
src/xenvif/fdo.h [new file with mode: 0644]
src/xenvif/frontend.c [new file with mode: 0644]
src/xenvif/frontend.h [new file with mode: 0644]
src/xenvif/log.h [new file with mode: 0644]
src/xenvif/mac.c [new file with mode: 0644]
src/xenvif/mac.h [new file with mode: 0644]
src/xenvif/mrsw.h [new file with mode: 0644]
src/xenvif/mutex.h [new file with mode: 0644]
src/xenvif/names.h [new file with mode: 0644]
src/xenvif/notifier.c [new file with mode: 0644]
src/xenvif/notifier.h [new file with mode: 0644]
src/xenvif/parse.c [new file with mode: 0644]
src/xenvif/parse.h [new file with mode: 0644]
src/xenvif/pdo.c [new file with mode: 0644]
src/xenvif/pdo.h [new file with mode: 0644]
src/xenvif/pool.c [new file with mode: 0644]
src/xenvif/pool.h [new file with mode: 0644]
src/xenvif/receiver.c [new file with mode: 0644]
src/xenvif/receiver.h [new file with mode: 0644]
src/xenvif/registry.c [new file with mode: 0644]
src/xenvif/registry.h [new file with mode: 0644]
src/xenvif/thread.c [new file with mode: 0644]
src/xenvif/thread.h [new file with mode: 0644]
src/xenvif/transmitter.c [new file with mode: 0644]
src/xenvif/transmitter.h [new file with mode: 0644]
src/xenvif/types.h [new file with mode: 0644]
src/xenvif/vif.c [new file with mode: 0644]
src/xenvif/vif.h [new file with mode: 0644]
src/xenvif/xenvif.rc [new file with mode: 0644]

diff --git a/LICENSE b/LICENSE
new file mode 100644 (file)
index 0000000..00be666
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,30 @@
+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.
+
diff --git a/build.py b/build.py
new file mode 100644 (file)
index 0000000..88cdccc
--- /dev/null
+++ b/build.py
@@ -0,0 +1,300 @@
+#!python -u
+
+import os, sys
+import datetime
+import re
+import glob
+import tarfile
+import subprocess
+
+def next_build_number():
+    try:
+        file = open('.build_number', 'r')
+        build_number = file.read()
+        file.close()
+    except IOError:
+        build_number = '0'
+
+    file = open('.build_number', 'w')
+    file.write(str(int(build_number) + 1))
+    file.close()
+
+    return build_number
+
+
+def make_header():
+    now = datetime.datetime.now()
+
+    file = open('include\\version.h', 'w')
+    file.write('#define MAJOR_VERSION\t' + os.environ['MAJOR_VERSION'] + '\n')
+    file.write('#define MAJOR_VERSION_STR\t"' + os.environ['MAJOR_VERSION'] + '"\n')
+    file.write('\n')
+
+    file.write('#define MINOR_VERSION\t' + os.environ['MINOR_VERSION'] + '\n')
+    file.write('#define MINOR_VERSION_STR\t"' + os.environ['MINOR_VERSION'] + '"\n')
+    file.write('\n')
+
+    file.write('#define MICRO_VERSION\t' + os.environ['MICRO_VERSION'] + '\n')
+    file.write('#define MICRO_VERSION_STR\t"' + os.environ['MICRO_VERSION'] + '"\n')
+    file.write('\n')
+
+    file.write('#define BUILD_NUMBER\t' + os.environ['BUILD_NUMBER'] + '\n')
+    file.write('#define BUILD_NUMBER_STR\t"' + os.environ['BUILD_NUMBER'] + '"\n')
+    file.write('\n')
+
+    file.write('#define YEAR\t' + str(now.year) + '\n')
+    file.write('#define YEAR_STR\t"' + str(now.year) + '"\n')
+
+    file.write('#define MONTH\t' + str(now.month) + '\n')
+    file.write('#define MONTH_STR\t"' + str(now.month) + '"\n')
+
+    file.write('#define DAY\t' + str(now.day) + '\n')
+    file.write('#define DAY_STR\t"' + str(now.day) + '"\n')
+
+    file.close()
+
+
+def copy_inf(name):
+    src = open('src\\%s.inf' % name, 'r')
+    dst = open('proj\\%s.inf' % name, 'w')
+
+    for line in src:
+        line = re.sub('@MAJOR_VERSION@', os.environ['MAJOR_VERSION'], line)
+        line = re.sub('@MINOR_VERSION@', os.environ['MINOR_VERSION'], line)
+        line = re.sub('@MICRO_VERSION@', os.environ['MICRO_VERSION'], line)
+        line = re.sub('@BUILD_NUMBER@', os.environ['BUILD_NUMBER'], line)
+        dst.write(line)
+
+    dst.close()
+    src.close()
+
+
+def get_expired_symbols(name, age = 30):
+    path = os.path.join(os.environ['SYMBOL_SERVER'], '000Admin\\history.txt')
+
+    try:
+        file = open(path, 'r')
+    except IOError:
+        return []
+
+    threshold = datetime.datetime.utcnow() - datetime.timedelta(days = age)
+
+    expired = []
+
+    for line in file:
+        item = line.split(',')
+
+        if (re.match('add', item[1])):
+            id = item[0]
+            date = item[3].split('/')
+            time = item[4].split(':')
+            tag = item[5].strip('"')
+
+            age = datetime.datetime(year = int(date[2]),
+                                    month = int(date[0]),
+                                    day = int(date[1]),
+                                    hour = int(time[0]),
+                                    minute = int(time[1]),
+                                    second = int(time[2]))
+            if (tag == name and age < threshold):
+                expired.append(id)
+
+        elif (re.match('del', item[1])):
+            id = item[2].rstrip()
+            try:
+                expired.remove(id)
+            except ValueError:
+                pass
+
+    file.close()
+
+    return expired
+
+
+def get_configuration(debug):
+    configuration = 'Windows Vista'
+
+    if debug:
+        configuration += ' Debug'
+    else:
+        configuration += ' Release'
+
+    return configuration
+
+def get_configuration_name(debug):
+    configuration = 'WindowsVista'
+
+    if debug:
+        configuration += 'Debug'
+    else:
+        configuration += 'Release'
+
+    return configuration
+
+def get_target_path(arch, debug):
+    configuration = get_configuration_name(debug)
+
+    target = { 'x86': 'proj', 'x64': os.sep.join(['proj', 'x64']) }
+    target_path = os.sep.join([target[arch], configuration])
+
+    return target_path
+
+
+def shell(command):
+    print(command)
+    sys.stdout.flush()
+
+    pipe = os.popen(command, 'r', 1)
+
+    for line in pipe:
+        print(line.rstrip())
+
+    return pipe.close()
+
+
+class msbuild_failure(Exception):
+    def __init__(self, value):
+        self.value = value
+    def __str__(self):
+        return repr(self.value)
+
+def msbuild(name, arch, debug):
+    cwd = os.getcwd()
+    configuration = get_configuration(debug)
+
+    os.environ['SOLUTION'] = name
+
+    if arch == 'x86':
+        os.environ['PLATFORM'] = 'Win32'
+    elif arch == 'x64':
+        os.environ['PLATFORM'] = 'x64'
+
+    os.environ['CONFIGURATION'] = configuration
+    os.environ['TARGET'] = 'Build'
+
+    os.chdir('proj')
+    status = shell('msbuild.bat')
+    os.chdir(cwd)
+
+    if (status != None):
+        raise msbuild_failure(configuration)
+
+
+def symstore_del(name, age):
+    symstore_path = [os.environ['KIT'], 'Debuggers']
+    if os.environ['PROCESSOR_ARCHITECTURE'] == 'x86':
+        symstore_path.append('x86')
+    else:
+        symstore_path.append('x64')
+    symstore_path.append('symstore.exe')
+
+    symstore = os.path.join(*symstore_path)
+
+    for id in get_expired_symbols(name, age):
+        command=['"' + symstore + '"']
+        command.append('del')
+        command.append('/i')
+        command.append(str(id))
+        command.append('/s')
+        command.append(os.environ['SYMBOL_SERVER'])
+
+        shell(' '.join(command))
+
+def symstore_add(name, arch, debug):
+    cwd = os.getcwd()
+    configuration = get_configuration_name(debug)
+    target_path = get_target_path(arch, debug)
+
+    symstore_path = [os.environ['KIT'], 'Debuggers']
+    if os.environ['PROCESSOR_ARCHITECTURE'] == 'x86':
+        symstore_path.append('x86')
+    else:
+        symstore_path.append('x64')
+    symstore_path.append('symstore.exe')
+
+    symstore = os.path.join(*symstore_path)
+
+    version = '.'.join([os.environ['MAJOR_VERSION'],
+                        os.environ['MINOR_VERSION'],
+                        os.environ['MICRO_VERSION'],
+                        os.environ['BUILD_NUMBER']])
+
+    os.chdir(target_path)
+    command=['"' + symstore + '"']
+    command.append('add')
+    command.append('/s')
+    command.append(os.environ['SYMBOL_SERVER'])
+    command.append('/r')
+    command.append('/f')
+    command.append('*.pdb')
+    command.append('/t')
+    command.append(name)
+    command.append('/v')
+    command.append(version)
+
+    shell(' '.join(command))
+
+    os.chdir(cwd)
+
+
+def callfnout(cmd):
+    print(cmd)
+
+    sub = subprocess.Popen(cmd, stdout=subprocess.PIPE)
+    output = sub.communicate()[0]
+    ret = sub.returncode
+
+    if ret != 0:
+        raise(Exception("Error %d in : %s" % (ret, cmd)))
+
+    return output.decode('utf-8')
+
+
+def archive(filename, files, tgz=False):
+    access='w'
+    if tgz:
+        access='w:gz'
+    tar = tarfile.open(filename, access)
+    for name in files :
+        try:
+            print('adding '+name)
+            tar.add(name)
+        except:
+            pass
+    tar.close()
+
+
+if __name__ == '__main__':
+    debug = { 'checked': True, 'free': False }
+    driver = 'xenvif'
+
+    os.environ['MAJOR_VERSION'] = '7'
+    os.environ['MINOR_VERSION'] = '0'
+    os.environ['MICRO_VERSION'] = '0'
+
+    if 'BUILD_NUMBER' not in os.environ.keys():
+        os.environ['BUILD_NUMBER'] = next_build_number()
+
+    print("BUILD_NUMBER=%s" % os.environ['BUILD_NUMBER'])
+
+    if 'MERCURIAL_REVISION' in os.environ.keys():
+        revision = open('revision', 'w')
+        print(os.environ['MERCURIAL_REVISION'], file=revision)
+        revision.close()
+
+    make_header()
+
+    copy_inf(driver)
+
+    symstore_del(driver, 30)
+
+    msbuild(driver, 'x86', debug[sys.argv[1]])
+    msbuild(driver, 'x64', debug[sys.argv[1]])
+
+    symstore_add(driver, 'x86', debug[sys.argv[1]])
+    symstore_add(driver, 'x64', debug[sys.argv[1]])
+
+    listfile = callfnout(['hg','manifest'])   
+    archive(driver + '\\source.tgz', listfile.splitlines(), tgz=True)
+    archive(driver + '.tar', [driver,'revision'])
+
diff --git a/clean.py b/clean.py
new file mode 100644 (file)
index 0000000..4f77ead
--- /dev/null
+++ b/clean.py
@@ -0,0 +1,14 @@
+#!/usr/bin/env python
+
+import os, sys
+
+file = os.popen('hg status')
+
+for line in file:
+    item = line.split(' ')
+    if item[0] == '?':
+        path = ' '.join(item[1:]).rstrip()
+        print(path)
+        os.remove(path)
+
+file.close()
diff --git a/include/debug_interface.h b/include/debug_interface.h
new file mode 100644 (file)
index 0000000..e521a8f
--- /dev/null
@@ -0,0 +1,116 @@
+/* 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 _XENBUS_DEBUG_INTERFACE_H
+#define _XENBUS_DEBUG_INTERFACE_H
+
+typedef struct _XENBUS_DEBUG_CALLBACK   XENBUS_DEBUG_CALLBACK, *PXENBUS_DEBUG_CALLBACK;
+
+#define DEFINE_DEBUG_OPERATIONS                                                 \
+        DEBUG_OPERATION(VOID,                                                   \
+                        Acquire,                                                \
+                        (                                                       \
+                        IN  PXENBUS_DEBUG_CONTEXT  Context                      \
+                        )                                                       \
+                        )                                                       \
+        DEBUG_OPERATION(VOID,                                                   \
+                        Release,                                                \
+                        (                                                       \
+                        IN  PXENBUS_DEBUG_CONTEXT  Context                      \
+                        )                                                       \
+                        )                                                       \
+        DEBUG_OPERATION(NTSTATUS,                                               \
+                        Register,                                               \
+                        (                                                       \
+                        IN  PXENBUS_DEBUG_CONTEXT  Context,                     \
+                        IN  const CHAR             *Prefix,                     \
+                        IN  VOID                   (*Function)(PVOID, BOOLEAN), \
+                        IN  PVOID                  Argument OPTIONAL,           \
+                        OUT PXENBUS_DEBUG_CALLBACK *Callback                    \
+                        )                                                       \
+                        )                                                       \
+        DEBUG_OPERATION(VOID,                                                   \
+                        Printf,                                                 \
+                        (                                                       \
+                        IN  PXENBUS_DEBUG_CONTEXT  Context,                     \
+                        IN  PXENBUS_DEBUG_CALLBACK Callback,                    \
+                        IN  const CHAR             *Format,                     \
+                        ...                                                     \
+                        )                                                       \
+                        )                                                       \
+        DEBUG_OPERATION(VOID,                                                   \
+                        Deregister,                                             \
+                        (                                                       \
+                        IN  PXENBUS_DEBUG_CONTEXT  Context,                     \
+                        IN  PXENBUS_DEBUG_CALLBACK Callback                     \
+                        )                                                       \
+                        )
+
+typedef struct _XENBUS_DEBUG_CONTEXT    XENBUS_DEBUG_CONTEXT, *PXENBUS_DEBUG_CONTEXT;
+
+#define DEBUG_OPERATION(_Type, _Name, _Arguments) \
+        _Type (*DEBUG_ ## _Name) _Arguments;
+
+typedef struct _XENBUS_DEBUG_OPERATIONS {
+    DEFINE_DEBUG_OPERATIONS
+} XENBUS_DEBUG_OPERATIONS, *PXENBUS_DEBUG_OPERATIONS;
+
+#undef DEBUG_OPERATION
+
+typedef struct _XENBUS_DEBUG_INTERFACE   XENBUS_DEBUG_INTERFACE, *PXENBUS_DEBUG_INTERFACE;
+
+// {54AAE52C-1838-49d3-AA43-9DDDD891603B}
+DEFINE_GUID(GUID_DEBUG_INTERFACE, 
+            0x54aae52c,
+            0x1838,
+            0x49d3,
+            0xaa,
+            0x43,
+            0x9d,
+            0xdd,
+            0xd8,
+            0x91,
+            0x60,
+            0x3b);
+
+#define DEBUG_INTERFACE_VERSION    4
+
+#define DEBUG_OPERATIONS(_Interface) \
+        (PXENBUS_DEBUG_OPERATIONS *)((ULONG_PTR)(_Interface))
+
+#define DEBUG_CONTEXT(_Interface) \
+        (PXENBUS_DEBUG_CONTEXT *)((ULONG_PTR)(_Interface) + sizeof (PVOID))
+
+#define DEBUG(_Operation, _Interface, ...) \
+        (*DEBUG_OPERATIONS(_Interface))->DEBUG_ ## _Operation((*DEBUG_CONTEXT(_Interface)), __VA_ARGS__)
+
+#endif  // _XENBUS_DEBUG_INTERFACE_H
+
diff --git a/include/emulated_interface.h b/include/emulated_interface.h
new file mode 100644 (file)
index 0000000..53cdd09
--- /dev/null
@@ -0,0 +1,96 @@
+/* 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 _XENFILT_EMULATED_INTERFACE_H
+#define _XENFILT_EMULATED_INTERFACE_H
+
+#define DEFINE_EMULATED_OPERATIONS                                  \
+        EMULATED_OPERATION(VOID,                                    \
+                           Acquire,                                 \
+                           (                                        \
+                           IN  PXENFILT_EMULATED_CONTEXT Context    \
+                           )                                        \
+                           )                                        \
+        EMULATED_OPERATION(VOID,                                    \
+                           Release,                                 \
+                           (                                        \
+                           IN  PXENFILT_EMULATED_CONTEXT Context    \
+                           )                                        \
+                           )                                        \
+        EMULATED_OPERATION(BOOLEAN,                                 \
+                           IsPresent,                               \
+                           (                                        \
+                           IN  PXENFILT_EMULATED_CONTEXT Context,   \
+                           IN  PCHAR                     Class,     \
+                           IN  PCHAR                     Device     \
+                           )                                        \
+                           )
+
+typedef struct _XENFILT_EMULATED_CONTEXT    XENFILT_EMULATED_CONTEXT, *PXENFILT_EMULATED_CONTEXT;
+
+#define EMULATED_OPERATION(_Type, _Name, _Arguments) \
+        _Type (*EMULATED_ ## _Name) _Arguments;
+
+typedef struct _XENFILT_EMULATED_OPERATIONS {
+    DEFINE_EMULATED_OPERATIONS
+} XENFILT_EMULATED_OPERATIONS, *PXENFILT_EMULATED_OPERATIONS;
+
+#undef EMULATED_OPERATION
+
+typedef struct _XENFILT_EMULATED_INTERFACE   XENFILT_EMULATED_INTERFACE, *PXENFILT_EMULATED_INTERFACE;
+
+// {062AAC96-2BF8-4A69-AD6B-154CF051E977}
+DEFINE_GUID(GUID_EMULATED_INTERFACE, 
+            0x62aac96,
+            0x2bf8,
+            0x4a69,
+            0xad,
+            0x6b,
+            0x15,
+            0x4c,
+            0xf0,
+            0x51,
+            0xe9,
+            0x77);
+
+#define EMULATED_INTERFACE_VERSION    3
+
+#define EMULATED_OPERATIONS(_Interface) \
+        (PXENFILT_EMULATED_OPERATIONS *)((ULONG_PTR)(_Interface))
+
+#define EMULATED_CONTEXT(_Interface) \
+        (PXENFILT_EMULATED_CONTEXT *)((ULONG_PTR)(_Interface) + sizeof (PVOID))
+
+#define EMULATED(_Operation, _Interface, ...) \
+        (*EMULATED_OPERATIONS(_Interface))->EMULATED_ ## _Operation((*EMULATED_CONTEXT(_Interface)), __VA_ARGS__)
+
+#endif  // _XENFILT_EMULATED_INTERFACE_H
+
diff --git a/include/ethernet.h b/include/ethernet.h
new file mode 100644 (file)
index 0000000..95e98dd
--- /dev/null
@@ -0,0 +1,145 @@
+/* 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 _ETHERNET_H_
+#define _ETHERNET_H_
+
+#pragma warning(push)
+#pragma warning(disable:4201) // nonstandard extension used : nameless struct/union
+
+#define NTOHS(_Value)   _byteswap_ushort(_Value)
+#define HTONS(_Value)   _byteswap_ushort(_Value)
+#define NTOHL(_Value)   _byteswap_ulong(_Value)
+#define HTONL(_Value)   _byteswap_ulong(_Value)
+
+#pragma pack(push, 1)
+
+// Ethernet data structures
+//
+// NOTE: Fields are in network byte order
+
+#define ETHERNET_MTU            1500
+#define ETHERNET_MIN            60
+#define ETHERNET_MAX            1514
+
+typedef struct _ETHERNET_ADDRESS {
+    UCHAR   Byte[6];
+} ETHERNET_ADDRESS, *PETHERNET_ADDRESS;
+
+#define ETHERNET_ADDRESS_LENGTH (sizeof (ETHERNET_ADDRESS))
+
+typedef enum _ETHERNET_ADDRESS_TYPE {
+    ETHERNET_ADDRESS_TYPE_INVALID = 0,
+    ETHERNET_ADDRESS_UNICAST,
+    ETHERNET_ADDRESS_MULTICAST,
+    ETHERNET_ADDRESS_BROADCAST,
+    ETHERNET_ADDRESS_TYPE_COUNT
+} ETHERNET_ADDRESS_TYPE, *PETHERNET_ADDRESS_TYPE;
+
+#define GET_ETHERNET_ADDRESS_TYPE(_Address)                 \
+        (((_Address)->Byte[0] & 0x01) ?                     \
+            (((((_Address)->Byte[0] & ~0x03) == 0xFC) &&    \
+              (((_Address)->Byte[1]        ) == 0xFF) &&    \
+              (((_Address)->Byte[2]        ) == 0xFF) &&    \
+              (((_Address)->Byte[3]        ) == 0xFF) &&    \
+              (((_Address)->Byte[4]        ) == 0xFF) &&    \
+              (((_Address)->Byte[5]        ) == 0xFF)       \
+             ) ?                                            \
+                ETHERNET_ADDRESS_BROADCAST :                \
+                ETHERNET_ADDRESS_MULTICAST                  \
+            ) :                                             \
+            ETHERNET_ADDRESS_UNICAST                        \
+        )
+
+typedef struct _ETHERNET_UNTAGGED_HEADER {
+    ETHERNET_ADDRESS    DestinationAddress;
+    ETHERNET_ADDRESS    SourceAddress;
+    USHORT              TypeOrLength;
+
+#define ETHERTYPE_INVALID   0x0000
+#define ETHERTYPE_IPV4      0x0800
+#define ETHERTYPE_IPV6      0x86DD
+#define ETHERTYPE_ARP       0x0806
+#define ETHERTYPE_RARP      0x0835
+#define ETHERTYPE_TPID      0x8100
+#define ETHERTYPE_IPX       0xFFFF
+
+} ETHERNET_UNTAGGED_HEADER, *PETHERNET_UNTAGGED_HEADER;
+
+typedef struct _ETHERNET_TAG {
+    USHORT  ProtocolID;    // == ETHERTYPE_TPID
+    USHORT  ControlInformation;
+
+#define PACK_TAG_CONTROL_INFORMATION(_ControlInformation, _UserPriority, _CanonicalFormatId, _VlanId)   \
+        do {                                                                                            \
+            (_ControlInformation) = (USHORT)(_VlanId) & 0x0FFF;                                         \
+            (_ControlInformation) |= (USHORT)((_CanonicalFormatId) << 12) & 0x1000;                     \
+            (_ControlInformation) |= (USHORT)((_UserPriority) << 13) & 0xE000;                          \
+        } while (FALSE)
+
+#define UNPACK_TAG_CONTROL_INFORMATION(_ControlInformation, _UserPriority, _CanonicalFormatId, _VlanId) \
+        do {                                                                                            \
+            (_VlanId) = (_ControlInformation) & 0xFFF;                                                  \
+            (_CanonicalFormatId) = ((_ControlInformation) & 0x1000) >> 12;                              \
+            (_UserPriority) = ((_ControlInformation) & 0xE000) >> 13;                                   \
+        } while (FALSE)
+
+} ETHERNET_TAG, *PETHERNET_TAG;
+
+typedef struct _ETHERNET_TAGGED_HEADER {
+  ETHERNET_ADDRESS  DestinationAddress;
+  ETHERNET_ADDRESS  SourceAddress;
+  ETHERNET_TAG      Tag;
+  USHORT            TypeOrLength;
+} ETHERNET_TAGGED_HEADER, *PETHERNET_TAGGED_HEADER;
+
+typedef union _ETHERNET_HEADER {
+    struct {
+        ETHERNET_ADDRESS        DestinationAddress;
+        ETHERNET_ADDRESS        SourceAddress;
+    };
+    ETHERNET_UNTAGGED_HEADER    Untagged;
+    ETHERNET_TAGGED_HEADER      Tagged;
+} ETHERNET_HEADER, *PETHERNET_HEADER;
+
+#define ETHERNET_HEADER_IS_TAGGED(_Header)                          \
+        ((_Header)->Untagged.TypeOrLength == NTOHS(ETHERTYPE_TPID))
+
+#define ETHERNET_HEADER_LENGTH(_Header)         \
+        ETHERNET_HEADER_IS_TAGGED(_Header) ?    \
+        sizeof (ETHERNET_TAGGED_HEADER) :       \
+        sizeof (ETHERNET_UNTAGGEDHEADER))
+
+#pragma pack(pop)
+
+#pragma warning(pop)
+
+#endif  // _ETHERNET_H
diff --git a/include/evtchn_interface.h b/include/evtchn_interface.h
new file mode 100644 (file)
index 0000000..cc601a0
--- /dev/null
@@ -0,0 +1,146 @@
+/* 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 _XENBUS_EVTCHN_INTERFACE_H
+#define _XENBUS_EVTCHN_INTERFACE_H
+
+typedef enum _XENBUS_EVTCHN_TYPE {
+    EVTCHN_TYPE_INVALID = 0,
+    EVTCHN_FIXED,
+    EVTCHN_UNBOUND,
+    EVTCHN_INTER_DOMAIN,
+    EVTCHN_VIRQ
+} XENBUS_EVTCHN_TYPE, *PXENBUS_EVTCHN_TYPE;
+
+typedef struct _XENBUS_EVTCHN_DESCRIPTOR    XENBUS_EVTCHN_DESCRIPTOR, *PXENBUS_EVTCHN_DESCRIPTOR;
+
+#define PORT_INVALID  0xFFFFFFFF
+
+#define DEFINE_EVTCHN_OPERATIONS                                                    \
+        EVTCHN_OPERATION(VOID,                                                      \
+                         Acquire,                                                   \
+                         (                                                          \
+                         IN PXENBUS_EVTCHN_CONTEXT    Context                       \
+                         )                                                          \
+                         )                                                          \
+        EVTCHN_OPERATION(VOID,                                                      \
+                         Release,                                                   \
+                         (                                                          \
+                         IN PXENBUS_EVTCHN_CONTEXT    Context                       \
+                         )                                                          \
+                         )                                                          \
+        EVTCHN_OPERATION(PXENBUS_EVTCHN_DESCRIPTOR,                                 \
+                         Open,                                                      \
+                         (                                                          \
+                         IN PXENBUS_EVTCHN_CONTEXT  Context,                        \
+                         IN XENBUS_EVTCHN_TYPE      Type,                           \
+                         IN PKSERVICE_ROUTINE       Function,                       \
+                         IN PVOID                   Argument OPTIONAL,              \
+                         ...                                                        \
+                         )                                                          \
+                         )                                                          \
+        EVTCHN_OPERATION(BOOLEAN,                                                   \
+                         Unmask,                                                    \
+                         (                                                          \
+                         IN PXENBUS_EVTCHN_CONTEXT    Context,                      \
+                         IN PXENBUS_EVTCHN_DESCRIPTOR Descriptor,                   \
+                         IN BOOLEAN                   Locked                        \
+                         )                                                          \
+                         )                                                          \
+        EVTCHN_OPERATION(NTSTATUS,                                                  \
+                         Send,                                                      \
+                         (                                                          \
+                         IN PXENBUS_EVTCHN_CONTEXT    Context,                      \
+                         IN PXENBUS_EVTCHN_DESCRIPTOR Descriptor                    \
+                         )                                                          \
+                         )                                                          \
+        EVTCHN_OPERATION(BOOLEAN,                                                   \
+                         Trigger,                                                   \
+                         (                                                          \
+                         IN PXENBUS_EVTCHN_CONTEXT    Context,                      \
+                         IN PXENBUS_EVTCHN_DESCRIPTOR Descriptor                    \
+                         )                                                          \
+                         )                                                          \
+        EVTCHN_OPERATION(VOID,                                                      \
+                         Close,                                                     \
+                         (                                                          \
+                         IN PXENBUS_EVTCHN_CONTEXT    Context,                      \
+                         IN PXENBUS_EVTCHN_DESCRIPTOR Descriptor                    \
+                         )                                                          \
+                         )                                                          \
+        EVTCHN_OPERATION(ULONG,                                                     \
+                         Port,                                                      \
+                         (                                                          \
+                         IN PXENBUS_EVTCHN_CONTEXT    Context,                      \
+                         IN PXENBUS_EVTCHN_DESCRIPTOR Descriptor                    \
+                         )                                                          \
+                         )
+
+typedef struct _XENBUS_EVTCHN_CONTEXT   XENBUS_EVTCHN_CONTEXT, *PXENBUS_EVTCHN_CONTEXT;
+
+#define EVTCHN_OPERATION(_Type, _Name, _Arguments) \
+        _Type (*EVTCHN_ ## _Name) _Arguments;
+
+typedef struct _XENBUS_EVTCHN_OPERATIONS {
+    DEFINE_EVTCHN_OPERATIONS
+} XENBUS_EVTCHN_OPERATIONS, *PXENBUS_EVTCHN_OPERATIONS;
+
+#undef EVTCHN_OPERATION
+
+typedef struct _XENBUS_EVTCHN_INTERFACE  XENBUS_EVTCHN_INTERFACE, *PXENBUS_EVTCHN_INTERFACE;
+
+// {F87E8751-D6FB-44e8-85E3-DAC19FFA17A6}
+DEFINE_GUID(GUID_EVTCHN_INTERFACE, 
+            0xf87e8751,
+            0xd6fb,
+            0x44e8,
+            0x85,
+            0xe3,
+            0xda,
+            0xc1,
+            0x9f,
+            0xfa,
+            0x17,
+            0xa6);
+
+#define EVTCHN_INTERFACE_VERSION    4
+
+#define EVTCHN_OPERATIONS(_Interface) \
+        (PXENBUS_EVTCHN_OPERATIONS *)((ULONG_PTR)(_Interface))
+
+#define EVTCHN_CONTEXT(_Interface) \
+        (PXENBUS_EVTCHN_CONTEXT *)((ULONG_PTR)(_Interface) + sizeof (PVOID))
+
+#define EVTCHN(_Operation, _Interface, ...) \
+        (*EVTCHN_OPERATIONS(_Interface))->EVTCHN_ ## _Operation((*EVTCHN_CONTEXT(_Interface)), __VA_ARGS__)
+
+#endif  // _XENBUS_EVTCHN_INTERFACE_H
+
diff --git a/include/get_xen_headers.sh b/include/get_xen_headers.sh
new file mode 100644 (file)
index 0000000..7bf85fb
--- /dev/null
@@ -0,0 +1,47 @@
+#!/bin/sh -x
+
+REPO="$1"
+TAG="$2"
+
+copy()
+{
+    SRCDIR="$1"
+    DSTDIR="$2"
+    HEADER="$3"
+
+    CWD=$(pwd)
+
+    mkdir -p ${DSTDIR}
+    cd ${DSTDIR}
+
+    URL="${REPO}/raw-file/${TAG}/xen/include/${SRCDIR}/${HEADER}"
+
+    wget ${URL}
+
+    mv ${HEADER} ${HEADER}.orig
+    sed -e 's/ unsigned long/ ULONG_PTR/g' \
+        -e 's/(unsigned long/(ULONG_PTR/g' \
+        -e 's/ long/ LONG_PTR/g' \
+        -e 's/(long/(LONG_PTR/g' \
+        < ${HEADER}.orig > ${HEADER}
+
+    cd ${CWD}
+}
+
+rm -rf xen
+mkdir -p xen
+cd xen
+
+copy public . xen.h
+copy public . xen-compat.h
+copy public . grant_table.h
+
+copy public/arch-x86 arch-x86 xen.h
+copy public/arch-x86 arch-x86 xen-x86_32.h
+copy public/arch-x86 arch-x86 xen-x86_64.h
+     
+copy public/io io netif.h
+copy public/io io ring.h
+copy public/io io xenbus.h
+
+
diff --git a/include/gnttab_interface.h b/include/gnttab_interface.h
new file mode 100644 (file)
index 0000000..e95cc45
--- /dev/null
@@ -0,0 +1,144 @@
+/* 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 _XENBUS_GNTTAB_INTERFACE_H
+#define _XENBUS_GNTTAB_INTERFACE_H
+
+typedef enum _XENBUS_GNTTAB_ENTRY_TYPE {
+    GNTTAB_ENTRY_TYPE_INVALID = 0,
+    GNTTAB_ENTRY_FULL_PAGE,
+    GNTTAB_ENTRY_SUB_PAGE,
+    GNTTAB_ENTRY_TRANSITIVE
+} XENBUS_GNTTAB_ENTRY_TYPE, *PXENBUS_GNTTAB_ENTRY_TYPE;
+
+typedef struct _XENBUS_GNTTAB_COPY_OPERATION {
+    LIST_ENTRY  ListEntry;
+    USHORT      RemoteDomain;
+    ULONG       RemoteReference;
+    ULONG       RemoteOffset;
+    PFN_NUMBER  Pfn;
+    ULONG       Offset;
+    ULONG       Length;
+} XENBUS_GNTTAB_COPY_OPERATION, *PXENBUS_GNTTAB_COPY_OPERATION;
+
+#define DEFINE_GNTTAB_OPERATIONS                                    \
+        GNTTAB_OPERATION(VOID,                                      \
+                         Acquire,                                   \
+                         (                                          \
+                         IN  PXENBUS_GNTTAB_CONTEXT  Context        \
+                         )                                          \
+                         )                                          \
+        GNTTAB_OPERATION(VOID,                                      \
+                         Release,                                   \
+                         (                                          \
+                         IN  PXENBUS_GNTTAB_CONTEXT  Context        \
+                         )                                          \
+                         )                                          \
+        GNTTAB_OPERATION(NTSTATUS,                                  \
+                         Get,                                       \
+                         (                                          \
+                         IN  PXENBUS_GNTTAB_CONTEXT  Context,       \
+                         OUT PULONG                  Reference      \
+                         )                                          \
+                         )                                          \
+        GNTTAB_OPERATION(VOID,                                      \
+                         Put,                                       \
+                         (                                          \
+                         IN  PXENBUS_GNTTAB_CONTEXT  Context,       \
+                         IN  ULONG                   Reference      \
+                         )                                          \
+                         )                                          \
+        GNTTAB_OPERATION(VOID,                                      \
+                         PermitForeignAccess,                       \
+                         (                                          \
+                         IN  PXENBUS_GNTTAB_CONTEXT   Context,      \
+                         IN  ULONG                    Reference,    \
+                         IN  USHORT                   Domain,       \
+                         IN  XENBUS_GNTTAB_ENTRY_TYPE Type,         \
+                         ...                                        \
+                         )                                          \
+                         )                                          \
+        GNTTAB_OPERATION(VOID,                                      \
+                         RevokeForeignAccess,                       \
+                         (                                          \
+                         IN  PXENBUS_GNTTAB_CONTEXT  Context,       \
+                         IN  ULONG                   Reference      \
+                         )                                          \
+                         )                                          \
+        GNTTAB_OPERATION(NTSTATUS,                                  \
+                         Copy,                                      \
+                         (                                          \
+                         IN  PXENBUS_GNTTAB_CONTEXT  Context,       \
+                         IN  PLIST_ENTRY             List,          \
+                         IN  ULONG                   Count          \
+                         )                                          \
+                         )
+
+typedef struct _XENBUS_GNTTAB_CONTEXT   XENBUS_GNTTAB_CONTEXT, *PXENBUS_GNTTAB_CONTEXT;
+
+#define GNTTAB_OPERATION(_Type, _Name, _Arguments) \
+        _Type (*GNTTAB_ ## _Name) _Arguments;
+
+typedef struct _XENBUS_GNTTAB_OPERATIONS {
+    DEFINE_GNTTAB_OPERATIONS
+} XENBUS_GNTTAB_OPERATIONS, *PXENBUS_GNTTAB_OPERATIONS;
+
+#undef GNTTAB_OPERATION
+
+typedef struct _XENBUS_GNTTAB_INTERFACE  XENBUS_GNTTAB_INTERFACE, *PXENBUS_GNTTAB_INTERFACE;
+
+// {CC32D7DF-88BE-4248-9A53-1F178BE9D60E}
+DEFINE_GUID(GUID_GNTTAB_INTERFACE, 
+            0xcc32d7df,
+            0x88be,
+            0x4248,
+            0x9a,
+            0x53,
+            0x1f,
+            0x17,
+            0x8b,
+            0xe9,
+            0xd6,
+            0xe);
+
+#define GNTTAB_INTERFACE_VERSION    2
+
+#define GNTTAB_OPERATIONS(_Interface) \
+        (PXENBUS_GNTTAB_OPERATIONS *)((ULONG_PTR)(_Interface))
+
+#define GNTTAB_CONTEXT(_Interface) \
+        (PXENBUS_GNTTAB_CONTEXT *)((ULONG_PTR)(_Interface) + sizeof (PVOID))
+
+#define GNTTAB(_Operation, _Interface, ...) \
+        (*GNTTAB_OPERATIONS(_Interface))->GNTTAB_ ## _Operation((*GNTTAB_CONTEXT(_Interface)), __VA_ARGS__)
+
+#endif  // _XENBUS_GNTTAB_INTERFACE_H
+
diff --git a/include/ipx.h b/include/ipx.h
new file mode 100644 (file)
index 0000000..0b6bbf7
--- /dev/null
@@ -0,0 +1,70 @@
+/* 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 _IPX_H
+#define _IPX_H
+
+#pragma warning(push)
+#pragma warning(disable:4214) // nonstandard extension used : bit field types other than int
+#pragma warning(disable:4201) // nonstandard extension used : nameless struct/union
+
+#pragma pack(push, 1)
+
+// IPX data structures
+//
+// NOTE: Fields are in network byte order
+
+typedef struct _IPX_NODE {
+    UCHAR   Byte[6];
+} IPX_NODE, *PIPX_NODE;
+
+typedef struct _IPX_NETWORK {
+    UCHAR   Byte[4];
+} IPX_NETWORK, *PIPX_NETWORK;
+
+typedef struct _IPX_HEADER {
+    USHORT      Checksum;
+    USHORT      PacketLength;
+    UCHAR       TransportControl;
+    UCHAR       PacketType;
+    IPX_NETWORK DestinationNetwork;
+    IPX_NODE    DestinationNode;
+    UCHAR       DestinationSocket;
+    IPX_NETWORK SourceNetwork;
+    IPX_NODE    SourceNode;
+    UCHAR       SourceSocket;
+} IPX_HEADER, *PIPX_HEADER;
+
+#pragma pack(pop)
+
+#pragma warning(pop)
+
+#endif  //_IPX_H
diff --git a/include/llc.h b/include/llc.h
new file mode 100644 (file)
index 0000000..addd93f
--- /dev/null
@@ -0,0 +1,70 @@
+/* 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 _LLC_H
+#define _LLC_H
+
+#pragma warning(push)
+#pragma warning(disable:4214) // nonstandard extension used : bit field types other than int
+#pragma warning(disable:4201) // nonstandard extension used : nameless struct/union
+
+#pragma pack(push, 1)
+
+// LLC data structures
+//
+// NOTE: Fields are in network byte order
+
+typedef struct _LLC_U_HEADER {
+    UCHAR   DestinationSAP;
+    UCHAR   SourceSAP;
+    UCHAR   Control;
+} LLC_U_HEADER, *PLLC_U_HEADER;
+
+#define LLC_SAP_MASK (~(UCHAR)1)
+#define LLC_U_FRAME 0x03
+
+typedef struct _LLC_SNAP_HEADER {
+    UCHAR   DestinationSAP;
+    UCHAR   SourceSAP;
+    UCHAR   Control;
+    UCHAR   OUI[3];
+    USHORT  Type;
+} LLC_SNAP_HEADER, *PLLC_SNAP_HEADER;
+
+#define SNAPTYPE_IPX    0x8137
+#define SNAPTYPE_IPV4   ETHERTYPE_IPV4
+#define SNAPTYPE_IPV6   ETHERTYPE_IPV6
+
+#pragma pack(pop)
+
+#pragma warning(pop)
+
+#endif  //_LLC_H
diff --git a/include/store_interface.h b/include/store_interface.h
new file mode 100644 (file)
index 0000000..af6ab71
--- /dev/null
@@ -0,0 +1,186 @@
+/* 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 _XENBUS_STORE_INTERFACE_H
+#define _XENBUS_STORE_INTERFACE_H
+
+typedef struct _XENBUS_STORE_TRANSACTION    XENBUS_STORE_TRANSACTION, *PXENBUS_STORE_TRANSACTION;
+typedef struct _XENBUS_STORE_WATCH          XENBUS_STORE_WATCH, *PXENBUS_STORE_WATCH;
+
+#define DEFINE_STORE_OPERATIONS                                                 \
+        STORE_OPERATION(VOID,                                                   \
+                        Acquire,                                                \
+                        (                                                       \
+                        IN  PXENBUS_STORE_CONTEXT       Context                 \
+                        )                                                       \
+                        )                                                       \
+        STORE_OPERATION(VOID,                                                   \
+                        Release,                                                \
+                        (                                                       \
+                        IN  PXENBUS_STORE_CONTEXT       Context                 \
+                        )                                                       \
+                        )                                                       \
+        STORE_OPERATION(VOID,                                                   \
+                        Free,                                                   \
+                        (                                                       \
+                        IN  PXENBUS_STORE_CONTEXT       Context,                \
+                        IN  PCHAR                       Value                   \
+                        )                                                       \
+                        )                                                       \
+        STORE_OPERATION(NTSTATUS,                                               \
+                        Read,                                                   \
+                        (                                                       \
+                        IN  PXENBUS_STORE_CONTEXT       Context,                \
+                        IN  PXENBUS_STORE_TRANSACTION   Transaction OPTIONAL,   \
+                        IN  PCHAR                       Prefix OPTIONAL,        \
+                        IN  PCHAR                       Node,                   \
+                        OUT PCHAR                       *Value                  \
+                        )                                                       \
+                        )                                                       \
+        STORE_OPERATION(NTSTATUS,                                               \
+                        Write,                                                  \
+                        (                                                       \
+                        IN  PXENBUS_STORE_CONTEXT       Context,                \
+                        IN  PXENBUS_STORE_TRANSACTION   Transaction OPTIONAL,   \
+                        IN  PCHAR                       Prefix OPTIONAL,        \
+                        IN  PCHAR                       Node,                   \
+                        IN  PCHAR                       Value                   \
+                        )                                                       \
+                        )                                                       \
+        STORE_OPERATION(NTSTATUS,                                               \
+                        Printf,                                                 \
+                        (                                                       \
+                        IN  PXENBUS_STORE_CONTEXT       Context,                \
+                        IN  PXENBUS_STORE_TRANSACTION   Transaction OPTIONAL,   \
+                        IN  PCHAR                       Prefix OPTIONAL,        \
+                        IN  PCHAR                       Node,                   \
+                        IN  const CHAR                  *Format,                \
+                        ...                                                     \
+                        )                                                       \
+                        )                                                       \
+        STORE_OPERATION(NTSTATUS,                                               \
+                        Remove,                                                 \
+                        (                                                       \
+                        IN  PXENBUS_STORE_CONTEXT       Context,                \
+                        IN  PXENBUS_STORE_TRANSACTION   Transaction OPTIONAL,   \
+                        IN  PCHAR                       Prefix OPTIONAL,        \
+                        IN  PCHAR                       Node                    \
+                        )                                                       \
+                        )                                                       \
+        STORE_OPERATION(NTSTATUS,                                               \
+                        Directory,                                              \
+                        (                                                       \
+                        IN  PXENBUS_STORE_CONTEXT       Context,                \
+                        IN  PXENBUS_STORE_TRANSACTION   Transaction OPTIONAL,   \
+                        IN  PCHAR                       Prefix OPTIONAL,        \
+                        IN  PCHAR                       Node,                   \
+                        OUT PCHAR                       *Value                  \
+                        )                                                       \
+                        )                                                       \
+        STORE_OPERATION(NTSTATUS,                                               \
+                        TransactionStart,                                       \
+                        (                                                       \
+                        IN  PXENBUS_STORE_CONTEXT       Context,                \
+                        OUT PXENBUS_STORE_TRANSACTION   *Transaction            \
+                        )                                                       \
+                        )                                                       \
+        STORE_OPERATION(NTSTATUS,                                               \
+                        TransactionEnd,                                         \
+                        (                                                       \
+                        IN  PXENBUS_STORE_CONTEXT       Context,                \
+                        IN  PXENBUS_STORE_TRANSACTION   Transaction,            \
+                        IN  BOOLEAN                     Commit                  \
+                        )                                                       \
+                        )                                                       \
+        STORE_OPERATION(NTSTATUS,                                               \
+                        Watch,                                                  \
+                        (                                                       \
+                        IN  PXENBUS_STORE_CONTEXT       Context,                \
+                        IN  PCHAR                       Prefix OPTIONAL,        \
+                        IN  PCHAR                       Node,                   \
+                        IN  PKEVENT                     Event,                  \
+                        OUT PXENBUS_STORE_WATCH         *Watch                  \
+                        )                                                       \
+                        )                                                       \
+        STORE_OPERATION(NTSTATUS,                                               \
+                        Unwatch,                                                \
+                        (                                                       \
+                        IN  PXENBUS_STORE_CONTEXT       Context,                \
+                        IN  PXENBUS_STORE_WATCH         Watch                   \
+                        )                                                       \
+                        )                                                       \
+        STORE_OPERATION(VOID,                                                   \
+                        Poll,                                                   \
+                        (                                                       \
+                        IN  PXENBUS_STORE_CONTEXT       Context                 \
+                        )                                                       \
+                        )
+
+typedef struct _XENBUS_STORE_CONTEXT    XENBUS_STORE_CONTEXT, *PXENBUS_STORE_CONTEXT;
+
+#define STORE_OPERATION(_Type, _Name, _Arguments) \
+        _Type (*STORE_ ## _Name) _Arguments;
+
+typedef struct _XENBUS_STORE_OPERATIONS {
+    DEFINE_STORE_OPERATIONS
+} XENBUS_STORE_OPERATIONS, *PXENBUS_STORE_OPERATIONS;
+
+#undef STORE_OPERATION
+
+typedef struct _XENBUS_STORE_INTERFACE   XENBUS_STORE_INTERFACE, *PXENBUS_STORE_INTERFACE;
+
+// {916920F1-F9EE-465d-8137-5CC61786B840}
+DEFINE_GUID(GUID_STORE_INTERFACE,
+            0x916920f1,
+            0xf9ee,
+            0x465d,
+            0x81,
+            0x37,
+            0x5c,
+            0xc6,
+            0x17,
+            0x86,
+            0xb8,
+            0x40);
+
+#define STORE_INTERFACE_VERSION 4
+
+#define STORE_OPERATIONS(_Interface) \
+        (PXENBUS_STORE_OPERATIONS *)((ULONG_PTR)(_Interface))
+
+#define STORE_CONTEXT(_Interface) \
+        (PXENBUS_STORE_CONTEXT *)((ULONG_PTR)(_Interface) + sizeof (PVOID))
+
+#define STORE(_Operation, _Interface, ...) \
+        (*STORE_OPERATIONS(_Interface))->STORE_ ## _Operation((*STORE_CONTEXT(_Interface)), __VA_ARGS__)
+
+#endif  // _XENBUS_STORE_INTERFACE_H
+
diff --git a/include/suspend_interface.h b/include/suspend_interface.h
new file mode 100644 (file)
index 0000000..2910d26
--- /dev/null
@@ -0,0 +1,120 @@
+/* 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 _XENBUS_SUSPEND_INTERFACE_H
+#define _XENBUS_SUSPEND_INTERFACE_H
+
+typedef enum _XENBUS_SUSPEND_CALLBACK_TYPE {
+    SUSPEND_CALLBACK_TYPE_INVALID = 0,
+    SUSPEND_CALLBACK_EARLY,
+    SUSPEND_CALLBACK_LATE
+} XENBUS_SUSPEND_CALLBACK_TYPE, *PXENBUS_SUSPEND_CALLBACK_TYPE;
+
+typedef struct _XENBUS_SUSPEND_CALLBACK   XENBUS_SUSPEND_CALLBACK, *PXENBUS_SUSPEND_CALLBACK;
+
+#define DEFINE_SUSPEND_OPERATIONS                                                   \
+        SUSPEND_OPERATION(VOID,                                                     \
+                          Acquire,                                                  \
+                          (                                                         \
+                          IN  PXENBUS_SUSPEND_CONTEXT  Context                      \
+                          )                                                         \
+                          )                                                         \
+        SUSPEND_OPERATION(VOID,                                                     \
+                          Release,                                                  \
+                          (                                                         \
+                          IN  PXENBUS_SUSPEND_CONTEXT  Context                      \
+                          )                                                         \
+                          )                                                         \
+        SUSPEND_OPERATION(NTSTATUS,                                                 \
+                          Register,                                                 \
+                          (                                                         \
+                          IN  PXENBUS_SUSPEND_CONTEXT      Context,                 \
+                          IN  XENBUS_SUSPEND_CALLBACK_TYPE Type,                    \
+                          IN  VOID                         (*Function)(PVOID),      \
+                          IN  PVOID                        Argument OPTIONAL,       \
+                          OUT PXENBUS_SUSPEND_CALLBACK     *Callback                \
+                          )                                                         \
+                          )                                                         \
+        SUSPEND_OPERATION(VOID,                                                     \
+                          Deregister,                                               \
+                          (                                                         \
+                          IN  PXENBUS_SUSPEND_CONTEXT  Context,                     \
+                          IN  PXENBUS_SUSPEND_CALLBACK Callback                     \
+                          )                                                         \
+                          )                                                         \
+        SUSPEND_OPERATION(ULONG,                                                    \
+                          Count,                                                    \
+                          (                                                         \
+                          IN  PXENBUS_SUSPEND_CONTEXT  Context                      \
+                          )                                                         \
+                          )
+
+typedef struct _XENBUS_SUSPEND_CONTEXT  XENBUS_SUSPEND_CONTEXT, *PXENBUS_SUSPEND_CONTEXT;
+
+#define SUSPEND_OPERATION(_Type, _Name, _Arguments) \
+        _Type (*SUSPEND_ ## _Name) _Arguments;
+
+typedef struct _XENBUS_SUSPEND_OPERATIONS {
+    DEFINE_SUSPEND_OPERATIONS
+} XENBUS_SUSPEND_OPERATIONS, *PXENBUS_SUSPEND_OPERATIONS;
+
+#undef SUSPEND_OPERATION
+
+typedef struct _XENBUS_SUSPEND_INTERFACE XENBUS_SUSPEND_INTERFACE, *PXENBUS_SUSPEND_INTERFACE;
+
+// 104f0a14-e2d5-42b6-b10f-a669ccd410a1
+
+DEFINE_GUID(GUID_SUSPEND_INTERFACE, 
+            0x104f0a14,
+            0xe2d5,
+            0x42b6,
+            0xb1,
+            0x0f,
+            0xa6,
+            0x69,
+            0xcc,
+            0xd4,
+            0x10,
+            0xa1);
+
+#define SUSPEND_INTERFACE_VERSION   2
+
+#define SUSPEND_OPERATIONS(_Interface) \
+        (PXENBUS_SUSPEND_OPERATIONS *)((ULONG_PTR)(_Interface))
+
+#define SUSPEND_CONTEXT(_Interface) \
+        (PXENBUS_SUSPEND_CONTEXT *)((ULONG_PTR)(_Interface) + sizeof (PVOID))
+
+#define SUSPEND(_Operation, _Interface, ...) \
+        (*SUSPEND_OPERATIONS(_Interface))->SUSPEND_ ## _Operation((*SUSPEND_CONTEXT(_Interface)), __VA_ARGS__)
+
+#endif  // _XENBUS_SUSPEND_INTERFACE_H
+
diff --git a/include/tcpip.h b/include/tcpip.h
new file mode 100644 (file)
index 0000000..0ad018d
--- /dev/null
@@ -0,0 +1,310 @@
+/* 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 "ethernet.h"
+
+#ifndef _TCPIP_H
+#define _TCPIP_H
+
+#pragma warning(push)
+#pragma warning(disable:4214) // nonstandard extension used : bit field types other than int
+#pragma warning(disable:4201) // nonstandard extension used : nameless struct/union
+
+#pragma pack(push, 1)
+
+// TCP/IP data structures
+//
+// NOTE: Fields are in network byte order
+
+// IPv4
+
+typedef struct _IPV4_ADDRESS {
+    union {
+        ULONG   Dword[1];
+        UCHAR   Byte[4];
+    };
+} IPV4_ADDRESS, *PIPV4_ADDRESS;
+
+#define IPV4_ADDRESS_LENGTH (sizeof (IPV4_ADDRESS))
+
+typedef struct _IPV4_HEADER {
+    UCHAR           HeaderLength:4;
+    UCHAR           Version:4;
+    UCHAR           TypeOfService;
+    USHORT          PacketLength;
+    USHORT          PacketID;
+    USHORT          FragmentOffsetAndFlags;
+
+#define IPV4_FRAGMENT_OFFSET(_FragmentOffsetAndFlags)   \
+        ((_FragmentOffsetAndFlags) & 0x1fff)
+#define IPV4_DONT_FRAGMENT(_FragmentOffsetAndFlags)     \
+        ((_FragmentOffsetAndFlags) & 0x4000)
+#define IPV4_MORE_FRAGMENTS(_FragmentOffsetAndFlags)    \
+        ((_FragmentOffsetAndFlags) & 0x2000)
+#define IPV4_IS_A_FRAGMENT(_FragmentOffsetAndFlags)     \
+        ((_FragmentOffsetAndFlags) & 0x3fff)
+
+    UCHAR           TimeToLive;
+    UCHAR           Protocol;
+    USHORT          Checksum;
+    IPV4_ADDRESS    SourceAddress;
+    IPV4_ADDRESS    DestinationAddress;
+} IPV4_HEADER, *PIPV4_HEADER;
+
+#define IPV4_HEADER_LENGTH(_Header) \
+        (((ULONG)((_Header)->HeaderLength)) << 2)
+
+#define MAXIMUM_IPV4_HEADER_LENGTH \
+        (0xF << 2)
+
+// IPv6
+
+typedef struct _IPV6_ADDRESS {
+    union {
+        ULONG   Dword[4];
+        USHORT  Word[8];
+        UCHAR   Byte[16];
+    };
+} IPV6_ADDRESS, *PIPV6_ADDRESS;
+
+#define IPV6_ADDRESS_LENGTH (sizeof (IPV6_ADDRESS))
+
+#define IS_IPV6_UNSPECIFIED_ADDRESS(_Address)   \
+        (((_Address)->Byte[0] == 0) &&          \
+         ((_Address)->Byte[1] == 0) &&          \
+         ((_Address)->Byte[2] == 0) &&          \
+         ((_Address)->Byte[3] == 0) &&          \
+         ((_Address)->Byte[4] == 0) &&          \
+         ((_Address)->Byte[5] == 0) &&          \
+         ((_Address)->Byte[6] == 0) &&          \
+         ((_Address)->Byte[7] == 0) &&          \
+         ((_Address)->Byte[8] == 0) &&          \
+         ((_Address)->Byte[9] == 0) &&          \
+         ((_Address)->Byte[10] == 0) &&         \
+         ((_Address)->Byte[11] == 0) &&         \
+         ((_Address)->Byte[12] == 0) &&         \
+         ((_Address)->Byte[13] == 0) &&         \
+         ((_Address)->Byte[14] == 0) &&         \
+         ((_Address)->Byte[15] == 0))
+
+typedef struct _IPV6_HEADER {
+    union {
+      struct {
+        UCHAR       __Pad:4;
+        UCHAR       Version:4;
+      };
+      ULONG         VCF;
+    };
+    USHORT          PayloadLength;
+    UCHAR           NextHeader;
+    UCHAR           HopLimit;
+    IPV6_ADDRESS    SourceAddress;
+    IPV6_ADDRESS    DestinationAddress;
+} IPV6_HEADER, *PIPV6_HEADER;
+
+#define IPV6_HEADER_LENGTH(_Header) \
+        (ULONG)(sizeof (IPV6_HEADER))
+
+#define MAXIMUM_IPV6_HEADER_LENGTH \
+        (ULONG)(sizeof (IPV6_HEADER))
+
+// IP
+
+typedef union _IP_ADDRESS {
+    IPV4_ADDRESS    Version4;
+    IPV6_ADDRESS    Version6;
+} IP_ADDRESS, *PIP_ADDRESS;
+
+typedef union _IP_HEADER {
+    struct {
+        UCHAR   __Pad:4;
+        UCHAR   Version:4;
+    };
+    IPV4_HEADER Version4;
+    IPV6_HEADER Version6;
+} IP_HEADER, *PIP_HEADER;
+  
+#define IP_HEADER_LENGTH(_Header)                   \
+        (((_Header)->Version == 4) ?                \
+        IPV4_HEADER_LENGTH(&(_Header)->Version4) :  \
+        IPV6_HEADER_LENGTH(&(_Header)->Version6))
+
+#define IPPROTO_HOP_OPTIONS 0
+#define IPPROTO_DST_OPTIONS 60
+#define IPPROTO_ROUTING     43
+#define IPPROTO_FRAGMENT    44
+
+// Options
+
+typedef struct _IPV6_OPTION_HEADER {
+    UCHAR   NextHeader;
+    UCHAR   Length;
+} IPV6_OPTION_HEADER, *PIPV6_OPTION_HEADER;
+
+#define IPV6_OPTION_HEADER_LENGTH(_Header)  \
+        (ULONG)(sizeof (IPV6_OPTION_HEADER) + (_Header)->PayloadLength)
+
+typedef struct _IPV6_FRAGMENT_HEADER {
+    UCHAR   NextHeader;
+    UCHAR   Reserved;
+    USHORT  OffsetAndFlags;
+    ULONG   ID;
+} IPV6_FRAGMENT_HEADER, *PIPV6_FRAGMENT_HEADER;
+
+#define IPV6_FRAGMENT_OFFSET(_FragmentOffsetAndFlags)   \
+        ((_FragmentOffsetAndFlags) & 0x1fff)
+#define IPV6_MORE_FRAGMENTS(_FragmentOffsetAndFlags)    \
+        ((_FragmentOffsetAndFlags) & 0x8000)
+#define IPV6_IS_A_FRAGMENT(_FragmentOffsetAndFlags)     \
+        ((_FragmentOffsetAndFlags) & 0x9fff)
+
+#define IPPROTO_TCP         6
+
+// TCP
+
+typedef struct _TCP_HEADER {
+    USHORT  SourcePort;
+    USHORT  DestinationPort;
+    ULONG   Seq;
+    ULONG   Ack;
+    UCHAR   Reserved:4;
+    UCHAR   HeaderLength:4;
+    UCHAR   Flags;
+
+#define        TCP_FIN   0x01
+#define        TCP_SYN   0x02
+#define        TCP_RST   0x04
+#define        TCP_PSH   0x08
+#define        TCP_ACK   0x10
+#define        TCP_URG   0x20
+#define        TCP_ECE   0x40
+#define        TCP_CWR   0x80
+
+    USHORT  Window;
+    USHORT  Checksum;
+    USHORT  UrgentPointer;
+} TCP_HEADER, *PTCP_HEADER;
+
+#define TCP_HEADER_LENGTH(_Header)  \
+        (((ULONG)((_Header)->HeaderLength)) << 2)
+
+#define MAXIMUM_TCP_HEADER_LENGTH \
+        (0xF << 2)
+
+#define TCPOPT_NOP          1
+#define TCPOPT_TIMESTAMP    8
+#define TCPOLEN_TIMESTAMP   10
+
+#define IPPROTO_UDP         17
+
+// UDP
+
+typedef struct _UDP_HEADER {
+    USHORT  SourcePort;
+    USHORT  DestinationPort;
+    USHORT  PacketLength;
+    USHORT  Checksum;
+} UDP_HEADER, *PUDP_HEADER;
+
+#define UDP_HEADER_LENGTH(_Header)  \
+        (ULONG)(sizeof (UDP_HEADER))
+
+#define IPPROTO_ICMPV6      58
+
+// ICMPV6
+
+typedef struct _ICMPV6_HEADER {
+    UCHAR   Type;
+    UCHAR   Code;
+    USHORT  Checksum;
+    ULONG   Data;
+} ICMPV6_HEADER, *PICMPV6_HEADER;
+
+#define ICMPV6_TYPE_RS  133
+#define ICMPV6_TYPE_RA  134
+#define ICMPV6_TYPE_NS  135
+#define ICMPV6_TYPE_NA  136
+
+#define IPPROTO_NONE        59
+
+// Checksum
+
+typedef struct _IPV4_PSEUDO_HEADER {
+    IPV4_ADDRESS    SourceAddress;
+    IPV4_ADDRESS    DestinationAddress;
+    UCHAR           Zero;
+    UCHAR           Protocol;   // TCP or UDP
+    USHORT          Length;     // Including TCP/UDP header
+} IPV4_PSEUDO_HEADER, *PIPV4_PSEUDO_HEADER;
+
+typedef struct _IPV6_PSEUDO_HEADER {
+    IPV6_ADDRESS    SourceAddress;
+    IPV6_ADDRESS    DestinationAddress;
+    USHORT          Length;     // Including TCP/UDP header
+    UCHAR           Zero[3];
+    UCHAR           NextHeader; // TCP or UDP
+} IPV6_PSEUDO_HEADER, *PIPV6_PSEUDO_HEADER;
+
+typedef union _PSEUDO_HEADER {
+    IPV4_PSEUDO_HEADER  Version4;
+    IPV6_PSEUDO_HEADER  Version6;
+} PSEUDO_HEADER, *PPSEUDO_HEADER;
+
+// ARP
+
+typedef struct _ARP_HEADER {
+    USHORT  HardwareType;
+
+#define HARDWARE_ETHER  1
+
+    USHORT  ProtocolType;
+
+#define PROTOCOL_IPV4   ETHERTYPE_IPV4
+
+    UCHAR   HardwareAddressLength;
+    UCHAR   ProtocolAddressLength;
+    USHORT  Operation;
+
+#define ARP_REQUEST     1
+#define ARP_REPLY       2
+#define RARP_REQUEST    3
+#define RARP_REPLY      4
+
+} ARP_HEADER, *PARP_HEADER;
+
+#define ARP_HEADER_LENGTH(_Header)  \
+        (ULONG)(sizeof (ARP_HEADER))
+
+#pragma pack(pop)
+
+#pragma warning(pop)
+
+#endif  //_TCPIP_H
diff --git a/include/util.h b/include/util.h
new file mode 100644 (file)
index 0000000..e3aaaef
--- /dev/null
@@ -0,0 +1,279 @@
+/* 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"
+
+#define        P2ROUNDUP(_x, _a)   \
+        (-(-(_x) & -(_a)))
+
+static FORCEINLINE LONG
+__ffs(
+    IN  unsigned long long  mask
+    )
+{
+    unsigned char           *array = (unsigned char *)&mask;
+    unsigned int            byte;
+    unsigned int            bit;
+    unsigned char           val;
+
+    val = 0;
+
+    byte = 0;
+    while (byte < 8) {
+        val = array[byte];
+
+        if (val != 0)
+            break;
+
+        byte++;
+    }
+    if (byte == 8)
+        return -1;
+
+    bit = 0;
+    while (bit < 8) {
+        if (val & 0x01)
+            break;
+
+        val >>= 1;
+        bit++;
+    }
+
+    return (byte * 8) + bit;
+}
+
+#define __ffu(_mask)  \
+        __ffs(~(_mask))
+
+static FORCEINLINE LONG
+__InterlockedAdd(
+    IN  LONG    *Value,
+    IN  LONG    Delta
+    )
+{
+    LONG        New;
+    LONG        Old;
+
+    do {
+        Old = *Value;
+        New = Old + Delta;
+    } while (InterlockedCompareExchange(Value, New, Old) != Old);
+
+    return New;
+}
+
+static FORCEINLINE LONG
+__InterlockedSubtract(
+    IN  LONG    *Value,
+    IN  LONG    Delta
+    )
+{
+    LONG        New;
+    LONG        Old;
+
+    do {
+        Old = *Value;
+        New = Old - Delta;
+    } while (InterlockedCompareExchange(Value, New, Old) != Old);
+
+    return New;
+}
+
+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  SIZE_T                  Length,
+    IN  ULONG                   Tag
+    )
+{
+    PUCHAR                      Buffer;
+    PNON_PAGED_BUFFER_HEADER    Header;
+    PNON_PAGED_BUFFER_TRAILER   Trailer;
+
+    ASSERT(Length != 0);
+
+    Buffer = ExAllocatePoolWithTag(NonPagedPool,
+                                   sizeof (NON_PAGED_BUFFER_HEADER) +
+                                   Length +
+                                   sizeof (NON_PAGED_BUFFER_TRAILER),
+                                   Tag);
+    if (Buffer == NULL)
+        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 = _Buffer;
+    SIZE_T                      Length;
+    PNON_PAGED_BUFFER_HEADER    Header;
+    PNON_PAGED_BUFFER_TRAILER   Trailer;
+
+    ASSERT(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
+__AllocatePage(
+    VOID
+    )
+{
+    PHYSICAL_ADDRESS    LowAddress;
+    PHYSICAL_ADDRESS    HighAddress;
+    LARGE_INTEGER       SkipBytes;
+    SIZE_T              TotalBytes;
+    PMDL                Mdl;
+    PUCHAR              MdlMappedSystemVa;
+    NTSTATUS            status;
+
+    LowAddress.QuadPart = 0ull;
+    HighAddress.QuadPart = ~0ull;
+    SkipBytes.QuadPart = 0ull;
+    TotalBytes = (SIZE_T)PAGE_SIZE;
+
+    Mdl = MmAllocatePagesForMdlEx(LowAddress,
+                                  HighAddress,
+                                  SkipBytes,
+                                  TotalBytes,
+                                  MmCached,
+                                  0);
+
+    status = STATUS_NO_MEMORY;
+    if (Mdl == NULL)
+        goto fail1;
+
+    ASSERT((Mdl->MdlFlags & (MDL_MAPPED_TO_SYSTEM_VA |
+                             MDL_PARTIAL_HAS_BEEN_MAPPED |
+                             MDL_PARTIAL |
+                             MDL_PARENT_MAPPED_SYSTEM_VA |
+                             MDL_SOURCE_IS_NONPAGED_POOL |
+                             MDL_IO_SPACE)) == 0);
+
+    MdlMappedSystemVa = MmMapLockedPagesSpecifyCache(Mdl,
+                                                     KernelMode,
+                                                                            MmCached,   
+                                                                            NULL,
+                                                                            FALSE,
+                                                                            NormalPagePriority);
+
+    status = STATUS_UNSUCCESSFUL;
+    if (MdlMappedSystemVa == NULL)
+        goto fail2;
+
+    ASSERT3P(MdlMappedSystemVa, ==, Mdl->MappedSystemVa);
+
+    RtlZeroMemory(MdlMappedSystemVa, PAGE_SIZE);
+
+    return Mdl;
+
+fail2:
+    Error("fail2\n");
+
+    MmFreePagesFromMdl(Mdl);
+    ExFreePool(Mdl);
+
+fail1:
+    Error("fail1 (%08x)\n", status);
+
+    return NULL;
+}
+
+static FORCEINLINE VOID
+__FreePage(
+    IN PMDL    Mdl
+    )
+{
+    PUCHAR     MdlMappedSystemVa;
+
+    ASSERT(Mdl->MdlFlags & MDL_MAPPED_TO_SYSTEM_VA);
+    MdlMappedSystemVa = Mdl->MappedSystemVa;
+
+    RtlFillMemory(MdlMappedSystemVa, PAGE_SIZE, 0xAA);
+    
+    MmUnmapLockedPages(MdlMappedSystemVa, Mdl);
+
+    MmFreePagesFromMdl(Mdl);
+}
+
+#endif  // _UTIL_H
diff --git a/include/vif_interface.h b/include/vif_interface.h
new file mode 100644 (file)
index 0000000..ae5d6b4
--- /dev/null
@@ -0,0 +1,418 @@
+/* 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 _XENVIF_VIF_INTERFACE_H
+#define _XENVIF_VIF_INTERFACE_H
+
+#include <ifdef.h>
+#include <ethernet.h>
+
+#define MAX_SKB_FRAGS   ((65536/PAGE_SIZE) + 2)
+
+typedef UCHAR   XENVIF_PACKET_STATUS, *PXENVIF_PACKET_STATUS;
+
+#define PACKET_STATUS_INVALID   0
+#define PACKET_PENDING          1
+#define PACKET_OK               2
+#define PACKET_DROPPED          3
+#define PACKET_ERROR            4
+
+typedef struct  _XENVIF_PACKET_HEADER {
+    ULONG   Offset;
+    ULONG   Length;
+} XENVIF_PACKET_HEADER, *PXENVIF_PACKET_HEADER;
+
+typedef struct _XENVIF_PACKET_INFO {
+    XENVIF_PACKET_HEADER    EthernetHeader;
+    XENVIF_PACKET_HEADER    LLCSnapHeader;
+    XENVIF_PACKET_HEADER    IpHeader;
+    XENVIF_PACKET_HEADER    IpOptions;
+    XENVIF_PACKET_HEADER    TcpHeader;
+    XENVIF_PACKET_HEADER    TcpOptions;
+    XENVIF_PACKET_HEADER    UdpHeader;
+    ULONG                   Length;
+} XENVIF_PACKET_INFO, *PXENVIF_PACKET_INFO;
+
+#pragma warning(push)
+#pragma warning(disable:4214)   // nonstandard extension used : bit field types other than int
+#pragma warning(disable:4201)   // nonstandard extension used : nameless struct/union
+
+typedef struct _XENVIF_CHECKSUM_FLAGS {
+    struct {
+        ULONG   IpChecksumSucceeded:1;
+        ULONG   IpChecksumFailed:1;
+        ULONG   IpChecksumPresent:1;
+        ULONG   TcpChecksumSucceeded:1;
+        ULONG   TcpChecksumFailed:1;
+        ULONG   TcpChecksumPresent:1;
+        ULONG   UdpChecksumSucceeded:1;
+        ULONG   UdpChecksumFailed:1;
+        ULONG   UdpChecksumPresent:1;
+        ULONG   Reserved:23;
+    };
+} XENVIF_CHECKSUM_FLAGS, *PXENVIF_CHECKSUM_FLAGS;
+
+#pragma warning(pop)
+
+typedef struct _XENVIF_RECEIVER_PACKET {
+    LIST_ENTRY              ListEntry;
+    ULONG                   Offset;
+    ULONG                   Length;
+    XENVIF_PACKET_INFO      Info;
+    XENVIF_CHECKSUM_FLAGS   Flags;
+    USHORT                  TagControlInformation;
+    USHORT                  MaximumSegmentSize;
+    PVOID                   Cookie;
+    MDL                     Mdl;
+    PFN_NUMBER              __Pfn;
+} XENVIF_RECEIVER_PACKET, *PXENVIF_RECEIVER_PACKET;
+
+typedef struct _XENVIF_RECEIVER_PACKET_STATISTICS {
+    ULONGLONG   Drop;
+    ULONGLONG   BackendError;
+    ULONGLONG   FrontendError;
+    ULONGLONG   Unicast;
+    ULONGLONG   UnicastBytes;
+    ULONGLONG   Multicast;
+    ULONGLONG   MulticastBytes;
+    ULONGLONG   Broadcast;
+    ULONGLONG   BroadcastBytes;
+} XENVIF_RECEIVER_PACKET_STATISTICS, *PXENVIF_RECEIVER_PACKET_STATISTICS;
+
+#pragma warning(push)
+#pragma warning(disable:4214)   // nonstandard extension used : bit field types other than int
+#pragma warning(disable:4201)   // nonstandard extension used : nameless struct/union
+
+typedef struct _XENVIF_OFFLOAD_OPTIONS {
+    union {
+        struct {
+            USHORT  OffloadTagManipulation:1;
+            USHORT  OffloadIpVersion4LargePacket:1;
+            USHORT  OffloadIpVersion4HeaderChecksum:1;
+            USHORT  OffloadIpVersion4TcpChecksum:1;
+            USHORT  OffloadIpVersion4UdpChecksum:1;
+            USHORT  OffloadIpVersion6LargePacket:1;
+            USHORT  OffloadIpVersion6TcpChecksum:1;
+            USHORT  OffloadIpVersion6UdpChecksum:1;
+            USHORT  NeedChecksumValue:1;
+            USHORT  NeedLargePacketSplit:1;
+            USHORT  Reserved:6;
+        };
+
+        USHORT  Value;
+    };
+} XENVIF_OFFLOAD_OPTIONS, *PXENVIF_OFFLOAD_OPTIONS;
+
+#pragma warning(pop)
+
+typedef struct _XENVIF_TRANSMITTER_PACKET   XENVIF_TRANSMITTER_PACKET, *PXENVIF_TRANSMITTER_PACKET;
+
+// To fit into the reserved space in NDIS_PACKET and NET_BUFFER structures the XENVIF_TRANSMITTER_PACKET
+// structure must be at most the size of 3 pointer types.
+
+#pragma pack(push, 1) 
+typedef struct _XENVIF_SEND_INFO {
+    XENVIF_OFFLOAD_OPTIONS  OffloadOptions;
+    USHORT                  MaximumSegmentSize;     // Only used if OffloadOptions.OffloadIpVersion[4|6}LargePacket is set
+    USHORT                  TagControlInformation;  // Only used if OffloadOptions.OffloadTagManipulation is set
+} XENVIF_SEND_INFO, *PXENVIF_SEND_INFO;
+
+typedef struct _XENVIF_COMPLETION_INFO {
+    UCHAR                   Type;
+    XENVIF_PACKET_STATUS    Status;
+    USHORT                  PacketLength;
+    USHORT                  PayloadLength;
+} XENVIF_COMPLETION_INFO, *PXENVIF_COMPLETION_INFO;
+
+#pragma warning(push)
+#pragma warning(disable:4201)   // nonstandard extension used : nameless struct/union
+
+struct _XENVIF_TRANSMITTER_PACKET {
+    PXENVIF_TRANSMITTER_PACKET  Next;
+    union {
+        XENVIF_SEND_INFO        Send;
+        XENVIF_COMPLETION_INFO  Completion;
+    };
+};
+
+#pragma warning(pop)
+
+#pragma pack(pop) 
+
+C_ASSERT(sizeof (XENVIF_TRANSMITTER_PACKET) <= (3 * sizeof (PVOID)));
+
+// Because we're so tight on space in the XENVIF_TRANSMITTER_PACKET structure, certain packet metadata
+// needs to be accessed via magic offsets
+typedef struct _XENVIF_TRANSMITTER_PACKET_METADATA {
+    LONG_PTR    OffsetOffset;
+    LONG_PTR    LengthOffset;
+    LONG_PTR    MdlOffset;
+} XENVIF_TRANSMITTER_PACKET_METADATA, *PXENVIF_TRANSMITTER_PACKET_METADATA;
+
+typedef struct _XENVIF_TRANSMITTER_PACKET_STATISTICS {
+    ULONG   Drop;
+    ULONG   BackendError;
+    ULONG   FrontendError;
+    ULONG   Unicast;
+    ULONG   UnicastBytes;
+    ULONG   Multicast;
+    ULONG   MulticastBytes;
+    ULONG   Broadcast;
+    ULONG   BroadcastBytes;
+} XENVIF_TRANSMITTER_PACKET_STATISTICS, *PXENVIF_TRANSMITTER_PACKET_STATISTICS;
+
+typedef struct _XENVIF_PACKET_STATISTICS {
+    XENVIF_RECEIVER_PACKET_STATISTICS       Receiver;
+    XENVIF_TRANSMITTER_PACKET_STATISTICS    Transmitter;
+} XENVIF_PACKET_STATISTICS, *PXENVIF_PACKET_STATISTICS;
+
+#define MAXIMUM_MULTICAST_ADDRESS_COUNT 32  // Minimum number to pass WHQL
+
+typedef enum _XENVIF_MAC_FILTER_LEVEL {
+    MAC_FILTER_NONE = 0,
+    MAC_FILTER_MATCHING = 1,
+    MAC_FILTER_ALL = 2
+} XENVIF_MAC_FILTER_LEVEL, *PXENVIF_MAC_FILTER_LEVEL;
+
+typedef struct _XENVIF_MEDIA_STATE {
+    NET_IF_MEDIA_CONNECT_STATE  MediaConnectState;
+    ULONG64                     LinkSpeed;
+    NET_IF_MEDIA_DUPLEX_STATE   MediaDuplexState;
+} XENVIF_MEDIA_STATE, *PXENVIF_MEDIA_STATE;
+
+typedef enum XENVIF_CALLBACK_TYPE {
+    XENVIF_CALLBACK_TYPE_INVALID = 0,
+    XENVIF_CALLBACK_COMPLETE_PACKETS,
+    XENVIF_CALLBACK_RECEIVE_PACKETS,
+    XENVIF_CALLBACK_MEDIA_STATE_CHANGE
+} XENVIF_CALLBACK_TYPE, *PXENVIF_CALLBACK_TYPE;
+
+#define DEFINE_VIF_OPERATIONS                                                                   \
+        VIF_OPERATION(VOID,                                                                     \
+                      Acquire,                                                                  \
+                      (                                                                         \
+                      IN  PXENVIF_VIF_CONTEXT  Context                                          \
+                      )                                                                         \
+                      )                                                                         \
+        VIF_OPERATION(VOID,                                                                     \
+                      Release,                                                                  \
+                      (                                                                         \
+                      IN  PXENVIF_VIF_CONTEXT  Context                                          \
+                      )                                                                         \
+                      )                                                                         \
+        VIF_OPERATION(NTSTATUS,                                                                 \
+                      Enable,                                                                   \
+                      (                                                                         \
+                      IN  PXENVIF_VIF_CONTEXT  Context,                                         \
+                      IN  VOID                 (*Function)(PVOID, XENVIF_CALLBACK_TYPE, ...),   \
+                      IN  PVOID                Argument OPTIONAL                                \
+                      )                                                                         \
+                      )                                                                         \
+        VIF_OPERATION(VOID,                                                                     \
+                      Disable,                                                                  \
+                      (                                                                         \
+                      IN  PXENVIF_VIF_CONTEXT  Context                                          \
+                      )                                                                         \
+                      )                                                                         \
+        VIF_OPERATION(VOID,                                                                     \
+                      QueryPacketStatistics,                                                    \
+                      (                                                                         \
+                      IN  PXENVIF_VIF_CONTEXT       Context,                                    \
+                      OUT PXENVIF_PACKET_STATISTICS Statistics                                  \
+                      )                                                                         \
+                      )                                                                         \
+        VIF_OPERATION(VOID,                                                                     \
+                      UpdatePacketMetadata,                                                     \
+                      (                                                                         \
+                      IN  PXENVIF_VIF_CONTEXT                   Context,                        \
+                      IN  PXENVIF_TRANSMITTER_PACKET_METADATA   Metadata                        \
+                      )                                                                         \
+                      )                                                                         \
+        VIF_OPERATION(VOID,                                                                     \
+                      ReturnPacket,                                                             \
+                      (                                                                         \
+                      IN  PXENVIF_VIF_CONTEXT       Context,                                    \
+                      IN  PXENVIF_RECEIVER_PACKET   Packet                                      \
+                      )                                                                         \
+                      )                                                                         \
+        VIF_OPERATION(NTSTATUS,                                                                 \
+                      QueuePackets,                                                             \
+                      (                                                                         \
+                      IN  PXENVIF_VIF_CONTEXT           Context,                                \
+                      IN  PXENVIF_TRANSMITTER_PACKET    HeadPacket                              \
+                      )                                                                         \
+                      )                                                                         \
+        VIF_OPERATION(VOID,                                                                     \
+                      QueryOffloadOptions,                                                      \
+                      (                                                                         \
+                      IN  PXENVIF_VIF_CONTEXT       Context,                                    \
+                      OUT PXENVIF_OFFLOAD_OPTIONS   Options                                     \
+                      )                                                                         \
+                      )                                                                         \
+        VIF_OPERATION(VOID,                                                                     \
+                      UpdateOffloadOptions,                                                     \
+                      (                                                                         \
+                      IN  PXENVIF_VIF_CONTEXT       Context,                                    \
+                      IN  XENVIF_OFFLOAD_OPTIONS    Options                                     \
+                      )                                                                         \
+                      )                                                                         \
+        VIF_OPERATION(VOID,                                                                     \
+                      QueryLargePacketSize,                                                     \
+                      (                                                                         \
+                      IN  PXENVIF_VIF_CONTEXT   Context,                                        \
+                      IN  UCHAR                 Version,                                        \
+                      OUT PULONG                Size                                            \
+                      )                                                                         \
+                      )                                                                         \
+        VIF_OPERATION(VOID,                                                                     \
+                      QueryMediaState,                                                          \
+                      (                                                                         \
+                      IN  PXENVIF_VIF_CONTEXT           Context,                                \
+                      OUT PNET_IF_MEDIA_CONNECT_STATE   MediaConnectState OPTIONAL,             \
+                      OUT PULONG64                      LinkSpeed OPTIONAL,                     \
+                      OUT PNET_IF_MEDIA_DUPLEX_STATE    MediaDuplexState OPTIONAL               \
+                      )                                                                         \
+                      )                                                                         \
+        VIF_OPERATION(VOID,                                                                     \
+                      QueryMaximumFrameSize,                                                    \
+                      (                                                                         \
+                      IN  PXENVIF_VIF_CONTEXT   Context,                                        \
+                      OUT PULONG                Size                                            \
+                      )                                                                         \
+                      )                                                                         \
+        VIF_OPERATION(VOID,                                                                     \
+                      QueryPermanentAddress,                                                    \
+                      (                                                                         \
+                      IN  PXENVIF_VIF_CONTEXT   Context,                                        \
+                      OUT PETHERNET_ADDRESS     Address                                         \
+                      )                                                                         \
+                      )                                                                         \
+        VIF_OPERATION(VOID,                                                                     \
+                      QueryCurrentAddress,                                                      \
+                      (                                                                         \
+                      IN  PXENVIF_VIF_CONTEXT   Context,                                        \
+                      OUT PETHERNET_ADDRESS     Address                                         \
+                      )                                                                         \
+                      )                                                                         \
+        VIF_OPERATION(NTSTATUS,                                                                 \
+                      UpdateCurrentAddress,                                                     \
+                      (                                                                         \
+                      IN  PXENVIF_VIF_CONTEXT   Context,                                        \
+                      IN  PETHERNET_ADDRESS     Address                                         \
+                      )                                                                         \
+                      )                                                                         \
+        VIF_OPERATION(NTSTATUS,                                                                 \
+                      QueryMulticastAddresses,                                                  \
+                      (                                                                         \
+                      IN  PXENVIF_VIF_CONTEXT   Context,                                        \
+                      OUT PETHERNET_ADDRESS     Address OPTIONAL,                               \
+                      OUT PULONG                Count                                           \
+                      )                                                                         \
+                      )                                                                         \
+        VIF_OPERATION(NTSTATUS,                                                                 \
+                      UpdateMulticastAddresses,                                                 \
+                      (                                                                         \
+                      IN  PXENVIF_VIF_CONTEXT   Context,                                        \
+                      IN  PETHERNET_ADDRESS     Address,                                        \
+                      IN  ULONG                 Count                                           \
+                      )                                                                         \
+                      )                                                                         \
+        VIF_OPERATION(VOID,                                                                     \
+                      QueryFilterLevel,                                                         \
+                      (                                                                         \
+                      IN  PXENVIF_VIF_CONTEXT       Context,                                    \
+                      IN  ETHERNET_ADDRESS_TYPE     Type,                                       \
+                      OUT PXENVIF_MAC_FILTER_LEVEL  Level                                       \
+                      )                                                                         \
+                      )                                                                         \
+        VIF_OPERATION(NTSTATUS,                                                                 \
+                      UpdateFilterLevel,                                                        \
+                      (                                                                         \
+                      IN  PXENVIF_VIF_CONTEXT       Context,                                    \
+                      IN  ETHERNET_ADDRESS_TYPE     Type,                                       \
+                      IN  XENVIF_MAC_FILTER_LEVEL   Level                                       \
+                      )                                                                         \
+                      )                                                                         \
+        VIF_OPERATION(VOID,                                                                     \
+                      QueryReceiverRingSize,                                                    \
+                      (                                                                         \
+                      IN  PXENVIF_VIF_CONTEXT       Context,                                    \
+                      OUT PULONG                    Size                                        \
+                      )                                                                         \
+                      )                                                                         \
+        VIF_OPERATION(VOID,                                                                     \
+                      QueryTransmitterRingSize,                                                 \
+                      (                                                                         \
+                      IN  PXENVIF_VIF_CONTEXT       Context,                                    \
+                      OUT PULONG                    Size                                        \
+                      )                                                                         \
+                      )
+
+typedef struct _XENVIF_VIF_CONTEXT  XENVIF_VIF_CONTEXT, *PXENVIF_VIF_CONTEXT;
+
+#define VIF_OPERATION(_Type, _Name, _Arguments) \
+        _Type (*VIF_ ## _Name) _Arguments;
+
+typedef struct _XENVIF_VIF_OPERATIONS {
+    DEFINE_VIF_OPERATIONS
+} XENVIF_VIF_OPERATIONS, *PXENVIF_VIF_OPERATIONS;
+
+#undef VIF_OPERATION
+
+typedef struct _XENVIF_VIF_INTERFACE XENVIF_VIF_INTERFACE, *PXENVIF_VIF_INTERFACE;
+
+// {BAA55367-D5CD-4fab-8A2D-BB40476795C3}
+DEFINE_GUID(GUID_VIF_INTERFACE, 
+            0xbaa55367,
+            0xd5cd,
+            0x4fab,
+            0x8a,
+            0x2d,
+            0xbb,
+            0x40,
+            0x47,
+            0x67,
+            0x95,
+            0xc3);
+
+#define VIF_INTERFACE_VERSION    12
+
+#define VIF_OPERATIONS(_Interface) \
+        (PXENVIF_VIF_OPERATIONS *)((ULONG_PTR)(_Interface))
+
+#define VIF_CONTEXT(_Interface) \
+        (PXENVIF_VIF_CONTEXT *)((ULONG_PTR)(_Interface) + sizeof (PVOID))
+
+#define VIF(_Operation, _Interface, ...) \
+        (*VIF_OPERATIONS(_Interface))->VIF_ ## _Operation((*VIF_CONTEXT(_Interface)), __VA_ARGS__)
+
+#endif  // _XENVIF_INTERFACE_H
+
diff --git a/include/xen-types.h b/include/xen-types.h
new file mode 100644 (file)
index 0000000..232d799
--- /dev/null
@@ -0,0 +1,55 @@
+/* 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 _XEN_TYPES_H
+#define _XEN_TYPES_H
+
+#include <ntddk.h>
+
+// Define types necessary to include xen headers
+
+typedef CHAR    int8_t;
+typedef SHORT   int16_t;
+typedef LONG    int32_t;
+typedef LONG64  int64_t;
+
+typedef UCHAR   uint8_t;
+typedef USHORT  uint16_t;
+typedef ULONG   uint32_t;
+typedef ULONG64 uint64_t;
+
+#define offsetof(_type, _field) FIELD_OFFSET(_type, _field)
+
+#define xen_mb()    KeMemoryBarrier()
+#define xen_wmb()   KeMemoryBarrier()
+#define xen_rmb()   KememoryBarrier()
+
+#endif  // _XEN_TYPES_H
diff --git a/include/xen-version.h b/include/xen-version.h
new file mode 100644 (file)
index 0000000..5da4233
--- /dev/null
@@ -0,0 +1,37 @@
+/* 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 _XEN_VERSION_H
+#define _XEN_VERSION_H
+
+#define __XEN_INTERFACE_VERSION__ __XEN_LATEST_INTERFACE_VERSION__
+
+#endif  // _XEN_VERSION_H
diff --git a/include/xen-warnings.h b/include/xen-warnings.h
new file mode 100644 (file)
index 0000000..5a095b4
--- /dev/null
@@ -0,0 +1,42 @@
+/* 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 _XEN_WARNINGS_H
+#define _XEN_WARNINGS_H
+
+#include <ntddk.h>
+
+// Disable warnings necessary to include xen headers
+
+# pragma warning(disable:4214) // nonstandard extension used : bit field types other than int
+# pragma warning(disable:4200) // nonstandard extension used : zero-sized array in struct/union
+
+#endif  // _XEN_WARNINGS_H
diff --git a/include/xen.h b/include/xen.h
new file mode 100644 (file)
index 0000000..9575285
--- /dev/null
@@ -0,0 +1,44 @@
+/* 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 _XEN_H
+#define _XEN_H
+
+#include <ntddk.h>
+
+#include <xen-version.h>
+#include <xen-types.h>
+#include <xen-warnings.h>
+#include <xen/io/ring.h>
+#include <xen/io/netif.h>
+#include <xen/io/xenbus.h>
+
+#endif  // _XEN_H
diff --git a/include/xen/arch-x86/xen-x86_32.h b/include/xen/arch-x86/xen-x86_32.h
new file mode 100644 (file)
index 0000000..5c125fb
--- /dev/null
@@ -0,0 +1,180 @@
+/******************************************************************************
+ * xen-x86_32.h
+ * 
+ * Guest OS interface to x86 32-bit Xen.
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Copyright (c) 2004-2007, K A Fraser
+ */
+
+#ifndef __XEN_PUBLIC_ARCH_X86_XEN_X86_32_H__
+#define __XEN_PUBLIC_ARCH_X86_XEN_X86_32_H__
+
+/*
+ * Hypercall interface:
+ *  Input:  %ebx, %ecx, %edx, %esi, %edi (arguments 1-5)
+ *  Output: %eax
+ * Access is via hypercall page (set up by guest loader or via a Xen MSR):
+ *  call hypercall_page + hypercall-number * 32
+ * Clobbered: Argument registers (e.g., 2-arg hypercall clobbers %ebx,%ecx)
+ */
+
+#if __XEN_INTERFACE_VERSION__ < 0x00030203
+/*
+ * Legacy hypercall interface:
+ * As above, except the entry sequence to the hypervisor is:
+ *  mov $hypercall-number*32,%eax ; int $0x82
+ */
+#define TRAP_INSTR "int $0x82"
+#endif
+
+/*
+ * These flat segments are in the Xen-private section of every GDT. Since these
+ * are also present in the initial GDT, many OSes will be able to avoid
+ * installing their own GDT.
+ */
+#define FLAT_RING1_CS 0xe019    /* GDT index 259 */
+#define FLAT_RING1_DS 0xe021    /* GDT index 260 */
+#define FLAT_RING1_SS 0xe021    /* GDT index 260 */
+#define FLAT_RING3_CS 0xe02b    /* GDT index 261 */
+#define FLAT_RING3_DS 0xe033    /* GDT index 262 */
+#define FLAT_RING3_SS 0xe033    /* GDT index 262 */
+
+#define FLAT_KERNEL_CS FLAT_RING1_CS
+#define FLAT_KERNEL_DS FLAT_RING1_DS
+#define FLAT_KERNEL_SS FLAT_RING1_SS
+#define FLAT_USER_CS    FLAT_RING3_CS
+#define FLAT_USER_DS    FLAT_RING3_DS
+#define FLAT_USER_SS    FLAT_RING3_SS
+
+#define __HYPERVISOR_VIRT_START_PAE    0xF5800000
+#define __MACH2PHYS_VIRT_START_PAE     0xF5800000
+#define __MACH2PHYS_VIRT_END_PAE       0xF6800000
+#define HYPERVISOR_VIRT_START_PAE      \
+    mk_unsigned_long(__HYPERVISOR_VIRT_START_PAE)
+#define MACH2PHYS_VIRT_START_PAE       \
+    mk_unsigned_long(__MACH2PHYS_VIRT_START_PAE)
+#define MACH2PHYS_VIRT_END_PAE         \
+    mk_unsigned_long(__MACH2PHYS_VIRT_END_PAE)
+
+/* Non-PAE bounds are obsolete. */
+#define __HYPERVISOR_VIRT_START_NONPAE 0xFC000000
+#define __MACH2PHYS_VIRT_START_NONPAE  0xFC000000
+#define __MACH2PHYS_VIRT_END_NONPAE    0xFC400000
+#define HYPERVISOR_VIRT_START_NONPAE   \
+    mk_unsigned_long(__HYPERVISOR_VIRT_START_NONPAE)
+#define MACH2PHYS_VIRT_START_NONPAE    \
+    mk_unsigned_long(__MACH2PHYS_VIRT_START_NONPAE)
+#define MACH2PHYS_VIRT_END_NONPAE      \
+    mk_unsigned_long(__MACH2PHYS_VIRT_END_NONPAE)
+
+#define __HYPERVISOR_VIRT_START __HYPERVISOR_VIRT_START_PAE
+#define __MACH2PHYS_VIRT_START  __MACH2PHYS_VIRT_START_PAE
+#define __MACH2PHYS_VIRT_END    __MACH2PHYS_VIRT_END_PAE
+
+#ifndef HYPERVISOR_VIRT_START
+#define HYPERVISOR_VIRT_START mk_unsigned_long(__HYPERVISOR_VIRT_START)
+#endif
+
+#define MACH2PHYS_VIRT_START  mk_unsigned_long(__MACH2PHYS_VIRT_START)
+#define MACH2PHYS_VIRT_END    mk_unsigned_long(__MACH2PHYS_VIRT_END)
+#define MACH2PHYS_NR_ENTRIES  ((MACH2PHYS_VIRT_END-MACH2PHYS_VIRT_START)>>2)
+#ifndef machine_to_phys_mapping
+#define machine_to_phys_mapping ((ULONG_PTR *)MACH2PHYS_VIRT_START)
+#endif
+
+/* 32-/64-bit invariability for control interfaces (domctl/sysctl). */
+#if defined(__XEN__) || defined(__XEN_TOOLS__)
+#undef ___DEFINE_XEN_GUEST_HANDLE
+#define ___DEFINE_XEN_GUEST_HANDLE(name, type)                  \
+    typedef struct { type *p; }                                 \
+        __guest_handle_ ## name;                                \
+    typedef struct { union { type *p; uint64_aligned_t q; }; }  \
+        __guest_handle_64_ ## name
+#undef set_xen_guest_handle_raw
+#define set_xen_guest_handle_raw(hnd, val)                  \
+    do { if ( sizeof(hnd) == 8 ) *(uint64_t *)&(hnd) = 0;   \
+         (hnd).p = val;                                     \
+    } while ( 0 )
+#define uint64_aligned_t uint64_t __attribute__((aligned(8)))
+#define __XEN_GUEST_HANDLE_64(name) __guest_handle_64_ ## name
+#define XEN_GUEST_HANDLE_64(name) __XEN_GUEST_HANDLE_64(name)
+#endif
+
+#ifndef __ASSEMBLY__
+
+struct cpu_user_regs {
+    uint32_t ebx;
+    uint32_t ecx;
+    uint32_t edx;
+    uint32_t esi;
+    uint32_t edi;
+    uint32_t ebp;
+    uint32_t eax;
+    uint16_t error_code;    /* private */
+    uint16_t entry_vector;  /* private */
+    uint32_t eip;
+    uint16_t cs;
+    uint8_t  saved_upcall_mask;
+    uint8_t  _pad0;
+    uint32_t eflags;        /* eflags.IF == !saved_upcall_mask */
+    uint32_t esp;
+    uint16_t ss, _pad1;
+    uint16_t es, _pad2;
+    uint16_t ds, _pad3;
+    uint16_t fs, _pad4;
+    uint16_t gs, _pad5;
+};
+typedef struct cpu_user_regs cpu_user_regs_t;
+DEFINE_XEN_GUEST_HANDLE(cpu_user_regs_t);
+
+/*
+ * Page-directory addresses above 4GB do not fit into architectural %cr3.
+ * When accessing %cr3, or equivalent field in vcpu_guest_context, guests
+ * must use the following accessor macros to pack/unpack valid MFNs.
+ */
+#define xen_pfn_to_cr3(pfn) (((unsigned)(pfn) << 12) | ((unsigned)(pfn) >> 20))
+#define xen_cr3_to_pfn(cr3) (((unsigned)(cr3) >> 12) | ((unsigned)(cr3) << 20))
+
+struct arch_vcpu_info {
+    ULONG_PTR cr2;
+    ULONG_PTR pad[5]; /* sizeof(vcpu_info_t) == 64 */
+};
+typedef struct arch_vcpu_info arch_vcpu_info_t;
+
+struct xen_callback {
+    ULONG_PTR cs;
+    ULONG_PTR eip;
+};
+typedef struct xen_callback xen_callback_t;
+
+#endif /* !__ASSEMBLY__ */
+
+#endif /* __XEN_PUBLIC_ARCH_X86_XEN_X86_32_H__ */
+
+/*
+ * Local variables:
+ * mode: C
+ * c-set-style: "BSD"
+ * c-basic-offset: 4
+ * tab-width: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
diff --git a/include/xen/arch-x86/xen-x86_64.h b/include/xen/arch-x86/xen-x86_64.h
new file mode 100644 (file)
index 0000000..1821e3a
--- /dev/null
@@ -0,0 +1,212 @@
+/******************************************************************************
+ * xen-x86_64.h
+ * 
+ * Guest OS interface to x86 64-bit Xen.
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Copyright (c) 2004-2006, K A Fraser
+ */
+
+#ifndef __XEN_PUBLIC_ARCH_X86_XEN_X86_64_H__
+#define __XEN_PUBLIC_ARCH_X86_XEN_X86_64_H__
+
+/*
+ * Hypercall interface:
+ *  Input:  %rdi, %rsi, %rdx, %r10, %r8 (arguments 1-5)
+ *  Output: %rax
+ * Access is via hypercall page (set up by guest loader or via a Xen MSR):
+ *  call hypercall_page + hypercall-number * 32
+ * Clobbered: argument registers (e.g., 2-arg hypercall clobbers %rdi,%rsi)
+ */
+
+#if __XEN_INTERFACE_VERSION__ < 0x00030203
+/*
+ * Legacy hypercall interface:
+ * As above, except the entry sequence to the hypervisor is:
+ *  mov $hypercall-number*32,%eax ; syscall
+ * Clobbered: %rcx, %r11, argument registers (as above)
+ */
+#define TRAP_INSTR "syscall"
+#endif
+
+/*
+ * 64-bit segment selectors
+ * These flat segments are in the Xen-private section of every GDT. Since these
+ * are also present in the initial GDT, many OSes will be able to avoid
+ * installing their own GDT.
+ */
+
+#define FLAT_RING3_CS32 0xe023  /* GDT index 260 */
+#define FLAT_RING3_CS64 0xe033  /* GDT index 261 */
+#define FLAT_RING3_DS32 0xe02b  /* GDT index 262 */
+#define FLAT_RING3_DS64 0x0000  /* NULL selector */
+#define FLAT_RING3_SS32 0xe02b  /* GDT index 262 */
+#define FLAT_RING3_SS64 0xe02b  /* GDT index 262 */
+
+#define FLAT_KERNEL_DS64 FLAT_RING3_DS64
+#define FLAT_KERNEL_DS32 FLAT_RING3_DS32
+#define FLAT_KERNEL_DS   FLAT_KERNEL_DS64
+#define FLAT_KERNEL_CS64 FLAT_RING3_CS64
+#define FLAT_KERNEL_CS32 FLAT_RING3_CS32
+#define FLAT_KERNEL_CS   FLAT_KERNEL_CS64
+#define FLAT_KERNEL_SS64 FLAT_RING3_SS64
+#define FLAT_KERNEL_SS32 FLAT_RING3_SS32
+#define FLAT_KERNEL_SS   FLAT_KERNEL_SS64
+
+#define FLAT_USER_DS64 FLAT_RING3_DS64
+#define FLAT_USER_DS32 FLAT_RING3_DS32
+#define FLAT_USER_DS   FLAT_USER_DS64
+#define FLAT_USER_CS64 FLAT_RING3_CS64
+#define FLAT_USER_CS32 FLAT_RING3_CS32
+#define FLAT_USER_CS   FLAT_USER_CS64
+#define FLAT_USER_SS64 FLAT_RING3_SS64
+#define FLAT_USER_SS32 FLAT_RING3_SS32
+#define FLAT_USER_SS   FLAT_USER_SS64
+
+#define __HYPERVISOR_VIRT_START 0xFFFF800000000000
+#define __HYPERVISOR_VIRT_END   0xFFFF880000000000
+#define __MACH2PHYS_VIRT_START  0xFFFF800000000000
+#define __MACH2PHYS_VIRT_END    0xFFFF804000000000
+
+#ifndef HYPERVISOR_VIRT_START
+#define HYPERVISOR_VIRT_START mk_unsigned_long(__HYPERVISOR_VIRT_START)
+#define HYPERVISOR_VIRT_END   mk_unsigned_long(__HYPERVISOR_VIRT_END)
+#endif
+
+#define MACH2PHYS_VIRT_START  mk_unsigned_long(__MACH2PHYS_VIRT_START)
+#define MACH2PHYS_VIRT_END    mk_unsigned_long(__MACH2PHYS_VIRT_END)
+#define MACH2PHYS_NR_ENTRIES  ((MACH2PHYS_VIRT_END-MACH2PHYS_VIRT_START)>>3)
+#ifndef machine_to_phys_mapping
+#define machine_to_phys_mapping ((ULONG_PTR *)HYPERVISOR_VIRT_START)
+#endif
+
+/*
+ * int HYPERVISOR_set_segment_base(unsigned int which, ULONG_PTR base)
+ *  @which == SEGBASE_*  ;  @base == 64-bit base address
+ * Returns 0 on success.
+ */
+#define SEGBASE_FS          0
+#define SEGBASE_GS_USER     1
+#define SEGBASE_GS_KERNEL   2
+#define SEGBASE_GS_USER_SEL 3 /* Set user %gs specified in base[15:0] */
+
+/*
+ * int HYPERVISOR_iret(void)
+ * All arguments are on the kernel stack, in the following format.
+ * Never returns if successful. Current kernel context is lost.
+ * The saved CS is mapped as follows:
+ *   RING0 -> RING3 kernel mode.
+ *   RING1 -> RING3 kernel mode.
+ *   RING2 -> RING3 kernel mode.
+ *   RING3 -> RING3 user mode.
+ * However RING0 indicates that the guest kernel should return to iteself
+ * directly with
+ *      orb   $3,1*8(%rsp)
+ *      iretq
+ * If flags contains VGCF_in_syscall:
+ *   Restore RAX, RIP, RFLAGS, RSP.
+ *   Discard R11, RCX, CS, SS.
+ * Otherwise:
+ *   Restore RAX, R11, RCX, CS:RIP, RFLAGS, SS:RSP.
+ * All other registers are saved on hypercall entry and restored to user.
+ */
+/* Guest exited in SYSCALL context? Return to guest with SYSRET? */
+#define _VGCF_in_syscall 8
+#define VGCF_in_syscall  (1<<_VGCF_in_syscall)
+#define VGCF_IN_SYSCALL  VGCF_in_syscall
+
+#ifndef __ASSEMBLY__
+
+struct iret_context {
+    /* Top of stack (%rsp at point of hypercall). */
+    uint64_t rax, r11, rcx, flags, rip, cs, rflags, rsp, ss;
+    /* Bottom of iret stack frame. */
+};
+
+#if defined(__GNUC__) && !defined(__STRICT_ANSI__)
+/* Anonymous union includes both 32- and 64-bit names (e.g., eax/rax). */
+#define __DECL_REG(name) union { \
+    uint64_t r ## name, e ## name; \
+    uint32_t _e ## name; \
+}
+#else
+/* Non-gcc sources must always use the proper 64-bit name (e.g., rax). */
+#define __DECL_REG(name) uint64_t r ## name
+#endif
+
+struct cpu_user_regs {
+    uint64_t r15;
+    uint64_t r14;
+    uint64_t r13;
+    uint64_t r12;
+    __DECL_REG(bp);
+    __DECL_REG(bx);
+    uint64_t r11;
+    uint64_t r10;
+    uint64_t r9;
+    uint64_t r8;
+    __DECL_REG(ax);
+    __DECL_REG(cx);
+    __DECL_REG(dx);
+    __DECL_REG(si);
+    __DECL_REG(di);
+    uint32_t error_code;    /* private */
+    uint32_t entry_vector;  /* private */
+    __DECL_REG(ip);
+    uint16_t cs, _pad0[1];
+    uint8_t  saved_upcall_mask;
+    uint8_t  _pad1[3];
+    __DECL_REG(flags);      /* rflags.IF == !saved_upcall_mask */
+    __DECL_REG(sp);
+    uint16_t ss, _pad2[3];
+    uint16_t es, _pad3[3];
+    uint16_t ds, _pad4[3];
+    uint16_t fs, _pad5[3]; /* Non-zero => takes precedence over fs_base.     */
+    uint16_t gs, _pad6[3]; /* Non-zero => takes precedence over gs_base_usr. */
+};
+typedef struct cpu_user_regs cpu_user_regs_t;
+DEFINE_XEN_GUEST_HANDLE(cpu_user_regs_t);
+
+#undef __DECL_REG
+
+#define xen_pfn_to_cr3(pfn) ((ULONG_PTR)(pfn) << 12)
+#define xen_cr3_to_pfn(cr3) ((ULONG_PTR)(cr3) >> 12)
+
+struct arch_vcpu_info {
+    ULONG_PTR cr2;
+    ULONG_PTR pad; /* sizeof(vcpu_info_t) == 64 */
+};
+typedef struct arch_vcpu_info arch_vcpu_info_t;
+
+typedef ULONG_PTR xen_callback_t;
+
+#endif /* !__ASSEMBLY__ */
+
+#endif /* __XEN_PUBLIC_ARCH_X86_XEN_X86_64_H__ */
+
+/*
+ * Local variables:
+ * mode: C
+ * c-set-style: "BSD"
+ * c-basic-offset: 4
+ * tab-width: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
diff --git a/include/xen/arch-x86/xen.h b/include/xen/arch-x86/xen.h
new file mode 100644 (file)
index 0000000..72eb853
--- /dev/null
@@ -0,0 +1,201 @@
+/******************************************************************************
+ * arch-x86/xen.h
+ * 
+ * Guest OS interface to x86 Xen.
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Copyright (c) 2004-2006, K A Fraser
+ */
+
+#include "../xen.h"
+
+#ifndef __XEN_PUBLIC_ARCH_X86_XEN_H__
+#define __XEN_PUBLIC_ARCH_X86_XEN_H__
+
+/* Structural guest handles introduced in 0x00030201. */
+#if __XEN_INTERFACE_VERSION__ >= 0x00030201
+#define ___DEFINE_XEN_GUEST_HANDLE(name, type) \
+    typedef struct { type *p; } __guest_handle_ ## name
+#else
+#define ___DEFINE_XEN_GUEST_HANDLE(name, type) \
+    typedef type * __guest_handle_ ## name
+#endif
+
+#define __DEFINE_XEN_GUEST_HANDLE(name, type) \
+    ___DEFINE_XEN_GUEST_HANDLE(name, type);   \
+    ___DEFINE_XEN_GUEST_HANDLE(const_##name, const type)
+#define DEFINE_XEN_GUEST_HANDLE(name)   __DEFINE_XEN_GUEST_HANDLE(name, name)
+#define __XEN_GUEST_HANDLE(name)        __guest_handle_ ## name
+#define XEN_GUEST_HANDLE(name)          __XEN_GUEST_HANDLE(name)
+#define set_xen_guest_handle_raw(hnd, val)  do { (hnd).p = val; } while (0)
+#ifdef __XEN_TOOLS__
+#define get_xen_guest_handle(val, hnd)  do { val = (hnd).p; } while (0)
+#endif
+#define set_xen_guest_handle(hnd, val) set_xen_guest_handle_raw(hnd, val)
+
+#if defined(__i386__)
+#include "xen-x86_32.h"
+#elif defined(__x86_64__)
+#include "xen-x86_64.h"
+#endif
+
+#ifndef __ASSEMBLY__
+typedef ULONG_PTR xen_pfn_t;
+#define PRI_xen_pfn "lx"
+#endif
+
+/*
+ * SEGMENT DESCRIPTOR TABLES
+ */
+/*
+ * A number of GDT entries are reserved by Xen. These are not situated at the
+ * start of the GDT because some stupid OSes export hard-coded selector values
+ * in their ABI. These hard-coded values are always near the start of the GDT,
+ * so Xen places itself out of the way, at the far end of the GDT.
+ */
+#define FIRST_RESERVED_GDT_PAGE  14
+#define FIRST_RESERVED_GDT_BYTE  (FIRST_RESERVED_GDT_PAGE * 4096)
+#define FIRST_RESERVED_GDT_ENTRY (FIRST_RESERVED_GDT_BYTE / 8)
+
+/* Maximum number of virtual CPUs in legacy multi-processor guests. */
+#define XEN_LEGACY_MAX_VCPUS 32
+
+#ifndef __ASSEMBLY__
+
+typedef ULONG_PTR xen_ulong_t;
+
+/*
+ * Send an array of these to HYPERVISOR_set_trap_table().
+ * The privilege level specifies which modes may enter a trap via a software
+ * interrupt. On x86/64, since rings 1 and 2 are unavailable, we allocate
+ * privilege levels as follows:
+ *  Level == 0: Noone may enter
+ *  Level == 1: Kernel may enter
+ *  Level == 2: Kernel may enter
+ *  Level == 3: Everyone may enter
+ */
+#define TI_GET_DPL(_ti)      ((_ti)->flags & 3)
+#define TI_GET_IF(_ti)       ((_ti)->flags & 4)
+#define TI_SET_DPL(_ti,_dpl) ((_ti)->flags |= (_dpl))
+#define TI_SET_IF(_ti,_if)   ((_ti)->flags |= ((!!(_if))<<2))
+struct trap_info {
+    uint8_t       vector;  /* exception vector                              */
+    uint8_t       flags;   /* 0-3: privilege level; 4: clear event enable?  */
+    uint16_t      cs;      /* code selector                                 */
+    ULONG_PTR address; /* code offset                                   */
+};
+typedef struct trap_info trap_info_t;
+DEFINE_XEN_GUEST_HANDLE(trap_info_t);
+
+typedef uint64_t tsc_timestamp_t; /* RDTSC timestamp */
+
+/*
+ * The following is all CPU context. Note that the fpu_ctxt block is filled 
+ * in by FXSAVE if the CPU has feature FXSR; otherwise FSAVE is used.
+ */
+struct vcpu_guest_context {
+    /* FPU registers come first so they can be aligned for FXSAVE/FXRSTOR. */
+    struct { char x[512]; } fpu_ctxt;       /* User-level FPU registers     */
+#define VGCF_I387_VALID                (1<<0)
+#define VGCF_IN_KERNEL                 (1<<2)
+#define _VGCF_i387_valid               0
+#define VGCF_i387_valid                (1<<_VGCF_i387_valid)
+#define _VGCF_in_kernel                2
+#define VGCF_in_kernel                 (1<<_VGCF_in_kernel)
+#define _VGCF_failsafe_disables_events 3
+#define VGCF_failsafe_disables_events  (1<<_VGCF_failsafe_disables_events)
+#define _VGCF_syscall_disables_events  4
+#define VGCF_syscall_disables_events   (1<<_VGCF_syscall_disables_events)
+#define _VGCF_online                   5
+#define VGCF_online                    (1<<_VGCF_online)
+    ULONG_PTR flags;                    /* VGCF_* flags                 */
+    struct cpu_user_regs user_regs;         /* User-level CPU registers     */
+    struct trap_info trap_ctxt[256];        /* Virtual IDT                  */
+    ULONG_PTR ldt_base, ldt_ents;       /* LDT (linear address, # ents) */
+    ULONG_PTR gdt_frames[16], gdt_ents; /* GDT (machine frames, # ents) */
+    ULONG_PTR kernel_ss, kernel_sp;     /* Virtual TSS (only SS1/SP1)   */
+    /* NB. User pagetable on x86/64 is placed in ctrlreg[1]. */
+    ULONG_PTR ctrlreg[8];               /* CR0-CR7 (control registers)  */
+    ULONG_PTR debugreg[8];              /* DB0-DB7 (debug registers)    */
+#ifdef __i386__
+    ULONG_PTR event_callback_cs;        /* CS:EIP of event callback     */
+    ULONG_PTR event_callback_eip;
+    ULONG_PTR failsafe_callback_cs;     /* CS:EIP of failsafe callback  */
+    ULONG_PTR failsafe_callback_eip;
+#else
+    ULONG_PTR event_callback_eip;
+    ULONG_PTR failsafe_callback_eip;
+#ifdef __XEN__
+    union {
+        ULONG_PTR syscall_callback_eip;
+        struct {
+            unsigned int event_callback_cs;    /* compat CS of event cb     */
+            unsigned int failsafe_callback_cs; /* compat CS of failsafe cb  */
+        };
+    };
+#else
+    ULONG_PTR syscall_callback_eip;
+#endif
+#endif
+    ULONG_PTR vm_assist;                /* VMASST_TYPE_* bitmap */
+#ifdef __x86_64__
+    /* Segment base addresses. */
+    uint64_t      fs_base;
+    uint64_t      gs_base_kernel;
+    uint64_t      gs_base_user;
+#endif
+};
+typedef struct vcpu_guest_context vcpu_guest_context_t;
+DEFINE_XEN_GUEST_HANDLE(vcpu_guest_context_t);
+
+struct arch_shared_info {
+    ULONG_PTR max_pfn;                  /* max pfn that appears in table */
+    /* Frame containing list of mfns containing list of mfns containing p2m. */
+    xen_pfn_t     pfn_to_mfn_frame_list_list;
+    ULONG_PTR nmi_reason;
+    uint64_t pad[32];
+};
+typedef struct arch_shared_info arch_shared_info_t;
+
+#endif /* !__ASSEMBLY__ */
+
+/*
+ * Prefix forces emulation of some non-trapping instructions.
+ * Currently only CPUID.
+ */
+#ifdef __ASSEMBLY__
+#define XEN_EMULATE_PREFIX .byte 0x0f,0x0b,0x78,0x65,0x6e ;
+#define XEN_CPUID          XEN_EMULATE_PREFIX cpuid
+#else
+#define XEN_EMULATE_PREFIX ".byte 0x0f,0x0b,0x78,0x65,0x6e ; "
+#define XEN_CPUID          XEN_EMULATE_PREFIX "cpuid"
+#endif
+
+#endif /* __XEN_PUBLIC_ARCH_X86_XEN_H__ */
+
+/*
+ * Local variables:
+ * mode: C
+ * c-set-style: "BSD"
+ * c-basic-offset: 4
+ * tab-width: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
diff --git a/include/xen/grant_table.h b/include/xen/grant_table.h
new file mode 100644 (file)
index 0000000..c003d5c
--- /dev/null
@@ -0,0 +1,597 @@
+/******************************************************************************
+ * grant_table.h
+ * 
+ * Interface for granting foreign access to page frames, and receiving
+ * page-ownership transfers.
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Copyright (c) 2004, K A Fraser
+ */
+
+#ifndef __XEN_PUBLIC_GRANT_TABLE_H__
+#define __XEN_PUBLIC_GRANT_TABLE_H__
+
+#include "xen.h"
+
+/***********************************
+ * GRANT TABLE REPRESENTATION
+ */
+
+/* Some rough guidelines on accessing and updating grant-table entries
+ * in a concurrency-safe manner. For more information, Linux contains a
+ * reference implementation for guest OSes (arch/xen/kernel/grant_table.c).
+ * 
+ * NB. WMB is a no-op on current-generation x86 processors. However, a
+ *     compiler barrier will still be required.
+ * 
+ * Introducing a valid entry into the grant table:
+ *  1. Write ent->domid.
+ *  2. Write ent->frame:
+ *      GTF_permit_access:   Frame to which access is permitted.
+ *      GTF_accept_transfer: Pseudo-phys frame slot being filled by new
+ *                           frame, or zero if none.
+ *  3. Write memory barrier (WMB).
+ *  4. Write ent->flags, inc. valid type.
+ * 
+ * Invalidating an unused GTF_permit_access entry:
+ *  1. flags = ent->flags.
+ *  2. Observe that !(flags & (GTF_reading|GTF_writing)).
+ *  3. Check result of SMP-safe CMPXCHG(&ent->flags, flags, 0).
+ *  NB. No need for WMB as reuse of entry is control-dependent on success of
+ *      step 3, and all architectures guarantee ordering of ctrl-dep writes.
+ *
+ * Invalidating an in-use GTF_permit_access entry:
+ *  This cannot be done directly. Request assistance from the domain controller
+ *  which can set a timeout on the use of a grant entry and take necessary
+ *  action. (NB. This is not yet implemented!).
+ * 
+ * Invalidating an unused GTF_accept_transfer entry:
+ *  1. flags = ent->flags.
+ *  2. Observe that !(flags & GTF_transfer_committed). [*]
+ *  3. Check result of SMP-safe CMPXCHG(&ent->flags, flags, 0).
+ *  NB. No need for WMB as reuse of entry is control-dependent on success of
+ *      step 3, and all architectures guarantee ordering of ctrl-dep writes.
+ *  [*] If GTF_transfer_committed is set then the grant entry is 'committed'.
+ *      The guest must /not/ modify the grant entry until the address of the
+ *      transferred frame is written. It is safe for the guest to spin waiting
+ *      for this to occur (detect by observing GTF_transfer_completed in
+ *      ent->flags).
+ *
+ * Invalidating a committed GTF_accept_transfer entry:
+ *  1. Wait for (ent->flags & GTF_transfer_completed).
+ *
+ * Changing a GTF_permit_access from writable to read-only:
+ *  Use SMP-safe CMPXCHG to set GTF_readonly, while checking !GTF_writing.
+ * 
+ * Changing a GTF_permit_access from read-only to writable:
+ *  Use SMP-safe bit-setting instruction.
+ */
+
+/*
+ * Reference to a grant entry in a specified domain's grant table.
+ */
+typedef uint32_t grant_ref_t;
+
+/*
+ * A grant table comprises a packed array of grant entries in one or more
+ * page frames shared between Xen and a guest.
+ * [XEN]: This field is written by Xen and read by the sharing guest.
+ * [GST]: This field is written by the guest and read by Xen.
+ */
+
+/*
+ * Version 1 of the grant table entry structure is maintained purely
+ * for backwards compatibility.  New guests should use version 2.
+ */
+#if __XEN_INTERFACE_VERSION__ < 0x0003020a
+#define grant_entry_v1 grant_entry
+#define grant_entry_v1_t grant_entry_t
+#endif
+struct grant_entry_v1 {
+    /* GTF_xxx: various type and flag information.  [XEN,GST] */
+    uint16_t flags;
+    /* The domain being granted foreign privileges. [GST] */
+    domid_t  domid;
+    /*
+     * GTF_permit_access: Frame that @domid is allowed to map and access. [GST]
+     * GTF_accept_transfer: Frame whose ownership transferred by @domid. [XEN]
+     */
+    uint32_t frame;
+};
+typedef struct grant_entry_v1 grant_entry_v1_t;
+
+/*
+ * Type of grant entry.
+ *  GTF_invalid: This grant entry grants no privileges.
+ *  GTF_permit_access: Allow @domid to map/access @frame.
+ *  GTF_accept_transfer: Allow @domid to transfer ownership of one page frame
+ *                       to this guest. Xen writes the page number to @frame.
+ *  GTF_transitive: Allow @domid to transitively access a subrange of
+ *                  @trans_grant in @trans_domid.  No mappings are allowed.
+ */
+#define GTF_invalid         (0U<<0)
+#define GTF_permit_access   (1U<<0)
+#define GTF_accept_transfer (2U<<0)
+#define GTF_transitive      (3U<<0)
+#define GTF_type_mask       (3U<<0)
+
+/*
+ * Subflags for GTF_permit_access.
+ *  GTF_readonly: Restrict @domid to read-only mappings and accesses. [GST]
+ *  GTF_reading: Grant entry is currently mapped for reading by @domid. [XEN]
+ *  GTF_writing: Grant entry is currently mapped for writing by @domid. [XEN]
+ *  GTF_PAT, GTF_PWT, GTF_PCD: (x86) cache attribute flags for the grant [GST]
+ *  GTF_sub_page: Grant access to only a subrange of the page.  @domid
+ *                will only be allowed to copy from the grant, and not
+ *                map it. [GST]
+ */
+#define _GTF_readonly       (2)
+#define GTF_readonly        (1U<<_GTF_readonly)
+#define _GTF_reading        (3)
+#define GTF_reading         (1U<<_GTF_reading)
+#define _GTF_writing        (4)
+#define GTF_writing         (1U<<_GTF_writing)
+#define _GTF_PWT            (5)
+#define GTF_PWT             (1U<<_GTF_PWT)
+#define _GTF_PCD            (6)
+#define GTF_PCD             (1U<<_GTF_PCD)
+#define _GTF_PAT            (7)
+#define GTF_PAT             (1U<<_GTF_PAT)
+#define _GTF_sub_page       (8)
+#define GTF_sub_page        (1U<<_GTF_sub_page)
+
+/*
+ * Subflags for GTF_accept_transfer:
+ *  GTF_transfer_committed: Xen sets this flag to indicate that it is committed
+ *      to transferring ownership of a page frame. When a guest sees this flag
+ *      it must /not/ modify the grant entry until GTF_transfer_completed is
+ *      set by Xen.
+ *  GTF_transfer_completed: It is safe for the guest to spin-wait on this flag
+ *      after reading GTF_transfer_committed. Xen will always write the frame
+ *      address, followed by ORing this flag, in a timely manner.
+ */
+#define _GTF_transfer_committed (2)
+#define GTF_transfer_committed  (1U<<_GTF_transfer_committed)
+#define _GTF_transfer_completed (3)
+#define GTF_transfer_completed  (1U<<_GTF_transfer_completed)
+
+/*
+ * Version 2 grant table entries.  These fulfil the same role as
+ * version 1 entries, but can represent more complicated operations.
+ * Any given domain will have either a version 1 or a version 2 table,
+ * and every entry in the table will be the same version.
+ *
+ * The interface by which domains use grant references does not depend
+ * on the grant table version in use by the other domain.
+ */
+#if __XEN_INTERFACE_VERSION__ >= 0x0003020a
+/*
+ * Version 1 and version 2 grant entries share a common prefix.  The
+ * fields of the prefix are documented as part of struct
+ * grant_entry_v1.
+ */
+struct grant_entry_header {
+    uint16_t flags;
+    domid_t  domid;
+};
+typedef struct grant_entry_header grant_entry_header_t;
+
+/*
+ * Version 2 of the grant entry structure.
+ */
+union grant_entry_v2 {
+    grant_entry_header_t hdr;
+
+    /*
+     * This member is used for V1-style full page grants, where either:
+     *
+     * -- hdr.type is GTF_accept_transfer, or
+     * -- hdr.type is GTF_permit_access and GTF_sub_page is not set.
+     *
+     * In that case, the frame field has the same semantics as the
+     * field of the same name in the V1 entry structure.
+     */
+    struct {
+        grant_entry_header_t hdr;
+        uint32_t pad0;
+        uint64_t frame;
+    } full_page;
+
+    /*
+     * If the grant type is GTF_grant_access and GTF_sub_page is set,
+     * @domid is allowed to access bytes [@page_off,@page_off+@length)
+     * in frame @frame.
+     */
+    struct {
+        grant_entry_header_t hdr;
+        uint16_t page_off;
+        uint16_t length;
+        uint64_t frame;
+    } sub_page;
+
+    /*
+     * If the grant is GTF_transitive, @domid is allowed to use the
+     * grant @gref in domain @trans_domid, as if it was the local
+     * domain.  Obviously, the transitive access must be compatible
+     * with the original grant.
+     *
+     * The current version of Xen does not allow transitive grants
+     * to be mapped.
+     */
+    struct {
+        grant_entry_header_t hdr;
+        domid_t trans_domid;
+        uint16_t pad0;
+        grant_ref_t gref;
+    } transitive;
+
+    uint32_t __spacer[4]; /* Pad to a power of two */
+};
+typedef union grant_entry_v2 grant_entry_v2_t;
+
+typedef uint16_t grant_status_t;
+
+#endif /* __XEN_INTERFACE_VERSION__ */
+
+/***********************************
+ * GRANT TABLE QUERIES AND USES
+ */
+
+/*
+ * Handle to track a mapping created via a grant reference.
+ */
+typedef uint32_t grant_handle_t;
+
+/*
+ * GNTTABOP_map_grant_ref: Map the grant entry (<dom>,<ref>) for access
+ * by devices and/or host CPUs. If successful, <handle> is a tracking number
+ * that must be presented later to destroy the mapping(s). On error, <handle>
+ * is a negative status code.
+ * NOTES:
+ *  1. If GNTMAP_device_map is specified then <dev_bus_addr> is the address
+ *     via which I/O devices may access the granted frame.
+ *  2. If GNTMAP_host_map is specified then a mapping will be added at
+ *     either a host virtual address in the current address space, or at
+ *     a PTE at the specified machine address.  The type of mapping to
+ *     perform is selected through the GNTMAP_contains_pte flag, and the 
+ *     address is specified in <host_addr>.
+ *  3. Mappings should only be destroyed via GNTTABOP_unmap_grant_ref. If a
+ *     host mapping is destroyed by other means then it is *NOT* guaranteed
+ *     to be accounted to the correct grant reference!
+ */
+#define GNTTABOP_map_grant_ref        0
+struct gnttab_map_grant_ref {
+    /* IN parameters. */
+    uint64_t host_addr;
+    uint32_t flags;               /* GNTMAP_* */
+    grant_ref_t ref;
+    domid_t  dom;
+    /* OUT parameters. */
+    int16_t  status;              /* GNTST_* */
+    grant_handle_t handle;
+    uint64_t dev_bus_addr;
+};
+typedef struct gnttab_map_grant_ref gnttab_map_grant_ref_t;
+DEFINE_XEN_GUEST_HANDLE(gnttab_map_grant_ref_t);
+
+/*
+ * GNTTABOP_unmap_grant_ref: Destroy one or more grant-reference mappings
+ * tracked by <handle>. If <host_addr> or <dev_bus_addr> is zero, that
+ * field is ignored. If non-zero, they must refer to a device/host mapping
+ * that is tracked by <handle>
+ * NOTES:
+ *  1. The call may fail in an undefined manner if either mapping is not
+ *     tracked by <handle>.
+ *  3. After executing a batch of unmaps, it is guaranteed that no stale
+ *     mappings will remain in the device or host TLBs.
+ */
+#define GNTTABOP_unmap_grant_ref      1
+struct gnttab_unmap_grant_ref {
+    /* IN parameters. */
+    uint64_t host_addr;
+    uint64_t dev_bus_addr;
+    grant_handle_t handle;
+    /* OUT parameters. */
+    int16_t  status;              /* GNTST_* */
+};
+typedef struct gnttab_unmap_grant_ref gnttab_unmap_grant_ref_t;
+DEFINE_XEN_GUEST_HANDLE(gnttab_unmap_grant_ref_t);
+
+/*
+ * GNTTABOP_setup_table: Set up a grant table for <dom> comprising at least
+ * <nr_frames> pages. The frame addresses are written to the <frame_list>.
+ * Only <nr_frames> addresses are written, even if the table is larger.
+ * NOTES:
+ *  1. <dom> may be specified as DOMID_SELF.
+ *  2. Only a sufficiently-privileged domain may specify <dom> != DOMID_SELF.
+ *  3. Xen may not support more than a single grant-table page per domain.
+ */
+#define GNTTABOP_setup_table          2
+struct gnttab_setup_table {
+    /* IN parameters. */
+    domid_t  dom;
+    uint32_t nr_frames;
+    /* OUT parameters. */
+    int16_t  status;              /* GNTST_* */
+    XEN_GUEST_HANDLE(ulong) frame_list;
+};
+typedef struct gnttab_setup_table gnttab_setup_table_t;
+DEFINE_XEN_GUEST_HANDLE(gnttab_setup_table_t);
+
+/*
+ * GNTTABOP_dump_table: Dump the contents of the grant table to the
+ * xen console. Debugging use only.
+ */
+#define GNTTABOP_dump_table           3
+struct gnttab_dump_table {
+    /* IN parameters. */
+    domid_t dom;
+    /* OUT parameters. */
+    int16_t status;               /* GNTST_* */
+};
+typedef struct gnttab_dump_table gnttab_dump_table_t;
+DEFINE_XEN_GUEST_HANDLE(gnttab_dump_table_t);
+
+/*
+ * GNTTABOP_transfer_grant_ref: Transfer <frame> to a foreign domain. The
+ * foreign domain has previously registered its interest in the transfer via
+ * <domid, ref>.
+ * 
+ * Note that, even if the transfer fails, the specified page no LONG_PTRer belongs
+ * to the calling domain *unless* the error is GNTST_bad_page.
+ */
+#define GNTTABOP_transfer                4
+struct gnttab_transfer {
+    /* IN parameters. */
+    xen_pfn_t     mfn;
+    domid_t       domid;
+    grant_ref_t   ref;
+    /* OUT parameters. */
+    int16_t       status;
+};
+typedef struct gnttab_transfer gnttab_transfer_t;
+DEFINE_XEN_GUEST_HANDLE(gnttab_transfer_t);
+
+
+/*
+ * GNTTABOP_copy: Hypervisor based copy
+ * source and destinations can be eithers MFNs or, for foreign domains,
+ * grant references. the foreign domain has to grant read/write access
+ * in its grant table.
+ *
+ * The flags specify what type source and destinations are (either MFN
+ * or grant reference).
+ *
+ * Note that this can also be used to copy data between two domains
+ * via a third party if the source and destination domains had previously
+ * grant appropriate access to their pages to the third party.
+ *
+ * source_offset specifies an offset in the source frame, dest_offset
+ * the offset in the target frame and  len specifies the number of
+ * bytes to be copied.
+ */
+
+#define _GNTCOPY_source_gref      (0)
+#define GNTCOPY_source_gref       (1<<_GNTCOPY_source_gref)
+#define _GNTCOPY_dest_gref        (1)
+#define GNTCOPY_dest_gref         (1<<_GNTCOPY_dest_gref)
+#define _GNTCOPY_can_fail         (2)
+#define GNTCOPY_can_fail          (1<<_GNTCOPY_can_fail) 
+
+#define GNTTABOP_copy                 5
+typedef struct gnttab_copy {
+    /* IN parameters. */
+    struct {
+        union {
+            grant_ref_t ref;
+            xen_pfn_t   gmfn;
+        } u;
+        domid_t  domid;
+        uint16_t offset;
+    } source, dest;
+    uint16_t      len;
+    uint16_t      flags;          /* GNTCOPY_* */
+    /* OUT parameters. */
+    int16_t       status;
+} gnttab_copy_t;
+DEFINE_XEN_GUEST_HANDLE(gnttab_copy_t);
+
+/*
+ * GNTTABOP_query_size: Query the current and maximum sizes of the shared
+ * grant table.
+ * NOTES:
+ *  1. <dom> may be specified as DOMID_SELF.
+ *  2. Only a sufficiently-privileged domain may specify <dom> != DOMID_SELF.
+ */
+#define GNTTABOP_query_size           6
+struct gnttab_query_size {
+    /* IN parameters. */
+    domid_t  dom;
+    /* OUT parameters. */
+    uint32_t nr_frames;
+    uint32_t max_nr_frames;
+    int16_t  status;              /* GNTST_* */
+};
+typedef struct gnttab_query_size gnttab_query_size_t;
+DEFINE_XEN_GUEST_HANDLE(gnttab_query_size_t);
+
+/*
+ * GNTTABOP_unmap_and_replace: Destroy one or more grant-reference mappings
+ * tracked by <handle> but atomically replace the page table entry with one
+ * pointing to the machine address under <new_addr>.  <new_addr> will be
+ * redirected to the null entry.
+ * NOTES:
+ *  1. The call may fail in an undefined manner if either mapping is not
+ *     tracked by <handle>.
+ *  2. After executing a batch of unmaps, it is guaranteed that no stale
+ *     mappings will remain in the device or host TLBs.
+ */
+#define GNTTABOP_unmap_and_replace    7
+struct gnttab_unmap_and_replace {
+    /* IN parameters. */
+    uint64_t host_addr;
+    uint64_t new_addr;
+    grant_handle_t handle;
+    /* OUT parameters. */
+    int16_t  status;              /* GNTST_* */
+};
+typedef struct gnttab_unmap_and_replace gnttab_unmap_and_replace_t;
+DEFINE_XEN_GUEST_HANDLE(gnttab_unmap_and_replace_t);
+
+#if __XEN_INTERFACE_VERSION__ >= 0x0003020a
+/*
+ * GNTTABOP_set_version: Request a particular version of the grant
+ * table shared table structure.  This operation can only be performed
+ * once in any given domain.  It must be performed before any grants
+ * are activated; otherwise, the domain will be stuck with version 1.
+ * The only defined versions are 1 and 2.
+ */
+#define GNTTABOP_set_version          8
+struct gnttab_set_version {
+    /* IN/OUT parameters */
+    uint32_t version;
+};
+typedef struct gnttab_set_version gnttab_set_version_t;
+DEFINE_XEN_GUEST_HANDLE(gnttab_set_version_t);
+
+
+/*
+ * GNTTABOP_get_status_frames: Get the list of frames used to store grant
+ * status for <dom>. In grant format version 2, the status is separated
+ * from the other shared grant fields to allow more efficient synchronization
+ * using barriers instead of atomic cmpexch operations.
+ * <nr_frames> specify the size of vector <frame_list>.
+ * The frame addresses are returned in the <frame_list>.
+ * Only <nr_frames> addresses are returned, even if the table is larger.
+ * NOTES:
+ *  1. <dom> may be specified as DOMID_SELF.
+ *  2. Only a sufficiently-privileged domain may specify <dom> != DOMID_SELF.
+ */
+#define GNTTABOP_get_status_frames     9
+struct gnttab_get_status_frames {
+    /* IN parameters. */
+    uint32_t nr_frames;
+    domid_t  dom;
+    /* OUT parameters. */
+    int16_t  status;              /* GNTST_* */
+    XEN_GUEST_HANDLE(uint64_t) frame_list;
+};
+typedef struct gnttab_get_status_frames gnttab_get_status_frames_t;
+DEFINE_XEN_GUEST_HANDLE(gnttab_get_status_frames_t);
+
+/*
+ * GNTTABOP_get_version: Get the grant table version which is in
+ * effect for domain <dom>.
+ */
+#define GNTTABOP_get_version          10
+struct gnttab_get_version {
+    /* IN parameters */
+    domid_t dom;
+    uint16_t pad;
+    /* OUT parameters */
+    uint32_t version;
+};
+typedef struct gnttab_get_version gnttab_get_version_t;
+DEFINE_XEN_GUEST_HANDLE(gnttab_get_version_t);
+
+#endif /* __XEN_INTERFACE_VERSION__ */
+
+/*
+ * Bitfield values for gnttab_map_grant_ref.flags.
+ */
+ /* Map the grant entry for access by I/O devices. */
+#define _GNTMAP_device_map      (0)
+#define GNTMAP_device_map       (1<<_GNTMAP_device_map)
+ /* Map the grant entry for access by host CPUs. */
+#define _GNTMAP_host_map        (1)
+#define GNTMAP_host_map         (1<<_GNTMAP_host_map)
+ /* Accesses to the granted frame will be restricted to read-only access. */
+#define _GNTMAP_readonly        (2)
+#define GNTMAP_readonly         (1<<_GNTMAP_readonly)
+ /*
+  * GNTMAP_host_map subflag:
+  *  0 => The host mapping is usable only by the guest OS.
+  *  1 => The host mapping is usable by guest OS + current application.
+  */
+#define _GNTMAP_application_map (3)
+#define GNTMAP_application_map  (1<<_GNTMAP_application_map)
+
+ /*
+  * GNTMAP_contains_pte subflag:
+  *  0 => This map request contains a host virtual address.
+  *  1 => This map request contains the machine addess of the PTE to update.
+  */
+#define _GNTMAP_contains_pte    (4)
+#define GNTMAP_contains_pte     (1<<_GNTMAP_contains_pte)
+
+#define _GNTMAP_can_fail        (5)
+#define GNTMAP_can_fail         (1<<_GNTMAP_can_fail)
+
+/*
+ * Bits to be placed in guest kernel available PTE bits (architecture
+ * dependent; only supported when XENFEAT_gnttab_map_avail_bits is set).
+ */
+#define _GNTMAP_guest_avail0    (16)
+#define GNTMAP_guest_avail_mask ((uint32_t)~0 << _GNTMAP_guest_avail0)
+
+/*
+ * Values for error status returns. All errors are -ve.
+ */
+#define GNTST_okay             (0)  /* Normal return.                        */
+#define GNTST_general_error    (-1) /* General undefined error.              */
+#define GNTST_bad_domain       (-2) /* Unrecognsed domain id.                */
+#define GNTST_bad_gntref       (-3) /* Unrecognised or inappropriate gntref. */
+#define GNTST_bad_handle       (-4) /* Unrecognised or inappropriate handle. */
+#define GNTST_bad_virt_addr    (-5) /* Inappropriate virtual address to map. */
+#define GNTST_bad_dev_addr     (-6) /* Inappropriate device address to unmap.*/
+#define GNTST_no_device_space  (-7) /* Out of space in I/O MMU.              */
+#define GNTST_permission_denied (-8) /* Not enough privilege for operation.  */
+#define GNTST_bad_page         (-9) /* Specified page was invalid for op.    */
+#define GNTST_bad_copy_arg    (-10) /* copy arguments cross page boundary.   */
+#define GNTST_address_too_big (-11) /* transfer page address too large.      */
+#define GNTST_eagain          (-12) /* Could not map at the moment. Retry.   */
+
+#define GNTTABOP_error_msgs {                   \
+    "okay",                                     \
+    "undefined error",                          \
+    "unrecognised domain id",                   \
+    "invalid grant reference",                  \
+    "invalid mapping handle",                   \
+    "invalid virtual address",                  \
+    "invalid device address",                   \
+    "no spare translation slot in the I/O MMU", \
+    "permission denied",                        \
+    "bad page",                                 \
+    "copy arguments cross page boundary",       \
+    "page address size too large",              \
+    "could not map at the moment, retry"        \
+}
+
+#endif /* __XEN_PUBLIC_GRANT_TABLE_H__ */
+
+/*
+ * Local variables:
+ * mode: C
+ * c-set-style: "BSD"
+ * c-basic-offset: 4
+ * tab-width: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
diff --git a/include/xen/io/netif.h b/include/xen/io/netif.h
new file mode 100644 (file)
index 0000000..1e4d8c7
--- /dev/null
@@ -0,0 +1,206 @@
+/******************************************************************************
+ * netif.h
+ * 
+ * Unified network-device I/O interface for Xen guest OSes.
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Copyright (c) 2003-2004, Keir Fraser
+ */
+
+#ifndef __XEN_PUBLIC_IO_NETIF_H__
+#define __XEN_PUBLIC_IO_NETIF_H__
+
+#include "ring.h"
+#include "../grant_table.h"
+
+/*
+ * Notifications after enqueuing any type of message should be conditional on
+ * the appropriate req_event or rsp_event field in the shared ring.
+ * If the client sends notification for rx requests then it should specify
+ * feature 'feature-rx-notify' via xenbus. Otherwise the backend will assume
+ * that it cannot safely queue packets (as it may not be kicked to send them).
+ */
+
+/*
+ * This is the 'wire' format for packets:
+ *  Request 1: netif_tx_request -- NETTXF_* (any flags)
+ * [Request 2: netif_tx_extra]  (only if request 1 has NETTXF_extra_info)
+ * [Request 3: netif_tx_extra]  (only if request 2 has XEN_NETIF_EXTRA_MORE)
+ *  Request 4: netif_tx_request -- NETTXF_more_data
+ *  Request 5: netif_tx_request -- NETTXF_more_data
+ *  ...
+ *  Request N: netif_tx_request -- 0
+ */
+
+/* Protocol checksum field is blank in the packet (hardware offload)? */
+#define _NETTXF_csum_blank     (0)
+#define  NETTXF_csum_blank     (1U<<_NETTXF_csum_blank)
+
+/* Packet data has been validated against protocol checksum. */
+#define _NETTXF_data_validated (1)
+#define  NETTXF_data_validated (1U<<_NETTXF_data_validated)
+
+/* Packet continues in the next request descriptor. */
+#define _NETTXF_more_data      (2)
+#define  NETTXF_more_data      (1U<<_NETTXF_more_data)
+
+/* Packet to be followed by extra descriptor(s). */
+#define _NETTXF_extra_info     (3)
+#define  NETTXF_extra_info     (1U<<_NETTXF_extra_info)
+
+struct netif_tx_request {
+    grant_ref_t gref;      /* Reference to buffer page */
+    uint16_t offset;       /* Offset within buffer page */
+    uint16_t flags;        /* NETTXF_* */
+    uint16_t id;           /* Echoed in response message. */
+    uint16_t size;         /* Packet size in bytes.       */
+};
+typedef struct netif_tx_request netif_tx_request_t;
+
+/* Types of netif_extra_info descriptors. */
+#define XEN_NETIF_EXTRA_TYPE_NONE      (0)  /* Never used - invalid */
+#define XEN_NETIF_EXTRA_TYPE_GSO       (1)  /* u.gso */
+#define XEN_NETIF_EXTRA_TYPE_MCAST_ADD (2)  /* u.mcast */
+#define XEN_NETIF_EXTRA_TYPE_MCAST_DEL (3)  /* u.mcast */
+#define XEN_NETIF_EXTRA_TYPE_MAX       (4)
+
+/* netif_extra_info flags. */
+#define _XEN_NETIF_EXTRA_FLAG_MORE (0)
+#define XEN_NETIF_EXTRA_FLAG_MORE  (1U<<_XEN_NETIF_EXTRA_FLAG_MORE)
+
+/* GSO types */
+#define XEN_NETIF_GSO_TYPE_TCPV4        (1)
+#define XEN_NETIF_GSO_TYPE_TCPV6        (2)
+
+/*
+ * This structure needs to fit within both netif_tx_request and
+ * netif_rx_response for compatibility.
+ */
+struct netif_extra_info {
+    uint8_t type;  /* XEN_NETIF_EXTRA_TYPE_* */
+    uint8_t flags; /* XEN_NETIF_EXTRA_FLAG_* */
+
+    union {
+        /*
+         * XEN_NETIF_EXTRA_TYPE_GSO:
+         */
+        struct {
+            /*
+             * Maximum payload size of each segment. For example, for TCP this
+             * is just the path MSS.
+             */
+            uint16_t size;
+
+            /*
+             * GSO type. This determines the protocol of the packet and any
+             * extra features required to segment the packet properly.
+             */
+            uint8_t type; /* XEN_NETIF_GSO_TYPE_* */
+
+            /* Future expansion. */
+            uint8_t pad;
+
+            /*
+             * GSO features. This specifies any extra GSO features required
+             * to process this packet, such as ECN support for TCPv4.
+             */
+            uint16_t features; /* XEN_NETIF_GSO_FEAT_* */
+        } gso;
+
+        /*
+         * XEN_NETIF_EXTRA_TYPE_MCAST_{ADD,DEL}:
+         * Backend advertises availability via 'feature-multicast-control'
+         * xenbus node containing value '1'.
+         * Frontend requests this feature by advertising
+         * 'request-multicast-control' xenbus node containing value '1'.
+         * If multicast control is requested then multicast flooding is
+         * disabled and the frontend must explicitly register its interest
+         * in multicast groups using dummy transmit requests containing
+         * MCAST_{ADD,DEL} extra-info fragments.
+         */
+        struct {
+            uint8_t addr[6]; /* Address to add/remove. */
+        } mcast;
+
+        uint16_t pad[3];
+    } u;
+};
+typedef struct netif_extra_info netif_extra_info_t;
+
+struct netif_tx_response {
+    uint16_t id;
+    int16_t  status;       /* NETIF_RSP_* */
+};
+typedef struct netif_tx_response netif_tx_response_t;
+
+struct netif_rx_request {
+    uint16_t    id;        /* Echoed in response message.        */
+    grant_ref_t gref;      /* Reference to incoming granted frame */
+};
+typedef struct netif_rx_request netif_rx_request_t;
+
+/* Packet data has been validated against protocol checksum. */
+#define _NETRXF_data_validated (0)
+#define  NETRXF_data_validated (1U<<_NETRXF_data_validated)
+
+/* Protocol checksum field is blank in the packet (hardware offload)? */
+#define _NETRXF_csum_blank     (1)
+#define  NETRXF_csum_blank     (1U<<_NETRXF_csum_blank)
+
+/* Packet continues in the next request descriptor. */
+#define _NETRXF_more_data      (2)
+#define  NETRXF_more_data      (1U<<_NETRXF_more_data)
+
+/* Packet to be followed by extra descriptor(s). */
+#define _NETRXF_extra_info     (3)
+#define  NETRXF_extra_info     (1U<<_NETRXF_extra_info)
+
+struct netif_rx_response {
+    uint16_t id;
+    uint16_t offset;       /* Offset in page of start of received packet  */
+    uint16_t flags;        /* NETRXF_* */
+    int16_t  status;       /* -ve: BLKIF_RSP_* ; +ve: Rx'ed pkt size. */
+};
+typedef struct netif_rx_response netif_rx_response_t;
+
+/*
+ * Generate netif ring structures and types.
+ */
+
+DEFINE_RING_TYPES(netif_tx, struct netif_tx_request, struct netif_tx_response);
+DEFINE_RING_TYPES(netif_rx, struct netif_rx_request, struct netif_rx_response);
+
+#define NETIF_RSP_DROPPED         -2
+#define NETIF_RSP_ERROR           -1
+#define NETIF_RSP_OKAY             0
+/* No response: used for auxiliary requests (e.g., netif_tx_extra). */
+#define NETIF_RSP_NULL             1
+
+#endif
+
+/*
+ * Local variables:
+ * mode: C
+ * c-set-style: "BSD"
+ * c-basic-offset: 4
+ * tab-width: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
diff --git a/include/xen/io/ring.h b/include/xen/io/ring.h
new file mode 100644 (file)
index 0000000..938693f
--- /dev/null
@@ -0,0 +1,323 @@
+/******************************************************************************
+ * ring.h
+ * 
+ * Shared producer-consumer ring macros.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Tim Deegan and Andrew Warfield November 2004.
+ */
+
+#ifndef __XEN_PUBLIC_IO_RING_H__
+#define __XEN_PUBLIC_IO_RING_H__
+
+#include "../xen-compat.h"
+
+#if __XEN_INTERFACE_VERSION__ < 0x00030208
+#define xen_mb()  mb()
+#define xen_rmb() rmb()
+#define xen_wmb() wmb()
+#endif
+
+typedef unsigned int RING_IDX;
+
+/* Round a 32-bit unsigned constant down to the nearest power of two. */
+#define __RD2(_x)  (((_x) & 0x00000002) ? 0x2                  : ((_x) & 0x1))
+#define __RD4(_x)  (((_x) & 0x0000000c) ? __RD2((_x)>>2)<<2    : __RD2(_x))
+#define __RD8(_x)  (((_x) & 0x000000f0) ? __RD4((_x)>>4)<<4    : __RD4(_x))
+#define __RD16(_x) (((_x) & 0x0000ff00) ? __RD8((_x)>>8)<<8    : __RD8(_x))
+#define __RD32(_x) (((_x) & 0xffff0000) ? __RD16((_x)>>16)<<16 : __RD16(_x))
+
+/*
+ * Calculate size of a shared ring, given the total available space for the
+ * ring and indexes (_sz), and the name tag of the request/response structure.
+ * A ring contains as many entries as will fit, rounded down to the nearest 
+ * power of two (so we can mask with (size-1) to loop around).
+ */
+#define __CONST_RING_SIZE(_s, _sz) \
+    (__RD32(((_sz) - offsetof(struct _s##_sring, ring)) / \
+           sizeof(((struct _s##_sring *)0)->ring[0])))
+/*
+ * The same for passing in an actual pointer instead of a name tag.
+ */
+#define __RING_SIZE(_s, _sz) \
+    (__RD32(((_sz) - (LONG_PTR)(_s)->ring + (LONG_PTR)(_s)) / sizeof((_s)->ring[0])))
+
+/*
+ * Macros to make the correct C datatypes for a new kind of ring.
+ * 
+ * To make a new ring datatype, you need to have two message structures,
+ * let's say request_t, and response_t already defined.
+ *
+ * In a header where you want the ring datatype declared, you then do:
+ *
+ *     DEFINE_RING_TYPES(mytag, request_t, response_t);
+ *
+ * These expand out to give you a set of types, as you can see below.
+ * The most important of these are:
+ * 
+ *     mytag_sring_t      - The shared ring.
+ *     mytag_front_ring_t - The 'front' half of the ring.
+ *     mytag_back_ring_t  - The 'back' half of the ring.
+ *
+ * To initialize a ring in your code you need to know the location and size
+ * of the shared memory area (PAGE_SIZE, for instance). To initialise
+ * the front half:
+ *
+ *     mytag_front_ring_t front_ring;
+ *     SHARED_RING_INIT((mytag_sring_t *)shared_page);
+ *     FRONT_RING_INIT(&front_ring, (mytag_sring_t *)shared_page, PAGE_SIZE);
+ *
+ * Initializing the back follows similarly (note that only the front
+ * initializes the shared ring):
+ *
+ *     mytag_back_ring_t back_ring;
+ *     BACK_RING_INIT(&back_ring, (mytag_sring_t *)shared_page, PAGE_SIZE);
+ */
+
+#define DEFINE_RING_TYPES(__name, __req_t, __rsp_t)                     \
+                                                                        \
+/* Shared ring entry */                                                 \
+union __name##_sring_entry {                                            \
+    __req_t req;                                                        \
+    __rsp_t rsp;                                                        \
+};                                                                      \
+                                                                        \
+/* Shared ring page */                                                  \
+struct __name##_sring {                                                 \
+    RING_IDX req_prod, req_event;                                       \
+    RING_IDX rsp_prod, rsp_event;                                       \
+    union {                                                             \
+        struct {                                                        \
+            uint8_t smartpoll_active;                                   \
+        } netif;                                                        \
+        struct {                                                        \
+            uint8_t msg;                                                \
+        } tapif_user;                                                   \
+        uint8_t pvt_pad[4];                                             \
+    } private;                                                          \
+    uint8_t __pad[44];                                                  \
+    union __name##_sring_entry ring[1]; /* variable-length */           \
+};                                                                      \
+                                                                        \
+/* "Front" end's private variables */                                   \
+struct __name##_front_ring {                                            \
+    RING_IDX req_prod_pvt;                                              \
+    RING_IDX rsp_cons;                                                  \
+    unsigned int nr_ents;                                               \
+    struct __name##_sring *sring;                                       \
+};                                                                      \
+                                                                        \
+/* "Back" end's private variables */                                    \
+struct __name##_back_ring {                                             \
+    RING_IDX rsp_prod_pvt;                                              \
+    RING_IDX req_cons;                                                  \
+    unsigned int nr_ents;                                               \
+    struct __name##_sring *sring;                                       \
+};                                                                      \
+                                                                        \
+/* Syntactic sugar */                                                   \
+typedef struct __name##_sring __name##_sring_t;                         \
+typedef struct __name##_front_ring __name##_front_ring_t;               \
+typedef struct __name##_back_ring __name##_back_ring_t
+
+/*
+ * Macros for manipulating rings.
+ * 
+ * FRONT_RING_whatever works on the "front end" of a ring: here 
+ * requests are pushed on to the ring and responses taken off it.
+ * 
+ * BACK_RING_whatever works on the "back end" of a ring: here 
+ * requests are taken off the ring and responses put on.
+ * 
+ * N.B. these macros do NO INTERLOCKS OR FLOW CONTROL. 
+ * This is OK in 1-for-1 request-response situations where the 
+ * requestor (front end) never has more than RING_SIZE()-1
+ * outstanding requests.
+ */
+
+/* Initialising empty rings */
+#define SHARED_RING_INIT(_s) do {                                       \
+    (_s)->req_prod  = (_s)->rsp_prod  = 0;                              \
+    (_s)->req_event = (_s)->rsp_event = 1;                              \
+    (void)memset((_s)->private.pvt_pad, 0, sizeof((_s)->private.pvt_pad)); \
+    (void)memset((_s)->__pad, 0, sizeof((_s)->__pad));                  \
+} while(0)
+
+#define FRONT_RING_INIT(_r, _s, __size) do {                            \
+    (_r)->req_prod_pvt = 0;                                             \
+    (_r)->rsp_cons = 0;                                                 \
+    (_r)->nr_ents = __RING_SIZE(_s, __size);                            \
+    (_r)->sring = (_s);                                                 \
+} while (0)
+
+#define BACK_RING_INIT(_r, _s, __size) do {                             \
+    (_r)->rsp_prod_pvt = 0;                                             \
+    (_r)->req_cons = 0;                                                 \
+    (_r)->nr_ents = __RING_SIZE(_s, __size);                            \
+    (_r)->sring = (_s);                                                 \
+} while (0)
+
+/* Initialize to existing shared indexes -- for recovery */
+#define FRONT_RING_ATTACH(_r, _s, __size) do {                          \
+    (_r)->sring = (_s);                                                 \
+    (_r)->req_prod_pvt = (_s)->req_prod;                                \
+    (_r)->rsp_cons = (_s)->rsp_prod;                                    \
+    (_r)->nr_ents = __RING_SIZE(_s, __size);                            \
+} while (0)
+
+#define BACK_RING_ATTACH(_r, _s, __size) do {                           \
+    (_r)->sring = (_s);                                                 \
+    (_r)->rsp_prod_pvt = (_s)->rsp_prod;                                \
+    (_r)->req_cons = (_s)->req_prod;                                    \
+    (_r)->nr_ents = __RING_SIZE(_s, __size);                            \
+} while (0)
+
+/* How big is this ring? */
+#define RING_SIZE(_r)                                                   \
+    ((_r)->nr_ents)
+
+/* Number of free requests (for use on front side only). */
+#define RING_FREE_REQUESTS(_r)                                          \
+    (RING_SIZE(_r) - ((_r)->req_prod_pvt - (_r)->rsp_cons))
+
+/* Test if there is an empty slot available on the front ring.
+ * (This is only meaningful from the front. )
+ */
+#define RING_FULL(_r)                                                   \
+    (RING_FREE_REQUESTS(_r) == 0)
+
+/* Test if there are outstanding messages to be processed on a ring. */
+#define RING_HAS_UNCONSUMED_RESPONSES(_r)                               \
+    ((_r)->sring->rsp_prod - (_r)->rsp_cons)
+
+#ifdef __GNUC__
+#define RING_HAS_UNCONSUMED_REQUESTS(_r) ({                             \
+    unsigned int req = (_r)->sring->req_prod - (_r)->req_cons;          \
+    unsigned int rsp = RING_SIZE(_r) -                                  \
+        ((_r)->req_cons - (_r)->rsp_prod_pvt);                          \
+    req < rsp ? req : rsp;                                              \
+})
+#else
+/* Same as above, but without the nice GCC ({ ... }) syntax. */
+#define RING_HAS_UNCONSUMED_REQUESTS(_r)                                \
+    ((((_r)->sring->req_prod - (_r)->req_cons) <                        \
+      (RING_SIZE(_r) - ((_r)->req_cons - (_r)->rsp_prod_pvt))) ?        \
+     ((_r)->sring->req_prod - (_r)->req_cons) :                         \
+     (RING_SIZE(_r) - ((_r)->req_cons - (_r)->rsp_prod_pvt)))
+#endif
+
+/* Direct access to individual ring elements, by index. */
+#define RING_GET_REQUEST(_r, _idx)                                      \
+    (&((_r)->sring->ring[((_idx) & (RING_SIZE(_r) - 1))].req))
+
+#define RING_GET_RESPONSE(_r, _idx)                                     \
+    (&((_r)->sring->ring[((_idx) & (RING_SIZE(_r) - 1))].rsp))
+
+/* Loop termination condition: Would the specified index overflow the ring? */
+#define RING_REQUEST_CONS_OVERFLOW(_r, _cons)                           \
+    (((_cons) - (_r)->rsp_prod_pvt) >= RING_SIZE(_r))
+
+#define RING_PUSH_REQUESTS(_r) do {                                     \
+    xen_wmb(); /* back sees requests /before/ updated producer index */ \
+    (_r)->sring->req_prod = (_r)->req_prod_pvt;                         \
+} while (0)
+
+#define RING_PUSH_RESPONSES(_r) do {                                    \
+    xen_wmb(); /* front sees resps /before/ updated producer index */   \
+    (_r)->sring->rsp_prod = (_r)->rsp_prod_pvt;                         \
+} while (0)
+
+/*
+ * Notification hold-off (req_event and rsp_event):
+ * 
+ * When queueing requests or responses on a shared ring, it may not always be
+ * necessary to notify the remote end. For example, if requests are in flight
+ * in a backend, the front may be able to queue further requests without
+ * notifying the back (if the back checks for new requests when it queues
+ * responses).
+ * 
+ * When enqueuing requests or responses:
+ * 
+ *  Use RING_PUSH_{REQUESTS,RESPONSES}_AND_CHECK_NOTIFY(). The second argument
+ *  is a boolean return value. True indicates that the receiver requires an
+ *  asynchronous notification.
+ * 
+ * After dequeuing requests or responses (before sleeping the connection):
+ * 
+ *  Use RING_FINAL_CHECK_FOR_REQUESTS() or RING_FINAL_CHECK_FOR_RESPONSES().
+ *  The second argument is a boolean return value. True indicates that there
+ *  are pending messages on the ring (i.e., the connection should not be put
+ *  to sleep).
+ * 
+ *  These macros will set the req_event/rsp_event field to trigger a
+ *  notification on the very next message that is enqueued. If you want to
+ *  create batches of work (i.e., only receive a notification after several
+ *  messages have been enqueued) then you will need to create a customised
+ *  version of the FINAL_CHECK macro in your own code, which sets the event
+ *  field appropriately.
+ */
+
+#define RING_PUSH_REQUESTS_AND_CHECK_NOTIFY(_r, _notify) do {           \
+    RING_IDX __old = (_r)->sring->req_prod;                             \
+    RING_IDX __new = (_r)->req_prod_pvt;                                \
+    xen_wmb(); /* back sees requests /before/ updated producer index */ \
+    (_r)->sring->req_prod = __new;                                      \
+    xen_mb(); /* back sees new requests /before/ we check req_event */  \
+    (_notify) = ((RING_IDX)(__new - (_r)->sring->req_event) <           \
+                 (RING_IDX)(__new - __old));                            \
+} while (0)
+
+#define RING_PUSH_RESPONSES_AND_CHECK_NOTIFY(_r, _notify) do {          \
+    RING_IDX __old = (_r)->sring->rsp_prod;                             \
+    RING_IDX __new = (_r)->rsp_prod_pvt;                                \
+    xen_wmb(); /* front sees resps /before/ updated producer index */   \
+    (_r)->sring->rsp_prod = __new;                                      \
+    xen_mb(); /* front sees new resps /before/ we check rsp_event */    \
+    (_notify) = ((RING_IDX)(__new - (_r)->sring->rsp_event) <           \
+                 (RING_IDX)(__new - __old));                            \
+} while (0)
+
+#define RING_FINAL_CHECK_FOR_REQUESTS(_r, _work_to_do) do {             \
+    (_work_to_do) = RING_HAS_UNCONSUMED_REQUESTS(_r);                   \
+    if (_work_to_do) break;                                             \
+    (_r)->sring->req_event = (_r)->req_cons + 1;                        \
+    xen_mb();                                                           \
+    (_work_to_do) = RING_HAS_UNCONSUMED_REQUESTS(_r);                   \
+} while (0)
+
+#define RING_FINAL_CHECK_FOR_RESPONSES(_r, _work_to_do) do {            \
+    (_work_to_do) = RING_HAS_UNCONSUMED_RESPONSES(_r);                  \
+    if (_work_to_do) break;                                             \
+    (_r)->sring->rsp_event = (_r)->rsp_cons + 1;                        \
+    xen_mb();                                                           \
+    (_work_to_do) = RING_HAS_UNCONSUMED_RESPONSES(_r);                  \
+} while (0)
+
+#endif /* __XEN_PUBLIC_IO_RING_H__ */
+
+/*
+ * Local variables:
+ * mode: C
+ * c-set-style: "BSD"
+ * c-basic-offset: 4
+ * tab-width: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
diff --git a/include/xen/io/xenbus.h b/include/xen/io/xenbus.h
new file mode 100644 (file)
index 0000000..4a053df
--- /dev/null
@@ -0,0 +1,80 @@
+/*****************************************************************************
+ * xenbus.h
+ *
+ * Xenbus protocol details.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Copyright (C) 2005 XenSource Ltd.
+ */
+
+#ifndef _XEN_PUBLIC_IO_XENBUS_H
+#define _XEN_PUBLIC_IO_XENBUS_H
+
+/*
+ * The state of either end of the Xenbus, i.e. the current communication
+ * status of initialisation across the bus.  States here imply nothing about
+ * the state of the connection between the driver and the kernel's device
+ * layers.
+ */
+enum xenbus_state {
+    XenbusStateUnknown       = 0,
+
+    XenbusStateInitialising  = 1,
+
+    /*
+     * InitWait: Finished early initialisation but waiting for information
+     * from the peer or hotplug scripts.
+     */
+    XenbusStateInitWait      = 2,
+
+    /*
+     * Initialised: Waiting for a connection from the peer.
+     */
+    XenbusStateInitialised   = 3,
+
+    XenbusStateConnected     = 4,
+
+    /*
+     * Closing: The device is being closed due to an error or an unplug event.
+     */
+    XenbusStateClosing       = 5,
+
+    XenbusStateClosed        = 6,
+
+    /*
+     * Reconfiguring: The device is being reconfigured.
+     */
+    XenbusStateReconfiguring = 7,
+
+    XenbusStateReconfigured  = 8
+};
+typedef enum xenbus_state XenbusState;
+
+#endif /* _XEN_PUBLIC_IO_XENBUS_H */
+
+/*
+ * Local variables:
+ * mode: C
+ * c-set-style: "BSD"
+ * c-basic-offset: 4
+ * tab-width: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
diff --git a/include/xen/xen-compat.h b/include/xen/xen-compat.h
new file mode 100644 (file)
index 0000000..2e38003
--- /dev/null
@@ -0,0 +1,44 @@
+/******************************************************************************
+ * xen-compat.h
+ * 
+ * Guest OS interface to Xen.  Compatibility layer.
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Copyright (c) 2006, Christian Limpach
+ */
+
+#ifndef __XEN_PUBLIC_XEN_COMPAT_H__
+#define __XEN_PUBLIC_XEN_COMPAT_H__
+
+#define __XEN_LATEST_INTERFACE_VERSION__ 0x0003020a
+
+#if defined(__XEN__) || defined(__XEN_TOOLS__)
+/* Xen is built with matching headers and implements the latest interface. */
+#define __XEN_INTERFACE_VERSION__ __XEN_LATEST_INTERFACE_VERSION__
+#elif !defined(__XEN_INTERFACE_VERSION__)
+/* Guests which do not specify a version get the legacy interface. */
+#define __XEN_INTERFACE_VERSION__ 0x00000000
+#endif
+
+#if __XEN_INTERFACE_VERSION__ > __XEN_LATEST_INTERFACE_VERSION__
+#error "These header files do not support the requested interface version."
+#endif
+
+#endif /* __XEN_PUBLIC_XEN_COMPAT_H__ */
diff --git a/include/xen/xen.h b/include/xen/xen.h
new file mode 100644 (file)
index 0000000..d673be1
--- /dev/null
@@ -0,0 +1,726 @@
+/******************************************************************************
+ * xen.h
+ * 
+ * Guest OS interface to Xen.
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Copyright (c) 2004, K A Fraser
+ */
+
+#ifndef __XEN_PUBLIC_XEN_H__
+#define __XEN_PUBLIC_XEN_H__
+
+#include "xen-compat.h"
+
+#if defined(__i386__) || defined(__x86_64__)
+#include "arch-x86/xen.h"
+#elif defined(__ia64__)
+#include "arch-ia64.h"
+#else
+#error "Unsupported architecture"
+#endif
+
+#ifndef __ASSEMBLY__
+/* Guest handles for primitive C types. */
+DEFINE_XEN_GUEST_HANDLE(char);
+__DEFINE_XEN_GUEST_HANDLE(uchar, unsigned char);
+DEFINE_XEN_GUEST_HANDLE(int);
+__DEFINE_XEN_GUEST_HANDLE(uint,  unsigned int);
+DEFINE_XEN_GUEST_HANDLE(LONG_PTR);
+__DEFINE_XEN_GUEST_HANDLE(ulong, ULONG_PTR);
+DEFINE_XEN_GUEST_HANDLE(void);
+
+DEFINE_XEN_GUEST_HANDLE(uint64_t);
+DEFINE_XEN_GUEST_HANDLE(xen_pfn_t);
+#endif
+
+/*
+ * HYPERCALLS
+ */
+
+#define __HYPERVISOR_set_trap_table        0
+#define __HYPERVISOR_mmu_update            1
+#define __HYPERVISOR_set_gdt               2
+#define __HYPERVISOR_stack_switch          3
+#define __HYPERVISOR_set_callbacks         4
+#define __HYPERVISOR_fpu_taskswitch        5
+#define __HYPERVISOR_sched_op_compat       6 /* compat since 0x00030101 */
+#define __HYPERVISOR_platform_op           7
+#define __HYPERVISOR_set_debugreg          8
+#define __HYPERVISOR_get_debugreg          9
+#define __HYPERVISOR_update_descriptor    10
+#define __HYPERVISOR_memory_op            12
+#define __HYPERVISOR_multicall            13
+#define __HYPERVISOR_update_va_mapping    14
+#define __HYPERVISOR_set_timer_op         15
+#define __HYPERVISOR_event_channel_op_compat 16 /* compat since 0x00030202 */
+#define __HYPERVISOR_xen_version          17
+#define __HYPERVISOR_console_io           18
+#define __HYPERVISOR_physdev_op_compat    19 /* compat since 0x00030202 */
+#define __HYPERVISOR_grant_table_op       20
+#define __HYPERVISOR_vm_assist            21
+#define __HYPERVISOR_update_va_mapping_otherdomain 22
+#define __HYPERVISOR_iret                 23 /* x86 only */
+#define __HYPERVISOR_vcpu_op              24
+#define __HYPERVISOR_set_segment_base     25 /* x86/64 only */
+#define __HYPERVISOR_mmuext_op            26
+#define __HYPERVISOR_xsm_op               27
+#define __HYPERVISOR_nmi_op               28
+#define __HYPERVISOR_sched_op             29
+#define __HYPERVISOR_callback_op          30
+#define __HYPERVISOR_xenoprof_op          31
+#define __HYPERVISOR_event_channel_op     32
+#define __HYPERVISOR_physdev_op           33
+#define __HYPERVISOR_hvm_op               34
+#define __HYPERVISOR_sysctl               35
+#define __HYPERVISOR_domctl               36
+#define __HYPERVISOR_kexec_op             37
+#define __HYPERVISOR_tmem_op              38
+
+/* Architecture-specific hypercall definitions. */
+#define __HYPERVISOR_arch_0               48
+#define __HYPERVISOR_arch_1               49
+#define __HYPERVISOR_arch_2               50
+#define __HYPERVISOR_arch_3               51
+#define __HYPERVISOR_arch_4               52
+#define __HYPERVISOR_arch_5               53
+#define __HYPERVISOR_arch_6               54
+#define __HYPERVISOR_arch_7               55
+
+/*
+ * HYPERCALL COMPATIBILITY.
+ */
+
+/* New sched_op hypercall introduced in 0x00030101. */
+#if __XEN_INTERFACE_VERSION__ < 0x00030101
+#undef __HYPERVISOR_sched_op
+#define __HYPERVISOR_sched_op __HYPERVISOR_sched_op_compat
+#endif
+
+/* New event-channel and physdev hypercalls introduced in 0x00030202. */
+#if __XEN_INTERFACE_VERSION__ < 0x00030202
+#undef __HYPERVISOR_event_channel_op
+#define __HYPERVISOR_event_channel_op __HYPERVISOR_event_channel_op_compat
+#undef __HYPERVISOR_physdev_op
+#define __HYPERVISOR_physdev_op __HYPERVISOR_physdev_op_compat
+#endif
+
+/* New platform_op hypercall introduced in 0x00030204. */
+#if __XEN_INTERFACE_VERSION__ < 0x00030204
+#define __HYPERVISOR_dom0_op __HYPERVISOR_platform_op
+#endif
+
+/* 
+ * VIRTUAL INTERRUPTS
+ * 
+ * Virtual interrupts that a guest OS may receive from Xen.
+ * 
+ * In the side comments, 'V.' denotes a per-VCPU VIRQ while 'G.' denotes a
+ * global VIRQ. The former can be bound once per VCPU and cannot be re-bound.
+ * The latter can be allocated only once per guest: they must initially be
+ * allocated to VCPU0 but can subsequently be re-bound.
+ */
+#define VIRQ_TIMER      0  /* V. Timebase update, and/or requested timeout.  */
+#define VIRQ_DEBUG      1  /* V. Request guest to dump debug info.           */
+#define VIRQ_CONSOLE    2  /* G. (DOM0) Bytes received on emergency console. */
+#define VIRQ_DOM_EXC    3  /* G. (DOM0) Exceptional event for some domain.   */
+#define VIRQ_TBUF       4  /* G. (DOM0) Trace buffer has records available.  */
+#define VIRQ_DEBUGGER   6  /* G. (DOM0) A domain has paused for debugging.   */
+#define VIRQ_XENOPROF   7  /* V. XenOprofile interrupt: new sample available */
+#define VIRQ_CON_RING   8  /* G. (DOM0) Bytes received on console            */
+#define VIRQ_PCPU_STATE 9  /* G. (DOM0) PCPU state changed                   */
+#define VIRQ_MEM_EVENT  10 /* G. (DOM0) A memory event has occured           */
+
+/* Architecture-specific VIRQ definitions. */
+#define VIRQ_ARCH_0    16
+#define VIRQ_ARCH_1    17
+#define VIRQ_ARCH_2    18
+#define VIRQ_ARCH_3    19
+#define VIRQ_ARCH_4    20
+#define VIRQ_ARCH_5    21
+#define VIRQ_ARCH_6    22
+#define VIRQ_ARCH_7    23
+
+#define NR_VIRQS       24
+
+/*
+ * HYPERVISOR_mmu_update(reqs, count, pdone, foreigndom)
+ * 
+ * @reqs is an array of mmu_update_t structures ((ptr, val) pairs).
+ * @count is the length of the above array.
+ * @pdone is an output parameter indicating number of completed operations
+ * @foreigndom[15:0]: FD, the expected owner of data pages referenced in this
+ *                    hypercall invocation. Can be DOMID_SELF.
+ * @foreigndom[31:16]: PFD, the expected owner of pagetable pages referenced
+ *                     in this hypercall invocation. The value of this field
+ *                     (x) encodes the PFD as follows:
+ *                     x == 0 => PFD == DOMID_SELF
+ *                     x != 0 => PFD == x - 1
+ * 
+ * Sub-commands: ptr[1:0] specifies the appropriate MMU_* command.
+ * -------------
+ * ptr[1:0] == MMU_NORMAL_PT_UPDATE:
+ * Updates an entry in a page table belonging to PFD. If updating an L1 table,
+ * and the new table entry is valid/present, the mapped frame must belong to
+ * FD. If attempting to map an I/O page then the caller assumes the privilege
+ * of the FD.
+ * FD == DOMID_IO: Permit /only/ I/O mappings, at the priv level of the caller.
+ * FD == DOMID_XEN: Map restricted areas of Xen's heap space.
+ * ptr[:2]  -- Machine address of the page-table entry to modify.
+ * val      -- Value to write.
+ * 
+ * ptr[1:0] == MMU_MACHPHYS_UPDATE:
+ * Updates an entry in the machine->pseudo-physical mapping table.
+ * ptr[:2]  -- Machine address within the frame whose mapping to modify.
+ *             The frame must belong to the FD, if one is specified.
+ * val      -- Value to write into the mapping entry.
+ * 
+ * ptr[1:0] == MMU_PT_UPDATE_PRESERVE_AD:
+ * As MMU_NORMAL_PT_UPDATE above, but A/D bits currently in the PTE are ORed
+ * with those in @val.
+ */
+#define MMU_NORMAL_PT_UPDATE      0 /* checked '*ptr = val'. ptr is MA.      */
+#define MMU_MACHPHYS_UPDATE       1 /* ptr = MA of frame to modify entry for */
+#define MMU_PT_UPDATE_PRESERVE_AD 2 /* atomically: *ptr = val | (*ptr&(A|D)) */
+
+/*
+ * MMU EXTENDED OPERATIONS
+ * 
+ * HYPERVISOR_mmuext_op() accepts a list of mmuext_op structures.
+ * A foreigndom (FD) can be specified (or DOMID_SELF for none).
+ * Where the FD has some effect, it is described below.
+ * 
+ * cmd: MMUEXT_(UN)PIN_*_TABLE
+ * mfn: Machine frame number to be (un)pinned as a p.t. page.
+ *      The frame must belong to the FD, if one is specified.
+ * 
+ * cmd: MMUEXT_NEW_BASEPTR
+ * mfn: Machine frame number of new page-table base to install in MMU.
+ * 
+ * cmd: MMUEXT_NEW_USER_BASEPTR [x86/64 only]
+ * mfn: Machine frame number of new page-table base to install in MMU
+ *      when in user space.
+ * 
+ * cmd: MMUEXT_TLB_FLUSH_LOCAL
+ * No additional arguments. Flushes local TLB.
+ * 
+ * cmd: MMUEXT_INVLPG_LOCAL
+ * linear_addr: Linear address to be flushed from the local TLB.
+ * 
+ * cmd: MMUEXT_TLB_FLUSH_MULTI
+ * vcpumask: Pointer to bitmap of VCPUs to be flushed.
+ * 
+ * cmd: MMUEXT_INVLPG_MULTI
+ * linear_addr: Linear address to be flushed.
+ * vcpumask: Pointer to bitmap of VCPUs to be flushed.
+ * 
+ * cmd: MMUEXT_TLB_FLUSH_ALL
+ * No additional arguments. Flushes all VCPUs' TLBs.
+ * 
+ * cmd: MMUEXT_INVLPG_ALL
+ * linear_addr: Linear address to be flushed from all VCPUs' TLBs.
+ * 
+ * cmd: MMUEXT_FLUSH_CACHE
+ * No additional arguments. Writes back and flushes cache contents.
+ *
+ * cmd: MMUEXT_FLUSH_CACHE_GLOBAL
+ * No additional arguments. Writes back and flushes cache contents
+ * on all CPUs in the system.
+ * 
+ * cmd: MMUEXT_SET_LDT
+ * linear_addr: Linear address of LDT base (NB. must be page-aligned).
+ * nr_ents: Number of entries in LDT.
+ *
+ * cmd: MMUEXT_CLEAR_PAGE
+ * mfn: Machine frame number to be cleared.
+ *
+ * cmd: MMUEXT_COPY_PAGE
+ * mfn: Machine frame number of the destination page.
+ * src_mfn: Machine frame number of the source page.
+ *
+ * cmd: MMUEXT_[UN]MARK_SUPER
+ * mfn: Machine frame number of head of superpage to be [un]marked.
+ */
+#define MMUEXT_PIN_L1_TABLE      0
+#define MMUEXT_PIN_L2_TABLE      1
+#define MMUEXT_PIN_L3_TABLE      2
+#define MMUEXT_PIN_L4_TABLE      3
+#define MMUEXT_UNPIN_TABLE       4
+#define MMUEXT_NEW_BASEPTR       5
+#define MMUEXT_TLB_FLUSH_LOCAL   6
+#define MMUEXT_INVLPG_LOCAL      7
+#define MMUEXT_TLB_FLUSH_MULTI   8
+#define MMUEXT_INVLPG_MULTI      9
+#define MMUEXT_TLB_FLUSH_ALL    10
+#define MMUEXT_INVLPG_ALL       11
+#define MMUEXT_FLUSH_CACHE      12
+#define MMUEXT_SET_LDT          13
+#define MMUEXT_NEW_USER_BASEPTR 15
+#define MMUEXT_CLEAR_PAGE       16
+#define MMUEXT_COPY_PAGE        17
+#define MMUEXT_FLUSH_CACHE_GLOBAL 18
+#define MMUEXT_MARK_SUPER       19
+#define MMUEXT_UNMARK_SUPER     20
+
+#ifndef __ASSEMBLY__
+struct mmuext_op {
+    unsigned int cmd;
+    union {
+        /* [UN]PIN_TABLE, NEW_BASEPTR, NEW_USER_BASEPTR
+         * CLEAR_PAGE, COPY_PAGE, [UN]MARK_SUPER */
+        xen_pfn_t     mfn;
+        /* INVLPG_LOCAL, INVLPG_ALL, SET_LDT */
+        ULONG_PTR linear_addr;
+    } arg1;
+    union {
+        /* SET_LDT */
+        unsigned int nr_ents;
+        /* TLB_FLUSH_MULTI, INVLPG_MULTI */
+#if __XEN_INTERFACE_VERSION__ >= 0x00030205
+        XEN_GUEST_HANDLE(const_void) vcpumask;
+#else
+        const void *vcpumask;
+#endif
+        /* COPY_PAGE */
+        xen_pfn_t src_mfn;
+    } arg2;
+};
+typedef struct mmuext_op mmuext_op_t;
+DEFINE_XEN_GUEST_HANDLE(mmuext_op_t);
+#endif
+
+/* These are passed as 'flags' to update_va_mapping. They can be ORed. */
+/* When specifying UVMF_MULTI, also OR in a pointer to a CPU bitmap.   */
+/* UVMF_LOCAL is merely UVMF_MULTI with a NULL bitmap pointer.         */
+#define UVMF_NONE               (0UL<<0) /* No flushing at all.   */
+#define UVMF_TLB_FLUSH          (1UL<<0) /* Flush entire TLB(s).  */
+#define UVMF_INVLPG             (2UL<<0) /* Flush only one entry. */
+#define UVMF_FLUSHTYPE_MASK     (3UL<<0)
+#define UVMF_MULTI              (0UL<<2) /* Flush subset of TLBs. */
+#define UVMF_LOCAL              (0UL<<2) /* Flush local TLB.      */
+#define UVMF_ALL                (1UL<<2) /* Flush all TLBs.       */
+
+/*
+ * Commands to HYPERVISOR_console_io().
+ */
+#define CONSOLEIO_write         0
+#define CONSOLEIO_read          1
+
+/*
+ * Commands to HYPERVISOR_vm_assist().
+ */
+#define VMASST_CMD_enable                0
+#define VMASST_CMD_disable               1
+
+/* x86/32 guests: simulate full 4GB segment limits. */
+#define VMASST_TYPE_4gb_segments         0
+
+/* x86/32 guests: trap (vector 15) whenever above vmassist is used. */
+#define VMASST_TYPE_4gb_segments_notify  1
+
+/*
+ * x86 guests: support writes to bottom-level PTEs.
+ * NB1. Page-directory entries cannot be written.
+ * NB2. Guest must continue to remove all writable mappings of PTEs.
+ */
+#define VMASST_TYPE_writable_pagetables  2
+
+/* x86/PAE guests: support PDPTs above 4GB. */
+#define VMASST_TYPE_pae_extended_cr3     3
+
+#define MAX_VMASST_TYPE                  3
+
+#ifndef __ASSEMBLY__
+
+typedef uint16_t domid_t;
+
+/* Domain ids >= DOMID_FIRST_RESERVED cannot be used for ordinary domains. */
+#define DOMID_FIRST_RESERVED (0x7FF0U)
+
+/* DOMID_SELF is used in certain contexts to refer to oneself. */
+#define DOMID_SELF (0x7FF0U)
+
+/*
+ * DOMID_IO is used to restrict page-table updates to mapping I/O memory.
+ * Although no Foreign Domain need be specified to map I/O pages, DOMID_IO
+ * is useful to ensure that no mappings to the OS's own heap are accidentally
+ * installed. (e.g., in Linux this could cause havoc as reference counts
+ * aren't adjusted on the I/O-mapping code path).
+ * This only makes sense in MMUEXT_SET_FOREIGNDOM, but in that context can
+ * be specified by any calling domain.
+ */
+#define DOMID_IO   (0x7FF1U)
+
+/*
+ * DOMID_XEN is used to allow privileged domains to map restricted parts of
+ * Xen's heap space (e.g., the machine_to_phys table).
+ * This only makes sense in MMUEXT_SET_FOREIGNDOM, and is only permitted if
+ * the caller is privileged.
+ */
+#define DOMID_XEN  (0x7FF2U)
+
+/*
+ * DOMID_COW is used as the owner of sharable pages */
+#define DOMID_COW  (0x7FF3U)
+
+/* DOMID_INVALID is used to identify pages with unknown owner. */
+#define DOMID_INVALID (0x7FF4U)
+
+/* Idle domain. */
+#define DOMID_IDLE (0x7FFFU)
+
+/*
+ * Send an array of these to HYPERVISOR_mmu_update().
+ * NB. The fields are natural pointer/address size for this architecture.
+ */
+struct mmu_update {
+    uint64_t ptr;       /* Machine address of PTE. */
+    uint64_t val;       /* New contents of PTE.    */
+};
+typedef struct mmu_update mmu_update_t;
+DEFINE_XEN_GUEST_HANDLE(mmu_update_t);
+
+/*
+ * Send an array of these to HYPERVISOR_multicall().
+ * NB. The fields are natural register size for this architecture.
+ */
+struct multicall_entry {
+    ULONG_PTR op, result;
+    ULONG_PTR args[6];
+};
+typedef struct multicall_entry multicall_entry_t;
+DEFINE_XEN_GUEST_HANDLE(multicall_entry_t);
+
+/*
+ * Event channel endpoints per domain:
+ *  1024 if a LONG_PTR is 32 bits; 4096 if a LONG_PTR is 64 bits.
+ */
+#define NR_EVENT_CHANNELS (sizeof(ULONG_PTR) * sizeof(ULONG_PTR) * 64)
+
+struct vcpu_time_info {
+    /*
+     * Updates to the following values are preceded and followed by an
+     * increment of 'version'. The guest can therefore detect updates by
+     * looking for changes to 'version'. If the least-significant bit of
+     * the version number is set then an update is in progress and the guest
+     * must wait to read a consistent set of values.
+     * The correct way to interact with the version number is similar to
+     * Linux's seqlock: see the implementations of read_seqbegin/read_seqretry.
+     */
+    uint32_t version;
+    uint32_t pad0;
+    uint64_t tsc_timestamp;   /* TSC at last update of time vals.  */
+    uint64_t system_time;     /* Time, in nanosecs, since boot.    */
+    /*
+     * Current system time:
+     *   system_time +
+     *   ((((tsc - tsc_timestamp) << tsc_shift) * tsc_to_system_mul) >> 32)
+     * CPU frequency (Hz):
+     *   ((10^9 << 32) / tsc_to_system_mul) >> tsc_shift
+     */
+    uint32_t tsc_to_system_mul;
+    int8_t   tsc_shift;
+    int8_t   pad1[3];
+}; /* 32 bytes */
+typedef struct vcpu_time_info vcpu_time_info_t;
+
+struct vcpu_info {
+    /*
+     * 'evtchn_upcall_pending' is written non-zero by Xen to indicate
+     * a pending notification for a particular VCPU. It is then cleared 
+     * by the guest OS /before/ checking for pending work, thus avoiding
+     * a set-and-check race. Note that the mask is only accessed by Xen
+     * on the CPU that is currently hosting the VCPU. This means that the
+     * pending and mask flags can be updated by the guest without special
+     * synchronisation (i.e., no need for the x86 LOCK prefix).
+     * This may seem suboptimal because if the pending flag is set by
+     * a different CPU then an IPI may be scheduled even when the mask
+     * is set. However, note:
+     *  1. The task of 'interrupt holdoff' is covered by the per-event-
+     *     channel mask bits. A 'noisy' event that is continually being
+     *     triggered can be masked at source at this very precise
+     *     granularity.
+     *  2. The main purpose of the per-VCPU mask is therefore to restrict
+     *     reentrant execution: whether for concurrency control, or to
+     *     prevent unbounded stack usage. Whatever the purpose, we expect
+     *     that the mask will be asserted only for short periods at a time,
+     *     and so the likelihood of a 'spurious' IPI is suitably small.
+     * The mask is read before making an event upcall to the guest: a
+     * non-zero mask therefore guarantees that the VCPU will not receive
+     * an upcall activation. The mask is cleared when the VCPU requests
+     * to block: this avoids wakeup-waiting races.
+     */
+    uint8_t evtchn_upcall_pending;
+    uint8_t evtchn_upcall_mask;
+    ULONG_PTR evtchn_pending_sel;
+    struct arch_vcpu_info arch;
+    struct vcpu_time_info time;
+}; /* 64 bytes (x86) */
+#ifndef __XEN__
+typedef struct vcpu_info vcpu_info_t;
+#endif
+
+/*
+ * Xen/kernel shared data -- pointer provided in start_info.
+ *
+ * This structure is defined to be both smaller than a page, and the
+ * only data on the shared page, but may vary in actual size even within
+ * compatible Xen versions; guests should not rely on the size
+ * of this structure remaining constant.
+ */
+struct shared_info {
+    struct vcpu_info vcpu_info[XEN_LEGACY_MAX_VCPUS];
+
+    /*
+     * A domain can create "event channels" on which it can send and receive
+     * asynchronous event notifications. There are three classes of event that
+     * are delivered by this mechanism:
+     *  1. Bi-directional inter- and intra-domain connections. Domains must
+     *     arrange out-of-band to set up a connection (usually by allocating
+     *     an unbound 'listener' port and avertising that via a storage service
+     *     such as xenstore).
+     *  2. Physical interrupts. A domain with suitable hardware-access
+     *     privileges can bind an event-channel port to a physical interrupt
+     *     source.
+     *  3. Virtual interrupts ('events'). A domain can bind an event-channel
+     *     port to a virtual interrupt source, such as the virtual-timer
+     *     device or the emergency console.
+     * 
+     * Event channels are addressed by a "port index". Each channel is
+     * associated with two bits of information:
+     *  1. PENDING -- notifies the domain that there is a pending notification
+     *     to be processed. This bit is cleared by the guest.
+     *  2. MASK -- if this bit is clear then a 0->1 transition of PENDING
+     *     will cause an asynchronous upcall to be scheduled. This bit is only
+     *     updated by the guest. It is read-only within Xen. If a channel
+     *     becomes pending while the channel is masked then the 'edge' is lost
+     *     (i.e., when the channel is unmasked, the guest must manually handle
+     *     pending notifications as no upcall will be scheduled by Xen).
+     * 
+     * To expedite scanning of pending notifications, any 0->1 pending
+     * transition on an unmasked channel causes a corresponding bit in a
+     * per-vcpu selector word to be set. Each bit in the selector covers a
+     * 'C LONG_PTR' in the PENDING bitfield array.
+     */
+    ULONG_PTR evtchn_pending[sizeof(ULONG_PTR) * 8];
+    ULONG_PTR evtchn_mask[sizeof(ULONG_PTR) * 8];
+
+    /*
+     * Wallclock time: updated only by control software. Guests should base
+     * their gettimeofday() syscall on this wallclock-base value.
+     */
+    uint32_t wc_version;      /* Version counter: see vcpu_time_info_t. */
+    uint32_t wc_sec;          /* Secs  00:00:00 UTC, Jan 1, 1970.  */
+    uint32_t wc_nsec;         /* Nsecs 00:00:00 UTC, Jan 1, 1970.  */
+
+    struct arch_shared_info arch;
+
+};
+#ifndef __XEN__
+typedef struct shared_info shared_info_t;
+#endif
+
+/*
+ * Start-of-day memory layout:
+ *  1. The domain is started within contiguous virtual-memory region.
+ *  2. The contiguous region ends on an aligned 4MB boundary.
+ *  3. This the order of bootstrap elements in the initial virtual region:
+ *      a. relocated kernel image
+ *      b. initial ram disk              [mod_start, mod_len]
+ *      c. list of allocated page frames [mfn_list, nr_pages]
+ *         (unless relocated due to XEN_ELFNOTE_INIT_P2M)
+ *      d. start_info_t structure        [register ESI (x86)]
+ *      e. bootstrap page tables         [pt_base, CR3 (x86)]
+ *      f. bootstrap stack               [register ESP (x86)]
+ *  4. Bootstrap elements are packed together, but each is 4kB-aligned.
+ *  5. The initial ram disk may be omitted.
+ *  6. The list of page frames forms a contiguous 'pseudo-physical' memory
+ *     layout for the domain. In particular, the bootstrap virtual-memory
+ *     region is a 1:1 mapping to the first section of the pseudo-physical map.
+ *  7. All bootstrap elements are mapped read-writable for the guest OS. The
+ *     only exception is the bootstrap page table, which is mapped read-only.
+ *  8. There is guaranteed to be at least 512kB padding after the final
+ *     bootstrap element. If necessary, the bootstrap virtual region is
+ *     extended by an extra 4MB to ensure this.
+ */
+
+#define MAX_GUEST_CMDLINE 1024
+struct start_info {
+    /* THE FOLLOWING ARE FILLED IN BOTH ON INITIAL BOOT AND ON RESUME.    */
+    char magic[32];             /* "xen-<version>-<platform>".            */
+    ULONG_PTR nr_pages;     /* Total pages allocated to this domain.  */
+    ULONG_PTR shared_info;  /* MACHINE address of shared info struct. */
+    uint32_t flags;             /* SIF_xxx flags.                         */
+    xen_pfn_t store_mfn;        /* MACHINE page number of shared page.    */
+    uint32_t store_evtchn;      /* Event channel for store communication. */
+    union {
+        struct {
+            xen_pfn_t mfn;      /* MACHINE page number of console page.   */
+            uint32_t  evtchn;   /* Event channel for console page.        */
+        } domU;
+        struct {
+            uint32_t info_off;  /* Offset of console_info struct.         */
+            uint32_t info_size; /* Size of console_info struct from start.*/
+        } dom0;
+    } console;
+    /* THE FOLLOWING ARE ONLY FILLED IN ON INITIAL BOOT (NOT RESUME).     */
+    ULONG_PTR pt_base;      /* VIRTUAL address of page directory.     */
+    ULONG_PTR nr_pt_frames; /* Number of bootstrap p.t. frames.       */
+    ULONG_PTR mfn_list;     /* VIRTUAL address of page-frame list.    */
+    ULONG_PTR mod_start;    /* VIRTUAL address of pre-loaded module   */
+                                /* (PFN of pre-loaded module if           */
+                                /*  SIF_MOD_START_PFN set in flags).      */
+    ULONG_PTR mod_len;      /* Size (bytes) of pre-loaded module.     */
+    int8_t cmd_line[MAX_GUEST_CMDLINE];
+    /* The pfn range here covers both page table and p->m table frames.   */
+    ULONG_PTR first_p2m_pfn;/* 1st pfn forming initial P->M table.    */
+    ULONG_PTR nr_p2m_frames;/* # of pfns forming initial P->M table.  */
+};
+typedef struct start_info start_info_t;
+
+/* New console union for dom0 introduced in 0x00030203. */
+#if __XEN_INTERFACE_VERSION__ < 0x00030203
+#define console_mfn    console.domU.mfn
+#define console_evtchn console.domU.evtchn
+#endif
+
+/* These flags are passed in the 'flags' field of start_info_t. */
+#define SIF_PRIVILEGED    (1<<0)  /* Is the domain privileged? */
+#define SIF_INITDOMAIN    (1<<1)  /* Is this the initial control domain? */
+#define SIF_MULTIBOOT_MOD (1<<2)  /* Is mod_start a multiboot module? */
+#define SIF_MOD_START_PFN (1<<3)  /* Is mod_start a PFN? */
+#define SIF_PM_MASK       (0xFF<<8) /* reserve 1 byte for xen-pm options */
+
+/*
+ * A multiboot module is a package containing modules very similar to a
+ * multiboot module array. The only differences are:
+ * - the array of module descriptors is by convention simply at the beginning
+ *   of the multiboot module,
+ * - addresses in the module descriptors are based on the beginning of the
+ *   multiboot module,
+ * - the number of modules is determined by a termination descriptor that has
+ *   mod_start == 0.
+ *
+ * This permits to both build it statically and reference it in a configuration
+ * file, and let the PV guest easily rebase the addresses to virtual addresses
+ * and at the same time count the number of modules.
+ */
+struct xen_multiboot_mod_list
+{
+    /* Address of first byte of the module */
+    uint32_t mod_start;
+    /* Address of last byte of the module (inclusive) */
+    uint32_t mod_end;
+    /* Address of zero-terminated command line */
+    uint32_t cmdline;
+    /* Unused, must be zero */
+    uint32_t pad;
+};
+
+typedef struct dom0_vga_console_info {
+    uint8_t video_type; /* DOM0_VGA_CONSOLE_??? */
+#define XEN_VGATYPE_TEXT_MODE_3 0x03
+#define XEN_VGATYPE_VESA_LFB    0x23
+
+    union {
+        struct {
+            /* Font height, in pixels. */
+            uint16_t font_height;
+            /* Cursor location (column, row). */
+            uint16_t cursor_x, cursor_y;
+            /* Number of rows and columns (dimensions in characters). */
+            uint16_t rows, columns;
+        } text_mode_3;
+
+        struct {
+            /* Width and height, in pixels. */
+            uint16_t width, height;
+            /* Bytes per scan line. */
+            uint16_t bytes_per_line;
+            /* Bits per pixel. */
+            uint16_t bits_per_pixel;
+            /* LFB physical address, and size (in units of 64kB). */
+            uint32_t lfb_base;
+            uint32_t lfb_size;
+            /* RGB mask offsets and sizes, as defined by VBE 1.2+ */
+            uint8_t  red_pos, red_size;
+            uint8_t  green_pos, green_size;
+            uint8_t  blue_pos, blue_size;
+            uint8_t  rsvd_pos, rsvd_size;
+#if __XEN_INTERFACE_VERSION__ >= 0x00030206
+            /* VESA capabilities (offset 0xa, VESA command 0x4f00). */
+            uint32_t gbl_caps;
+            /* Mode attributes (offset 0x0, VESA command 0x4f01). */
+            uint16_t mode_attrs;
+#endif
+        } vesa_lfb;
+    } u;
+} dom0_vga_console_info_t;
+#define xen_vga_console_info dom0_vga_console_info
+#define xen_vga_console_info_t dom0_vga_console_info_t
+
+typedef uint8_t xen_domain_handle_t[16];
+
+/* Turn a plain number into a C ULONG_PTR constant. */
+#define __mk_unsigned_long(x) x ## UL
+#define mk_unsigned_long(x) __mk_unsigned_long(x)
+
+__DEFINE_XEN_GUEST_HANDLE(uint8,  uint8_t);
+__DEFINE_XEN_GUEST_HANDLE(uint16, uint16_t);
+__DEFINE_XEN_GUEST_HANDLE(uint32, uint32_t);
+__DEFINE_XEN_GUEST_HANDLE(uint64, uint64_t);
+
+#else /* __ASSEMBLY__ */
+
+/* In assembly code we cannot use C numeric constant suffixes. */
+#define mk_unsigned_long(x) x
+
+#endif /* !__ASSEMBLY__ */
+
+/* Default definitions for macros used by domctl/sysctl. */
+#if defined(__XEN__) || defined(__XEN_TOOLS__)
+
+#ifndef uint64_aligned_t
+#define uint64_aligned_t uint64_t
+#endif
+#ifndef XEN_GUEST_HANDLE_64
+#define XEN_GUEST_HANDLE_64(name) XEN_GUEST_HANDLE(name)
+#endif
+
+#ifndef __ASSEMBLY__
+struct xenctl_cpumap {
+    XEN_GUEST_HANDLE_64(uint8) bitmap;
+    uint32_t nr_cpus;
+};
+#endif
+
+#endif /* defined(__XEN__) || defined(__XEN_TOOLS__) */
+
+#endif /* __XEN_PUBLIC_XEN_H__ */
+
+/*
+ * Local variables:
+ * mode: C
+ * c-set-style: "BSD"
+ * c-basic-offset: 4
+ * tab-width: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
diff --git a/kdfiles.py b/kdfiles.py
new file mode 100644 (file)
index 0000000..71bb406
--- /dev/null
@@ -0,0 +1,37 @@
+#!python -u
+
+import os, sys
+import subprocess
+from pprint import pprint
+
+def callfnout(cmd):
+    print(cmd)
+
+    sub = subprocess.Popen(cmd, stdout=subprocess.PIPE)
+    output = sub.communicate()[0]
+    ret = sub.returncode
+
+    if ret != 0:
+        raise(Exception("Error %d in : %s" % (ret, cmd)))
+
+    return output.decode('utf-8')
+
+def regenerate_kdfiles(filename, arch, pkg, source):
+       cwd = os.getcwd()
+       file = open(filename, 'w')
+       os.chdir(pkg + '/' + arch)
+       drivers = callfnout(['ls','*.sys']).split()
+       pprint(drivers)
+       for driver in drivers:
+               file.write("map\n")
+               file.write('\SystemRoot\System32\drivers\\' + driver + '\n')
+               file.write(source + '\\' + pkg + '\\' + arch + '\\' + driver + '\n')
+               file.write('\n')
+       os.chdir(cwd)
+       file.close()
+
+if __name__ == '__main__':
+       pkg = 'xenvif'
+       source = os.getcwd()
+       regenerate_kdfiles('kdfiles32.txt', 'x86', pkg, source)
+       regenerate_kdfiles('kdfiles64.txt', 'x64', pkg, source)
diff --git a/proj/msbuild.bat b/proj/msbuild.bat
new file mode 100644 (file)
index 0000000..0d3f6cb
--- /dev/null
@@ -0,0 +1,7 @@
+call "%VS%\VC\vcvarsall.bat" x86
+msbuild.exe /m:4 /p:Configuration="%CONFIGURATION%" /p:Platform="%PLATFORM%" /t:"%TARGET%" /p:SignMode="ProductionSign" %SOLUTION%.sln
+if errorlevel 1 goto error
+exit 0
+
+:error
+exit 1
diff --git a/proj/package/package.vcxproj b/proj/package/package.vcxproj
new file mode 100644 (file)
index 0000000..3ee67e9
--- /dev/null
@@ -0,0 +1,159 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <ItemGroup Label="ProjectConfigurations">
+    <ProjectConfiguration Include="Windows Developer Preview Debug|Win32">
+      <Configuration>Windows Developer Preview Debug</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Windows Developer Preview Release|Win32">
+      <Configuration>Windows Developer Preview Release</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Windows 7 Debug|Win32">
+      <Configuration>Windows 7 Debug</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Windows 7 Release|Win32">
+      <Configuration>Windows 7 Release</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Windows Vista Debug|Win32">
+      <Configuration>Windows Vista Debug</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Windows Vista Release|Win32">
+      <Configuration>Windows Vista Release</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Windows Developer Preview Debug|x64">
+      <Configuration>Windows Developer Preview Debug</Configuration>
+      <Platform>x64</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Windows Developer Preview Release|x64">
+      <Configuration>Windows Developer Preview Release</Configuration>
+      <Platform>x64</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Windows 7 Debug|x64">
+      <Configuration>Windows 7 Debug</Configuration>
+      <Platform>x64</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Windows 7 Release|x64">
+      <Configuration>Windows 7 Release</Configuration>
+      <Platform>x64</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Windows Vista Debug|x64">
+      <Configuration>Windows Vista Debug</Configuration>
+      <Platform>x64</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Windows Vista Release|x64">
+      <Configuration>Windows Vista Release</Configuration>
+      <Platform>x64</Platform>
+    </ProjectConfiguration>
+  </ItemGroup>
+  <PropertyGroup Label="Globals">
+    <ProjectGuid>{73768CC9-DB26-4297-9EC8-1042F815EB15}</ProjectGuid>
+    <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
+    <MinimumVisualStudioVersion>11.0</MinimumVisualStudioVersion>
+  </PropertyGroup>
+  <PropertyGroup Label="PropertySheets">
+    <PlatformToolset>WindowsKernelModeDriver8.0</PlatformToolset>
+    <ConfigurationType>Utility</ConfigurationType>
+    <DriverType>Package</DriverType>
+    <Configuration>Windows Developer Preview Debug</Configuration>
+    <DisableFastUpToDateCheck>true</DisableFastUpToDateCheck>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Windows Developer Preview Debug|Win32'" Label="Configuration">
+    <TargetVersion>Windows8</TargetVersion>
+    <UseDebugLibraries>true</UseDebugLibraries>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Windows Developer Preview Release|Win32'" Label="Configuration">
+    <TargetVersion>Windows8</TargetVersion>
+    <UseDebugLibraries>false</UseDebugLibraries>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Windows 7 Debug|Win32'" Label="Configuration">
+    <TargetVersion>Windows7</TargetVersion>
+    <UseDebugLibraries>true</UseDebugLibraries>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Windows 7 Release|Win32'" Label="Configuration">
+    <TargetVersion>Windows7</TargetVersion>
+    <UseDebugLibraries>false</UseDebugLibraries>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Windows Vista Debug|Win32'" Label="Configuration">
+    <TargetVersion>Vista</TargetVersion>
+    <UseDebugLibraries>true</UseDebugLibraries>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Windows Vista Release|Win32'" Label="Configuration">
+    <TargetVersion>Vista</TargetVersion>
+    <UseDebugLibraries>false</UseDebugLibraries>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Windows Developer Preview Debug|x64'" Label="Configuration">
+    <TargetVersion>Windows8</TargetVersion>
+    <UseDebugLibraries>true</UseDebugLibraries>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Windows Developer Preview Release|x64'" Label="Configuration">
+    <TargetVersion>Windows8</TargetVersion>
+    <UseDebugLibraries>false</UseDebugLibraries>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Windows 7 Debug|x64'" Label="Configuration">
+    <TargetVersion>Windows7</TargetVersion>
+    <UseDebugLibraries>true</UseDebugLibraries>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Windows 7 Release|x64'" Label="Configuration">
+    <TargetVersion>Windows7</TargetVersion>
+    <UseDebugLibraries>false</UseDebugLibraries>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Windows Vista Debug|x64'" Label="Configuration">
+    <TargetVersion>Vista</TargetVersion>
+    <UseDebugLibraries>true</UseDebugLibraries>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Windows Vista Release|x64'" Label="Configuration">
+    <TargetVersion>Vista</TargetVersion>
+    <UseDebugLibraries>false</UseDebugLibraries>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+  <ImportGroup Label="ExtensionSettings">
+  </ImportGroup>
+  <ImportGroup Label="PropertySheets">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <PropertyGroup Label="UserMacros" />
+  <PropertyGroup>
+    <EnableInf2cat>true</EnableInf2cat>
+    <Inf2CatWindowsVersionList Condition="'$(Platform)'=='x64'">Vista_$(DDKPlatform);7_$(DDKPlatform);Server2008_$(DDKPlatform);Server2008R2_$(DDKPlatform)</Inf2CatWindowsVersionList>
+    <Inf2CatWindowsVersionList Condition="'$(Platform)'=='Win32'">Vista_$(DDKPlatform);7_$(DDKPlatform);Server2008_$(DDKPlatform)</Inf2CatWindowsVersionList>
+  </PropertyGroup>
+  <PropertyGroup>
+    <DebuggerFlavor>DbgengKernelDebugger</DebuggerFlavor>
+    <EnableDeployment>False</EnableDeployment>
+    <ImportToStore>False</ImportToStore>
+    <InstallMode>None</InstallMode>
+    <HardwareIdString />
+    <CommandLine />
+    <ScriptPath />
+    <DeployFiles />
+    <ScriptName />
+    <ScriptDeviceQuery>%PathToInf%</ScriptDeviceQuery>
+    <EnableVerifier>False</EnableVerifier>
+    <AllDrivers>False</AllDrivers>
+    <VerifyProjectOutput>True</VerifyProjectOutput>
+    <VerifyDrivers />
+    <VerifyFlags>133563</VerifyFlags>
+    <PackageDir>..\..\xenvif\$(DDKPlatform)</PackageDir>
+  </PropertyGroup>
+  <ItemGroup>
+    <ProjectReference Include="..\xenvif\xenvif.vcxproj">
+      <Project>{C3F96D4C-E441-47F7-A44C-D2D0543C1D18}</Project>
+    </ProjectReference>
+    <ProjectReference Include="..\xenvif_coinst\xenvif_coinst.vcxproj">
+      <Project>{2BFAC7E6-3420-47A5-A092-BDC5C9D78A44}</Project>
+    </ProjectReference>
+  </ItemGroup>
+  <ItemGroup>
+    <FilesToPackage Include="$(KIT)\Redist\DIFx\dpinst\EngMui\x86\dpinst.exe" Condition="'$(Platform)'=='Win32'" />
+    <FilesToPackage Include="$(KIT)\Redist\DIFx\dpinst\EngMui\x64\dpinst.exe" Condition="'$(Platform)'=='x64'" />
+  </ItemGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+  <ImportGroup Label="ExtensionTargets">
+  </ImportGroup>
+</Project>
\ No newline at end of file
diff --git a/proj/package/package.vcxproj.user b/proj/package/package.vcxproj.user
new file mode 100644 (file)
index 0000000..7dea6a2
--- /dev/null
@@ -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>ProductionSign</SignMode>
+    <ProductionCertificate>CN="Citrix Systems, Inc.", OU=Digital ID Class 3 - Microsoft Software Validation v2, O="Citrix Systems, Inc.", L=Santa Clara, S=California, C=US | 9505C081954CC0A3E8B904458ACACD72EABD98B5</ProductionCertificate>
+    <TimeStampServer>http://timestamp.verisign.com/scripts/timstamp.dll</TimeStampServer>
+  </PropertyGroup>
+</Project>
\ No newline at end of file
diff --git a/proj/xenvif.sln b/proj/xenvif.sln
new file mode 100644 (file)
index 0000000..46e21fc
--- /dev/null
@@ -0,0 +1,134 @@
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio 11
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "xenvif", "xenvif\xenvif.vcxproj", "{C3F96D4C-E441-47F7-A44C-D2D0543C1D18}"
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "package", "package\package.vcxproj", "{73768CC9-DB26-4297-9EC8-1042F815EB15}"
+       ProjectSection(ProjectDependencies) = postProject
+               {2BFAC7E6-3420-47A5-A092-BDC5C9D78A44} = {2BFAC7E6-3420-47A5-A092-BDC5C9D78A44}
+       EndProjectSection
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "xenvif_coinst", "xenvif_coinst\xenvif_coinst.vcxproj", "{2BFAC7E6-3420-47A5-A092-BDC5C9D78A44}"
+EndProject
+Global
+       GlobalSection(SolutionConfigurationPlatforms) = preSolution
+               Windows 7 Debug|Win32 = Windows 7 Debug|Win32
+               Windows 7 Debug|x64 = Windows 7 Debug|x64
+               Windows 7 Release|Win32 = Windows 7 Release|Win32
+               Windows 7 Release|x64 = Windows 7 Release|x64
+               Windows Developer Preview Debug|Win32 = Windows Developer Preview Debug|Win32
+               Windows Developer Preview Debug|x64 = Windows Developer Preview Debug|x64
+               Windows Developer Preview Release|Win32 = Windows Developer Preview Release|Win32
+               Windows Developer Preview Release|x64 = Windows Developer Preview Release|x64
+               Windows Vista Debug|Win32 = Windows Vista Debug|Win32
+               Windows Vista Debug|x64 = Windows Vista Debug|x64
+               Windows Vista Release|Win32 = Windows Vista Release|Win32
+               Windows Vista Release|x64 = Windows Vista Release|x64
+       EndGlobalSection
+       GlobalSection(ProjectConfigurationPlatforms) = postSolution
+               {2BFAC7E6-3420-47A5-A092-BDC5C9D78A44}.Windows 7 Debug|Win32.ActiveCfg = Windows 7 Debug|Win32
+               {2BFAC7E6-3420-47A5-A092-BDC5C9D78A44}.Windows 7 Debug|Win32.Build.0 = Windows 7 Debug|Win32
+               {2BFAC7E6-3420-47A5-A092-BDC5C9D78A44}.Windows 7 Debug|Win32.Deploy.0 = Windows 7 Debug|Win32
+               {2BFAC7E6-3420-47A5-A092-BDC5C9D78A44}.Windows 7 Debug|x64.ActiveCfg = Windows 7 Debug|x64
+               {2BFAC7E6-3420-47A5-A092-BDC5C9D78A44}.Windows 7 Debug|x64.Build.0 = Windows 7 Debug|x64
+               {2BFAC7E6-3420-47A5-A092-BDC5C9D78A44}.Windows 7 Release|Win32.ActiveCfg = Windows 7 Release|Win32
+               {2BFAC7E6-3420-47A5-A092-BDC5C9D78A44}.Windows 7 Release|Win32.Build.0 = Windows 7 Release|Win32
+               {2BFAC7E6-3420-47A5-A092-BDC5C9D78A44}.Windows 7 Release|Win32.Deploy.0 = Windows 7 Release|Win32
+               {2BFAC7E6-3420-47A5-A092-BDC5C9D78A44}.Windows 7 Release|x64.ActiveCfg = Windows 7 Release|x64
+               {2BFAC7E6-3420-47A5-A092-BDC5C9D78A44}.Windows 7 Release|x64.Build.0 = Windows 7 Release|x64
+               {2BFAC7E6-3420-47A5-A092-BDC5C9D78A44}.Windows Developer Preview Debug|Win32.ActiveCfg = Windows Developer Preview Debug|Win32
+               {2BFAC7E6-3420-47A5-A092-BDC5C9D78A44}.Windows Developer Preview Debug|Win32.Build.0 = Windows Developer Preview Debug|Win32
+               {2BFAC7E6-3420-47A5-A092-BDC5C9D78A44}.Windows Developer Preview Debug|Win32.Deploy.0 = Windows Developer Preview Debug|Win32
+               {2BFAC7E6-3420-47A5-A092-BDC5C9D78A44}.Windows Developer Preview Debug|x64.ActiveCfg = Windows Developer Preview Debug|x64
+               {2BFAC7E6-3420-47A5-A092-BDC5C9D78A44}.Windows Developer Preview Debug|x64.Build.0 = Windows Developer Preview Debug|x64
+               {2BFAC7E6-3420-47A5-A092-BDC5C9D78A44}.Windows Developer Preview Release|Win32.ActiveCfg = Windows Developer Preview Release|Win32
+               {2BFAC7E6-3420-47A5-A092-BDC5C9D78A44}.Windows Developer Preview Release|Win32.Build.0 = Windows Developer Preview Release|Win32
+               {2BFAC7E6-3420-47A5-A092-BDC5C9D78A44}.Windows Developer Preview Release|Win32.Deploy.0 = Windows Developer Preview Release|Win32
+               {2BFAC7E6-3420-47A5-A092-BDC5C9D78A44}.Windows Developer Preview Release|x64.ActiveCfg = Windows Developer Preview Release|x64
+               {2BFAC7E6-3420-47A5-A092-BDC5C9D78A44}.Windows Developer Preview Release|x64.Build.0 = Windows Developer Preview Release|x64
+               {2BFAC7E6-3420-47A5-A092-BDC5C9D78A44}.Windows Vista Debug|Win32.ActiveCfg = Windows Vista Debug|Win32
+               {2BFAC7E6-3420-47A5-A092-BDC5C9D78A44}.Windows Vista Debug|Win32.Build.0 = Windows Vista Debug|Win32
+               {2BFAC7E6-3420-47A5-A092-BDC5C9D78A44}.Windows Vista Debug|Win32.Deploy.0 = Windows Vista Debug|Win32
+               {2BFAC7E6-3420-47A5-A092-BDC5C9D78A44}.Windows Vista Debug|x64.ActiveCfg = Windows Vista Debug|x64
+               {2BFAC7E6-3420-47A5-A092-BDC5C9D78A44}.Windows Vista Debug|x64.Build.0 = Windows Vista Debug|x64
+               {2BFAC7E6-3420-47A5-A092-BDC5C9D78A44}.Windows Vista Release|Win32.ActiveCfg = Windows Vista Release|Win32
+               {2BFAC7E6-3420-47A5-A092-BDC5C9D78A44}.Windows Vista Release|Win32.Build.0 = Windows Vista Release|Win32
+               {2BFAC7E6-3420-47A5-A092-BDC5C9D78A44}.Windows Vista Release|Win32.Deploy.0 = Windows Vista Release|Win32
+               {2BFAC7E6-3420-47A5-A092-BDC5C9D78A44}.Windows Vista Release|x64.ActiveCfg = Windows Vista Release|x64
+               {2BFAC7E6-3420-47A5-A092-BDC5C9D78A44}.Windows Vista Release|x64.Build.0 = Windows Vista Release|x64
+               {73768CC9-DB26-4297-9EC8-1042F815EB15}.Windows 7 Debug|Win32.ActiveCfg = Windows 7 Debug|Win32
+               {73768CC9-DB26-4297-9EC8-1042F815EB15}.Windows 7 Debug|Win32.Build.0 = Windows 7 Debug|Win32
+               {73768CC9-DB26-4297-9EC8-1042F815EB15}.Windows 7 Debug|Win32.Deploy.0 = Windows 7 Debug|Win32
+               {73768CC9-DB26-4297-9EC8-1042F815EB15}.Windows 7 Debug|x64.ActiveCfg = Windows 7 Debug|x64
+               {73768CC9-DB26-4297-9EC8-1042F815EB15}.Windows 7 Debug|x64.Build.0 = Windows 7 Debug|x64
+               {73768CC9-DB26-4297-9EC8-1042F815EB15}.Windows 7 Debug|x64.Deploy.0 = Windows 7 Debug|x64
+               {73768CC9-DB26-4297-9EC8-1042F815EB15}.Windows 7 Release|Win32.ActiveCfg = Windows 7 Release|Win32
+               {73768CC9-DB26-4297-9EC8-1042F815EB15}.Windows 7 Release|Win32.Build.0 = Windows 7 Release|Win32
+               {73768CC9-DB26-4297-9EC8-1042F815EB15}.Windows 7 Release|Win32.Deploy.0 = Windows 7 Release|Win32
+               {73768CC9-DB26-4297-9EC8-1042F815EB15}.Windows 7 Release|x64.ActiveCfg = Windows 7 Release|x64
+               {73768CC9-DB26-4297-9EC8-1042F815EB15}.Windows 7 Release|x64.Build.0 = Windows 7 Release|x64
+               {73768CC9-DB26-4297-9EC8-1042F815EB15}.Windows 7 Release|x64.Deploy.0 = Windows 7 Release|x64
+               {73768CC9-DB26-4297-9EC8-1042F815EB15}.Windows Developer Preview Debug|Win32.ActiveCfg = Windows Developer Preview Debug|Win32
+               {73768CC9-DB26-4297-9EC8-1042F815EB15}.Windows Developer Preview Debug|Win32.Build.0 = Windows Developer Preview Debug|Win32
+               {73768CC9-DB26-4297-9EC8-1042F815EB15}.Windows Developer Preview Debug|Win32.Deploy.0 = Windows Developer Preview Debug|Win32
+               {73768CC9-DB26-4297-9EC8-1042F815EB15}.Windows Developer Preview Debug|x64.ActiveCfg = Windows Developer Preview Debug|x64
+               {73768CC9-DB26-4297-9EC8-1042F815EB15}.Windows Developer Preview Debug|x64.Build.0 = Windows Developer Preview Debug|x64
+               {73768CC9-DB26-4297-9EC8-1042F815EB15}.Windows Developer Preview Debug|x64.Deploy.0 = Windows Developer Preview Debug|x64
+               {73768CC9-DB26-4297-9EC8-1042F815EB15}.Windows Developer Preview Release|Win32.ActiveCfg = Windows Developer Preview Release|Win32
+               {73768CC9-DB26-4297-9EC8-1042F815EB15}.Windows Developer Preview Release|Win32.Build.0 = Windows Developer Preview Release|Win32
+               {73768CC9-DB26-4297-9EC8-1042F815EB15}.Windows Developer Preview Release|Win32.Deploy.0 = Windows Developer Preview Release|Win32
+               {73768CC9-DB26-4297-9EC8-1042F815EB15}.Windows Developer Preview Release|x64.ActiveCfg = Windows Developer Preview Release|x64
+               {73768CC9-DB26-4297-9EC8-1042F815EB15}.Windows Developer Preview Release|x64.Build.0 = Windows Developer Preview Release|x64
+               {73768CC9-DB26-4297-9EC8-1042F815EB15}.Windows Developer Preview Release|x64.Deploy.0 = Windows Developer Preview Release|x64
+               {73768CC9-DB26-4297-9EC8-1042F815EB15}.Windows Vista Debug|Win32.ActiveCfg = Windows Vista Debug|Win32
+               {73768CC9-DB26-4297-9EC8-1042F815EB15}.Windows Vista Debug|Win32.Build.0 = Windows Vista Debug|Win32
+               {73768CC9-DB26-4297-9EC8-1042F815EB15}.Windows Vista Debug|Win32.Deploy.0 = Windows Vista Debug|Win32
+               {73768CC9-DB26-4297-9EC8-1042F815EB15}.Windows Vista Debug|x64.ActiveCfg = Windows Vista Debug|x64
+               {73768CC9-DB26-4297-9EC8-1042F815EB15}.Windows Vista Debug|x64.Build.0 = Windows Vista Debug|x64
+               {73768CC9-DB26-4297-9EC8-1042F815EB15}.Windows Vista Debug|x64.Deploy.0 = Windows Vista Debug|x64
+               {73768CC9-DB26-4297-9EC8-1042F815EB15}.Windows Vista Release|Win32.ActiveCfg = Windows Vista Release|Win32
+               {73768CC9-DB26-4297-9EC8-1042F815EB15}.Windows Vista Release|Win32.Build.0 = Windows Vista Release|Win32
+               {73768CC9-DB26-4297-9EC8-1042F815EB15}.Windows Vista Release|Win32.Deploy.0 = Windows Vista Release|Win32
+               {73768CC9-DB26-4297-9EC8-1042F815EB15}.Windows Vista Release|x64.ActiveCfg = Windows Vista Release|x64
+               {73768CC9-DB26-4297-9EC8-1042F815EB15}.Windows Vista Release|x64.Build.0 = Windows Vista Release|x64
+               {73768CC9-DB26-4297-9EC8-1042F815EB15}.Windows Vista Release|x64.Deploy.0 = Windows Vista Release|x64
+               {C3F96D4C-E441-47F7-A44C-D2D0543C1D18}.Windows 7 Debug|Win32.ActiveCfg = Windows 7 Debug|Win32
+               {C3F96D4C-E441-47F7-A44C-D2D0543C1D18}.Windows 7 Debug|Win32.Build.0 = Windows 7 Debug|Win32
+               {C3F96D4C-E441-47F7-A44C-D2D0543C1D18}.Windows 7 Debug|Win32.Deploy.0 = Windows 7 Debug|Win32
+               {C3F96D4C-E441-47F7-A44C-D2D0543C1D18}.Windows 7 Debug|x64.ActiveCfg = Windows 7 Debug|x64
+               {C3F96D4C-E441-47F7-A44C-D2D0543C1D18}.Windows 7 Debug|x64.Build.0 = Windows 7 Debug|x64
+               {C3F96D4C-E441-47F7-A44C-D2D0543C1D18}.Windows 7 Debug|x64.Deploy.0 = Windows 7 Debug|x64
+               {C3F96D4C-E441-47F7-A44C-D2D0543C1D18}.Windows 7 Release|Win32.ActiveCfg = Windows 7 Release|Win32
+               {C3F96D4C-E441-47F7-A44C-D2D0543C1D18}.Windows 7 Release|Win32.Build.0 = Windows 7 Release|Win32
+               {C3F96D4C-E441-47F7-A44C-D2D0543C1D18}.Windows 7 Release|Win32.Deploy.0 = Windows 7 Release|Win32
+               {C3F96D4C-E441-47F7-A44C-D2D0543C1D18}.Windows 7 Release|x64.ActiveCfg = Windows 7 Release|x64
+               {C3F96D4C-E441-47F7-A44C-D2D0543C1D18}.Windows 7 Release|x64.Build.0 = Windows 7 Release|x64
+               {C3F96D4C-E441-47F7-A44C-D2D0543C1D18}.Windows 7 Release|x64.Deploy.0 = Windows 7 Release|x64
+               {C3F96D4C-E441-47F7-A44C-D2D0543C1D18}.Windows Developer Preview Debug|Win32.ActiveCfg = Windows Developer Preview Debug|Win32
+               {C3F96D4C-E441-47F7-A44C-D2D0543C1D18}.Windows Developer Preview Debug|Win32.Build.0 = Windows Developer Preview Debug|Win32
+               {C3F96D4C-E441-47F7-A44C-D2D0543C1D18}.Windows Developer Preview Debug|Win32.Deploy.0 = Windows Developer Preview Debug|Win32
+               {C3F96D4C-E441-47F7-A44C-D2D0543C1D18}.Windows Developer Preview Debug|x64.ActiveCfg = Windows Developer Preview Debug|x64
+               {C3F96D4C-E441-47F7-A44C-D2D0543C1D18}.Windows Developer Preview Debug|x64.Build.0 = Windows Developer Preview Debug|x64
+               {C3F96D4C-E441-47F7-A44C-D2D0543C1D18}.Windows Developer Preview Debug|x64.Deploy.0 = Windows Developer Preview Debug|x64
+               {C3F96D4C-E441-47F7-A44C-D2D0543C1D18}.Windows Developer Preview Release|Win32.ActiveCfg = Windows Developer Preview Release|Win32
+               {C3F96D4C-E441-47F7-A44C-D2D0543C1D18}.Windows Developer Preview Release|Win32.Build.0 = Windows Developer Preview Release|Win32
+               {C3F96D4C-E441-47F7-A44C-D2D0543C1D18}.Windows Developer Preview Release|Win32.Deploy.0 = Windows Developer Preview Release|Win32
+               {C3F96D4C-E441-47F7-A44C-D2D0543C1D18}.Windows Developer Preview Release|x64.ActiveCfg = Windows Developer Preview Release|x64
+               {C3F96D4C-E441-47F7-A44C-D2D0543C1D18}.Windows Developer Preview Release|x64.Build.0 = Windows Developer Preview Release|x64
+               {C3F96D4C-E441-47F7-A44C-D2D0543C1D18}.Windows Developer Preview Release|x64.Deploy.0 = Windows Developer Preview Release|x64
+               {C3F96D4C-E441-47F7-A44C-D2D0543C1D18}.Windows Vista Debug|Win32.ActiveCfg = Windows Vista Debug|Win32
+               {C3F96D4C-E441-47F7-A44C-D2D0543C1D18}.Windows Vista Debug|Win32.Build.0 = Windows Vista Debug|Win32
+               {C3F96D4C-E441-47F7-A44C-D2D0543C1D18}.Windows Vista Debug|Win32.Deploy.0 = Windows Vista Debug|Win32
+               {C3F96D4C-E441-47F7-A44C-D2D0543C1D18}.Windows Vista Debug|x64.ActiveCfg = Windows Vista Debug|x64
+               {C3F96D4C-E441-47F7-A44C-D2D0543C1D18}.Windows Vista Debug|x64.Build.0 = Windows Vista Debug|x64
+               {C3F96D4C-E441-47F7-A44C-D2D0543C1D18}.Windows Vista Debug|x64.Deploy.0 = Windows Vista Debug|x64
+               {C3F96D4C-E441-47F7-A44C-D2D0543C1D18}.Windows Vista Release|Win32.ActiveCfg = Windows Vista Release|Win32
+               {C3F96D4C-E441-47F7-A44C-D2D0543C1D18}.Windows Vista Release|Win32.Build.0 = Windows Vista Release|Win32
+               {C3F96D4C-E441-47F7-A44C-D2D0543C1D18}.Windows Vista Release|Win32.Deploy.0 = Windows Vista Release|Win32
+               {C3F96D4C-E441-47F7-A44C-D2D0543C1D18}.Windows Vista Release|x64.ActiveCfg = Windows Vista Release|x64
+               {C3F96D4C-E441-47F7-A44C-D2D0543C1D18}.Windows Vista Release|x64.Build.0 = Windows Vista Release|x64
+               {C3F96D4C-E441-47F7-A44C-D2D0543C1D18}.Windows Vista Release|x64.Deploy.0 = Windows Vista Release|x64
+       EndGlobalSection
+       GlobalSection(SolutionProperties) = preSolution
+               HideSolutionNode = FALSE
+       EndGlobalSection
+EndGlobal
diff --git a/proj/xenvif/xenvif.vcxproj b/proj/xenvif/xenvif.vcxproj
new file mode 100644 (file)
index 0000000..32ceb10
--- /dev/null
@@ -0,0 +1,204 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <ItemGroup Label="ProjectConfigurations">
+    <ProjectConfiguration Include="Windows Developer Preview Debug|Win32">
+      <Configuration>Windows Developer Preview Debug</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Windows Developer Preview Release|Win32">
+      <Configuration>Windows Developer Preview Release</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Windows 7 Debug|Win32">
+      <Configuration>Windows 7 Debug</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Windows 7 Release|Win32">
+      <Configuration>Windows 7 Release</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Windows Vista Debug|Win32">
+      <Configuration>Windows Vista Debug</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Windows Vista Release|Win32">
+      <Configuration>Windows Vista Release</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Windows Developer Preview Debug|x64">
+      <Configuration>Windows Developer Preview Debug</Configuration>
+      <Platform>x64</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Windows Developer Preview Release|x64">
+      <Configuration>Windows Developer Preview Release</Configuration>
+      <Platform>x64</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Windows 7 Debug|x64">
+      <Configuration>Windows 7 Debug</Configuration>
+      <Platform>x64</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Windows 7 Release|x64">
+      <Configuration>Windows 7 Release</Configuration>
+      <Platform>x64</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Windows Vista Debug|x64">
+      <Configuration>Windows Vista Debug</Configuration>
+      <Platform>x64</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Windows Vista Release|x64">
+      <Configuration>Windows Vista Release</Configuration>
+      <Platform>x64</Platform>
+    </ProjectConfiguration>
+  </ItemGroup>
+  <PropertyGroup Label="Globals">
+    <ProjectGuid>{C3F96D4C-E441-47F7-A44C-D2D0543C1D18}</ProjectGuid>
+    <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
+    <MinimumVisualStudioVersion>11.0</MinimumVisualStudioVersion>
+    <ProjectName>xenvif</ProjectName>
+  </PropertyGroup>
+  <PropertyGroup Label="PropertySheets">
+    <PlatformToolset>WindowsKernelModeDriver8.0</PlatformToolset>
+    <ConfigurationType>Driver</ConfigurationType>
+    <DriverType>WDM</DriverType>
+    <Configuration>Windows Developer Preview Debug</Configuration>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Windows Developer Preview Debug|Win32'" Label="Configuration">
+    <TargetVersion>Windows8</TargetVersion>
+    <UseDebugLibraries>true</UseDebugLibraries>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Windows Developer Preview Release|Win32'" Label="Configuration">
+    <TargetVersion>Windows8</TargetVersion>
+    <UseDebugLibraries>false</UseDebugLibraries>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Windows 7 Debug|Win32'" Label="Configuration">
+    <TargetVersion>Windows7</TargetVersion>
+    <UseDebugLibraries>true</UseDebugLibraries>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Windows 7 Release|Win32'" Label="Configuration">
+    <TargetVersion>Windows7</TargetVersion>
+    <UseDebugLibraries>false</UseDebugLibraries>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Windows Vista Debug|Win32'" Label="Configuration">
+    <TargetVersion>Vista</TargetVersion>
+    <UseDebugLibraries>true</UseDebugLibraries>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Windows Vista Release|Win32'" Label="Configuration">
+    <TargetVersion>Vista</TargetVersion>
+    <UseDebugLibraries>false</UseDebugLibraries>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Windows Developer Preview Debug|x64'" Label="Configuration">
+    <TargetVersion>Windows8</TargetVersion>
+    <UseDebugLibraries>true</UseDebugLibraries>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Windows Developer Preview Release|x64'" Label="Configuration">
+    <TargetVersion>Windows8</TargetVersion>
+    <UseDebugLibraries>false</UseDebugLibraries>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Windows 7 Debug|x64'" Label="Configuration">
+    <TargetVersion>Windows7</TargetVersion>
+    <UseDebugLibraries>true</UseDebugLibraries>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Windows 7 Release|x64'" Label="Configuration">
+    <TargetVersion>Windows7</TargetVersion>
+    <UseDebugLibraries>false</UseDebugLibraries>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Windows Vista Debug|x64'" Label="Configuration">
+    <TargetVersion>Vista</TargetVersion>
+    <UseDebugLibraries>true</UseDebugLibraries>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Windows Vista Release|x64'" Label="Configuration">
+    <TargetVersion>Vista</TargetVersion>
+    <UseDebugLibraries>false</UseDebugLibraries>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+  <ImportGroup Label="PropertySheets">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <PropertyGroup Label="UserMacros" />
+  <PropertyGroup>
+    <IncludePath>$(IncludePath)</IncludePath>
+    <RunCodeAnalysis>true</RunCodeAnalysis>
+    <EnableInf2cat>false</EnableInf2cat>
+  </PropertyGroup>
+  <ItemDefinitionGroup Condition="'$(Platform)'=='Win32'">
+    <ClCompile>
+      <AdditionalIncludeDirectories>$(SolutionDir)..\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <PreprocessorDefinitions>__i386__;__MODULE__="XENVIF";POOL_NX_OPTIN=1;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <WarningLevel>EnableAllWarnings</WarningLevel>
+      <DisableSpecificWarnings>4548;4820;4668;4255;6001;6054;28196;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+      <MultiProcessorCompilation>true</MultiProcessorCompilation>
+      <EnablePREfast>true</EnablePREfast>
+    </ClCompile>
+    <Link>
+      <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
+      <AdditionalDependencies>$(DDK_LIB_PATH)/libcntpr.lib;$(DDK_LIB_PATH)/netio.lib;%(AdditionalDependencies)</AdditionalDependencies>
+      <EnableCOMDATFolding>false</EnableCOMDATFolding>
+    </Link>
+    <ResourceCompile>
+      <AdditionalIncludeDirectories>$(SolutionDir)..\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+    </ResourceCompile>
+    <Inf>
+      <SpecifyArchitecture>true</SpecifyArchitecture>
+      <Architecture>x86</Architecture>
+      <SpecifyDriverVerDirectiveVersion>true</SpecifyDriverVerDirectiveVersion>
+      <TimeStamp>$(MAJOR_VERSION).$(MINOR_VERSION).$(MICRO_VERSION).$(BUILD_NUMBER)</TimeStamp>
+      <EnableVerbose>true</EnableVerbose>
+    </Inf>
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Platform)'=='x64'">
+    <ClCompile>
+      <AdditionalIncludeDirectories>$(SolutionDir)..\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <PreprocessorDefinitions>__x86_64__;__MODULE__="XENVIF";POOL_NX_OPTIN=1;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <WarningLevel>EnableAllWarnings</WarningLevel>
+      <DisableSpecificWarnings>4548;4820;4668;4255;6001;6054;28196;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+      <MultiProcessorCompilation>true</MultiProcessorCompilation>
+      <EnablePREfast>true</EnablePREfast>
+    </ClCompile>
+    <Link>
+      <AdditionalDependencies>$(DDK_LIB_PATH)/libcntpr.lib;$(DDK_LIB_PATH)/netio.lib;%(AdditionalDependencies)</AdditionalDependencies>
+      <EnableCOMDATFolding>false</EnableCOMDATFolding>
+    </Link>
+    <ResourceCompile>
+      <AdditionalIncludeDirectories>$(SolutionDir)..\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+    </ResourceCompile>
+    <Inf>
+      <SpecifyArchitecture>true</SpecifyArchitecture>
+      <Architecture>amd64</Architecture>
+      <SpecifyDriverVerDirectiveVersion>true</SpecifyDriverVerDirectiveVersion>
+      <TimeStamp>$(MAJOR_VERSION).$(MINOR_VERSION).$(MICRO_VERSION).$(BUILD_NUMBER)</TimeStamp>
+      <EnableVerbose>true</EnableVerbose>
+    </Inf>
+  </ItemDefinitionGroup>
+  <ItemGroup>
+    <FilesToPackage Include="$(TargetPath)" />
+    <FilesToPackage Include="$(OutDir)$(TargetName).pdb" />
+    <FilesToPackage Include="@(Inf->'%(CopyOutput)')" Condition="'@(Inf)'!=''" />
+  </ItemGroup>
+  <ItemGroup>
+    <ClCompile Include="../../src/xenvif/checksum.c" />
+    <ClCompile Include="../../src/xenvif/driver.c" />
+    <ClCompile Include="../../src/xenvif/fdo.c" />
+    <ClCompile Include="../../src/xenvif/frontend.c" />
+    <ClCompile Include="../../src/xenvif/mac.c" />
+    <ClCompile Include="../../src/xenvif/notifier.c" />
+    <ClCompile Include="../../src/xenvif/parse.c" />
+    <ClCompile Include="../../src/xenvif/pdo.c" />
+    <ClCompile Include="../../src/xenvif/pool.c" />
+    <ClCompile Include="../../src/xenvif/receiver.c" />
+    <ClCompile Include="../../src/xenvif/registry.c" />
+    <ClCompile Include="../../src/xenvif/thread.c" />
+    <ClCompile Include="../../src/xenvif/transmitter.c" />
+    <ClCompile Include="../../src/xenvif/vif.c" />
+  </ItemGroup>
+  <ItemGroup>
+    <ResourceCompile Include="..\..\src\xenvif\xenvif.rc" />
+  </ItemGroup>
+  <ItemGroup>
+    <Inf Include="..\xenvif.inf" />
+  </ItemGroup>
+  <ItemGroup>
+    <None Include="..\package\package.vcxproj" />
+  </ItemGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+</Project>
\ No newline at end of file
diff --git a/proj/xenvif/xenvif.vcxproj.user b/proj/xenvif/xenvif.vcxproj.user
new file mode 100644 (file)
index 0000000..7dea6a2
--- /dev/null
@@ -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>ProductionSign</SignMode>
+    <ProductionCertificate>CN="Citrix Systems, Inc.", OU=Digital ID Class 3 - Microsoft Software Validation v2, O="Citrix Systems, Inc.", L=Santa Clara, S=California, C=US | 9505C081954CC0A3E8B904458ACACD72EABD98B5</ProductionCertificate>
+    <TimeStampServer>http://timestamp.verisign.com/scripts/timstamp.dll</TimeStampServer>
+  </PropertyGroup>
+</Project>
\ No newline at end of file
diff --git a/proj/xenvif_coinst/xenvif_coinst.vcxproj b/proj/xenvif_coinst/xenvif_coinst.vcxproj
new file mode 100644 (file)
index 0000000..6d80946
--- /dev/null
@@ -0,0 +1,177 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <ItemGroup Label="ProjectConfigurations">
+    <ProjectConfiguration Include="Windows Developer Preview Debug|Win32">
+      <Configuration>Windows Developer Preview Debug</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Windows Developer Preview Release|Win32">
+      <Configuration>Windows Developer Preview Release</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Windows 7 Debug|Win32">
+      <Configuration>Windows 7 Debug</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Windows 7 Release|Win32">
+      <Configuration>Windows 7 Release</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Windows Vista Debug|Win32">
+      <Configuration>Windows Vista Debug</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Windows Vista Release|Win32">
+      <Configuration>Windows Vista Release</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Windows Developer Preview Debug|x64">
+      <Configuration>Windows Developer Preview Debug</Configuration>
+      <Platform>x64</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Windows Developer Preview Release|x64">
+      <Configuration>Windows Developer Preview Release</Configuration>
+      <Platform>x64</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Windows 7 Debug|x64">
+      <Configuration>Windows 7 Debug</Configuration>
+      <Platform>x64</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Windows 7 Release|x64">
+      <Configuration>Windows 7 Release</Configuration>
+      <Platform>x64</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Windows Vista Debug|x64">
+      <Configuration>Windows Vista Debug</Configuration>
+      <Platform>x64</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Windows Vista Release|x64">
+      <Configuration>Windows Vista Release</Configuration>
+      <Platform>x64</Platform>
+    </ProjectConfiguration>
+  </ItemGroup>
+  <PropertyGroup Label="Globals">
+    <ProjectGuid>{2BFAC7E6-3420-47A5-A092-BDC5C9D78A44}</ProjectGuid>
+    <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
+    <MinimumVisualStudioVersion>11.0</MinimumVisualStudioVersion>
+    <ProjectName>xenvif_coinst</ProjectName>
+  </PropertyGroup>
+  <PropertyGroup Label="PropertySheets">
+    <PlatformToolset>WindowsApplicationForDrivers8.0</PlatformToolset>
+    <ConfigurationType>DynamicLibrary</ConfigurationType>
+    <DriverType>WDM</DriverType>
+    <Configuration>Windows Developer Preview Debug</Configuration>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Windows Developer Preview Debug|Win32'" Label="Configuration">
+    <TargetVersion>Windows8</TargetVersion>
+    <UseDebugLibraries>true</UseDebugLibraries>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Windows Developer Preview Release|Win32'" Label="Configuration">
+    <TargetVersion>Windows8</TargetVersion>
+    <UseDebugLibraries>false</UseDebugLibraries>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Windows 7 Debug|Win32'" Label="Configuration">
+    <TargetVersion>Windows7</TargetVersion>
+    <UseDebugLibraries>true</UseDebugLibraries>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Windows 7 Release|Win32'" Label="Configuration">
+    <TargetVersion>Windows7</TargetVersion>
+    <UseDebugLibraries>false</UseDebugLibraries>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Windows Vista Debug|Win32'" Label="Configuration">
+    <TargetVersion>Vista</TargetVersion>
+    <UseDebugLibraries>true</UseDebugLibraries>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Windows Vista Release|Win32'" Label="Configuration">
+    <TargetVersion>Vista</TargetVersion>
+    <UseDebugLibraries>false</UseDebugLibraries>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Windows Developer Preview Debug|x64'" Label="Configuration">
+    <TargetVersion>Windows8</TargetVersion>
+    <UseDebugLibraries>true</UseDebugLibraries>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Windows Developer Preview Release|x64'" Label="Configuration">
+    <TargetVersion>Windows8</TargetVersion>
+    <UseDebugLibraries>false</UseDebugLibraries>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Windows 7 Debug|x64'" Label="Configuration">
+    <TargetVersion>Windows7</TargetVersion>
+    <UseDebugLibraries>true</UseDebugLibraries>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Windows 7 Release|x64'" Label="Configuration">
+    <TargetVersion>Windows7</TargetVersion>
+    <UseDebugLibraries>false</UseDebugLibraries>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Windows Vista Debug|x64'" Label="Configuration">
+    <TargetVersion>Vista</TargetVersion>
+    <UseDebugLibraries>true</UseDebugLibraries>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Windows Vista Release|x64'" Label="Configuration">
+    <TargetVersion>Vista</TargetVersion>
+    <UseDebugLibraries>false</UseDebugLibraries>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+  <ImportGroup Label="PropertySheets">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <PropertyGroup Label="UserMacros" />
+  <PropertyGroup>
+    <DebuggerFlavor>DbgengKernelDebugger</DebuggerFlavor>
+  </PropertyGroup>
+  <PropertyGroup>
+    <IncludePath>$(IncludePath)</IncludePath>
+    <RunCodeAnalysis>true</RunCodeAnalysis>
+    <EnableInf2cat>false</EnableInf2cat>
+  </PropertyGroup>
+  <ItemDefinitionGroup Condition="'$(Platform)'=='Win32'">
+    <ClCompile>
+      <AdditionalIncludeDirectories>$(SolutionDir)..\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <PreprocessorDefinitions>__i386__;__MODULE__="XENVIF_COINST";%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <WarningLevel>EnableAllWarnings</WarningLevel>
+      <DisableSpecificWarnings>4548;4820;4668;4255;6001;6054;28196;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+      <MultiProcessorCompilation>true</MultiProcessorCompilation>
+      <EnablePREfast>true</EnablePREfast>
+      <RuntimeLibrary Condition="'$(UseDebugLibraries)'=='true'">MultiThreadedDebug</RuntimeLibrary>
+      <RuntimeLibrary Condition="'$(UseDebugLibraries)'=='false'">MultiThreaded</RuntimeLibrary>
+    </ClCompile>
+    <Link>
+      <ModuleDefinitionFile>../../src/coinst/xenvif_coinst.def</ModuleDefinitionFile>
+      <AdditionalDependencies>setupapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
+    </Link>
+    <ResourceCompile>
+      <AdditionalIncludeDirectories>$(SolutionDir)..\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+    </ResourceCompile>
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Platform)'=='x64'">
+    <ClCompile>
+      <AdditionalIncludeDirectories>$(SolutionDir)..\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <PreprocessorDefinitions>__x86_64__;__MODULE__="XENVIF_COINST";%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <WarningLevel>EnableAllWarnings</WarningLevel>
+      <DisableSpecificWarnings>4548;4820;4668;4255;6001;6054;28196;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+      <MultiProcessorCompilation>true</MultiProcessorCompilation>
+      <EnablePREfast>true</EnablePREfast>
+      <RuntimeLibrary Condition="'$(UseDebugLibraries)'=='true'">MultiThreadedDebug</RuntimeLibrary>
+      <RuntimeLibrary Condition="'$(UseDebugLibraries)'=='false'">MultiThreaded</RuntimeLibrary>
+    </ClCompile>
+    <Link>
+      <ModuleDefinitionFile>../../src/coinst/xenvif_coinst.def</ModuleDefinitionFile>
+      <AdditionalDependencies>setupapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
+    </Link>
+    <ResourceCompile>
+      <AdditionalIncludeDirectories>$(SolutionDir)..\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+    </ResourceCompile>
+  </ItemDefinitionGroup>
+  <ItemGroup>
+    <FilesToPackage Include="$(TargetPath)" />
+    <FilesToPackage Include="$(OutDir)$(TargetName).pdb" />
+    <FilesToPackage Include="@(Inf->'%(CopyOutput)')" Condition="'@(Inf)'!=''" />
+  </ItemGroup>
+  <ItemGroup>
+    <ClCompile Include="..\..\src\coinst\coinst.c" />
+  </ItemGroup>
+  <ItemGroup>
+    <None Include="..\..\src\coinst\xenvif_coinst.def" />
+  </ItemGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+</Project>
\ No newline at end of file
diff --git a/proj/xenvif_coinst/xenvif_coinst.vcxproj.user b/proj/xenvif_coinst/xenvif_coinst.vcxproj.user
new file mode 100644 (file)
index 0000000..7dea6a2
--- /dev/null
@@ -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>ProductionSign</SignMode>
+    <ProductionCertificate>CN="Citrix Systems, Inc.", OU=Digital ID Class 3 - Microsoft Software Validation v2, O="Citrix Systems, Inc.", L=Santa Clara, S=California, C=US | 9505C081954CC0A3E8B904458ACACD72EABD98B5</ProductionCertificate>
+    <TimeStampServer>http://timestamp.verisign.com/scripts/timstamp.dll</TimeStampServer>
+  </PropertyGroup>
+</Project>
\ No newline at end of file
diff --git a/src/coinst/coinst.c b/src/coinst/coinst.c
new file mode 100644 (file)
index 0000000..4924a26
--- /dev/null
@@ -0,0 +1,891 @@
+/* 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 <setupapi.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <strsafe.h>
+#include <malloc.h>
+
+#include <version.h>
+
+__user_code;
+
+#define MAXIMUM_BUFFER_SIZE 1024
+
+#define SERVICES_KEY "SYSTEM\\CurrentControlSet\\Services"
+
+#define PARAMETERS_KEY(_Driver)    \
+        SERVICES_KEY ## "\\" ## #_Driver ## "\\Parameters"
+
+#define STATUS_KEY(_Driver)    \
+        SERVICES_KEY ## "\\" ## #_Driver ## "\\Status"
+
+static VOID
+#pragma prefast(suppress:6262) // Function uses '1036' bytes of stack: exceeds /analyze:stacksize'1024'
+__Log(
+    IN  const CHAR  *Format,
+    IN  ...
+    )
+{
+    TCHAR               Buffer[MAXIMUM_BUFFER_SIZE];
+    va_list             Arguments;
+    size_t              Length;
+    SP_LOG_TOKEN        LogToken;
+    DWORD               Category;
+    DWORD               Flags;
+    HRESULT             Result;
+
+    va_start(Arguments, Format);
+    Result = StringCchVPrintf(Buffer, MAXIMUM_BUFFER_SIZE, Format, Arguments);
+    va_end(Arguments);
+
+    if (Result != S_OK && Result != STRSAFE_E_INSUFFICIENT_BUFFER)
+        return;
+
+    Result = StringCchLength(Buffer, MAXIMUM_BUFFER_SIZE, &Length);
+    if (Result != S_OK)
+        return;
+
+    LogToken = SetupGetThreadLogToken();
+    Category = TXTLOG_VENDOR;
+    Flags = TXTLOG_DETAILS;
+
+    SetupWriteTextLog(LogToken, Category, Flags, Buffer);
+    Length = __min(MAXIMUM_BUFFER_SIZE - 1, Length + 2);
+
+    __analysis_assume(Length < MAXIMUM_BUFFER_SIZE);
+    __analysis_assume(Length >= 2);
+    Buffer[Length] = '\0';
+    Buffer[Length - 1] = '\n';
+    Buffer[Length - 2] = '\r';
+
+    OutputDebugString(Buffer);
+}
+
+#define Log(_Format, ...) \
+        __Log(__MODULE__ "|" __FUNCTION__ ": " _Format, __VA_ARGS__)
+
+static PTCHAR
+GetErrorMessage(
+    IN  DWORD   Error
+    )
+{
+    PTCHAR      Message;
+    ULONG       Index;
+
+    FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | 
+                  FORMAT_MESSAGE_FROM_SYSTEM |
+                  FORMAT_MESSAGE_IGNORE_INSERTS,
+                  NULL,
+                  Error,
+                  MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+                  (LPTSTR)&Message,
+                  0,
+                  NULL);
+
+    for (Index = 0; Message[Index] != '\0'; Index++) {
+        if (Message[Index] == '\r' || Message[Index] == '\n') {
+            Message[Index] = '\0';
+            break;
+        }
+    }
+
+    return Message;
+}
+
+static const CHAR *
+FunctionName(
+    IN  DI_FUNCTION Function
+    )
+{
+#define _NAME(_Function)        \
+        case DIF_ ## _Function: \
+            return #_Function;
+
+    switch (Function) {
+    _NAME(INSTALLDEVICE);
+    _NAME(REMOVE);
+    _NAME(SELECTDEVICE);
+    _NAME(ASSIGNRESOURCES);
+    _NAME(PROPERTIES);
+    _NAME(FIRSTTIMESETUP);
+    _NAME(FOUNDDEVICE);
+    _NAME(SELECTCLASSDRIVERS);
+    _NAME(VALIDATECLASSDRIVERS);
+    _NAME(INSTALLCLASSDRIVERS);
+    _NAME(CALCDISKSPACE);
+    _NAME(DESTROYPRIVATEDATA);
+    _NAME(VALIDATEDRIVER);
+    _NAME(MOVEDEVICE);
+    _NAME(DETECT);
+    _NAME(INSTALLWIZARD);
+    _NAME(DESTROYWIZARDDATA);
+    _NAME(PROPERTYCHANGE);
+    _NAME(ENABLECLASS);
+    _NAME(DETECTVERIFY);
+    _NAME(INSTALLDEVICEFILES);
+    _NAME(ALLOW_INSTALL);
+    _NAME(SELECTBESTCOMPATDRV);
+    _NAME(REGISTERDEVICE);
+    _NAME(NEWDEVICEWIZARD_PRESELECT);
+    _NAME(NEWDEVICEWIZARD_SELECT);
+    _NAME(NEWDEVICEWIZARD_PREANALYZE);
+    _NAME(NEWDEVICEWIZARD_POSTANALYZE);
+    _NAME(NEWDEVICEWIZARD_FINISHINSTALL);
+    _NAME(INSTALLINTERFACES);
+    _NAME(DETECTCANCEL);
+    _NAME(REGISTER_COINSTALLERS);
+    _NAME(ADDPROPERTYPAGE_ADVANCED);
+    _NAME(ADDPROPERTYPAGE_BASIC);
+    _NAME(TROUBLESHOOTER);
+    _NAME(POWERMESSAGEWAKE);
+    default:
+        break;
+    }
+
+    return "UNKNOWN";
+
+#undef  _NAME
+}
+
+static BOOLEAN
+InstallClass(
+    IN  PTCHAR  Class
+    )
+{
+    HKEY        Key;
+    DWORD       OldLength;
+    DWORD       NewLength;
+    DWORD       Type;
+    LONG        Error;
+    PTCHAR      Classes;
+    ULONG       Offset;
+
+    Error = RegCreateKeyEx(HKEY_LOCAL_MACHINE,
+                           PARAMETERS_KEY(XENFILT),
+                           0,
+                           NULL,
+                           REG_OPTION_NON_VOLATILE,
+                           KEY_ALL_ACCESS,
+                           NULL,
+                           &Key,
+                           NULL);
+    if (Error != ERROR_SUCCESS) {
+        SetLastError(Error);
+        goto fail1;
+    }
+
+    OldLength = 0;
+    Error = RegQueryValueEx(Key,
+                            "UnplugClasses",
+                            NULL,
+                            &Type,
+                            NULL,
+                            &OldLength);
+    if (Error != ERROR_SUCCESS) {
+        if (Error != ERROR_FILE_NOT_FOUND) {
+            SetLastError(Error);
+            goto fail2;
+        }
+
+        OldLength = sizeof (TCHAR);
+        Type = REG_MULTI_SZ;
+    }
+
+    if (Type != REG_MULTI_SZ) {
+        SetLastError(ERROR_BAD_FORMAT);
+        goto fail3;
+    }
+
+    NewLength = OldLength + (DWORD)((strlen(Class) + 1) * sizeof (TCHAR));
+
+    Classes = malloc(NewLength);
+    if (Classes == NULL)
+        goto fail4;
+
+    memset(Classes, 0, NewLength);
+
+    Offset = 0;
+    if (OldLength != sizeof (TCHAR)) {
+        Error = RegQueryValueEx(Key,
+                                "UnplugClasses",
+                                NULL,
+                                NULL,
+                                (PBYTE)Classes,
+                                &OldLength);
+        if (Error != ERROR_SUCCESS) {
+            SetLastError(Error);
+            goto fail5;
+        }
+
+        while (Classes[Offset] != '\0') {
+            ULONG   ClassLength;
+
+            ClassLength = (ULONG)strlen(&Classes[Offset]) / sizeof (TCHAR);
+
+            if (_stricmp(&Classes[Offset], Class) == 0) {
+                Log("%s already present", Class);
+                goto done;
+            }
+
+            Offset += ClassLength + 1;
+        }
+    }
+
+    memmove(&Classes[Offset], Class, strlen(Class));
+    Log("added %s", Class);
+
+    Error = RegSetValueEx(Key,
+                          "UnplugClasses",
+                          0,
+                          Type,
+                          (PBYTE)Classes,
+                          NewLength);
+    if (Error != ERROR_SUCCESS) {
+        SetLastError(Error);
+        goto fail6;
+    }
+
+done:
+    free(Classes);
+
+    RegCloseKey(Key);
+
+    return TRUE;
+
+fail6:
+fail5:
+    free(Classes);
+
+fail4:
+fail3:
+fail2:
+    RegCloseKey(Key);
+
+fail1:
+    return FALSE;
+}
+
+static BOOLEAN
+RemoveClass(
+    IN  PTCHAR  Class
+    )
+{
+    HKEY        Key;
+    DWORD       OldLength;
+    DWORD       NewLength;
+    DWORD       Type;
+    LONG        Error;
+    PTCHAR      Classes;
+    ULONG       Offset;
+    ULONG       ClassLength;
+
+    Error = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
+                         PARAMETERS_KEY(XENFILT),
+                         0,
+                         KEY_ALL_ACCESS,
+                         &Key);
+    if (Error != ERROR_SUCCESS) {
+        SetLastError(Error);
+        goto fail1;
+    }
+
+    OldLength = 0;
+    Error = RegQueryValueEx(Key,
+                            "UnplugClasses",
+                            NULL,
+                            &Type,
+                            NULL,
+                            &OldLength);
+    if (Error != ERROR_SUCCESS) {
+        if (Error != ERROR_FILE_NOT_FOUND) {
+            SetLastError(Error);
+            goto fail2;
+        }
+
+        goto done;
+    }
+
+    if (Type != REG_MULTI_SZ) {
+        SetLastError(ERROR_BAD_FORMAT);
+        goto fail3;
+    }
+
+    Classes = malloc(OldLength);
+    if (Classes == NULL)
+        goto fail4;
+
+    memset(Classes, 0, OldLength);
+
+    Error = RegQueryValueEx(Key,
+                            "UnplugClasses",
+                            NULL,
+                            NULL,
+                            (PBYTE)Classes,
+                            &OldLength);
+    if (Error != ERROR_SUCCESS) {
+        SetLastError(Error);
+        goto fail5;
+    }
+
+    Offset = 0;
+    ClassLength = 0;
+    while (Classes[Offset] != '\0') {
+        ClassLength = (ULONG)strlen(&Classes[Offset]) / sizeof (TCHAR);
+
+        if (_stricmp(&Classes[Offset], Class) == 0)
+            goto remove;
+
+        Offset += ClassLength + 1;
+    }
+
+    free(Classes);
+    goto done;
+
+remove:
+    NewLength = OldLength - ((ClassLength + 1) * sizeof (TCHAR));
+
+    memmove(&Classes[Offset],
+            &Classes[Offset + ClassLength + 1],
+            (NewLength - Offset) * sizeof (TCHAR));
+            
+    Log("removed %s", Class);
+
+    if (NewLength == 1) {
+        Error = RegDeleteValue(Key,
+                               "UnplugClasses");
+    } else {
+        Error = RegSetValueEx(Key,
+                              "UnplugClasses",
+                              0,
+                              Type,
+                              (PBYTE)Classes,
+                              NewLength);
+    }
+    if (Error != ERROR_SUCCESS) {
+        SetLastError(Error);
+        goto fail6;
+    }
+
+    free(Classes);
+
+done:
+    RegCloseKey(Key);
+
+    return TRUE;
+
+fail6:
+fail5:
+    free(Classes);
+
+fail4:
+fail3:
+fail2:
+    RegCloseKey(Key);
+
+fail1:
+    return FALSE;
+}
+
+static BOOLEAN
+IsClassEmulated(
+    IN  PTCHAR      Class,
+    OUT PBOOLEAN    Present
+    )
+{
+    HKEY            Key;
+    DWORD           Length;
+    DWORD           Type;
+    LONG            Error;
+    PTCHAR          Devices;
+    ULONG           Count;
+    ULONG           Offset;
+
+    Error = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
+                         STATUS_KEY(XENFILT),
+                         0,
+                         KEY_READ,
+                         &Key);
+    if (Error != ERROR_SUCCESS) {
+        if (Error == ERROR_FILE_NOT_FOUND)
+            goto done;
+
+        SetLastError(Error);
+        goto fail1;
+    }
+
+    Length = 0;
+    Error = RegQueryValueEx(Key,
+                            Class,
+                            NULL,
+                            &Type,
+                            NULL,
+                            &Length);
+    if (Error != ERROR_SUCCESS) {
+        if (Error == ERROR_FILE_NOT_FOUND) {
+            RegCloseKey(Key);
+            goto done;
+        }
+
+        SetLastError(Error);
+        goto fail2;
+    }
+
+    if (Type != REG_MULTI_SZ) {
+        SetLastError(ERROR_BAD_FORMAT);
+        goto fail3;
+    }
+
+    Devices = malloc(Length);
+    if (Devices == NULL)
+        goto fail4;
+
+    memset(Devices, 0, Length);
+
+    Error = RegQueryValueEx(Key,
+                            Class,
+                            NULL,
+                            NULL,
+                            (PBYTE)Devices,
+                            &Length);
+    if (Error != ERROR_SUCCESS) {
+        SetLastError(Error);
+        goto fail5;
+    }
+
+    Count = 0;
+
+    Offset = 0;
+    while (Devices[Offset] != '\0') {
+        ULONG   DeviceLength;
+
+        DeviceLength = (ULONG)strlen(&Devices[Offset]) / sizeof (TCHAR);
+
+        Count++;
+        Offset += DeviceLength + 1;
+    }
+
+    *Present = (Count != 0) ? TRUE : FALSE;
+
+    free(Devices);
+
+    RegCloseKey(Key);
+
+done:
+    return TRUE;
+
+fail5:
+    free(Devices);
+
+fail4:
+fail3:
+fail2:
+    RegCloseKey(Key);
+
+fail1:
+    return FALSE;
+}
+
+static BOOLEAN
+RequestReboot(
+    IN  HDEVINFO            DeviceInfoSet,
+    IN  PSP_DEVINFO_DATA    DeviceInfoData
+    )
+{
+    SP_DEVINSTALL_PARAMS    DeviceInstallParams;
+
+    DeviceInstallParams.cbSize = sizeof (DeviceInstallParams);
+
+    if (!SetupDiGetDeviceInstallParams(DeviceInfoSet,
+                                       DeviceInfoData,
+                                       &DeviceInstallParams))
+        goto fail1;
+
+    DeviceInstallParams.Flags |= DI_NEEDREBOOT;
+
+    Log("Flags = %08x", DeviceInstallParams.Flags);
+
+    if (!SetupDiSetDeviceInstallParams(DeviceInfoSet,
+                                       DeviceInfoData,
+                                       &DeviceInstallParams))
+        goto fail2;
+
+    return TRUE;
+
+fail2:
+fail1:
+    return FALSE;
+}
+
+static FORCEINLINE HRESULT
+__DifInstallPreProcess(
+    IN  HDEVINFO                    DeviceInfoSet,
+    IN  PSP_DEVINFO_DATA            DeviceInfoData,
+    IN  PCOINSTALLER_CONTEXT_DATA   Context
+    )
+{
+    UNREFERENCED_PARAMETER(DeviceInfoSet);
+    UNREFERENCED_PARAMETER(DeviceInfoData);
+    UNREFERENCED_PARAMETER(Context);
+
+    Log("<===>");
+
+    return ERROR_DI_POSTPROCESSING_REQUIRED; 
+}
+
+static FORCEINLINE HRESULT
+__DifInstallPostProcess(
+    IN  HDEVINFO                    DeviceInfoSet,
+    IN  PSP_DEVINFO_DATA            DeviceInfoData,
+    IN  PCOINSTALLER_CONTEXT_DATA   Context
+    )
+{
+    HRESULT                         Error;
+    BOOLEAN                         Present;
+    BOOLEAN                         Success;
+
+    Log("====>");
+
+    Error = Context->InstallResult;
+    if (Error != NO_ERROR) {
+        SetLastError(Error);
+        goto fail1;
+    }
+
+    Success = InstallClass("VIF");
+    if (!Success)
+        goto fail2;
+
+    Success = IsClassEmulated("VIF", &Present);
+    if (!Success)
+        goto fail3;
+
+    if (!Present)
+        goto done;
+
+    Success = RequestReboot(DeviceInfoSet, DeviceInfoData);
+    if (!Success)
+        goto fail4;
+
+done:
+    Log("<====");
+
+    return NO_ERROR;
+
+fail4:
+    Log("fail4");
+
+fail3:
+    Log("fail3");
+
+fail2:
+    Log("fail2");
+
+fail1:
+    Error = GetLastError();
+
+    {
+        PTCHAR  Message;
+
+        Message = GetErrorMessage(Error);
+        Log("fail1 (%s)", Message);
+        LocalFree(Message);
+    }
+
+    return Error;
+}
+
+static DECLSPEC_NOINLINE HRESULT
+DifInstall(
+    IN  HDEVINFO                    DeviceInfoSet,
+    IN  PSP_DEVINFO_DATA            DeviceInfoData,
+    IN  PCOINSTALLER_CONTEXT_DATA   Context
+    )
+{
+    SP_DEVINSTALL_PARAMS            DeviceInstallParams;
+    HRESULT                         Error;
+
+    DeviceInstallParams.cbSize = sizeof (DeviceInstallParams);
+
+    if (!SetupDiGetDeviceInstallParams(DeviceInfoSet,
+                                       DeviceInfoData,
+                                       &DeviceInstallParams))
+        goto fail1;
+
+    Log("Flags = %08x", DeviceInstallParams.Flags);
+
+    Error = (!Context->PostProcessing) ?
+            __DifInstallPreProcess(DeviceInfoSet, DeviceInfoData, Context) :
+            __DifInstallPostProcess(DeviceInfoSet, DeviceInfoData, Context);
+
+    return Error;
+
+fail1:
+    Error = GetLastError();
+
+    {
+        PTCHAR  Message;
+
+        Message = GetErrorMessage(Error);
+        Log("fail1 (%s)", Message);
+        LocalFree(Message);
+    }
+
+    return Error;
+}
+
+static FORCEINLINE HRESULT
+__DifRemovePreProcess(
+    IN  HDEVINFO                    DeviceInfoSet,
+    IN  PSP_DEVINFO_DATA            DeviceInfoData,
+    IN  PCOINSTALLER_CONTEXT_DATA   Context
+    )
+{
+    UNREFERENCED_PARAMETER(DeviceInfoSet);
+    UNREFERENCED_PARAMETER(DeviceInfoData);
+    UNREFERENCED_PARAMETER(Context);
+
+    Log("<===>");
+
+    return ERROR_DI_POSTPROCESSING_REQUIRED; 
+}
+
+static FORCEINLINE HRESULT
+__DifRemovePostProcess(
+    IN  HDEVINFO                    DeviceInfoSet,
+    IN  PSP_DEVINFO_DATA            DeviceInfoData,
+    IN  PCOINSTALLER_CONTEXT_DATA   Context
+    )
+{
+    HRESULT                         Error;
+    BOOLEAN                         Success;
+
+    Log("====>");
+
+    Error = Context->InstallResult;
+    if (Error != NO_ERROR) {
+        SetLastError(Error);
+        goto fail1;
+    }
+
+    Success = RemoveClass("VIF");
+    if (!Success)
+        goto fail2;
+
+    Success = RequestReboot(DeviceInfoSet, DeviceInfoData);
+    if (!Success)
+        goto fail3;
+
+    Log("<====");
+
+    return NO_ERROR;
+
+fail3:
+    Log("fail3");
+
+fail2:
+    Log("fail2");
+
+fail1:
+    Error = GetLastError();
+
+    {
+        PTCHAR  Message;
+
+        Message = GetErrorMessage(Error);
+        Log("fail1 (%s)", Message);
+        LocalFree(Message);
+    }
+
+    return Error;
+}
+
+static DECLSPEC_NOINLINE HRESULT
+DifRemove(
+    IN  HDEVINFO                    DeviceInfoSet,
+    IN  PSP_DEVINFO_DATA            DeviceInfoData,
+    IN  PCOINSTALLER_CONTEXT_DATA   Context
+    )
+{
+    SP_DEVINSTALL_PARAMS            DeviceInstallParams;
+    HRESULT                         Error;
+
+    DeviceInstallParams.cbSize = sizeof (DeviceInstallParams);
+
+    if (!SetupDiGetDeviceInstallParams(DeviceInfoSet,
+                                       DeviceInfoData,
+                                       &DeviceInstallParams))
+        goto fail1;
+
+    Log("Flags = %08x", DeviceInstallParams.Flags);
+
+    Error = (!Context->PostProcessing) ?
+            __DifRemovePreProcess(DeviceInfoSet, DeviceInfoData, Context) :
+            __DifRemovePostProcess(DeviceInfoSet, DeviceInfoData, Context);
+
+    return Error;
+
+fail1:
+    Error = GetLastError();
+
+    {
+        PTCHAR  Message;
+
+        Message = GetErrorMessage(Error);
+        Log("fail1 (%s)", Message);
+        LocalFree(Message);
+    }
+
+    return Error;
+}
+
+DWORD CALLBACK
+Entry(
+    IN  DI_FUNCTION                 Function,
+    IN  HDEVINFO                    DeviceInfoSet,
+    IN  PSP_DEVINFO_DATA            DeviceInfoData,
+    IN  PCOINSTALLER_CONTEXT_DATA   Context
+    )
+{
+    HRESULT                         Error;
+
+    Log("%s (%s) ===>",
+        MAJOR_VERSION_STR "." MINOR_VERSION_STR "." MICRO_VERSION_STR "." BUILD_NUMBER_STR,
+        DAY_STR "/" MONTH_STR "/" YEAR_STR);
+
+    switch (Function) {
+    case DIF_INSTALLDEVICE: {
+        SP_DRVINFO_DATA         DriverInfoData;
+        BOOLEAN                 DriverInfoAvailable;
+
+        DriverInfoData.cbSize = sizeof (DriverInfoData);
+        DriverInfoAvailable = SetupDiGetSelectedDriver(DeviceInfoSet,
+                                                       DeviceInfoData,
+                                                       &DriverInfoData) ?
+                              TRUE :
+                              FALSE;
+
+        // If there is no driver information then the NULL driver is being
+        // installed. Treat this as we would a DIF_REMOVE.
+        Error = (DriverInfoAvailable) ?
+                DifInstall(DeviceInfoSet, DeviceInfoData, Context) :
+                DifRemove(DeviceInfoSet, DeviceInfoData, Context);
+        break;
+    }
+    case DIF_REMOVE:
+        Error = DifRemove(DeviceInfoSet, DeviceInfoData, Context);
+        break;
+    default:
+        if (!Context->PostProcessing) {
+            Log("%s PreProcessing",
+                FunctionName(Function));
+
+            Error = NO_ERROR;
+        } else {
+            Log("%s PostProcessing (%08x)",
+                FunctionName(Function),
+                Context->InstallResult);
+
+            Error = Context->InstallResult;
+        }
+
+        break;
+    }
+
+    Log("%s (%s) <===",
+        MAJOR_VERSION_STR "." MINOR_VERSION_STR "." MICRO_VERSION_STR "." BUILD_NUMBER_STR,
+        DAY_STR "/" MONTH_STR "/" YEAR_STR);
+
+    return (DWORD)Error;
+}
+
+DWORD CALLBACK
+Version(
+    IN  HWND        Window,
+    IN  HINSTANCE   Module,
+    IN  PTCHAR      Buffer,
+    IN  INT         Reserved
+    )
+{
+    UNREFERENCED_PARAMETER(Window);
+    UNREFERENCED_PARAMETER(Module);
+    UNREFERENCED_PARAMETER(Buffer);
+    UNREFERENCED_PARAMETER(Reserved);
+
+    Log("%s (%s)",
+        MAJOR_VERSION_STR "." MINOR_VERSION_STR "." MICRO_VERSION_STR "." BUILD_NUMBER_STR,
+        DAY_STR "/" MONTH_STR "/" YEAR_STR);
+
+    return NO_ERROR;
+}
+
+static FORCEINLINE const CHAR *
+__ReasonName(
+    IN  DWORD       Reason
+    )
+{
+#define _NAME(_Reason)          \
+        case DLL_ ## _Reason:   \
+            return #_Reason;
+
+    switch (Reason) {
+    _NAME(PROCESS_ATTACH);
+    _NAME(PROCESS_DETACH);
+    _NAME(THREAD_ATTACH);
+    _NAME(THREAD_DETACH);
+    default:
+        break;
+    }
+
+    return "UNKNOWN";
+
+#undef  _NAME
+}
+
+BOOL WINAPI
+DllMain(
+    IN  HINSTANCE   Module,
+    IN  DWORD       Reason,
+    IN  PVOID       Reserved
+    )
+{
+    UNREFERENCED_PARAMETER(Module);
+    UNREFERENCED_PARAMETER(Reserved);
+
+    Log("%s (%s): %s",
+        MAJOR_VERSION_STR "." MINOR_VERSION_STR "." MICRO_VERSION_STR "." BUILD_NUMBER_STR,
+        DAY_STR "/" MONTH_STR "/" YEAR_STR,
+        __ReasonName(Reason));
+
+    return TRUE;
+}
diff --git a/src/coinst/xenvif_coinst.def b/src/coinst/xenvif_coinst.def
new file mode 100644 (file)
index 0000000..df65c7f
--- /dev/null
@@ -0,0 +1,37 @@
+; 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.
+
+LIBRARY  XENBUS_COINST
+
+EXPORTS   
+   Entry 
+   Version
+
+   DllMain PRIVATE
diff --git a/src/xenvif.inf b/src/xenvif.inf
new file mode 100644 (file)
index 0000000..ce9e2f5
--- /dev/null
@@ -0,0 +1,76 @@
+; Copyright 2011 Citrix Systems Inc. All rights reserved.
+; Use is subject to license terms.
+; 
+[Version] 
+Signature="$Windows NT$" 
+Class=System
+ClassGUID={4d36e97d-e325-11ce-bfc1-08002be10318}
+Provider=%Citrix% 
+CatalogFile=xenvif.cat
+DriverVer=01/01/1900,0.0.0.0
+
+[DestinationDirs] 
+DefaultDestDir=12 
+CoInst_CopyFiles=11
+
+[SourceDisksNames]
+0=%DiskDesc%
+
+[SourceDisksFiles]
+xenvif.sys=0,,
+xenvif_coinst.dll=0,,
+
+[CoInst_CopyFiles]
+xenvif_coinst_@MAJOR_VERSION@_@MINOR_VERSION@_@MICRO_VERSION@_@BUILD_NUMBER@.dll,xenvif_coinst.dll
+
+[Manufacturer] 
+%Citrix%=Citrix,NT$ARCH$
+
+[Citrix.NT$ARCH$]
+%DriverDesc%=XenVif_Inst,XENBUS\CLASS_VIF&REV_02
+
+[XenVif_Inst] 
+CopyFiles=XenVif_Copyfiles
+
+[XenVif_Copyfiles]
+xenvif.sys
+
+[XenVif_Inst.Services] 
+AddService=xenvif,0x02,XenVif_Service,
+
+[XenVif_Service] 
+ServiceType=%SERVICE_KERNEL_DRIVER% 
+StartType=%SERVICE_DEMAND_START% 
+ErrorControl=%SERVICE_ERROR_NORMAL% 
+ServiceBinary=%12%\xenvif.sys 
+LoadOrderGroup="NDIS"
+AddReg = XenVif_Parameters
+
+[XenVif_Parameters]
+HKR,"Parameters","ReceiverMaximumProtocol",0x00010001,0x00000000
+
+[XenVif_Inst.CoInstallers]
+CopyFiles=CoInst_CopyFiles
+AddReg=CoInst_AddReg
+
+[CoInst_AddReg]
+HKR,,CoInstallers32,0x00010000,"xenvif_coinst_@MAJOR_VERSION@_@MINOR_VERSION@_@MICRO_VERSION@_@BUILD_NUMBER@.dll,Entry"
+
+[Strings] 
+
+Citrix="Citrix Systems Inc." 
+DiskDesc="Citrix Tools for Virtual Machines" 
+DriverDesc="Citrix VIF Class"
+
+SERVICE_BOOT_START=0x0 
+SERVICE_SYSTEM_START=0x1 
+SERVICE_AUTO_START=0x2 
+SERVICE_DEMAND_START=0x3 
+SERVICE_DISABLED=0x4 
+
+SERVICE_KERNEL_DRIVER=0x1 
+SERVICE_ERROR_IGNORE=0x0 
+SERVICE_ERROR_NORMAL=0x1 
+SERVICE_ERROR_SEVERE=0x2 
+SERVICE_ERROR_CRITICAL=0x3 
diff --git a/src/xenvif/assert.h b/src/xenvif/assert.h
new file mode 100644 (file)
index 0000000..151796b
--- /dev/null
@@ -0,0 +1,174 @@
+/* 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 _XENVIF_ASSERT_H
+#define _XENVIF_ASSERT_H
+
+#include <ntddk.h>
+
+#include "log.h"
+
+static FORCEINLINE VOID
+__BugCheck(
+    IN  ULONG       Code,
+    IN  ULONG_PTR   Parameter1,
+    IN  ULONG_PTR   Parameter2,
+    IN  ULONG_PTR   Parameter3,
+    IN  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_ON(_EXP)                \
+        if (_EXP) BUG(#_EXP)
+
+#if DBG
+
+#define __NT_ASSERT(_EXP)                                       \
+        ((!(_EXP)) ?                                            \
+        (Error("ASSERTION FAILED: " #_EXP "\n"),                \
+         __annotation(L"Debug", L"AssertFail", L#_EXP),         \
+         DbgRaiseAssertionFailure(), FALSE) :                   \
+        TRUE)
+
+#define __ASSERT(_EXP)  __NT_ASSERT(_EXP)
+
+#else   // DBG
+
+#define __ASSERT(_EXP)  BUG_ON(!(_EXP))
+
+#endif  // DBG
+
+#undef  ASSERT
+
+#define ASSERT(_EXP)                    \
+        do {                            \
+            __ASSERT(_EXP);             \
+            __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)
+
+#ifndef TEST_MEMORY
+#define TEST_MEMORY DBG
+#endif
+
+#if TEST_MEMORY
+
+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  // _XENVIF_ASSERT_H
+
diff --git a/src/xenvif/checksum.c b/src/xenvif/checksum.c
new file mode 100644 (file)
index 0000000..da443ee
--- /dev/null
@@ -0,0 +1,414 @@
+/* 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 <ntstrsafe.h>
+#include <stdlib.h>
+#include <util.h>
+#include <ethernet.h>
+#include <tcpip.h>
+#include <vif_interface.h>
+
+#include "checksum.h"
+#include "log.h"
+#include "assert.h"
+
+static FORCEINLINE VOID
+__AccumulateChecksum(
+    IN OUT  PULONG  Accumulator,
+    IN      PVOID   MappedSystemVa,
+    IN      ULONG   ByteCount
+    )
+{
+    PUSHORT         Word;
+
+    Word = (PUSHORT)MappedSystemVa;
+    while (ByteCount > 1) {
+        *Accumulator += *Word++;
+        ByteCount -= 2;
+    }
+
+    if (ByteCount != 0) {
+        UCHAR Extra[2];
+
+        Extra[0] = *((PUCHAR)Word);
+        Extra[1] = 0;
+
+        Word = (PUSHORT)Extra;
+        *Accumulator += *Word;
+    }
+}
+
+VOID
+AccumulateChecksum(
+    IN OUT  PULONG  Accumulator,
+    IN      PVOID   MappedSystemVa,
+    IN      ULONG   ByteCount
+    )
+{
+    __AccumulateChecksum(Accumulator, MappedSystemVa, ByteCount);
+}
+
+static FORCEINLINE USHORT
+__FoldChecksum(
+    IN  ULONG   Accumulator,
+    IN  BOOLEAN Invert
+    )
+{
+    Accumulator = (Accumulator & 0xFFFF) + (Accumulator >> 16);
+    Accumulator += Accumulator >> 16;
+
+    return (USHORT)((Invert) ? ~Accumulator : Accumulator);
+}
+
+USHORT
+FoldChecksum(
+    IN  ULONG   Accumulator,
+    IN  BOOLEAN Invert
+    )
+{
+    return __FoldChecksum(Accumulator, Invert);
+}
+
+static FORCEINLINE USHORT
+__ChecksumIpVersion4PseudoHeader(
+    IN  PIPV4_ADDRESS   SourceAddress,
+    IN  PIPV4_ADDRESS   DestinationAddress,
+    IN  USHORT          Length,
+    IN  UCHAR           Protocol
+    )
+{
+    IPV4_PSEUDO_HEADER  Header;
+    ULONG               Accumulator;
+
+    RtlZeroMemory(&Header, sizeof (IPV4_PSEUDO_HEADER));
+
+    Header.SourceAddress = *SourceAddress;
+    Header.DestinationAddress = *DestinationAddress;
+    Header.Length = HTONS(Length);
+    Header.Protocol = Protocol;
+
+    Accumulator = 0;
+    __AccumulateChecksum(&Accumulator, (PUCHAR)&Header, sizeof (IPV4_PSEUDO_HEADER));
+
+    return __FoldChecksum(Accumulator, FALSE);
+}
+
+USHORT
+ChecksumIpVersion4PseudoHeader(
+    IN  PIPV4_ADDRESS   SourceAddress,
+    IN  PIPV4_ADDRESS   DestinationAddress,
+    IN  USHORT          Length,
+    IN  UCHAR           Protocol
+    )
+{
+    return __ChecksumIpVersion4PseudoHeader(SourceAddress,
+                                            DestinationAddress,
+                                            Length,
+                                            Protocol);
+}
+
+static FORCEINLINE USHORT
+__ChecksumIpVersion6PseudoHeader(
+    IN  PIPV6_ADDRESS   SourceAddress,
+    IN  PIPV6_ADDRESS   DestinationAddress,
+    IN  USHORT          Length,
+    IN  UCHAR           Protocol
+    )
+{
+    IPV6_PSEUDO_HEADER  Header;
+    ULONG               Accumulator;
+
+    RtlZeroMemory(&Header, sizeof (IPV6_PSEUDO_HEADER));
+
+    Header.SourceAddress = *SourceAddress;
+    Header.DestinationAddress = *DestinationAddress;
+    Header.Length = HTONS(Length);
+    Header.NextHeader = Protocol;
+
+    Accumulator = 0;
+    __AccumulateChecksum(&Accumulator, (PUCHAR)&Header, sizeof (IPV6_PSEUDO_HEADER));
+
+    return __FoldChecksum(Accumulator, FALSE);
+}
+
+USHORT
+ChecksumIpVersion6PseudoHeader(
+    IN  PIPV6_ADDRESS   SourceAddress,
+    IN  PIPV6_ADDRESS   DestinationAddress,
+    IN  USHORT          Length,
+    IN  UCHAR           Protocol
+    )
+{
+    return __ChecksumIpVersion6PseudoHeader(SourceAddress,
+                                            DestinationAddress,
+                                            Length,
+                                            Protocol);
+}
+
+USHORT
+ChecksumPseudoHeader(
+    IN  PUCHAR              StartVa,
+    IN  PXENVIF_PACKET_INFO Info
+    )
+{
+    PIP_HEADER              Header;
+    UCHAR                   Protocol;
+    USHORT                  Checksum;
+
+    ASSERT(Info->IpHeader.Length != 0);
+    Header = (PIP_HEADER)(StartVa + Info->IpHeader.Offset);
+
+    if (Info->TcpHeader.Length != 0) {
+        Protocol = IPPROTO_TCP;
+    } else {
+        ASSERT(Info->UdpHeader.Length != 0);
+        Protocol = IPPROTO_UDP;
+    }
+
+    if (Header->Version == 4) {
+        USHORT              Length;
+
+        Length = NTOHS(Header->Version4.PacketLength) -
+                 sizeof (IPV4_HEADER) -
+                 (USHORT)Info->IpOptions.Length;
+
+        Checksum = __ChecksumIpVersion4PseudoHeader(&Header->Version4.SourceAddress,
+                                                    &Header->Version4.DestinationAddress,
+                                                    Length,
+                                                    Protocol);
+    } else {
+        USHORT              Length;
+
+        ASSERT3U(Header->Version, ==, 6);
+
+        Length = NTOHS(Header->Version6.PayloadLength) -
+                 (USHORT)Info->IpOptions.Length;
+
+        Checksum = __ChecksumIpVersion6PseudoHeader(&Header->Version6.SourceAddress,
+                                                    &Header->Version6.DestinationAddress,
+                                                    Length,
+                                                    Protocol);
+    }
+
+    return Checksum;
+}
+
+USHORT
+ChecksumIpVersion4Header(
+    IN  PUCHAR              StartVa,
+    IN  PXENVIF_PACKET_INFO Info
+    )
+{
+    ULONG                   Accumulator;
+    PIPV4_HEADER            Header;
+    USHORT                  Saved;
+
+    ASSERT(Info->IpHeader.Length != 0);
+    Header = (PIPV4_HEADER)(StartVa + Info->IpHeader.Offset);
+
+    ASSERT3U(Header->Version, ==, 4);
+
+    Saved = Header->Checksum;
+    Header->Checksum = 0;
+
+    Accumulator = 0;
+    __AccumulateChecksum(&Accumulator,
+                         StartVa + Info->IpHeader.Offset,
+                         Info->IpHeader.Length);
+
+    Header->Checksum = Saved;
+
+    if (Info->IpOptions.Length != 0)
+        __AccumulateChecksum(&Accumulator,
+                             StartVa + Info->IpOptions.Offset,
+                             Info->IpOptions.Length);
+
+    return __FoldChecksum(Accumulator, TRUE);
+}
+
+USHORT
+ChecksumTcpPacket(
+    IN  PUCHAR                  StartVa,
+    IN  PXENVIF_PACKET_INFO     Info,
+    IN  USHORT                  PseudoHeaderChecksum,
+    IN  PXENVIF_PACKET_PAYLOAD  Payload
+    )
+{
+    ULONG                       Accumulator;
+    PIP_HEADER                  IpHeader;
+    PTCP_HEADER                 TcpHeader;
+    USHORT                      Saved;
+    PMDL                        Mdl;
+    ULONG                       Offset;
+    ULONG                       Length;
+
+    ASSERT(Info->IpHeader.Length != 0);
+    IpHeader = (PIP_HEADER)(StartVa + Info->IpHeader.Offset);
+
+    ASSERT(Info->TcpHeader.Length != 0);
+    TcpHeader = (PTCP_HEADER)(StartVa + Info->TcpHeader.Offset);
+
+    Saved = TcpHeader->Checksum;
+    TcpHeader->Checksum = 0;
+
+    Accumulator = PseudoHeaderChecksum;
+    __AccumulateChecksum(&Accumulator,
+                         StartVa + Info->TcpHeader.Offset,
+                         Info->TcpHeader.Length);
+
+    TcpHeader->Checksum = Saved;
+
+    if (Info->TcpOptions.Length != 0)
+        __AccumulateChecksum(&Accumulator,
+                             StartVa + Info->TcpOptions.Offset,
+                             Info->TcpOptions.Length);
+
+    Mdl = Payload->Mdl;
+    Offset = Payload->Offset;
+
+    if (IpHeader->Version == 4) {
+        PIPV4_HEADER    Version4 = &IpHeader->Version4;
+        
+        Length = NTOHS(Version4->PacketLength) -
+                 sizeof (IPV4_HEADER) -
+                 (USHORT)Info->IpOptions.Length;
+    } else {
+        PIPV6_HEADER    Version6 = &IpHeader->Version6;
+
+        Length = NTOHS(Version6->PayloadLength) -
+                 (USHORT)Info->IpOptions.Length;
+    }
+
+    Length -= Info->TcpHeader.Length;
+    Length -= Info->TcpOptions.Length;
+    Length = __min(Length, Payload->Length);
+
+    while (Length != 0) {
+        PUCHAR  MappedSystemVa;
+        ULONG   ByteCount;
+
+        ASSERT(Mdl != NULL);
+
+        MappedSystemVa = MmGetSystemAddressForMdlSafe(Mdl, NormalPagePriority);
+        MappedSystemVa += Offset;
+
+        ByteCount = Mdl->ByteCount;
+        ASSERT3U(Offset, <=, ByteCount);
+        ByteCount -= Offset;
+        ByteCount = __min(ByteCount, Length);
+
+        __AccumulateChecksum(&Accumulator, MappedSystemVa, ByteCount);
+
+        Length -= ByteCount;
+
+        Mdl = Mdl->Next;
+        Offset = 0;
+    }
+
+    return __FoldChecksum(Accumulator, TRUE);
+}
+
+USHORT
+ChecksumUdpPacket(
+    IN  PUCHAR                  StartVa,
+    IN  PXENVIF_PACKET_INFO     Info,
+    IN  USHORT                  PseudoHeaderChecksum,
+    IN  PXENVIF_PACKET_PAYLOAD  Payload
+    )
+{
+    ULONG                       Accumulator;
+    PIP_HEADER                  IpHeader;
+    PUDP_HEADER                 UdpHeader;
+    USHORT                      Saved;
+    PMDL                        Mdl;
+    ULONG                       Offset;
+    ULONG                       Length;
+
+    ASSERT(Info->IpHeader.Length != 0);
+    IpHeader = (PIP_HEADER)(StartVa + Info->IpHeader.Offset);
+
+    ASSERT(Info->UdpHeader.Length != 0);
+    UdpHeader = (PUDP_HEADER)(StartVa + Info->UdpHeader.Offset);
+
+    Saved = UdpHeader->Checksum;
+    UdpHeader->Checksum = 0;
+
+    Accumulator = PseudoHeaderChecksum;
+    __AccumulateChecksum(&Accumulator,
+                         StartVa + Info->UdpHeader.Offset,
+                         Info->UdpHeader.Length);
+
+    UdpHeader->Checksum = Saved;
+
+    Mdl = Payload->Mdl;
+    Offset = Payload->Offset;
+
+    if (IpHeader->Version == 4) {
+        PIPV4_HEADER    Version4 = &IpHeader->Version4;
+        
+        Length = NTOHS(Version4->PacketLength) -
+                 sizeof (IPV4_HEADER) -
+                 (USHORT)Info->IpOptions.Length;
+    } else {
+        PIPV6_HEADER    Version6 = &IpHeader->Version6;
+
+        Length = NTOHS(Version6->PayloadLength) -
+                 (USHORT)Info->IpOptions.Length;
+    }
+
+    Length -= Info->UdpHeader.Length;
+    Length = __min(Length, Payload->Length);
+
+    while (Length != 0) {
+        PUCHAR  MappedSystemVa;
+        ULONG   ByteCount;
+
+        ASSERT(Mdl != NULL);
+
+        MappedSystemVa = MmGetSystemAddressForMdlSafe(Mdl, NormalPagePriority);
+        MappedSystemVa += Offset;
+
+        ByteCount = Mdl->ByteCount;
+        ASSERT3U(Offset, <=, ByteCount);
+        ByteCount -= Offset;
+        ByteCount = __min(ByteCount, Length);
+
+        __AccumulateChecksum(&Accumulator, MappedSystemVa, ByteCount);
+
+        Length -= ByteCount;
+
+        Mdl = Mdl->Next;
+        Offset = 0;
+    }
+
+    return __FoldChecksum(Accumulator, TRUE);
+}
diff --git a/src/xenvif/checksum.h b/src/xenvif/checksum.h
new file mode 100644 (file)
index 0000000..c7a0ef9
--- /dev/null
@@ -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 _XENVIF_CHECKSUM_H
+#define _XENVIF_CHECKSUM_H
+
+#include "parse.h"
+
+extern VOID
+AccumulateChecksum(
+    IN OUT  PULONG  Accumulator,
+    IN      PVOID   MappedSystemVa,
+    IN      ULONG   ByteCount
+    );
+
+extern USHORT
+FoldChecksum(
+    IN  ULONG   Accumulator,
+    IN  BOOLEAN Invert
+    );
+
+extern USHORT
+ChecksumIpVersion4Header(
+    IN  PUCHAR              StartVa,
+    IN  PXENVIF_PACKET_INFO Info
+    );
+
+extern USHORT
+ChecksumIpVersion4PseudoHeader(
+    IN  PIPV4_ADDRESS   SourceAddress,
+    IN  PIPV4_ADDRESS   DestinationAddress,
+    IN  USHORT          Length,
+    IN  UCHAR           Protocol
+    );
+
+extern USHORT
+ChecksumIpVersion6PseudoHeader(
+    IN  PIPV6_ADDRESS   SourceAddress,
+    IN  PIPV6_ADDRESS   DestinationAddress,
+    IN  USHORT          Length,
+    IN  UCHAR           Protocol
+    );
+
+extern USHORT
+ChecksumPseudoHeader(
+    IN  PUCHAR              StartVa,
+    IN  PXENVIF_PACKET_INFO Info
+    );
+
+extern USHORT
+ChecksumTcpPacket(
+    IN  PUCHAR                  StartVa,
+    IN  PXENVIF_PACKET_INFO     Info,
+    IN  USHORT                  PseudoHeaderChecksum,
+    IN  PXENVIF_PACKET_PAYLOAD  Payload
+    );
+
+extern USHORT
+ChecksumUdpPacket(
+    IN  PUCHAR                  StartVa,
+    IN  PXENVIF_PACKET_INFO     Info,
+    IN  USHORT                  PseudoHeaderChecksum,
+    IN  PXENVIF_PACKET_PAYLOAD  Payload
+    );
+
+#endif  // _XENVIF_CHECKSUM_H
diff --git a/src/xenvif/driver.c b/src/xenvif/driver.c
new file mode 100644 (file)
index 0000000..7ef48df
--- /dev/null
@@ -0,0 +1,237 @@
+/* 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 <util.h>
+#include <version.h>
+
+#include "registry.h"
+#include "fdo.h"
+#include "pdo.h"
+#include "receiver.h"
+#include "driver.h"
+#include "log.h"
+#include "assert.h"
+
+extern PULONG       InitSafeBootMode;
+
+PDRIVER_OBJECT      DriverObject;
+
+XENVIF_PARAMETERS   DriverParameters;
+
+DRIVER_UNLOAD       DriverUnload;
+
+VOID
+DriverUnload(
+    IN  PDRIVER_OBJECT  _DriverObject
+    )
+{
+    ASSERT3P(_DriverObject, ==, DriverObject);
+
+    Trace("====>\n");
+
+    if (*InitSafeBootMode > 0)
+        goto done;
+
+    RegistryFreeSzValue(DriverParameters.UnsupportedDevices);
+
+    RegistryTeardown();
+
+done:
+    DriverObject = NULL;
+
+    Trace("<====\n");
+}
+
+DRIVER_ADD_DEVICE   AddDevice;
+
+NTSTATUS
+AddDevice(
+    IN  PDRIVER_OBJECT  _DriverObject,
+    IN  PDEVICE_OBJECT  DeviceObject
+    )
+{
+    NTSTATUS            status;
+
+    ASSERT3P(_DriverObject, ==, DriverObject);
+
+    status = FdoCreate(DeviceObject);
+    if (!NT_SUCCESS(status))
+        goto fail1;
+
+    // prefast stupidity
+    ASSERT(!(DeviceObject->Flags & DO_DEVICE_INITIALIZING));
+    DeviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
+
+    return STATUS_SUCCESS;
+
+fail1:
+    Error("fail1 (%08x)\n", status);
+
+    return status;
+}
+
+DRIVER_DISPATCH Dispatch;
+
+NTSTATUS 
+Dispatch(
+    IN PDEVICE_OBJECT   DeviceObject,
+    IN PIRP             Irp
+    )
+{
+    PXENVIF_DX          Dx;
+    NTSTATUS            status;
+
+    Dx = (PXENVIF_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: {
+        PXENVIF_PDO Pdo = Dx->Pdo;
+
+        status = PdoDispatch(Pdo, Irp);
+        break;
+    }
+    case FUNCTION_DEVICE_OBJECT: {
+        PXENVIF_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
+    )
+{
+    HANDLE              Key;
+    ULONG               Index;
+    NTSTATUS            status;
+
+    ASSERT3P(DriverObject, ==, NULL);
+
+    ExInitializeDriverRuntime(DrvRtPoolNxOptIn);
+
+    Trace("====>\n");
+
+    Info("%s (%s)\n",
+         MAJOR_VERSION_STR "." MINOR_VERSION_STR "." MICRO_VERSION_STR "." BUILD_NUMBER_STR,
+         DAY_STR "/" MONTH_STR "/" YEAR_STR);
+
+    DriverObject = _DriverObject;
+    DriverObject->DriverUnload = DriverUnload;
+
+    if (*InitSafeBootMode > 0)
+        goto done;
+
+    status = RegistryInitialize(RegistryPath);
+    if (!NT_SUCCESS(status))
+        goto fail1;
+
+    status = RegistryOpenSubKey("Parameters", KEY_READ, &Key);
+    if (NT_SUCCESS(status)) {
+        status = RegistryQuerySzValue(Key,
+                                      "UnsupportedDevices",
+                                      &DriverParameters.UnsupportedDevices);
+        if (!NT_SUCCESS(status))
+            DriverParameters.UnsupportedDevices = NULL;
+
+        status = RegistryQueryDwordValue(Key,
+                                         "ReceiverMaximumProtocol",
+                                         &DriverParameters.ReceiverMaximumProtocol);
+        if (!NT_SUCCESS(status))
+            DriverParameters.ReceiverMaximumProtocol = 0;
+
+        status = RegistryQueryDwordValue(Key,
+                                         "ReceiverCalculateChecksums",
+                                         &DriverParameters.ReceiverCalculateChecksums);
+        if (!NT_SUCCESS(status))
+            DriverParameters.ReceiverCalculateChecksums = 0;
+
+        status = RegistryQueryDwordValue(Key,
+                                         "ReceiverAllowGsoPackets",
+                                         &DriverParameters.ReceiverAllowGsoPackets);
+        if (!NT_SUCCESS(status))
+            DriverParameters.ReceiverAllowGsoPackets = 1;
+
+        status = RegistryQueryDwordValue(Key,
+                                         "ReceiverIpAlignOffset",
+                                         &DriverParameters.ReceiverIpAlignOffset);
+        if (!NT_SUCCESS(status))
+            DriverParameters.ReceiverIpAlignOffset = 0;
+
+        status = RegistryQueryDwordValue(Key,
+                                         "CreatePDOs",
+                                         &DriverParameters.CreatePDOs);
+        if (!NT_SUCCESS(status))
+            DriverParameters.CreatePDOs = 1;
+
+        RegistryCloseKey(Key);
+    }
+
+    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;
+
+fail1:
+    Error("fail1 (%08x)\n", status);
+
+    return status;
+}
diff --git a/src/xenvif/driver.h b/src/xenvif/driver.h
new file mode 100644 (file)
index 0000000..89e44a2
--- /dev/null
@@ -0,0 +1,81 @@
+/* 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 _XENVIF_DRIVER_H
+#define _XENVIF_DRIVER_H
+
+typedef struct _XENVIF_PDO  XENVIF_PDO, *PXENVIF_PDO;
+typedef struct _XENVIF_FDO  XENVIF_FDO, *PXENVIF_FDO;
+
+#include "fdo.h"
+#include "pdo.h"
+
+extern PDRIVER_OBJECT   DriverObject;
+
+typedef struct _XENVIF_PARAMETERS {
+    PANSI_STRING    UnsupportedDevices;
+    ULONG           ReceiverMaximumProtocol;
+    ULONG           ReceiverCalculateChecksums;
+    ULONG           ReceiverAllowGsoPackets;
+    ULONG           ReceiverIpAlignOffset;
+    ULONG           CreatePDOs;
+} XENVIF_PARAMETERS, *PXENVIF_PARAMETERS;
+
+extern XENVIF_PARAMETERS    DriverParameters;
+
+#define MAX_DEVICE_ID_LEN   200
+
+#pragma warning(push)
+#pragma warning(disable:4201) // nonstandard extension used : nameless struct/union
+
+typedef struct _XENVIF_DX {
+    PDEVICE_OBJECT      DeviceObject;
+    DEVICE_OBJECT_TYPE  Type;
+
+    DEVICE_PNP_STATE    DevicePnpState;
+    DEVICE_PNP_STATE    PreviousDevicePnpState;
+
+    SYSTEM_POWER_STATE  SystemPowerState;
+    DEVICE_POWER_STATE  DevicePowerState;
+
+    CHAR                Name[MAX_DEVICE_ID_LEN];
+
+    LIST_ENTRY          ListEntry;
+
+    union {
+        PXENVIF_FDO     Fdo;
+        PXENVIF_PDO     Pdo;
+    };
+} XENVIF_DX, *PXENVIF_DX;
+
+#pragma warning(pop)
+
+#endif  // _XENVIF_DRIVER_H
diff --git a/src/xenvif/fdo.c b/src/xenvif/fdo.c
new file mode 100644 (file)
index 0000000..49ee429
--- /dev/null
@@ -0,0 +1,3076 @@
+/* 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 <util.h>
+
+#include <evtchn_interface.h>
+#include <debug_interface.h>
+#include <store_interface.h>
+#include <gnttab_interface.h>
+#include <suspend_interface.h>
+#include <emulated_interface.h>
+
+#include "driver.h"
+#include "registry.h"
+#include "fdo.h"
+#include "pdo.h"
+#include "thread.h"
+#include "mutex.h"
+#include "frontend.h"
+#include "names.h"
+#include "log.h"
+#include "assert.h"
+
+#define FDO_POOL 'ODF'
+
+#define MAXNAMELEN  128
+
+typedef enum _FDO_RESOURCE_TYPE {
+    MEMORY_RESOURCE = 0,
+    INTERRUPT_RESOURCE,
+    RESOURCE_COUNT
+} FDO_RESOURCE_TYPE, *PFDO_RESOURCE_TYPE;
+
+typedef struct _FDO_RESOURCE {
+    CM_PARTIAL_RESOURCE_DESCRIPTOR Raw;
+    CM_PARTIAL_RESOURCE_DESCRIPTOR Translated;
+} FDO_RESOURCE, *PFDO_RESOURCE;
+
+struct _XENVIF_FDO {
+    PXENVIF_DX                  Dx;
+    PDEVICE_OBJECT              LowerDeviceObject;
+    PDEVICE_OBJECT              PhysicalDeviceObject;
+    DEVICE_CAPABILITIES         LowerDeviceCapabilities;
+    ULONG                       Usage[DeviceUsageTypeDumpFile + 1];
+    BOOLEAN                     NotDisableable;
+
+    PXENVIF_THREAD              SystemPowerThread;
+    PIRP                        SystemPowerIrp;
+    PXENVIF_THREAD              DevicePowerThread;
+    PIRP                        DevicePowerIrp;
+
+    PXENVIF_THREAD              ScanThread;
+    KEVENT                      ScanEvent;
+    PXENBUS_STORE_WATCH         ScanWatch;
+    XENVIF_MUTEX                Mutex;
+    ULONG                       References;
+
+    FDO_RESOURCE                Resource[RESOURCE_COUNT];
+
+    PXENBUS_EVTCHN_INTERFACE    EvtchnInterface;
+    PXENBUS_DEBUG_INTERFACE     DebugInterface;
+    PXENBUS_STORE_INTERFACE     StoreInterface;
+    PXENBUS_GNTTAB_INTERFACE    GnttabInterface;
+    PXENBUS_SUSPEND_INTERFACE   SuspendInterface;
+    PXENFILT_EMULATED_INTERFACE EmulatedInterface;
+
+    PXENBUS_SUSPEND_CALLBACK    SuspendCallbackLate;
+};
+
+static FORCEINLINE PVOID
+__FdoAllocate(
+    IN  ULONG   Length
+    )
+{
+    return __AllocateNonPagedPoolWithTag(Length, FDO_POOL);
+}
+
+static FORCEINLINE VOID
+__FdoFree(
+    IN  PVOID   Buffer
+    )
+{
+    __FreePoolWithTag(Buffer, FDO_POOL);
+}
+
+static FORCEINLINE VOID
+__FdoSetDevicePnpState(
+    IN  PXENVIF_FDO         Fdo,
+    IN  DEVICE_PNP_STATE    State
+    )
+{
+    PXENVIF_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  PXENVIF_FDO         Fdo,
+    IN  DEVICE_PNP_STATE    State
+    )
+{
+    PXENVIF_DX              Dx = Fdo->Dx;
+
+    if (Dx->DevicePnpState == State)
+        Dx->DevicePnpState = Dx->PreviousDevicePnpState;
+}
+
+static FORCEINLINE DEVICE_PNP_STATE
+__FdoGetDevicePnpState(
+    IN  PXENVIF_FDO     Fdo
+    )
+{
+    PXENVIF_DX          Dx = Fdo->Dx;
+
+    return Dx->DevicePnpState;
+}
+
+static FORCEINLINE VOID
+__FdoSetDevicePowerState(
+    IN  PXENVIF_FDO         Fdo,
+    IN  DEVICE_POWER_STATE  State
+    )
+{
+    PXENVIF_DX              Dx = Fdo->Dx;
+
+    Dx->DevicePowerState = State;
+}
+
+static FORCEINLINE DEVICE_POWER_STATE
+__FdoGetDevicePowerState(
+    IN  PXENVIF_FDO     Fdo
+    )
+{
+    PXENVIF_DX          Dx = Fdo->Dx;
+
+    return Dx->DevicePowerState;
+}
+
+static FORCEINLINE VOID
+__FdoSetSystemPowerState(
+    IN  PXENVIF_FDO         Fdo,
+    IN  SYSTEM_POWER_STATE  State
+    )
+{
+    PXENVIF_DX              Dx = Fdo->Dx;
+
+    Dx->SystemPowerState = State;
+}
+
+static FORCEINLINE SYSTEM_POWER_STATE
+__FdoGetSystemPowerState(
+    IN  PXENVIF_FDO     Fdo
+    )
+{
+    PXENVIF_DX          Dx = Fdo->Dx;
+
+    return Dx->SystemPowerState;
+}
+
+static FORCEINLINE PDEVICE_OBJECT
+__FdoGetPhysicalDeviceObject(
+    IN  PXENVIF_FDO Fdo
+    )
+{
+    return Fdo->PhysicalDeviceObject;
+}
+
+PDEVICE_OBJECT
+FdoGetPhysicalDeviceObject(
+    IN  PXENVIF_FDO Fdo
+    )
+{
+    return __FdoGetPhysicalDeviceObject(Fdo);
+}
+
+static FORCEINLINE NTSTATUS
+__FdoSetName(
+    IN  PXENVIF_FDO Fdo,
+    IN  PWCHAR      Name
+    )
+{
+    PXENVIF_DX      Dx = Fdo->Dx;
+    UNICODE_STRING  Unicode;
+    ANSI_STRING     Ansi;
+    ULONG           Index;
+    NTSTATUS        status;
+
+    RtlInitUnicodeString(&Unicode, Name);
+
+    Ansi.Buffer = Dx->Name;
+    Ansi.MaximumLength = sizeof (Dx->Name);
+    Ansi.Length = 0;
+
+    status = RtlUnicodeStringToAnsiString(&Ansi, &Unicode, FALSE);
+    if (!NT_SUCCESS(status))
+        goto fail1;
+    
+    for (Index = 0; Dx->Name[Index] != '\0'; Index++) {
+        if (!isalnum((UCHAR)Dx->Name[Index]))
+            Dx->Name[Index] = '_';
+    }
+
+    return STATUS_SUCCESS;
+
+fail1:
+    Error("fail1 (%08x)\n", status);
+
+    return status;
+}
+
+static FORCEINLINE PCHAR
+__FdoGetName(
+    IN  PXENVIF_FDO Fdo
+    )
+{
+    PXENVIF_DX      Dx = Fdo->Dx;
+
+    return Dx->Name;
+}
+
+PCHAR
+FdoGetName(
+    IN  PXENVIF_FDO Fdo
+    )
+{
+    return __FdoGetName(Fdo);
+}
+
+__drv_functionClass(IO_COMPLETION_ROUTINE)
+__drv_sameIRQL
+static NTSTATUS
+__FdoDelegateIrp(
+    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;
+}
+
+NTSTATUS
+FdoDelegateIrp(
+    IN  PXENVIF_FDO     Fdo,
+    IN  PIRP            Irp
+    )
+{
+    PDEVICE_OBJECT      DeviceObject;
+    PIO_STACK_LOCATION  StackLocation;
+    PIRP                SubIrp;
+    KEVENT              Event;
+    PIO_STACK_LOCATION  SubStackLocation;
+    NTSTATUS            status;
+
+    ASSERT3U(KeGetCurrentIrql(), ==, PASSIVE_LEVEL);
+
+    StackLocation = IoGetCurrentIrpStackLocation(Irp);
+
+    // Find the top of the FDO stack and hold a reference
+    DeviceObject = IoGetAttachedDeviceReference(Fdo->Dx->DeviceObject);
+
+    // Get a new IRP for the FDO stack
+    SubIrp = IoAllocateIrp(DeviceObject->StackSize, FALSE);
+
+    status = STATUS_NO_MEMORY;
+    if (SubIrp == NULL)
+        goto done;
+
+    // Copy in the information from the original IRP
+    SubStackLocation = IoGetNextIrpStackLocation(SubIrp);
+
+    KeInitializeEvent(&Event, NotificationEvent, FALSE);
+
+    RtlCopyMemory(SubStackLocation, StackLocation,
+                  FIELD_OFFSET(IO_STACK_LOCATION, CompletionRoutine));
+    SubStackLocation->Control = 0;
+
+    IoSetCompletionRoutine(SubIrp,
+                           __FdoDelegateIrp,
+                           &Event,
+                           TRUE,
+                           TRUE,
+                           TRUE);
+
+    // Default completion status
+    SubIrp->IoStatus.Status = Irp->IoStatus.Status;
+
+    status = IoCallDriver(DeviceObject, SubIrp);
+    if (status == STATUS_PENDING) {
+        (VOID) KeWaitForSingleObject(&Event,
+                                     Executive,
+                                     KernelMode,
+                                     FALSE,
+                                     NULL);
+        status = SubIrp->IoStatus.Status;
+    } else {
+        ASSERT3U(status, ==, SubIrp->IoStatus.Status);
+    }
+
+    IoFreeIrp(SubIrp);
+
+done:
+    ObDereferenceObject(DeviceObject);
+
+    return 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  PXENVIF_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);
+    }
+
+    Trace("%08x\n", status);
+
+    return status;
+}
+
+VOID
+FdoAddPhysicalDeviceObject(
+    IN  PXENVIF_FDO     Fdo,
+    IN  PXENVIF_PDO     Pdo
+    )
+{
+    PDEVICE_OBJECT      DeviceObject;
+    PXENVIF_DX          Dx;
+
+    DeviceObject = PdoGetDeviceObject(Pdo);
+    Dx = (PXENVIF_DX)DeviceObject->DeviceExtension;
+    ASSERT3U(Dx->Type, ==, PHYSICAL_DEVICE_OBJECT);
+
+    InsertTailList(&Fdo->Dx->ListEntry, &Dx->ListEntry);
+    ASSERT3U(Fdo->References, !=, 0);
+    Fdo->References++;
+
+    PdoResume(Pdo);
+}
+
+VOID
+FdoRemovePhysicalDeviceObject(
+    IN  PXENVIF_FDO     Fdo,
+    IN  PXENVIF_PDO     Pdo
+    )
+{
+    PDEVICE_OBJECT      DeviceObject;
+    PXENVIF_DX          Dx;
+
+    DeviceObject = PdoGetDeviceObject(Pdo);
+    Dx = (PXENVIF_DX)DeviceObject->DeviceExtension;
+    ASSERT3U(Dx->Type, ==, PHYSICAL_DEVICE_OBJECT);
+
+    PdoSuspend(Pdo);
+
+    RemoveEntryList(&Dx->ListEntry);
+    ASSERT3U(Fdo->References, !=, 0);
+    --Fdo->References;
+
+    if (Fdo->ScanThread)
+        ThreadWake(Fdo->ScanThread);
+}
+
+static FORCEINLINE VOID
+__FdoAcquireMutex(
+    IN  PXENVIF_FDO     Fdo
+    )
+{
+    AcquireMutex(&Fdo->Mutex);
+}
+
+VOID
+FdoAcquireMutex(
+    IN  PXENVIF_FDO     Fdo
+    )
+{
+    __FdoAcquireMutex(Fdo);
+}
+
+static FORCEINLINE VOID
+__FdoReleaseMutex(
+    IN  PXENVIF_FDO     Fdo
+    )
+{
+    ReleaseMutex(&Fdo->Mutex);
+}
+
+VOID
+FdoReleaseMutex(
+    IN  PXENVIF_FDO     Fdo
+    )
+{
+    __FdoReleaseMutex(Fdo);
+
+    if (Fdo->References == 0)
+        FdoDestroy(Fdo);
+}
+
+static FORCEINLINE BOOLEAN
+__FdoEnumerate(
+    IN  PXENVIF_FDO     Fdo,
+    IN  PANSI_STRING    Devices
+    )
+{
+    BOOLEAN             NeedInvalidate;
+    PLIST_ENTRY         ListEntry;
+    ULONG               Index;
+
+    Trace("====>\n");
+
+    NeedInvalidate = FALSE;
+
+    if (DriverParameters.CreatePDOs == 0)
+        goto done;
+
+    __FdoAcquireMutex(Fdo);
+
+    ListEntry = Fdo->Dx->ListEntry.Flink;
+    while (ListEntry != &Fdo->Dx->ListEntry) {
+        PLIST_ENTRY     Next = ListEntry->Flink;
+        PXENVIF_DX      Dx = CONTAINING_RECORD(ListEntry, XENVIF_DX, ListEntry);
+        PCHAR           Name = Dx->Name;
+        PXENVIF_PDO     Pdo = Dx->Pdo;
+        BOOLEAN         Missing;
+
+        Missing = TRUE;
+
+        // If the PDO exists in the device list
+        // from xenstore then we don't want to remove it.
+
+        for (Index = 0; Devices[Index].Buffer != NULL; Index++) {
+            PANSI_STRING Device = &Devices[Index];
+
+            if (strcmp(Name, Device->Buffer) == 0) {
+                Missing = FALSE;
+                Device->Length = 0;  // avoid duplication
+                break;
+            }
+        }
+
+        if (Missing &&
+            !PdoIsMissing(Pdo) &&
+            PdoGetDevicePnpState(Pdo) != Deleted) {
+            PdoSetMissing(Pdo, "device disappeared");
+
+            // If the PDO has not yet been enumerated then we can go ahead
+            // and mark it as deleted, otherwise we need to notify PnP manager and
+            // wait for the REMOVE_DEVICE IRP.
+            if (PdoGetDevicePnpState(Pdo) == Present) {
+                PdoSetDevicePnpState(Pdo, Deleted);
+                PdoDestroy(Pdo);
+            } else {
+                NeedInvalidate = TRUE;
+            }
+        }
+
+        ListEntry = Next;
+    }
+
+    // Check the device list from xenstore against the unsupported list
+    for (Index = 0; Devices[Index].Buffer != NULL; Index++) {
+        PANSI_STRING    Device = &Devices[Index];
+        ULONG           Entry;
+        BOOLEAN         Supported;
+
+        Supported = TRUE;
+
+        for (Entry = 0;
+             DriverParameters.UnsupportedDevices != NULL && DriverParameters.UnsupportedDevices[Entry].Buffer != NULL;
+             Entry++) {
+            if (strncmp(Device->Buffer,
+                        DriverParameters.UnsupportedDevices[Entry].Buffer,
+                        Device->Length) == 0) {
+                Supported = FALSE;
+                break;
+            }
+        }
+
+        if (!Supported)
+            Device->Length = 0;  // avoid creation
+    }
+
+    // Walk the device list and create PDOs for any new devices
+
+    for (Index = 0; Devices[Index].Buffer != NULL; Index++) {
+        PANSI_STRING    Device = &Devices[Index];
+
+        if (Device->Length != 0) {
+            NTSTATUS        status;
+
+            status = PdoCreate(Fdo, Device);
+            if (NT_SUCCESS(status))
+                NeedInvalidate = TRUE;
+        }
+    }
+
+    __FdoReleaseMutex(Fdo);
+
+done:
+    Trace("<====\n");
+    return NeedInvalidate;
+}
+
+static FORCEINLINE PANSI_STRING
+__FdoMultiSzToUpcaseAnsi(
+    IN  PCHAR       Buffer
+    )
+{
+    PANSI_STRING    Ansi;
+    LONG            Index;
+    LONG            Count;
+    NTSTATUS        status;
+
+    Index = 0;
+    Count = 0;
+    for (;;) {
+        if (Buffer[Index] == '\0') {
+            Count++;
+            Index++;
+
+            // Check for double NUL
+            if (Buffer[Index] == '\0')
+                break;
+        } else {
+            Buffer[Index] = (CHAR)toupper(Buffer[Index]);
+            Index++;
+        }
+    }
+
+    Ansi = __FdoAllocate(sizeof (ANSI_STRING) * (Count + 1));
+
+    status = STATUS_NO_MEMORY;
+    if (Ansi == NULL)
+        goto fail1;
+
+    for (Index = 0; Index < Count; Index++) {
+        ULONG   Length;
+
+        Length = (ULONG)strlen(Buffer);
+        Ansi[Index].MaximumLength = (USHORT)(Length + 1);
+        Ansi[Index].Buffer = __FdoAllocate(Ansi[Index].MaximumLength);
+
+        status = STATUS_NO_MEMORY;
+        if (Ansi[Index].Buffer == NULL)
+            goto fail2;
+
+        RtlCopyMemory(Ansi[Index].Buffer, Buffer, Length);
+        Ansi[Index].Length = (USHORT)Length;
+
+        Buffer += Length + 1;
+    }
+
+    return Ansi;
+
+fail2:
+    Error("fail2\n");
+
+    while (--Index >= 0)
+        __FdoFree(Ansi[Index].Buffer);
+
+    __FdoFree(Ansi);
+
+fail1:
+    Error("fail1 (%08x)\n", status);
+
+    return NULL;
+}
+
+static FORCEINLINE VOID
+__FdoFreeAnsi(
+    IN  PANSI_STRING    Ansi
+    )
+{
+    ULONG               Index;
+
+    for (Index = 0; Ansi[Index].Buffer != NULL; Index++)
+        __FdoFree(Ansi[Index].Buffer);
+        
+    __FdoFree(Ansi);
+}
+
+static NTSTATUS
+FdoScan(
+    PXENVIF_THREAD      Self,
+    PVOID               Context
+    )
+{
+    PXENVIF_FDO         Fdo = Context;
+    BOOLEAN             NeedInvalidate;
+    PKEVENT             Event;
+
+    Trace("====>\n");
+
+    NeedInvalidate = FALSE;
+
+    Event = ThreadGetEvent(Self);
+
+    for (;;) {
+        PCHAR           Buffer;
+        PANSI_STRING    Devices;
+        NTSTATUS        status;
+
+        Trace("waiting...\n");
+
+        (VOID) KeWaitForSingleObject(Event,
+                                        Executive,
+                                        KernelMode,
+                                        FALSE,
+                                        NULL);
+        KeClearEvent(Event);
+
+        if (ThreadIsAlerted(Self))
+            break;
+
+        STORE(Acquire, Fdo->StoreInterface);
+
+        if (__FdoGetDevicePnpState(Fdo) != Started)
+            goto loop;
+
+        status = STORE(Directory,
+                       Fdo->StoreInterface,
+                       NULL,
+                       "device",
+                       "vif",
+                       &Buffer);
+        if (!NT_SUCCESS(status))
+            goto loop;
+
+        Devices = __FdoMultiSzToUpcaseAnsi(Buffer);
+
+        STORE(Free,
+              Fdo->StoreInterface,
+              Buffer);
+
+        if (Devices != NULL) {
+            NeedInvalidate = __FdoEnumerate(Fdo, Devices);
+            __FdoFreeAnsi(Devices);
+        }
+
+        if (NeedInvalidate) {
+            NeedInvalidate = FALSE;
+            IoInvalidateDeviceRelations(__FdoGetPhysicalDeviceObject(Fdo), 
+                                        BusRelations);
+        }
+
+loop:
+        STORE(Release, Fdo->StoreInterface);
+
+        KeSetEvent(&Fdo->ScanEvent, IO_NO_INCREMENT, FALSE);
+    }
+
+    KeSetEvent(&Fdo->ScanEvent, IO_NO_INCREMENT, FALSE);
+
+    Trace("<====\n");
+    return STATUS_SUCCESS;
+}
+
+static DECLSPEC_NOINLINE VOID
+FdoParseResources(
+    IN  PXENVIF_FDO             Fdo,
+    IN  PCM_RESOURCE_LIST       RawResourceList,
+    IN  PCM_RESOURCE_LIST       TranslatedResourceList
+    )
+{
+    PCM_PARTIAL_RESOURCE_LIST   RawPartialList;
+    PCM_PARTIAL_RESOURCE_LIST   TranslatedPartialList;
+    ULONG                       Index;
+
+    ASSERT3U(RawResourceList->Count, ==, 1);
+    RawPartialList = &RawResourceList->List[0].PartialResourceList;
+
+    ASSERT3U(RawPartialList->Version, ==, 1);
+    ASSERT3U(RawPartialList->Revision, ==, 1);
+
+    ASSERT3U(TranslatedResourceList->Count, ==, 1);
+    TranslatedPartialList = &TranslatedResourceList->List[0].PartialResourceList;
+
+    ASSERT3U(TranslatedPartialList->Version, ==, 1);
+    ASSERT3U(TranslatedPartialList->Revision, ==, 1);
+
+    for (Index = 0; Index < TranslatedPartialList->Count; Index++) {
+        PCM_PARTIAL_RESOURCE_DESCRIPTOR RawPartialDescriptor;
+        PCM_PARTIAL_RESOURCE_DESCRIPTOR TranslatedPartialDescriptor;
+
+        RawPartialDescriptor = &RawPartialList->PartialDescriptors[Index];
+        TranslatedPartialDescriptor = &TranslatedPartialList->PartialDescriptors[Index];
+
+        switch (TranslatedPartialDescriptor->Type) {
+        case CmResourceTypeMemory:
+            Fdo->Resource[MEMORY_RESOURCE].Raw = *RawPartialDescriptor;
+            Fdo->Resource[MEMORY_RESOURCE].Translated = *TranslatedPartialDescriptor;
+            break;
+
+        case CmResourceTypeInterrupt:
+            Fdo->Resource[INTERRUPT_RESOURCE].Raw = *RawPartialDescriptor;
+            Fdo->Resource[INTERRUPT_RESOURCE].Translated = *TranslatedPartialDescriptor;
+            break;
+
+        default:
+            break;
+        }
+    }
+}
+
+PXENBUS_EVTCHN_INTERFACE
+FdoGetEvtchnInterface(
+    IN  PXENVIF_FDO Fdo
+    )
+{
+    return Fdo->EvtchnInterface;
+}
+
+PXENBUS_DEBUG_INTERFACE
+FdoGetDebugInterface(
+    IN  PXENVIF_FDO Fdo
+    )
+{
+    return Fdo->DebugInterface;
+}
+
+PXENBUS_STORE_INTERFACE
+FdoGetStoreInterface(
+    IN  PXENVIF_FDO Fdo
+    )
+{
+    return Fdo->StoreInterface;
+}
+
+PXENBUS_GNTTAB_INTERFACE
+FdoGetGnttabInterface(
+    IN  PXENVIF_FDO Fdo
+    )
+{
+    return Fdo->GnttabInterface;
+}
+
+PXENBUS_SUSPEND_INTERFACE
+FdoGetSuspendInterface(
+    IN  PXENVIF_FDO Fdo
+    )
+{
+    return Fdo->SuspendInterface;
+}
+
+static FORCEINLINE PXENFILT_EMULATED_INTERFACE
+__FdoGetEmulatedInterface(
+    IN  PXENVIF_FDO Fdo
+    )
+{
+    return Fdo->EmulatedInterface;
+}
+
+PXENFILT_EMULATED_INTERFACE
+FdoGetEmulatedInterface(
+    IN  PXENVIF_FDO Fdo
+    )
+{
+    return __FdoGetEmulatedInterface(Fdo);
+}
+
+static FORCEINLINE NTSTATUS
+__FdoD3ToD0(
+    IN  PXENVIF_FDO Fdo
+    )
+{
+    POWER_STATE     PowerState;
+    NTSTATUS        status;
+
+    Trace("====>\n");
+
+    ASSERT3U(KeGetCurrentIrql(), ==, DISPATCH_LEVEL);
+    ASSERT3U(__FdoGetDevicePowerState(Fdo), ==, PowerDeviceD3);
+
+    __FdoSetDevicePowerState(Fdo, PowerDeviceD0);
+
+    STORE(Acquire, Fdo->StoreInterface);
+
+    status = STORE(Watch,
+                   Fdo->StoreInterface,
+                   "device",
+                   "vif",
+                   ThreadGetEvent(Fdo->ScanThread),
+                   &Fdo->ScanWatch);
+    if (!NT_SUCCESS(status))
+        goto fail1;
+
+    PowerState.DeviceState = PowerDeviceD0;
+    PoSetPowerState(Fdo->Dx->DeviceObject,
+                    DevicePowerState,
+                    PowerState);
+
+    Trace("<====\n");
+
+    return STATUS_SUCCESS;
+
+fail1:
+    Error("fail1 (%08x)\n", status);
+
+    STORE(Release, Fdo->StoreInterface);
+
+    __FdoSetDevicePowerState(Fdo, PowerDeviceD3);
+
+    return status;
+}
+
+static FORCEINLINE VOID
+__FdoD0ToD3(
+    IN  PXENVIF_FDO Fdo
+    )
+{
+    POWER_STATE     PowerState;
+
+    Trace("====>\n");
+
+    ASSERT3U(KeGetCurrentIrql(), ==, DISPATCH_LEVEL);
+    ASSERT3U(__FdoGetDevicePowerState(Fdo), ==, PowerDeviceD0);
+
+    PowerState.DeviceState = PowerDeviceD3;
+    PoSetPowerState(Fdo->Dx->DeviceObject,
+                    DevicePowerState,
+                    PowerState);
+
+    __FdoSetDevicePowerState(Fdo, PowerDeviceD3);
+
+    (VOID) STORE(Unwatch,
+                 Fdo->StoreInterface,
+                 Fdo->ScanWatch);
+    Fdo->ScanWatch = NULL;
+
+    STORE(Release, Fdo->StoreInterface);
+
+    Trace("<====\n");
+}
+
+static DECLSPEC_NOINLINE VOID
+FdoSuspendCallbackLate(
+    IN  PVOID   Argument
+    )
+{
+    PXENVIF_FDO Fdo = Argument;
+    NTSTATUS    status;
+
+    __FdoD0ToD3(Fdo);
+
+    status = __FdoD3ToD0(Fdo);
+    ASSERT(NT_SUCCESS(status));
+}
+
+static DECLSPEC_NOINLINE NTSTATUS
+FdoD3ToD0(
+    IN  PXENVIF_FDO Fdo
+    )
+{
+    KIRQL           Irql;
+    PLIST_ENTRY     ListEntry;
+    NTSTATUS        status;
+
+    ASSERT3U(KeGetCurrentIrql(), ==, PASSIVE_LEVEL);
+
+    KeRaiseIrql(DISPATCH_LEVEL, &Irql);
+
+    status = __FdoD3ToD0(Fdo);
+    if (!NT_SUCCESS(status))
+        goto fail1;
+
+    SUSPEND(Acquire, Fdo->SuspendInterface);
+    
+    status = SUSPEND(Register,
+                     Fdo->SuspendInterface,
+                     SUSPEND_CALLBACK_LATE,
+                     FdoSuspendCallbackLate,
+                     Fdo,
+                     &Fdo->SuspendCallbackLate);
+    if (!NT_SUCCESS(status))
+        goto fail2;
+
+    KeLowerIrql(Irql);
+
+    __FdoAcquireMutex(Fdo);
+
+    for (ListEntry = Fdo->Dx->ListEntry.Flink;
+         ListEntry != &Fdo->Dx->ListEntry;
+         ListEntry = ListEntry->Flink) {
+        PXENVIF_DX  Dx = CONTAINING_RECORD(ListEntry, XENVIF_DX, ListEntry);
+        PXENVIF_PDO Pdo = Dx->Pdo;
+
+        ASSERT3U(Dx->Type, ==, PHYSICAL_DEVICE_OBJECT);
+
+        PdoResume(Pdo);
+    }
+
+    __FdoReleaseMutex(Fdo);
+
+    return STATUS_SUCCESS;
+
+fail2:
+    Error("fail2\n");
+
+    SUSPEND(Release, Fdo->SuspendInterface);
+
+    __FdoD0ToD3(Fdo);
+
+fail1:
+    Error("fail1 (%08x)\n", status);
+
+    KeLowerIrql(Irql);
+
+    return status;
+}
+
+static DECLSPEC_NOINLINE VOID
+FdoD0ToD3(
+    IN  PXENVIF_FDO Fdo
+    )
+{
+    PLIST_ENTRY     ListEntry;
+    KIRQL           Irql;
+
+    ASSERT3U(KeGetCurrentIrql(), ==, PASSIVE_LEVEL);
+
+    __FdoAcquireMutex(Fdo);
+
+    for (ListEntry = Fdo->Dx->ListEntry.Flink;
+         ListEntry != &Fdo->Dx->ListEntry;
+         ListEntry = ListEntry->Flink) {
+        PXENVIF_DX  Dx = CONTAINING_RECORD(ListEntry, XENVIF_DX, ListEntry);
+        PXENVIF_PDO Pdo = Dx->Pdo;
+
+        ASSERT3U(Dx->Type, ==, PHYSICAL_DEVICE_OBJECT);
+
+        if (PdoGetDevicePnpState(Pdo) == Deleted ||
+            PdoIsMissing(Pdo))
+            continue;
+
+        PdoSuspend(Pdo);
+    }
+
+    __FdoReleaseMutex(Fdo);
+
+    KeRaiseIrql(DISPATCH_LEVEL, &Irql);
+
+    SUSPEND(Deregister,
+            Fdo->SuspendInterface,
+            Fdo->SuspendCallbackLate);
+    Fdo->SuspendCallbackLate = NULL;
+
+    SUSPEND(Release, Fdo->SuspendInterface);
+
+    __FdoD0ToD3(Fdo);
+
+    KeLowerIrql(Irql);
+}
+
+static DECLSPEC_NOINLINE VOID
+FdoS4ToS3(
+    IN  PXENVIF_FDO         Fdo
+    )
+{
+    ASSERT3U(KeGetCurrentIrql(), ==, PASSIVE_LEVEL);
+    ASSERT3U(__FdoGetSystemPowerState(Fdo), ==, PowerSystemHibernate);
+
+    __FdoSetSystemPowerState(Fdo, PowerSystemSleeping3);
+}
+
+static DECLSPEC_NOINLINE VOID
+FdoS3ToS4(
+    IN  PXENVIF_FDO Fdo
+    )
+{
+    ASSERT3U(KeGetCurrentIrql(), ==, PASSIVE_LEVEL);
+    ASSERT3U(__FdoGetSystemPowerState(Fdo), ==, PowerSystemSleeping3);
+
+    __FdoSetSystemPowerState(Fdo, PowerSystemHibernate);
+}
+
+static DECLSPEC_NOINLINE NTSTATUS
+FdoStartDevice(
+    IN  PXENVIF_FDO     Fdo,
+    IN  PIRP            Irp
+    )
+{
+    PIO_STACK_LOCATION  StackLocation;
+    NTSTATUS            status;
+
+    status = FdoForwardIrpSynchronously(Fdo, Irp);
+    if (!NT_SUCCESS(status))
+        goto fail1;
+
+    StackLocation = IoGetCurrentIrpStackLocation(Irp);
+
+    FdoParseResources(Fdo,
+                      StackLocation->Parameters.StartDevice.AllocatedResources,
+                      StackLocation->Parameters.StartDevice.AllocatedResourcesTranslated);
+
+    KeInitializeEvent(&Fdo->ScanEvent, NotificationEvent, FALSE);
+
+    status = ThreadCreate(FdoScan, Fdo, &Fdo->ScanThread);
+    if (!NT_SUCCESS(status))
+        goto fail2;
+
+    __FdoSetSystemPowerState(Fdo, PowerSystemHibernate);
+     FdoS4ToS3(Fdo);
+    __FdoSetSystemPowerState(Fdo, PowerSystemWorking);
+
+    status = FdoD3ToD0(Fdo);
+    if (!NT_SUCCESS(status))
+        goto fail3;
+
+    __FdoSetDevicePnpState(Fdo, Started);
+    ThreadWake(Fdo->ScanThread);
+
+    status = Irp->IoStatus.Status;
+    IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+    return status;
+
+fail3:
+    Error("fail3\n");
+
+    __FdoSetSystemPowerState(Fdo, PowerSystemSleeping3);
+    FdoS3ToS4(Fdo);
+    __FdoSetSystemPowerState(Fdo, PowerSystemShutdown);
+
+    ThreadAlert(Fdo->ScanThread);
+    ThreadJoin(Fdo->ScanThread);
+    Fdo->ScanThread = NULL;
+
+fail2:
+    Error("fail2\n");
+
+    RtlZeroMemory(&Fdo->ScanEvent, sizeof (KEVENT));
+
+    RtlZeroMemory(&Fdo->Resource, sizeof (FDO_RESOURCE) * RESOURCE_COUNT);
+
+fail1:
+    Error("fail1 (%08x)\n", status);
+
+    Irp->IoStatus.Status = status;
+    IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+    return status;
+}
+
+static DECLSPEC_NOINLINE NTSTATUS
+FdoQueryStopDevice(
+    IN  PXENVIF_FDO Fdo,
+    IN  PIRP        Irp
+    )
+{
+    NTSTATUS        status;
+
+    __FdoSetDevicePnpState(Fdo, StopPending);
+    Irp->IoStatus.Status = STATUS_SUCCESS;
+
+    IoSkipCurrentIrpStackLocation(Irp);
+    status = IoCallDriver(Fdo->LowerDeviceObject, Irp);
+
+    return status;
+}
+
+static DECLSPEC_NOINLINE NTSTATUS
+FdoCancelStopDevice(
+    IN  PXENVIF_FDO Fdo,
+    IN  PIRP        Irp
+    )
+{
+    NTSTATUS        status;
+
+    Irp->IoStatus.Status = STATUS_SUCCESS;
+
+    __FdoRestoreDevicePnpState(Fdo, StopPending);
+
+    IoSkipCurrentIrpStackLocation(Irp);
+    status = IoCallDriver(Fdo->LowerDeviceObject, Irp);
+
+    return status;
+}
+
+static DECLSPEC_NOINLINE NTSTATUS
+FdoStopDevice(
+    IN  PXENVIF_FDO Fdo,
+    IN  PIRP        Irp
+    )
+{
+    NTSTATUS        status;
+
+    FdoD0ToD3(Fdo);
+
+    __FdoSetSystemPowerState(Fdo, PowerSystemSleeping3);
+    FdoS3ToS4(Fdo);
+    __FdoSetSystemPowerState(Fdo, PowerSystemShutdown);
+
+    ThreadAlert(Fdo->ScanThread);
+    ThreadJoin(Fdo->ScanThread);
+    Fdo->ScanThread = NULL;
+
+    RtlZeroMemory(&Fdo->ScanEvent, sizeof (KEVENT));
+
+    RtlZeroMemory(&Fdo->Resource, sizeof (FDO_RESOURCE) * RESOURCE_COUNT);
+
+    __FdoSetDevicePnpState(Fdo, Stopped);
+    Irp->IoStatus.Status = STATUS_SUCCESS;
+
+    IoSkipCurrentIrpStackLocation(Irp);
+    status = IoCallDriver(Fdo->LowerDeviceObject, Irp);
+
+    return status;
+}
+
+static DECLSPEC_NOINLINE NTSTATUS
+FdoQueryRemoveDevice(
+    IN  PXENVIF_FDO Fdo,
+    IN  PIRP        Irp
+    )
+{
+    NTSTATUS        status;
+
+    __FdoSetDevicePnpState(Fdo, RemovePending);
+    Irp->IoStatus.Status = STATUS_SUCCESS;
+
+    IoSkipCurrentIrpStackLocation(Irp);
+    status = IoCallDriver(Fdo->LowerDeviceObject, Irp);
+
+    return status;
+}
+
+static DECLSPEC_NOINLINE NTSTATUS
+FdoCancelRemoveDevice(
+    IN  PXENVIF_FDO Fdo,
+    IN  PIRP        Irp
+    )
+{
+    NTSTATUS        status;
+
+    __FdoRestoreDevicePnpState(Fdo, RemovePending);
+
+    Irp->IoStatus.Status = STATUS_SUCCESS;
+
+    IoSkipCurrentIrpStackLocation(Irp);
+    status = IoCallDriver(Fdo->LowerDeviceObject, Irp);
+
+    return status;
+}
+
+static DECLSPEC_NOINLINE NTSTATUS
+FdoSurpriseRemoval(
+    IN  PXENVIF_FDO Fdo,
+    IN  PIRP        Irp
+    )
+{
+    PLIST_ENTRY     ListEntry;
+    NTSTATUS        status;
+
+    __FdoSetDevicePnpState(Fdo, SurpriseRemovePending);
+
+    __FdoAcquireMutex(Fdo);
+
+    for (ListEntry = Fdo->Dx->ListEntry.Flink;
+         ListEntry != &Fdo->Dx->ListEntry;
+         ListEntry = ListEntry->Flink) {
+        PXENVIF_DX  Dx = CONTAINING_RECORD(ListEntry, XENVIF_DX, ListEntry);
+        PXENVIF_PDO Pdo = Dx->Pdo;
+
+        ASSERT3U(Dx->Type, ==, PHYSICAL_DEVICE_OBJECT);
+
+        if (!PdoIsMissing(Pdo))
+            PdoSetMissing(Pdo, "FDO surprise removed");
+    }
+
+    __FdoReleaseMutex(Fdo);
+
+    Irp->IoStatus.Status = STATUS_SUCCESS;
+
+    IoSkipCurrentIrpStackLocation(Irp);
+    status = IoCallDriver(Fdo->LowerDeviceObject, Irp);
+
+    return status;
+}
+
+static DECLSPEC_NOINLINE NTSTATUS
+FdoRemoveDevice(
+    IN  PXENVIF_FDO Fdo,
+    IN  PIRP        Irp
+    )
+{
+    PLIST_ENTRY     ListEntry;
+    NTSTATUS        status;
+
+    ASSERT3U(KeGetCurrentIrql(), ==, PASSIVE_LEVEL);
+
+    if (__FdoGetDevicePowerState(Fdo) != PowerDeviceD0)
+        goto done;
+
+    KeClearEvent(&Fdo->ScanEvent);
+    ThreadWake(Fdo->ScanThread);
+
+    Trace("waiting for scan thread\n");
+
+    (VOID) KeWaitForSingleObject(&Fdo->ScanEvent,
+                                 Executive,
+                                 KernelMode,
+                                 FALSE,
+                                 NULL);
+
+    __FdoAcquireMutex(Fdo);
+
+    ListEntry = Fdo->Dx->ListEntry.Flink;
+    while (ListEntry != &Fdo->Dx->ListEntry) {
+        PLIST_ENTRY Flink = ListEntry->Flink;
+        PXENVIF_DX  Dx = CONTAINING_RECORD(ListEntry, XENVIF_DX, ListEntry);
+        PXENVIF_PDO Pdo = Dx->Pdo;
+
+        ASSERT3U(Dx->Type, ==, PHYSICAL_DEVICE_OBJECT);
+
+        if (!PdoIsMissing(Pdo))
+            PdoSetMissing(Pdo, "FDO removed");
+
+        if (PdoGetDevicePnpState(Pdo) != SurpriseRemovePending)
+            PdoSetDevicePnpState(Pdo, Deleted);
+
+        if (PdoGetDevicePnpState(Pdo) == Deleted)
+            PdoDestroy(Pdo);
+
+        ListEntry = Flink;
+    }
+
+    __FdoReleaseMutex(Fdo);
+
+    FdoD0ToD3(Fdo);
+
+    __FdoSetSystemPowerState(Fdo, PowerSystemSleeping3);
+    FdoS3ToS4(Fdo);
+    __FdoSetSystemPowerState(Fdo, PowerSystemShutdown);
+
+    ThreadAlert(Fdo->ScanThread);
+    ThreadJoin(Fdo->ScanThread);
+    Fdo->ScanThread = NULL;
+
+    RtlZeroMemory(&Fdo->ScanEvent, sizeof (KEVENT));
+
+    RtlZeroMemory(&Fdo->Resource, sizeof (FDO_RESOURCE) * RESOURCE_COUNT);
+
+done:
+    __FdoSetDevicePnpState(Fdo, Deleted);
+
+    Irp->IoStatus.Status = STATUS_SUCCESS;
+
+    IoSkipCurrentIrpStackLocation(Irp);
+    status = IoCallDriver(Fdo->LowerDeviceObject, Irp);
+
+    __FdoAcquireMutex(Fdo);
+    ASSERT3U(Fdo->References, !=, 0);
+    --Fdo->References;
+    __FdoReleaseMutex(Fdo);
+
+    if (Fdo->References == 0)
+        FdoDestroy(Fdo);
+
+    return status;
+}
+
+static DECLSPEC_NOINLINE NTSTATUS
+FdoQueryDeviceRelations(
+    IN  PXENVIF_FDO     Fdo,
+    IN  PIRP            Irp
+    )
+{
+    PIO_STACK_LOCATION  StackLocation;
+    ULONG               Size;
+    PDEVICE_RELATIONS   Relations;
+    ULONG               Count;
+    PLIST_ENTRY         ListEntry;
+    NTSTATUS            status;
+
+    ASSERT3U(KeGetCurrentIrql(), ==, PASSIVE_LEVEL);
+
+    StackLocation = IoGetCurrentIrpStackLocation(Irp);
+
+    status = Irp->IoStatus.Status;
+
+    if (StackLocation->Parameters.QueryDeviceRelations.Type != BusRelations) {
+        IoSkipCurrentIrpStackLocation(Irp);
+        status = IoCallDriver(Fdo->LowerDeviceObject, Irp);
+
+        goto done;
+    }
+
+    (VOID) KeWaitForSingleObject(&Fdo->ScanEvent,
+                                 Executive,
+                                 KernelMode,
+                                 FALSE,
+                                 NULL);
+
+    __FdoAcquireMutex(Fdo);
+
+    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, 'FIV');
+
+    status = STATUS_NO_MEMORY;
+    if (Relations == NULL)
+        goto fail1;
+
+    RtlZeroMemory(Relations, Size);
+
+    for (ListEntry = Fdo->Dx->ListEntry.Flink;
+         ListEntry != &Fdo->Dx->ListEntry;
+         ListEntry = ListEntry->Flink) {
+        PXENVIF_DX  Dx = CONTAINING_RECORD(ListEntry, XENVIF_DX, ListEntry);
+        PXENVIF_PDO Pdo = Dx->Pdo;
+
+        ASSERT3U(Dx->Type, ==, PHYSICAL_DEVICE_OBJECT);
+
+        if (PdoGetDevicePnpState(Pdo) == Deleted &&
+            !PdoIsMissing(Pdo))
+            PdoSetMissing(Pdo, "surprise remove");
+
+        if (PdoIsMissing(Pdo))
+            continue;
+
+        if (PdoGetDevicePnpState(Pdo) == Present)
+            PdoSetDevicePnpState(Pdo, Enumerated);
+
+        ObReferenceObject(Dx->DeviceObject);
+        Relations->Objects[Relations->Count++] = Dx->DeviceObject;
+    }
+
+    ASSERT3U(Relations->Count, <=, Count);
+
+    Trace("%d PDO(s)\n", Relations->Count);
+
+    __FdoReleaseMutex(Fdo);
+
+    Irp->IoStatus.Information = (ULONG_PTR)Relations;
+    Irp->IoStatus.Status = STATUS_SUCCESS;
+
+    status = FdoForwardIrpSynchronously(Fdo, Irp);
+    if (!NT_SUCCESS(status))
+        goto fail2;
+
+    IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+    __FdoAcquireMutex(Fdo);
+
+    for (ListEntry = Fdo->Dx->ListEntry.Flink;
+         ListEntry != &Fdo->Dx->ListEntry;
+         ListEntry = ListEntry->Flink) {
+        PXENVIF_DX  Dx = CONTAINING_RECORD(ListEntry, XENVIF_DX, ListEntry);
+        PXENVIF_PDO Pdo = Dx->Pdo;
+
+        ASSERT3U(Dx->Type, ==, PHYSICAL_DEVICE_OBJECT);
+
+        if (PdoGetDevicePnpState(Pdo) == Deleted &&
+            PdoIsMissing(Pdo))
+            PdoDestroy(Pdo);
+    }
+
+    __FdoReleaseMutex(Fdo);
+
+done:
+    return status;
+
+fail2:
+    Error("fail2\n");
+
+    __FdoAcquireMutex(Fdo);
+
+fail1:
+    Error("fail1 (%08x)\n", status);
+
+    __FdoReleaseMutex(Fdo);
+
+    Irp->IoStatus.Status = status;
+    IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+    return status;
+}
+
+static DECLSPEC_NOINLINE NTSTATUS
+FdoQueryCapabilities(
+    IN  PXENVIF_FDO         Fdo,
+    IN  PIRP                Irp
+    )
+{
+    PIO_STACK_LOCATION      StackLocation;
+    PDEVICE_CAPABILITIES    Capabilities;
+    SYSTEM_POWER_STATE      SystemPowerState;
+    NTSTATUS                status;
+
+    status = FdoForwardIrpSynchronously(Fdo, Irp);
+    if (!NT_SUCCESS(status))
+        goto fail1;
+
+    StackLocation = IoGetCurrentIrpStackLocation(Irp);
+    Capabilities = StackLocation->Parameters.DeviceCapabilities.Capabilities;
+
+    Fdo->LowerDeviceCapabilities = *Capabilities;
+
+    for (SystemPowerState = 0; SystemPowerState < PowerSystemMaximum; SystemPowerState++) {
+        DEVICE_POWER_STATE  DevicePowerState;
+
+        DevicePowerState = Fdo->LowerDeviceCapabilities.DeviceState[SystemPowerState];
+        Trace("%s -> %s\n",
+              PowerSystemStateName(SystemPowerState),
+              PowerDeviceStateName(DevicePowerState));
+    }
+
+    status = Irp->IoStatus.Status;
+    IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+    return status;
+
+fail1:
+    Error("fail1 (%08x)\n", status);
+
+    Irp->IoStatus.Status = status;
+    IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+    return status;
+}
+
+static DECLSPEC_NOINLINE NTSTATUS
+FdoDeviceUsageNotification(
+    IN  PXENVIF_FDO                 Fdo,
+    IN  PIRP                        Irp
+    )
+{
+    PIO_STACK_LOCATION              StackLocation;
+    DEVICE_USAGE_NOTIFICATION_TYPE  Type;
+    BOOLEAN                         InPath;
+    BOOLEAN                         NotDisableable;
+    NTSTATUS                        status;
+
+    StackLocation = IoGetCurrentIrpStackLocation(Irp);
+    Type = StackLocation->Parameters.UsageNotification.Type;
+    InPath = StackLocation->Parameters.UsageNotification.InPath;
+
+    if (InPath) {
+        Trace("%s: ADDING %s\n",
+              __FdoGetName(Fdo),
+              DeviceUsageTypeName(Type));
+        Fdo->Usage[Type]++;
+    } else {
+        ASSERT(Fdo->Usage[Type] != 0);
+
+        Trace("%s: REMOVING %s\n",
+              __FdoGetName(Fdo),
+              DeviceUsageTypeName(Type));
+        --Fdo->Usage[Type];
+    }
+
+    status = FdoForwardIrpSynchronously(Fdo, Irp);
+    if (!NT_SUCCESS(status))
+        goto fail1;
+
+    NotDisableable = FALSE;    
+    for (Type = 0; Type <= DeviceUsageTypeDumpFile; Type++) {
+        if (Fdo->Usage[Type] != 0) {
+            NotDisableable = TRUE;
+            break;
+        }
+    }
+
+    IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+    if (Fdo->NotDisableable != NotDisableable) {
+        Fdo->NotDisableable = NotDisableable;
+    
+        IoInvalidateDeviceState(__FdoGetPhysicalDeviceObject(Fdo));
+    }
+
+    return status;
+
+fail1:
+    Error("fail1 (%08x)\n", status);
+
+    IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+    return status;
+}
+
+static DECLSPEC_NOINLINE NTSTATUS
+FdoQueryPnpDeviceState(
+    IN  PXENVIF_FDO                 Fdo,
+    IN  PIRP                        Irp
+    )
+{
+    ULONG_PTR                       State;
+    NTSTATUS                        status;
+
+    if (Irp->IoStatus.Status == STATUS_SUCCESS)
+        State = Irp->IoStatus.Information;
+    else if (Irp->IoStatus.Status == STATUS_NOT_SUPPORTED)
+        State = 0;
+    else
+        goto done;
+
+    if (Fdo->NotDisableable) {
+        Info("%s: not disableable\n", __FdoGetName(Fdo));
+        State |= PNP_DEVICE_NOT_DISABLEABLE;
+    }
+
+    Irp->IoStatus.Information = State;
+    Irp->IoStatus.Status = STATUS_SUCCESS;
+
+done:
+    IoSkipCurrentIrpStackLocation(Irp);
+    status = IoCallDriver(Fdo->LowerDeviceObject, Irp);
+
+    return status;
+}
+
+static DECLSPEC_NOINLINE NTSTATUS
+FdoDispatchPnp(
+    IN  PXENVIF_FDO     Fdo,
+    IN  PIRP            Irp
+    )
+{
+    PIO_STACK_LOCATION  StackLocation;
+    UCHAR               MinorFunction;
+    NTSTATUS            status;
+
+    StackLocation = IoGetCurrentIrpStackLocation(Irp);
+    MinorFunction = StackLocation->MinorFunction;
+
+    Trace("====> (%02x:%s)\n",
+          MinorFunction, 
+          PnpMinorFunctionName(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;
+
+    case IRP_MN_QUERY_CAPABILITIES:
+        status = FdoQueryCapabilities(Fdo, Irp);
+        break;
+
+    case IRP_MN_DEVICE_USAGE_NOTIFICATION:
+        status = FdoDeviceUsageNotification(Fdo, Irp);
+        break;
+
+    case IRP_MN_QUERY_PNP_DEVICE_STATE:
+        status = FdoQueryPnpDeviceState(Fdo, Irp);
+        break;
+
+    default:
+        IoSkipCurrentIrpStackLocation(Irp);
+        status = IoCallDriver(Fdo->LowerDeviceObject, Irp);
+        break;
+    }
+
+    Trace("<==== (%02x:%s)(%08x)\n",
+          MinorFunction, 
+          PnpMinorFunctionName(MinorFunction),
+          status); 
+
+    return status;
+}
+
+static FORCEINLINE NTSTATUS
+__FdoSetDevicePowerUp(
+    IN  PXENVIF_FDO     Fdo,
+    IN  PIRP            Irp
+    )
+{
+    PIO_STACK_LOCATION  StackLocation;
+    DEVICE_POWER_STATE  DeviceState;
+    NTSTATUS            status;
+
+    Trace("====>\n");
+
+    StackLocation = IoGetCurrentIrpStackLocation(Irp);
+    DeviceState = StackLocation->Parameters.Power.State.DeviceState;
+
+    ASSERT3U(DeviceState, <,  __FdoGetDevicePowerState(Fdo));
+
+    status = FdoForwardIrpSynchronously(Fdo, Irp);
+    if (!NT_SUCCESS(status))
+        goto done;
+
+    Info("%s -> %s\n",
+         PowerDeviceStateName(__FdoGetDevicePowerState(Fdo)),
+         PowerDeviceStateName(DeviceState));
+
+    ASSERT3U(DeviceState, ==, PowerDeviceD0);
+    status = FdoD3ToD0(Fdo);
+    ASSERT(NT_SUCCESS(status));
+
+done:
+    Irp->IoStatus.Status = status;
+    IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+    Trace("<==== (%08x)\n", status);
+    return status;
+}
+
+static FORCEINLINE NTSTATUS
+__FdoSetDevicePowerDown(
+    IN  PXENVIF_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));
+
+    Info("%s -> %s\n",
+         PowerDeviceStateName(__FdoGetDevicePowerState(Fdo)),
+         PowerDeviceStateName(DeviceState));
+
+    ASSERT3U(DeviceState, ==, PowerDeviceD3);
+
+    if (__FdoGetDevicePowerState(Fdo) == PowerDeviceD0)
+        FdoD0ToD3(Fdo);
+
+    IoSkipCurrentIrpStackLocation(Irp);
+    status = IoCallDriver(Fdo->LowerDeviceObject, Irp);
+
+    return status;
+}
+
+static FORCEINLINE NTSTATUS
+__FdoSetDevicePower(
+    IN  PXENVIF_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));
+
+    ASSERT3U(PowerAction, <, PowerActionShutdown);
+
+    if (DeviceState == __FdoGetDevicePowerState(Fdo)) {
+        IoSkipCurrentIrpStackLocation(Irp);
+        status = IoCallDriver(Fdo->LowerDeviceObject, Irp);
+
+        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;
+}
+
+__drv_functionClass(REQUEST_POWER_COMPLETE)
+__drv_sameIRQL
+VOID
+__FdoRequestSetDevicePower(
+    IN  PDEVICE_OBJECT      DeviceObject,
+    IN  UCHAR               MinorFunction,
+    IN  POWER_STATE         PowerState,
+    IN  PVOID               Context,
+    IN  PIO_STATUS_BLOCK    IoStatus
+    )
+{
+    PKEVENT                 Event = Context;
+
+    UNREFERENCED_PARAMETER(DeviceObject);
+    UNREFERENCED_PARAMETER(MinorFunction);
+    UNREFERENCED_PARAMETER(PowerState);
+
+    ASSERT(NT_SUCCESS(IoStatus->Status));
+
+    KeSetEvent(Event, IO_NO_INCREMENT, FALSE);
+}
+
+static VOID
+FdoRequestSetDevicePower(
+    IN  PXENVIF_FDO         Fdo,
+    IN  DEVICE_POWER_STATE  DeviceState
+    )
+{
+    POWER_STATE             PowerState;
+    KEVENT                  Event;
+    NTSTATUS                status;
+
+    Trace("%s\n", PowerDeviceStateName(DeviceState));
+
+    ASSERT3U(KeGetCurrentIrql(), ==, PASSIVE_LEVEL);
+
+    PowerState.DeviceState = DeviceState;
+    KeInitializeEvent(&Event, NotificationEvent, FALSE);
+
+    status = PoRequestPowerIrp(Fdo->LowerDeviceObject,
+                               IRP_MN_SET_POWER,
+                               PowerState,
+                               __FdoRequestSetDevicePower,
+                               &Event,
+                               NULL);
+    ASSERT(NT_SUCCESS(status));
+
+    (VOID) KeWaitForSingleObject(&Event,
+                                 Executive,
+                                 KernelMode,
+                                 FALSE,
+                                 NULL);
+}
+
+static FORCEINLINE NTSTATUS
+__FdoSetSystemPowerUp(
+    IN  PXENVIF_FDO     Fdo,
+    IN  PIRP            Irp
+    )
+{
+
+    PIO_STACK_LOCATION  StackLocation;
+    SYSTEM_POWER_STATE  SystemState;
+    DEVICE_POWER_STATE  DeviceState;
+    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;
+
+    if (SystemState < PowerSystemHibernate &&
+        __FdoGetSystemPowerState(Fdo) >= PowerSystemHibernate)
+        __FdoSetSystemPowerState(Fdo, PowerSystemHibernate);
+        FdoS4ToS3(Fdo);
+
+    Info("%s -> %s\n",
+         PowerSystemStateName(__FdoGetSystemPowerState(Fdo)),
+         PowerSystemStateName(SystemState));
+
+    __FdoSetSystemPowerState(Fdo, SystemState);
+
+    DeviceState = Fdo->LowerDeviceCapabilities.DeviceState[SystemState];
+    FdoRequestSetDevicePower(Fdo, DeviceState);
+
+done:
+    Irp->IoStatus.Status = status;
+    IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+    return status;
+}
+
+static FORCEINLINE NTSTATUS
+__FdoSetSystemPowerDown(
+    IN  PXENVIF_FDO     Fdo,
+    IN  PIRP            Irp
+    )
+{
+    PIO_STACK_LOCATION  StackLocation;
+    SYSTEM_POWER_STATE  SystemState;
+    DEVICE_POWER_STATE  DeviceState;
+    NTSTATUS            status;
+
+    StackLocation = IoGetCurrentIrpStackLocation(Irp);
+    SystemState = StackLocation->Parameters.Power.State.SystemState;
+
+    ASSERT3U(SystemState, >,  __FdoGetSystemPowerState(Fdo));
+
+    DeviceState = Fdo->LowerDeviceCapabilities.DeviceState[SystemState];
+
+    FdoRequestSetDevicePower(Fdo, DeviceState);
+
+    Info("%s -> %s\n",
+         PowerSystemStateName(__FdoGetSystemPowerState(Fdo)),
+         PowerSystemStateName(SystemState));
+
+    if (SystemState >= PowerSystemHibernate &&
+        __FdoGetSystemPowerState(Fdo) < PowerSystemHibernate) {
+        __FdoSetSystemPowerState(Fdo, PowerSystemSleeping3);
+        FdoS3ToS4(Fdo);
+    }
+
+    __FdoSetSystemPowerState(Fdo, SystemState);
+
+    IoSkipCurrentIrpStackLocation(Irp);
+    status = IoCallDriver(Fdo->LowerDeviceObject, Irp);
+
+    return status;
+}
+
+static FORCEINLINE NTSTATUS
+__FdoSetSystemPower(
+    IN  PXENVIF_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));
+
+    ASSERT3U(PowerAction, <, PowerActionShutdown);
+
+    if (SystemState == __FdoGetSystemPowerState(Fdo)) {
+        IoSkipCurrentIrpStackLocation(Irp);
+        status = IoCallDriver(Fdo->LowerDeviceObject, Irp);
+
+        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  PXENVIF_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);
+
+    Irp->IoStatus.Status = status;
+    IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+    return status;
+}
+
+static FORCEINLINE NTSTATUS
+__FdoQueryDevicePowerDown(
+    IN  PXENVIF_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));
+
+    IoSkipCurrentIrpStackLocation(Irp);
+    status = IoCallDriver(Fdo->LowerDeviceObject, Irp);
+
+    return status;
+}
+
+static FORCEINLINE NTSTATUS
+__FdoQueryDevicePower(
+    IN  PXENVIF_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));
+
+    ASSERT3U(PowerAction, <, PowerActionShutdown);
+
+    if (DeviceState == __FdoGetDevicePowerState(Fdo)) {
+        IoSkipCurrentIrpStackLocation(Irp);
+        status = IoCallDriver(Fdo->LowerDeviceObject, Irp);
+
+        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;
+}
+
+__drv_functionClass(REQUEST_POWER_COMPLETE)
+__drv_sameIRQL
+VOID
+__FdoRequestQueryDevicePower(
+    IN  PDEVICE_OBJECT      DeviceObject,
+    IN  UCHAR               MinorFunction,
+    IN  POWER_STATE         PowerState,
+    IN  PVOID               Context,
+    IN  PIO_STATUS_BLOCK    IoStatus
+    )
+{
+    PKEVENT                 Event = Context;
+
+    UNREFERENCED_PARAMETER(DeviceObject);
+    UNREFERENCED_PARAMETER(MinorFunction);
+    UNREFERENCED_PARAMETER(PowerState);
+
+    ASSERT(NT_SUCCESS(IoStatus->Status));
+
+    KeSetEvent(Event, IO_NO_INCREMENT, FALSE);
+}
+
+static VOID
+FdoRequestQueryDevicePower(
+    IN  PXENVIF_FDO         Fdo,
+    IN  DEVICE_POWER_STATE  DeviceState
+    )
+{
+    POWER_STATE             PowerState;
+    KEVENT                  Event;
+    NTSTATUS                status;
+
+    Trace("%s\n", PowerDeviceStateName(DeviceState));
+
+    ASSERT3U(KeGetCurrentIrql(), ==, PASSIVE_LEVEL);
+
+    PowerState.DeviceState = DeviceState;
+    KeInitializeEvent(&Event, NotificationEvent, FALSE);
+
+    status = PoRequestPowerIrp(Fdo->LowerDeviceObject,
+                               IRP_MN_QUERY_POWER,
+                               PowerState,
+                               __FdoRequestQueryDevicePower,
+                               &Event,
+                               NULL);
+    ASSERT(NT_SUCCESS(status));
+
+    (VOID) KeWaitForSingleObject(&Event,
+                                 Executive,
+                                 KernelMode,
+                                 FALSE,
+                                 NULL);
+}
+
+static FORCEINLINE NTSTATUS
+__FdoQuerySystemPowerUp(
+    IN  PXENVIF_FDO     Fdo,
+    IN  PIRP            Irp
+    )
+{
+
+    PIO_STACK_LOCATION  StackLocation;
+    SYSTEM_POWER_STATE  SystemState;
+    DEVICE_POWER_STATE  DeviceState;
+    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;
+
+    DeviceState = Fdo->LowerDeviceCapabilities.DeviceState[SystemState];
+
+    FdoRequestQueryDevicePower(Fdo, DeviceState);
+
+done:
+    Irp->IoStatus.Status = status;
+    IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+    return status;
+}
+
+static FORCEINLINE NTSTATUS
+__FdoQuerySystemPowerDown(
+    IN  PXENVIF_FDO     Fdo,
+    IN  PIRP            Irp
+    )
+{
+    PIO_STACK_LOCATION  StackLocation;
+    SYSTEM_POWER_STATE  SystemState;
+    DEVICE_POWER_STATE  DeviceState;
+    NTSTATUS            status;
+
+    StackLocation = IoGetCurrentIrpStackLocation(Irp);
+    SystemState = StackLocation->Parameters.Power.State.SystemState;
+
+    ASSERT3U(SystemState, >,  __FdoGetSystemPowerState(Fdo));
+
+    DeviceState = Fdo->LowerDeviceCapabilities.DeviceState[SystemState];
+
+    FdoRequestQueryDevicePower(Fdo, DeviceState);
+
+    IoSkipCurrentIrpStackLocation(Irp);
+    status = IoCallDriver(Fdo->LowerDeviceObject, Irp);
+
+    return status;
+}
+
+static FORCEINLINE NTSTATUS
+__FdoQuerySystemPower(
+    IN  PXENVIF_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));
+
+    ASSERT3U(PowerAction, <, PowerActionShutdown);
+
+    if (SystemState == __FdoGetSystemPowerState(Fdo)) {
+        IoSkipCurrentIrpStackLocation(Irp);
+        status = IoCallDriver(Fdo->LowerDeviceObject, Irp);
+
+        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  PXENVIF_THREAD  Self,
+    IN  PVOID           Context
+    )
+{
+    PXENVIF_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;
+        }
+    }
+
+    return STATUS_SUCCESS;
+}
+
+static NTSTATUS
+FdoSystemPower(
+    IN  PXENVIF_THREAD  Self,
+    IN  PVOID           Context
+    )
+{
+    PXENVIF_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;
+        }
+    }
+
+    return STATUS_SUCCESS;
+}
+
+static DECLSPEC_NOINLINE NTSTATUS
+FdoDispatchPower(
+    IN  PXENVIF_FDO     Fdo,
+    IN  PIRP            Irp
+    )
+{
+    PIO_STACK_LOCATION  StackLocation;
+    UCHAR               MinorFunction;
+    POWER_STATE_TYPE    PowerType;
+    POWER_ACTION        PowerAction;
+    NTSTATUS            status;
+
+    StackLocation = IoGetCurrentIrpStackLocation(Irp);
+    MinorFunction = StackLocation->MinorFunction;
+
+    if (MinorFunction != IRP_MN_QUERY_POWER &&
+        MinorFunction != IRP_MN_SET_POWER) {
+        IoSkipCurrentIrpStackLocation(Irp);
+        status = IoCallDriver(Fdo->LowerDeviceObject, Irp);
+
+        goto done;
+    }
+
+    PowerType = StackLocation->Parameters.Power.Type;
+    PowerAction = StackLocation->Parameters.Power.ShutdownType;
+
+    if (PowerAction >= PowerActionShutdown) {
+        IoSkipCurrentIrpStackLocation(Irp);
+        status = IoCallDriver(Fdo->LowerDeviceObject, Irp);
+
+        goto done;
+    }
+
+    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:
+        IoSkipCurrentIrpStackLocation(Irp);
+        status = IoCallDriver(Fdo->LowerDeviceObject, Irp);
+        break;
+    }
+
+done:
+    return status;
+}
+
+static DECLSPEC_NOINLINE NTSTATUS
+FdoDispatchDefault(
+    IN  PXENVIF_FDO     Fdo,
+    IN  PIRP            Irp
+    )
+{
+    NTSTATUS            status;
+
+    IoSkipCurrentIrpStackLocation(Irp);
+    status = IoCallDriver(Fdo->LowerDeviceObject, Irp);
+
+    return status;
+}
+
+NTSTATUS
+FdoDispatch(
+    IN  PXENVIF_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;
+}
+
+static NTSTATUS
+FdoQueryEvtchnInterface(
+    IN  PXENVIF_FDO             Fdo
+    )
+{
+    KEVENT                      Event;
+    IO_STATUS_BLOCK             StatusBlock;
+    PIRP                        Irp;
+    PIO_STACK_LOCATION          StackLocation;
+    INTERFACE                   Interface;
+    NTSTATUS                    status;
+
+    ASSERT3U(KeGetCurrentIrql(), ==, PASSIVE_LEVEL);
+
+    KeInitializeEvent(&Event, NotificationEvent, FALSE);
+    RtlZeroMemory(&StatusBlock, sizeof(IO_STATUS_BLOCK));
+
+    Irp = IoBuildSynchronousFsdRequest(IRP_MJ_PNP,
+                                       Fdo->LowerDeviceObject,
+                                       NULL,
+                                       0,
+                                       NULL,
+                                       &Event,
+                                       &StatusBlock);
+
+    status = STATUS_UNSUCCESSFUL;
+    if (Irp == NULL)
+        goto fail1;
+
+    StackLocation = IoGetNextIrpStackLocation(Irp);
+    StackLocation->MinorFunction = IRP_MN_QUERY_INTERFACE;
+
+    StackLocation->Parameters.QueryInterface.InterfaceType = &GUID_EVTCHN_INTERFACE;
+    StackLocation->Parameters.QueryInterface.Size = sizeof (INTERFACE);
+    StackLocation->Parameters.QueryInterface.Version = EVTCHN_INTERFACE_VERSION;
+    StackLocation->Parameters.QueryInterface.Interface = &Interface;
+    
+    Irp->IoStatus.Status = STATUS_NOT_SUPPORTED;
+
+    status = IoCallDriver(Fdo->LowerDeviceObject, Irp);
+    if (status == STATUS_PENDING) {
+        (VOID) KeWaitForSingleObject(&Event,
+                                     Executive,
+                                     KernelMode,
+                                     FALSE,
+                                     NULL);
+        status = StatusBlock.Status;
+    }
+
+    if (!NT_SUCCESS(status))
+        goto fail2;
+
+    status = STATUS_INVALID_PARAMETER;
+    if (Interface.Version != EVTCHN_INTERFACE_VERSION)
+        goto fail3;
+
+    Fdo->EvtchnInterface = Interface.Context;
+
+    return STATUS_SUCCESS;
+
+fail3:
+    Error("fail3\n");
+
+fail2:
+    Error("fail2\n");
+
+fail1:
+    Error("fail1 (%08x)\n", status);
+
+    return status;
+}
+
+static NTSTATUS
+FdoQueryDebugInterface(
+    IN  PXENVIF_FDO     Fdo
+    )
+{
+    KEVENT              Event;
+    IO_STATUS_BLOCK     StatusBlock;
+    PIRP                Irp;
+    PIO_STACK_LOCATION  StackLocation;
+    INTERFACE           Interface;
+    NTSTATUS            status;
+
+    ASSERT3U(KeGetCurrentIrql(), ==, PASSIVE_LEVEL);
+
+    KeInitializeEvent(&Event, NotificationEvent, FALSE);
+    RtlZeroMemory(&StatusBlock, sizeof(IO_STATUS_BLOCK));
+
+    Irp = IoBuildSynchronousFsdRequest(IRP_MJ_PNP,
+                                       Fdo->LowerDeviceObject,
+                                       NULL,
+                                       0,
+                                       NULL,
+                                       &Event,
+                                       &StatusBlock);
+
+    status = STATUS_UNSUCCESSFUL;
+    if (Irp == NULL)
+        goto fail1;
+
+    StackLocation = IoGetNextIrpStackLocation(Irp);
+    StackLocation->MinorFunction = IRP_MN_QUERY_INTERFACE;
+
+    StackLocation->Parameters.QueryInterface.InterfaceType = &GUID_DEBUG_INTERFACE;
+    StackLocation->Parameters.QueryInterface.Size = sizeof (INTERFACE);
+    StackLocation->Parameters.QueryInterface.Version = DEBUG_INTERFACE_VERSION;
+    StackLocation->Parameters.QueryInterface.Interface = &Interface;
+    
+    Irp->IoStatus.Status = STATUS_NOT_SUPPORTED;
+
+    status = IoCallDriver(Fdo->LowerDeviceObject, Irp);
+    if (status == STATUS_PENDING) {
+        (VOID) KeWaitForSingleObject(&Event,
+                                     Executive,
+                                     KernelMode,
+                                     FALSE,
+                                     NULL);
+        status = StatusBlock.Status;
+    }
+
+    if (!NT_SUCCESS(status))
+        goto fail2;
+
+    status = STATUS_INVALID_PARAMETER;
+    if (Interface.Version != DEBUG_INTERFACE_VERSION)
+        goto fail3;
+
+    Fdo->DebugInterface = Interface.Context;
+
+    return STATUS_SUCCESS;
+
+fail3:
+    Error("fail3\n");
+
+fail2:
+    Error("fail2\n");
+
+fail1:
+    Error("fail1 (%08x)\n", status);
+
+    return status;
+}
+
+static NTSTATUS
+FdoQueryStoreInterface(
+    IN  PXENVIF_FDO     Fdo
+    )
+{
+    KEVENT              Event;
+    IO_STATUS_BLOCK     StatusBlock;
+    PIRP                Irp;
+    PIO_STACK_LOCATION  StackLocation;
+    INTERFACE           Interface;
+    NTSTATUS            status;
+
+    ASSERT3U(KeGetCurrentIrql(), ==, PASSIVE_LEVEL);
+
+    KeInitializeEvent(&Event, NotificationEvent, FALSE);
+    RtlZeroMemory(&StatusBlock, sizeof(IO_STATUS_BLOCK));
+
+    Irp = IoBuildSynchronousFsdRequest(IRP_MJ_PNP,
+                                       Fdo->LowerDeviceObject,
+                                       NULL,
+                                       0,
+                                       NULL,
+                                       &Event,
+                                       &StatusBlock);
+
+    status = STATUS_UNSUCCESSFUL;
+    if (Irp == NULL)
+        goto fail1;
+
+    StackLocation = IoGetNextIrpStackLocation(Irp);
+    StackLocation->MinorFunction = IRP_MN_QUERY_INTERFACE;
+
+    StackLocation->Parameters.QueryInterface.InterfaceType = &GUID_STORE_INTERFACE;
+    StackLocation->Parameters.QueryInterface.Size = sizeof (INTERFACE);
+    StackLocation->Parameters.QueryInterface.Version = STORE_INTERFACE_VERSION;
+    StackLocation->Parameters.QueryInterface.Interface = &Interface;
+    
+    Irp->IoStatus.Status = STATUS_NOT_SUPPORTED;
+
+    status = IoCallDriver(Fdo->LowerDeviceObject, Irp);
+    if (status == STATUS_PENDING) {
+        (VOID) KeWaitForSingleObject(&Event,
+                                     Executive,
+                                     KernelMode,
+                                     FALSE,
+                                     NULL);
+        status = StatusBlock.Status;
+    }
+
+    if (!NT_SUCCESS(status))
+        goto fail2;
+
+    status = STATUS_INVALID_PARAMETER;
+    if (Interface.Version != STORE_INTERFACE_VERSION)
+        goto fail3;
+
+    Fdo->StoreInterface = Interface.Context;
+
+    return STATUS_SUCCESS;
+
+fail3:
+    Error("fail3\n");
+
+fail2:
+    Error("fail2\n");
+
+fail1:
+    Error("fail1 (%08x)\n", status);
+
+    return status;
+}
+
+static NTSTATUS
+FdoQueryGnttabInterface(
+    IN  PXENVIF_FDO     Fdo
+    )
+{
+    KEVENT              Event;
+    IO_STATUS_BLOCK     StatusBlock;
+    PIRP                Irp;
+    PIO_STACK_LOCATION  StackLocation;
+    INTERFACE           Interface;
+    NTSTATUS            status;
+
+    ASSERT3U(KeGetCurrentIrql(), ==, PASSIVE_LEVEL);
+
+    KeInitializeEvent(&Event, NotificationEvent, FALSE);
+    RtlZeroMemory(&StatusBlock, sizeof(IO_STATUS_BLOCK));
+
+    Irp = IoBuildSynchronousFsdRequest(IRP_MJ_PNP,
+                                       Fdo->LowerDeviceObject,
+                                       NULL,
+                                       0,
+                                       NULL,
+                                       &Event,
+                                       &StatusBlock);
+
+    status = STATUS_UNSUCCESSFUL;
+    if (Irp == NULL)
+        goto fail1;
+
+    StackLocation = IoGetNextIrpStackLocation(Irp);
+    StackLocation->MinorFunction = IRP_MN_QUERY_INTERFACE;
+
+    StackLocation->Parameters.QueryInterface.InterfaceType = &GUID_GNTTAB_INTERFACE;
+    StackLocation->Parameters.QueryInterface.Size = sizeof (INTERFACE);
+    StackLocation->Parameters.QueryInterface.Version = GNTTAB_INTERFACE_VERSION;
+    StackLocation->Parameters.QueryInterface.Interface = &Interface;
+    
+    Irp->IoStatus.Status = STATUS_NOT_SUPPORTED;
+
+    status = IoCallDriver(Fdo->LowerDeviceObject, Irp);
+    if (status == STATUS_PENDING) {
+        (VOID) KeWaitForSingleObject(&Event,
+                                     Executive,
+                                     KernelMode,
+                                     FALSE,
+                                     NULL);
+        status = StatusBlock.Status;
+    }
+
+    if (!NT_SUCCESS(status))
+        goto fail2;
+
+    status = STATUS_INVALID_PARAMETER;
+    if (Interface.Version != GNTTAB_INTERFACE_VERSION)
+        goto fail3;
+
+    Fdo->GnttabInterface = Interface.Context;
+
+    return STATUS_SUCCESS;
+
+fail3:
+    Error("fail3\n");
+
+fail2:
+    Error("fail2\n");
+    
+fail1:
+    Error("fail1 (%08x)\n", status);
+
+    return status;
+}
+
+static NTSTATUS
+FdoQuerySuspendInterface(
+    IN  PXENVIF_FDO     Fdo
+    )
+{
+    KEVENT              Event;
+    IO_STATUS_BLOCK     StatusBlock;
+    PIRP                Irp;
+    PIO_STACK_LOCATION  StackLocation;
+    INTERFACE           Interface;
+    NTSTATUS            status;
+
+    ASSERT3U(KeGetCurrentIrql(), ==, PASSIVE_LEVEL);
+
+    KeInitializeEvent(&Event, NotificationEvent, FALSE);
+    RtlZeroMemory(&StatusBlock, sizeof(IO_STATUS_BLOCK));
+
+    Irp = IoBuildSynchronousFsdRequest(IRP_MJ_PNP,
+                                       Fdo->LowerDeviceObject,
+                                       NULL,
+                                       0,
+                                       NULL,
+                                       &Event,
+                                       &StatusBlock);
+
+    status = STATUS_UNSUCCESSFUL;
+    if (Irp == NULL)
+        goto fail1;
+
+    StackLocation = IoGetNextIrpStackLocation(Irp);
+    StackLocation->MinorFunction = IRP_MN_QUERY_INTERFACE;
+
+    StackLocation->Parameters.QueryInterface.InterfaceType = &GUID_SUSPEND_INTERFACE;
+    StackLocation->Parameters.QueryInterface.Size = sizeof (INTERFACE);
+    StackLocation->Parameters.QueryInterface.Version = SUSPEND_INTERFACE_VERSION;
+    StackLocation->Parameters.QueryInterface.Interface = &Interface;
+    
+    Irp->IoStatus.Status = STATUS_NOT_SUPPORTED;
+
+    status = IoCallDriver(Fdo->LowerDeviceObject, Irp);
+    if (status == STATUS_PENDING) {
+        (VOID) KeWaitForSingleObject(&Event,
+                                     Executive,
+                                     KernelMode,
+                                     FALSE,
+                                     NULL);
+        status = StatusBlock.Status;
+    }
+
+    if (!NT_SUCCESS(status))
+        goto fail2;
+
+    status = STATUS_INVALID_PARAMETER;
+    if (Interface.Version != SUSPEND_INTERFACE_VERSION)
+        goto fail3;
+
+    Fdo->SuspendInterface = Interface.Context;
+
+    return STATUS_SUCCESS;
+
+fail3:
+    Error("fail3\n");
+
+fail2:
+    Error("fail2\n");
+
+fail1:
+    Error("fail1 (%08x)\n", status);
+
+    return status;
+}
+
+static NTSTATUS
+FdoQueryEmulatedInterface(
+    IN  PXENVIF_FDO     Fdo
+    )
+{
+    KEVENT              Event;
+    IO_STATUS_BLOCK     StatusBlock;
+    PIRP                Irp;
+    PIO_STACK_LOCATION  StackLocation;
+    INTERFACE           Interface;
+    NTSTATUS            status;
+
+    ASSERT3U(KeGetCurrentIrql(), ==, PASSIVE_LEVEL);
+
+    KeInitializeEvent(&Event, NotificationEvent, FALSE);
+    RtlZeroMemory(&StatusBlock, sizeof(IO_STATUS_BLOCK));
+
+    Irp = IoBuildSynchronousFsdRequest(IRP_MJ_PNP,
+                                       Fdo->LowerDeviceObject,
+                                       NULL,
+                                       0,
+                                       NULL,
+                                       &Event,
+                                       &StatusBlock);
+
+    status = STATUS_UNSUCCESSFUL;
+    if (Irp == NULL)
+        goto fail1;
+
+    StackLocation = IoGetNextIrpStackLocation(Irp);
+    StackLocation->MinorFunction = IRP_MN_QUERY_INTERFACE;
+
+    StackLocation->Parameters.QueryInterface.InterfaceType = &GUID_EMULATED_INTERFACE;
+    StackLocation->Parameters.QueryInterface.Size = sizeof (INTERFACE);
+    StackLocation->Parameters.QueryInterface.Version = EMULATED_INTERFACE_VERSION;
+    StackLocation->Parameters.QueryInterface.Interface = &Interface;
+    
+    Irp->IoStatus.Status = STATUS_NOT_SUPPORTED;
+
+    status = IoCallDriver(Fdo->LowerDeviceObject, Irp);
+    if (status == STATUS_PENDING) {
+        (VOID) KeWaitForSingleObject(&Event,
+                                     Executive,
+                                     KernelMode,
+                                     FALSE,
+                                     NULL);
+        status = StatusBlock.Status;
+    }
+
+    if (!NT_SUCCESS(status))
+        goto fail2;
+
+    status = STATUS_INVALID_PARAMETER;
+    if (Interface.Version != EMULATED_INTERFACE_VERSION)
+        goto fail3;
+
+    Fdo->EmulatedInterface = Interface.Context;
+
+    return STATUS_SUCCESS;
+
+fail3:
+    Error("fail3\n");
+
+fail2:
+    Error("fail2\n");
+
+fail1:
+    Error("fail1 (%08x)\n", status);
+
+    return status;
+}
+
+NTSTATUS
+FdoCreate(
+    IN  PDEVICE_OBJECT  PhysicalDeviceObject
+    )
+{
+    PDEVICE_OBJECT      FunctionDeviceObject;
+    PXENVIF_DX          Dx;
+    PXENVIF_FDO         Fdo;
+    WCHAR               Name[MAXNAMELEN * sizeof (WCHAR)];
+    ULONG               Size;
+    NTSTATUS            status;
+
+#pragma prefast(suppress:28197) // Possibly leaking memory 'FunctionDeviceObject'
+    status = IoCreateDevice(DriverObject,
+                            sizeof (XENVIF_DX),
+                            NULL,
+                            FILE_DEVICE_BUS_EXTENDER,
+                            FILE_DEVICE_SECURE_OPEN,
+                            FALSE,
+                            &FunctionDeviceObject);
+    if (!NT_SUCCESS(status))
+        goto fail1;
+
+    Dx = (PXENVIF_DX)FunctionDeviceObject->DeviceExtension;
+    RtlZeroMemory(Dx, sizeof (XENVIF_DX));
+
+    Dx->Type = FUNCTION_DEVICE_OBJECT;
+    Dx->DeviceObject = FunctionDeviceObject;
+    Dx->DevicePnpState = Added;
+    Dx->SystemPowerState = PowerSystemShutdown;
+    Dx->DevicePowerState = PowerDeviceD3;
+
+    Fdo = __FdoAllocate(sizeof (XENVIF_FDO));
+
+    status = STATUS_NO_MEMORY;
+    if (Fdo == NULL)
+        goto fail2;
+
+    Fdo->Dx = Dx;
+    Fdo->PhysicalDeviceObject = PhysicalDeviceObject;
+    Fdo->LowerDeviceObject = IoAttachDeviceToDeviceStack(FunctionDeviceObject,
+                                                         PhysicalDeviceObject);
+
+    status = ThreadCreate(FdoSystemPower, Fdo, &Fdo->SystemPowerThread);
+    if (!NT_SUCCESS(status))
+        goto fail3;
+
+    status = ThreadCreate(FdoDevicePower, Fdo, &Fdo->DevicePowerThread);
+    if (!NT_SUCCESS(status))
+        goto fail4;
+
+    status = IoGetDeviceProperty(PhysicalDeviceObject,
+                                 DevicePropertyLocationInformation,
+                                 sizeof (Name),
+                                 Name,
+                                 &Size);
+    if (!NT_SUCCESS(status))
+        goto fail5;
+
+    status = __FdoSetName(Fdo, Name);
+    if (!NT_SUCCESS(status))
+        goto fail6;
+
+    status = FdoQueryEvtchnInterface(Fdo);
+    if (!NT_SUCCESS(status))
+        goto fail7;
+
+    status = FdoQueryDebugInterface(Fdo);
+    if (!NT_SUCCESS(status))
+        goto fail8;
+
+    status = FdoQueryStoreInterface(Fdo);
+    if (!NT_SUCCESS(status))
+        goto fail9;
+
+    status = FdoQueryGnttabInterface(Fdo);
+    if (!NT_SUCCESS(status))
+        goto fail10;
+
+    status = FdoQuerySuspendInterface(Fdo);
+    if (!NT_SUCCESS(status))
+        goto fail11;
+
+    status = FdoQueryEmulatedInterface(Fdo);
+    ASSERT(IMPLY(!NT_SUCCESS(status),
+                 __FdoGetEmulatedInterface(Fdo) == NULL));
+
+    InitializeMutex(&Fdo->Mutex);
+    InitializeListHead(&Dx->ListEntry);
+    Fdo->References = 1;
+
+    Info("%p (%s)\n",
+         FunctionDeviceObject,
+         __FdoGetName(Fdo));
+
+    Dx->Fdo = Fdo;
+    FunctionDeviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
+
+    return STATUS_SUCCESS;
+
+fail11:
+    Error("fail11\n");
+
+    Fdo->GnttabInterface = NULL;
+
+fail10:
+    Error("fail10\n");
+
+    Fdo->StoreInterface = NULL;
+
+fail9:
+    Error("fail9\n");
+
+    Fdo->DebugInterface = NULL;
+
+fail8:
+    Error("fail8\n");
+
+    Fdo->EvtchnInterface = NULL;
+
+fail7:
+    Error("fail7\n");
+
+fail6:
+    Error("fail6\n");
+
+fail5:
+    Error("fail5\n");
+
+    ThreadAlert(Fdo->DevicePowerThread);
+    ThreadJoin(Fdo->DevicePowerThread);
+    Fdo->DevicePowerThread = NULL;
+    
+fail4:
+    Error("fail4\n");
+
+    ThreadAlert(Fdo->SystemPowerThread);
+    ThreadJoin(Fdo->SystemPowerThread);
+    Fdo->SystemPowerThread = NULL;
+    
+fail3:
+    Error("fail3\n");
+
+#pragma prefast(suppress:28183) // Fdo->LowerDeviceObject could be NULL
+    IoDetachDevice(Fdo->LowerDeviceObject);
+
+    Fdo->PhysicalDeviceObject = NULL;
+    Fdo->LowerDeviceObject = NULL;
+    Fdo->Dx = NULL;
+
+    ASSERT(IsZeroMemory(Fdo, sizeof (XENVIF_FDO)));
+    __FdoFree(Fdo);
+
+fail2:
+    Error("fail2\n");
+
+    IoDeleteDevice(FunctionDeviceObject);
+
+fail1:
+    Error("fail1 (%08x)\n", status);
+
+    return status;
+}
+
+VOID
+FdoDestroy(
+    IN  PXENVIF_FDO     Fdo
+    )
+{
+    PXENVIF_DX          Dx = Fdo->Dx;
+    PDEVICE_OBJECT      FunctionDeviceObject = Dx->DeviceObject;
+
+    ASSERT(IsListEmpty(&Dx->ListEntry));
+    ASSERT3U(Fdo->References, ==, 0);
+    ASSERT3U(__FdoGetDevicePnpState(Fdo), ==, Deleted);
+
+    Fdo->NotDisableable = FALSE;
+
+    Info("%p (%s)\n",
+         FunctionDeviceObject,
+         __FdoGetName(Fdo));
+
+    Dx->Fdo = NULL;
+
+    RtlZeroMemory(&Fdo->Mutex, sizeof (XENVIF_MUTEX));
+
+    Fdo->EmulatedInterface = NULL;
+    Fdo->SuspendInterface = NULL;
+    Fdo->GnttabInterface = NULL;
+    Fdo->StoreInterface = NULL;
+    Fdo->DebugInterface = NULL;
+    Fdo->EvtchnInterface = NULL;
+
+    ThreadAlert(Fdo->DevicePowerThread);
+    ThreadJoin(Fdo->DevicePowerThread);
+    Fdo->DevicePowerThread = NULL;
+
+    ThreadAlert(Fdo->SystemPowerThread);
+    ThreadJoin(Fdo->SystemPowerThread);
+    Fdo->SystemPowerThread = NULL;
+
+    IoDetachDevice(Fdo->LowerDeviceObject);
+
+    RtlZeroMemory(&Fdo->LowerDeviceCapabilities, sizeof (DEVICE_CAPABILITIES));
+    Fdo->LowerDeviceObject = NULL;
+    Fdo->PhysicalDeviceObject = NULL;
+    Fdo->Dx = NULL;
+
+    ASSERT(IsZeroMemory(Fdo, sizeof (XENVIF_FDO)));
+    __FdoFree(Fdo);
+
+    IoDeleteDevice(FunctionDeviceObject);
+}
+
+
diff --git a/src/xenvif/fdo.h b/src/xenvif/fdo.h
new file mode 100644 (file)
index 0000000..5b96f40
--- /dev/null
@@ -0,0 +1,135 @@
+/* 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 _XENVIF_FDO_H
+#define _XENVIF_FDO_H
+
+#include <ntddk.h>
+#include <evtchn_interface.h>
+#include <debug_interface.h>
+#include <store_interface.h>
+#include <gnttab_interface.h>
+#include <suspend_interface.h>
+#include <emulated_interface.h>
+
+#include "driver.h"
+#include "types.h"
+
+extern PCHAR
+FdoGetName(
+    IN  PXENVIF_FDO Fdo
+    );
+
+extern NTSTATUS
+FdoCreate(
+    IN  PDEVICE_OBJECT  PhysicalDeviceObject
+    );
+
+extern VOID
+FdoDestroy(
+    IN  PXENVIF_FDO    Fdo
+    );
+
+extern VOID
+FdoAddPhysicalDeviceObject(
+    IN  PXENVIF_FDO     Fdo,
+    IN  PXENVIF_PDO     Pdo
+    );
+
+extern VOID
+FdoRemovePhysicalDeviceObject(
+    IN  PXENVIF_FDO     Fdo,
+    IN  PXENVIF_PDO     Pdo
+    );
+
+extern VOID
+FdoAcquireMutex(
+    IN  PXENVIF_FDO     Fdo
+    );
+
+extern VOID
+FdoReleaseMutex(
+    IN  PXENVIF_FDO     Fdo
+    );
+
+extern PDEVICE_OBJECT
+FdoGetPhysicalDeviceObject(
+    IN  PXENVIF_FDO Fdo
+    );
+
+extern VOID
+FdoReap(
+    IN  PXENVIF_FDO Fdo
+    );
+
+extern NTSTATUS
+FdoDelegateIrp(
+    IN  PXENVIF_FDO    Fdo,
+    IN  PIRP            Irp
+    );
+
+extern PXENBUS_EVTCHN_INTERFACE
+FdoGetEvtchnInterface(
+    IN  PXENVIF_FDO     Fdo
+    );
+
+extern PXENBUS_DEBUG_INTERFACE
+FdoGetDebugInterface(
+    IN  PXENVIF_FDO     Fdo
+    );
+
+extern PXENBUS_STORE_INTERFACE
+FdoGetStoreInterface(
+    IN  PXENVIF_FDO     Fdo
+    );
+
+extern PXENBUS_GNTTAB_INTERFACE
+FdoGetGnttabInterface(
+    IN  PXENVIF_FDO     Fdo
+    );
+
+extern PXENBUS_SUSPEND_INTERFACE
+FdoGetSuspendInterface(
+    IN  PXENVIF_FDO     Fdo
+    );
+
+extern PXENFILT_EMULATED_INTERFACE
+FdoGetEmulatedInterface(
+    IN  PXENVIF_FDO Fdo
+    );
+
+extern NTSTATUS
+FdoDispatch(
+    IN  PXENVIF_FDO    Fdo,
+    IN  PIRP            Irp
+    );
+
+#endif  // _XENVIF_FDO_H
diff --git a/src/xenvif/frontend.c b/src/xenvif/frontend.c
new file mode 100644 (file)
index 0000000..2bc9c1b
--- /dev/null
@@ -0,0 +1,1832 @@
+/* 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 <ntstrsafe.h>
+#include <stdlib.h>
+#include <netioapi.h>
+#include <util.h>
+#include <xen.h>
+#include <store_interface.h>
+#include <gnttab_interface.h>
+#include <vif_interface.h>
+
+#include "driver.h"
+#include "registry.h"
+#include "fdo.h"
+#include "pdo.h"
+#include "thread.h"
+#include "frontend.h"
+#include "names.h"
+#include "notifier.h"
+#include "mac.h"
+#include "tcpip.h"
+#include "receiver.h"
+#include "transmitter.h"
+#include "log.h"
+#include "assert.h"
+
+struct _XENVIF_FRONTEND {
+    PXENVIF_PDO                 Pdo;
+    PCHAR                       Path;
+    PCHAR                       Prefix;
+    XENVIF_FRONTEND_STATE       State;
+    KSPIN_LOCK                  Lock;
+    PXENVIF_THREAD              MibThread;
+    PXENVIF_THREAD              EjectThread;
+    BOOLEAN                     EjectRequested;
+    PXENBUS_STORE_WATCH         Watch;
+    PCHAR                       BackendPath;
+    USHORT                      BackendDomain;
+    PXENVIF_NOTIFIER            Notifier;
+    PXENVIF_MAC                 Mac;
+    PXENVIF_RECEIVER            Receiver;
+    PXENVIF_TRANSMITTER         Transmitter;
+
+    PXENBUS_SUSPEND_INTERFACE   SuspendInterface;
+    PXENBUS_STORE_INTERFACE     StoreInterface;
+    PXENBUS_DEBUG_INTERFACE     DebugInterface;
+
+    PXENBUS_SUSPEND_CALLBACK    SuspendCallbackLate;
+    PXENBUS_DEBUG_CALLBACK      DebugCallback;
+    HANDLE                      Handle;
+};
+
+static const PCHAR
+FrontendStateName(
+    IN  XENVIF_FRONTEND_STATE   State
+    )
+{
+#define _STATE_NAME(_State)     \
+    case  FRONTEND_ ## _State:  \
+        return #_State;
+
+    switch (State) {
+    _STATE_NAME(CLOSED);
+    _STATE_NAME(PREPARED);
+    _STATE_NAME(CONNECTED);
+    _STATE_NAME(ENABLED);
+    default:
+        break;
+    }
+
+    return "INVALID";
+
+#undef  _STATE_NAME
+}
+
+#define FRONTEND_POOL    'NORF'
+
+static FORCEINLINE PVOID
+__FrontendAllocate(
+    IN  ULONG   Length
+    )
+{
+    return __AllocateNonPagedPoolWithTag(Length, FRONTEND_POOL);
+}
+
+static FORCEINLINE VOID
+__FrontendFree(
+    IN  PVOID   Buffer
+    )
+{
+    __FreePoolWithTag(Buffer, FRONTEND_POOL);
+}
+
+static FORCEINLINE PXENVIF_PDO
+__FrontendGetPdo(
+    IN  PXENVIF_FRONTEND    Frontend
+    )
+{
+    return Frontend->Pdo;
+}
+
+static FORCEINLINE PXENBUS_EVTCHN_INTERFACE
+__FrontendGetEvtchnInterface(
+    IN  PXENVIF_FRONTEND    Frontend
+    )
+{
+    return PdoGetEvtchnInterface(__FrontendGetPdo(Frontend));
+}
+
+PXENBUS_EVTCHN_INTERFACE
+FrontendGetEvtchnInterface(
+    IN  PXENVIF_FRONTEND    Frontend
+    )
+{
+    return __FrontendGetEvtchnInterface(Frontend);
+}
+
+static FORCEINLINE PXENBUS_DEBUG_INTERFACE
+__FrontendGetDebugInterface(
+    IN  PXENVIF_FRONTEND    Frontend
+    )
+{
+    return PdoGetDebugInterface(__FrontendGetPdo(Frontend));
+}
+
+PXENBUS_DEBUG_INTERFACE
+FrontendGetDebugInterface(
+    IN  PXENVIF_FRONTEND    Frontend
+    )
+{
+    return __FrontendGetDebugInterface(Frontend);
+}
+
+static FORCEINLINE PXENBUS_SUSPEND_INTERFACE
+__FrontendGetSuspendInterface(
+    IN  PXENVIF_FRONTEND    Frontend
+    )
+{
+    return PdoGetSuspendInterface(__FrontendGetPdo(Frontend));
+}
+
+PXENBUS_SUSPEND_INTERFACE
+FrontendGetSuspendInterface(
+    IN  PXENVIF_FRONTEND    Frontend
+    )
+{
+    return __FrontendGetSuspendInterface(Frontend);
+}
+
+static FORCEINLINE PXENBUS_STORE_INTERFACE
+__FrontendGetStoreInterface(
+    IN  PXENVIF_FRONTEND    Frontend
+    )
+{
+    return PdoGetStoreInterface(__FrontendGetPdo(Frontend));
+}
+
+PXENBUS_STORE_INTERFACE
+FrontendGetStoreInterface(
+    IN  PXENVIF_FRONTEND    Frontend
+    )
+{
+    return __FrontendGetStoreInterface(Frontend);
+}
+
+static FORCEINLINE PXENBUS_GNTTAB_INTERFACE
+__FrontendGetGnttabInterface(
+    IN  PXENVIF_FRONTEND    Frontend
+    )
+{
+    return PdoGetGnttabInterface(__FrontendGetPdo(Frontend));
+}
+
+PXENBUS_GNTTAB_INTERFACE
+FrontendGetGnttabInterface(
+    IN  PXENVIF_FRONTEND    Frontend
+    )
+{
+    return __FrontendGetGnttabInterface(Frontend);
+}
+
+static FORCEINLINE PXENVIF_VIF_INTERFACE
+__FrontendGetVifInterface(
+    IN  PXENVIF_FRONTEND    Frontend
+    )
+{
+    return PdoGetVifInterface(__FrontendGetPdo(Frontend));
+}
+
+PXENVIF_VIF_INTERFACE
+FrontendGetVifInterface(
+    IN  PXENVIF_FRONTEND    Frontend
+    )
+{
+    return __FrontendGetVifInterface(Frontend);
+}
+
+static FORCEINLINE PCHAR
+__FrontendGetPath(
+    IN  PXENVIF_FRONTEND    Frontend
+    )
+{
+    return Frontend->Path;
+}
+
+PCHAR
+FrontendGetPath(
+    IN  PXENVIF_FRONTEND    Frontend
+    )
+{
+    return __FrontendGetPath(Frontend);
+}
+
+static FORCEINLINE PCHAR
+__FrontendGetPrefix(
+    IN  PXENVIF_FRONTEND    Frontend
+    )
+{
+    return Frontend->Prefix;
+}
+
+PCHAR
+FrontendGetPrefix(
+    IN  PXENVIF_FRONTEND    Frontend
+    )
+{
+    return __FrontendGetPrefix(Frontend);
+}
+
+static FORCEINLINE PCHAR
+__FrontendGetBackendPath(
+    IN  PXENVIF_FRONTEND    Frontend
+    )
+{
+    return Frontend->BackendPath;
+}
+
+PCHAR
+FrontendGetBackendPath(
+    IN  PXENVIF_FRONTEND    Frontend
+    )
+{
+    return __FrontendGetBackendPath(Frontend);
+}
+
+static FORCEINLINE USHORT
+__FrontendGetBackendDomain(
+    IN  PXENVIF_FRONTEND    Frontend
+    )
+{
+    return Frontend->BackendDomain;
+}
+
+USHORT
+FrontendGetBackendDomain(
+    IN  PXENVIF_FRONTEND    Frontend
+    )
+{
+    return __FrontendGetBackendDomain(Frontend);
+}
+
+static FORCEINLINE PXENVIF_NOTIFIER
+__FrontendGetNotifier(
+    IN  PXENVIF_FRONTEND    Frontend
+    )
+{
+    return Frontend->Notifier;
+}
+
+PXENVIF_NOTIFIER
+FrontendGetNotifier(
+    IN  PXENVIF_FRONTEND    Frontend
+    )
+{
+    return __FrontendGetNotifier(Frontend);
+}
+
+static FORCEINLINE PXENVIF_MAC
+__FrontendGetMac(
+    IN  PXENVIF_FRONTEND    Frontend
+    )
+{
+    return Frontend->Mac;
+}
+
+PXENVIF_MAC
+FrontendGetMac(
+    IN  PXENVIF_FRONTEND    Frontend
+    )
+{
+    return __FrontendGetMac(Frontend);
+}
+
+static FORCEINLINE PXENVIF_RECEIVER
+__FrontendGetReceiver(
+    IN  PXENVIF_FRONTEND    Frontend
+    )
+{
+    return Frontend->Receiver;
+}
+
+PXENVIF_RECEIVER
+FrontendGetReceiver(
+    IN  PXENVIF_FRONTEND    Frontend
+    )
+{
+    return __FrontendGetReceiver(Frontend);
+}
+
+static FORCEINLINE PXENVIF_TRANSMITTER
+__FrontendGetTransmitter(
+    IN  PXENVIF_FRONTEND    Frontend
+    )
+{
+    return Frontend->Transmitter;
+}
+
+PXENVIF_TRANSMITTER
+FrontendGetTransmitter(
+    IN  PXENVIF_FRONTEND    Frontend
+    )
+{
+    return __FrontendGetTransmitter(Frontend);
+}
+
+static FORCEINLINE  ULONG
+__FrontendGetLuidIndex(
+    IN  PXENVIF_FRONTEND    Frontend
+    )
+{
+    return PdoGetLuidIndex(__FrontendGetPdo(Frontend));
+}
+
+static FORCEINLINE PCHAR
+__FrontendGetAddress(
+    IN  PXENVIF_FRONTEND    Frontend
+    )
+{
+    return PdoGetAddress(__FrontendGetPdo(Frontend));
+}
+
+static DECLSPEC_NOINLINE NTSTATUS
+FrontendEject(
+    IN  PXENVIF_THREAD  Self,
+    IN  PVOID           Context
+    )
+{
+    PXENVIF_FRONTEND    Frontend = Context;
+    PKEVENT             Event;
+
+    Trace("%s: ====>\n", __FrontendGetPath(Frontend));
+
+    Event = ThreadGetEvent(Self);
+
+    for (;;) {
+        KIRQL               Irql;
+        BOOLEAN             Online;
+        XenbusState         State;
+        ULONG               Attempt;
+        NTSTATUS            status;
+
+        KeWaitForSingleObject(Event,
+                              Executive,
+                              KernelMode,
+                              FALSE,
+                              NULL);
+        KeClearEvent(Event);
+
+        if (ThreadIsAlerted(Self))
+            break;
+
+        KeAcquireSpinLock(&Frontend->Lock, &Irql);
+
+        if (Frontend->State == FRONTEND_CLOSED) {
+            KeReleaseSpinLock(&Frontend->Lock, Irql);
+            continue;
+        }
+
+        STORE(Acquire, Frontend->StoreInterface);
+
+        Online = TRUE;
+        State = XenbusStateUnknown;
+
+        Attempt = 0;
+        for (;;) {
+            PXENBUS_STORE_TRANSACTION   Transaction;
+            PCHAR                       Buffer;
+
+            status = STATUS_UNSUCCESSFUL;
+            if (__FrontendGetBackendPath(Frontend) == NULL)
+                break;
+
+            status = STORE(TransactionStart,
+                           Frontend->StoreInterface,
+                           &Transaction);
+            if (!NT_SUCCESS(status))
+                break;
+
+            status = STORE(Read,
+                           Frontend->StoreInterface,
+                           Transaction,
+                           __FrontendGetBackendPath(Frontend),
+                           "online",
+                           &Buffer);
+            if (!NT_SUCCESS(status))
+                goto abort;
+
+            Online = (BOOLEAN)strtol(Buffer, NULL, 2);
+
+            STORE(Free,
+                  Frontend->StoreInterface,
+                  Buffer);
+
+            status = STORE(Read,
+                           Frontend->StoreInterface,
+                           Transaction,
+                           __FrontendGetBackendPath(Frontend),
+                           "state",
+                           &Buffer);
+            if (!NT_SUCCESS(status))
+                goto abort;
+
+            State = (XenbusState)strtol(Buffer, NULL, 10);
+
+            STORE(Free,
+                  Frontend->StoreInterface,
+                  Buffer);
+
+            status = STORE(TransactionEnd,
+                           Frontend->StoreInterface,
+                           Transaction,
+                           TRUE);
+            if (status != STATUS_RETRY || ++Attempt > 10)
+                break;
+
+            continue;
+
+abort:
+            (VOID) STORE(TransactionEnd,
+                         Frontend->StoreInterface,
+                         Transaction,
+                         FALSE);
+            break;
+        }
+
+        if (!NT_SUCCESS(status)) {
+            Online = TRUE;
+            State = XenbusStateUnknown;
+        }
+
+        if (!Online && State == XenbusStateClosing) {
+            Info("%s: requesting device eject\n", __FrontendGetPath(Frontend));
+
+            PdoRequestEject(Frontend->Pdo);
+            Frontend->EjectRequested = TRUE;
+        }
+
+        STORE(Release, Frontend->StoreInterface);
+
+        KeReleaseSpinLock(&Frontend->Lock, Irql);
+    }
+
+    Trace("%s: <====\n", __FrontendGetPath(Frontend));
+
+    return STATUS_SUCCESS;
+}
+
+static FORCEINLINE NTSTATUS
+__FrontendGetAddressTable(
+    IN  PXENVIF_FRONTEND            Frontend,
+    OUT PSOCKADDR_INET              *AddressTable,
+    OUT PULONG                      AddressCount
+    )
+{
+    ULONG                           LuidIndex;
+    ULONG                           Index;
+    PMIB_UNICASTIPADDRESS_TABLE     MibTable;
+    ULONG                           Count;
+    NTSTATUS                        status;
+
+    LuidIndex = __FrontendGetLuidIndex(Frontend);
+
+    status = GetUnicastIpAddressTable(AF_UNSPEC, &MibTable);
+    if (!NT_SUCCESS(status))
+        goto fail1;
+
+    *AddressCount = 0;
+    for (Index = 0; Index < MibTable->NumEntries; Index++) {
+        PMIB_UNICASTIPADDRESS_ROW   Row = &MibTable->Table[Index];
+
+        if (Row->InterfaceLuid.Info.IfType != IF_TYPE_ETHERNET_CSMACD)
+            continue;
+
+        if (Row->InterfaceLuid.Info.NetLuidIndex != LuidIndex)
+            continue;
+
+        if (Row->Address.si_family != AF_INET &&
+            Row->Address.si_family != AF_INET6)
+            continue;
+
+        (*AddressCount)++;
+    }
+
+    *AddressTable = NULL;
+
+    if (*AddressCount == 0)
+        goto done;
+
+    *AddressTable = __FrontendAllocate(sizeof (SOCKADDR_INET) * *AddressCount);
+
+    status = STATUS_NO_MEMORY;
+    if (*AddressTable == NULL)
+        goto fail2;
+
+    Count = 0;
+
+    for (Index = 0; Index < MibTable->NumEntries; Index++) {
+        PMIB_UNICASTIPADDRESS_ROW   Row = &MibTable->Table[Index];
+
+        if (Row->InterfaceLuid.Info.IfType != IF_TYPE_ETHERNET_CSMACD)
+            continue;
+
+        if (Row->InterfaceLuid.Info.NetLuidIndex != LuidIndex)
+            continue;
+
+        switch (Row->Address.si_family) {
+        case AF_INET:
+        case AF_INET6:
+            (*AddressTable)[Count++] = Row->Address;
+            break;
+        default:
+            break;
+        }
+    }
+    ASSERT3U(Count, ==, *AddressCount);
+
+done:
+    FreeMibTable(MibTable);
+
+    return STATUS_SUCCESS;
+
+fail2:
+    Error("fail2\n");
+
+    FreeMibTable(MibTable);
+
+fail1:
+    Error("fail1 (%08x)\n", status);
+
+    return status;
+}
+
+static DECLSPEC_NOINLINE NTSTATUS
+FrontendMib(
+    IN  PXENVIF_THREAD  Self,
+    IN  PVOID           Context
+    )
+{
+    PXENVIF_FRONTEND    Frontend = Context;
+    PKEVENT             Event;
+
+    Trace("====>\n");
+
+    Event = ThreadGetEvent(Self);
+
+    for (;;) { 
+        PSOCKADDR_INET  Table;
+        ULONG           Count;
+        NTSTATUS        status;
+
+        (VOID) KeWaitForSingleObject(Event,
+                                     Executive,
+                                     KernelMode,
+                                     FALSE,
+                                     NULL);
+        KeClearEvent(Event);
+
+        if (ThreadIsAlerted(Self))
+            break;
+
+        status = __FrontendGetAddressTable(Frontend,
+                                           &Table,
+                                           &Count);
+        if (!NT_SUCCESS(status))
+            continue;
+
+        TransmitterUpdateAddressTable(__FrontendGetTransmitter(Frontend),
+                                      Table,
+                                      Count);
+
+        if (Count != 0)
+            __FrontendFree(Table);
+    }
+
+    Trace("<====\n");
+
+    return STATUS_SUCCESS;
+}
+
+static VOID
+FrontendIpAddressChange(
+    IN  PVOID                       Context,
+    IN  PMIB_UNICASTIPADDRESS_ROW   _Row OPTIONAL,
+    IN  MIB_NOTIFICATION_TYPE       NotificationType
+    )
+{
+    PXENVIF_FRONTEND                Frontend = Context;
+
+    UNREFERENCED_PARAMETER(_Row);
+    UNREFERENCED_PARAMETER(NotificationType);
+
+    ThreadWake(Frontend->MibThread);
+}
+
+#define TIME_US(_us)            ((_us) * 10)
+#define TIME_MS(_ms)            (TIME_US((_ms) * 1000))
+#define TIME_S(_s)              (TIME_MS((_s) * 1000))
+#define TIME_RELATIVE(_t)       (-(_t))
+
+static FORCEINLINE NTSTATUS
+__FrontendWaitForStateChange(
+    IN  PXENVIF_FRONTEND    Frontend,
+    IN  PCHAR               Path,
+    IN  XenbusState         *State
+    )
+{
+    KEVENT                  Event;
+    PXENBUS_STORE_WATCH     Watch;
+    LARGE_INTEGER           Start;
+    ULONGLONG               TimeDelta;
+    LARGE_INTEGER           Timeout;
+    XenbusState             Old = *State;
+    NTSTATUS                status;
+
+    Trace("%s: ====> (%s)\n", Path, XenbusStateName(*State));
+
+    KeInitializeEvent(&Event, NotificationEvent, FALSE);
+
+    status = STORE(Watch,
+                   Frontend->StoreInterface,
+                   Path,
+                   "state",
+                   &Event,
+                   &Watch);
+    if (!NT_SUCCESS(status))
+        goto fail1;
+
+    KeQuerySystemTime(&Start);
+    TimeDelta = 0;
+
+    Timeout.QuadPart = 0;
+
+    while (*State == Old && TimeDelta < 120000) {
+        ULONG           Attempt;
+        PCHAR           Buffer;
+        LARGE_INTEGER   Now;
+
+        Attempt = 0;
+        while (++Attempt < 1000) {
+            status = KeWaitForSingleObject(&Event,
+                                           Executive,
+                                           KernelMode,
+                                           FALSE,
+                                           &Timeout);
+            if (status != STATUS_TIMEOUT)
+                break;
+
+            // We are waiting for a watch event at DISPATCH_LEVEL so
+            // it is our responsibility to poll the store ring.
+            STORE(Poll,
+                  Frontend->StoreInterface);
+
+            KeStallExecutionProcessor(1000);   // 1ms
+        }
+
+        KeClearEvent(&Event);
+
+        status = STORE(Read,
+                       Frontend->StoreInterface,
+                       NULL,
+                       Path,
+                       "state",
+                       &Buffer);
+        if (!NT_SUCCESS(status))
+            goto fail2;
+
+        *State = (XenbusState)strtol(Buffer, NULL, 10);
+
+        STORE(Free,
+              Frontend->StoreInterface,
+              Buffer);
+
+        KeQuerySystemTime(&Now);
+
+        TimeDelta = (Now.QuadPart - Start.QuadPart) / 10000ull;
+    }
+
+    status = STATUS_UNSUCCESSFUL;
+    if (*State == Old)
+        goto fail3;
+
+    (VOID) STORE(Unwatch,
+                 Frontend->StoreInterface,
+                 Watch);
+
+    Trace("%s: <==== (%s)\n", Path, XenbusStateName(*State));
+
+    return STATUS_SUCCESS;
+
+fail3:
+    Error("fail3\n");
+
+fail2:
+    Error("fail2\n");
+
+    (VOID) STORE(Unwatch,
+                 Frontend->StoreInterface,
+                 Watch);
+
+fail1:
+    Error("fail1 (%08x)\n", status);
+                   
+    return status;
+}
+
+static FORCEINLINE NTSTATUS
+__FrontendClose(
+    IN  PXENVIF_FRONTEND    Frontend
+    )
+{
+    PCHAR                   Path;
+    XenbusState             State;
+    NTSTATUS                status;
+
+    Trace("====>\n");
+
+    ASSERT(Frontend->Watch != NULL);
+    (VOID) STORE(Unwatch,
+                 Frontend->StoreInterface,
+                 Frontend->Watch);
+    Frontend->Watch = NULL;
+
+    // Release cached information about the backend
+    ASSERT(Frontend->BackendPath != NULL);
+    STORE(Free,
+          Frontend->StoreInterface,
+          Frontend->BackendPath);
+    Frontend->BackendPath = NULL;
+
+    Frontend->BackendDomain = DOMID_INVALID;
+
+    status = STORE(Read,
+                   Frontend->StoreInterface,
+                   NULL,
+                   __FrontendGetPath(Frontend),
+                   "backend",
+                   &Path);
+    if (!NT_SUCCESS(status))
+        goto fail1;
+
+    State = XenbusStateInitialising;
+    status = __FrontendWaitForStateChange(Frontend, Path, &State);
+    if (!NT_SUCCESS(status))
+        goto fail2;
+
+    while (State != XenbusStateClosing &&
+           State != XenbusStateClosed &&
+           State != XenbusStateUnknown) {
+        (VOID) STORE(Printf,
+                     Frontend->StoreInterface,
+                     NULL,
+                     __FrontendGetPath(Frontend),
+                     "state",
+                     "%u",
+                     XenbusStateClosing);
+        status = __FrontendWaitForStateChange(Frontend, Path, &State);
+        if (!NT_SUCCESS(status))
+            goto fail2;
+    }
+
+    while (State != XenbusStateClosed &&
+           State != XenbusStateUnknown) {
+        (VOID) STORE(Printf,
+                     Frontend->StoreInterface,
+                     NULL,
+                     __FrontendGetPath(Frontend),
+                     "state",
+                     "%u",
+                     XenbusStateClosed);
+        status = __FrontendWaitForStateChange(Frontend, Path, &State);
+        if (!NT_SUCCESS(status))
+            goto fail3;
+    }
+
+    STORE(Free,
+          Frontend->StoreInterface,
+          Path);
+
+    STORE(Release, Frontend->StoreInterface);
+    Frontend->StoreInterface = NULL;
+
+    Trace("<====\n");
+    return STATUS_SUCCESS;
+
+fail3:
+    Error("fail3\n");
+
+fail2:
+    Error("fail2\n");
+
+    STORE(Free,
+          Frontend->StoreInterface,
+          Path);
+
+fail1:
+    Error("fail1 (%08x)\n", status);
+
+    return status;
+}
+
+static FORCEINLINE NTSTATUS
+__FrontendPrepare(
+    IN  PXENVIF_FRONTEND    Frontend
+    )
+{
+    PCHAR                   Path;
+    XenbusState             State;
+    PCHAR                   Buffer;
+    NTSTATUS                status;
+
+    Trace("====>\n");
+
+    Frontend->StoreInterface = __FrontendGetStoreInterface(Frontend);
+
+    STORE(Acquire, Frontend->StoreInterface);
+
+    status = STORE(Read,
+                   Frontend->StoreInterface,
+                   NULL,
+                   __FrontendGetPath(Frontend),
+                   "backend",
+                   &Path);
+    if (!NT_SUCCESS(status))
+        goto fail1;
+
+    State = XenbusStateUnknown;
+    status = __FrontendWaitForStateChange(Frontend, Path, &State);
+    if (!NT_SUCCESS(status))
+        goto fail2;
+
+    while (State != XenbusStateClosed &&
+           State != XenbusStateInitialising &&
+           State != XenbusStateInitWait) {
+        status = __FrontendWaitForStateChange(Frontend, Path, &State);
+        if (!NT_SUCCESS(status))
+            goto fail3;
+    }
+
+    status = STORE(Printf,
+                   Frontend->StoreInterface,
+                   NULL,
+                   __FrontendGetPath(Frontend),
+                   "state",
+                   "%u",
+                   XenbusStateInitialising);
+    if (!NT_SUCCESS(status))
+        goto fail4;
+
+    while (State == XenbusStateClosed ||
+           State == XenbusStateInitialising) {
+        status = __FrontendWaitForStateChange(Frontend, Path, &State);
+        if (!NT_SUCCESS(status))
+            goto fail5;
+    }
+
+    status = STATUS_UNSUCCESSFUL;
+    if (State != XenbusStateInitWait)
+        goto fail6;
+
+    Frontend->BackendPath = Path;
+
+    status = STORE(Read,
+                   Frontend->StoreInterface,
+                   NULL,
+                   __FrontendGetPath(Frontend),
+                   "backend-id",
+                   &Buffer);
+    if (!NT_SUCCESS(status)) {
+        Frontend->BackendDomain = 0;
+    } else {
+        Frontend->BackendDomain = (USHORT)strtol(Buffer, NULL, 10);
+
+        STORE(Free,
+              Frontend->StoreInterface,
+              Buffer);
+    }
+
+    status = STORE(Watch,
+                   Frontend->StoreInterface,
+                   __FrontendGetBackendPath(Frontend),
+                   "online",
+                   ThreadGetEvent(Frontend->EjectThread),
+                   &Frontend->Watch);
+    if (!NT_SUCCESS(status))
+        goto fail7;
+
+    Trace("<====\n");
+    return STATUS_SUCCESS;
+
+fail7:
+    Error("fail7\n");
+
+    Frontend->BackendDomain = DOMID_INVALID;
+    Frontend->BackendPath = NULL;
+
+fail6:
+    Error("fail6\n");
+
+fail5:
+    Error("fail5\n");
+
+fail4:
+    Error("fail4\n");
+
+fail3:
+    Error("fail3\n");
+
+fail2:
+    Error("fail2\n");
+
+    STORE(Free,
+          Frontend->StoreInterface,
+          Path);
+
+fail1:
+    Error("fail1 (%08x)\n", status);
+
+    STORE(Release, Frontend->StoreInterface);
+    Frontend->StoreInterface = NULL;
+
+    Trace("<====\n");
+    return status;
+}
+
+static VOID
+FrontendDebugCallback(
+    IN  PVOID           Argument,
+    IN  BOOLEAN         Crashing
+    )
+{
+    PXENVIF_FRONTEND    Frontend = Argument;
+
+    UNREFERENCED_PARAMETER(Crashing);
+
+    DEBUG(Printf,
+          Frontend->DebugInterface,
+          Frontend->DebugCallback,
+          "PATH: %s\n",
+          __FrontendGetPath(Frontend));
+}
+
+static FORCEINLINE NTSTATUS
+__FrontendConnect(
+    IN  PXENVIF_FRONTEND    Frontend
+    )
+{
+    PCHAR                   Path = __FrontendGetBackendPath(Frontend);
+    XenbusState             State;
+    ULONG                   Attempt;
+    NTSTATUS                status;
+
+    Trace("====>\n");
+
+    Frontend->DebugInterface = __FrontendGetDebugInterface(Frontend);
+
+    DEBUG(Acquire, Frontend->DebugInterface);
+
+    status = DEBUG(Register,
+                   Frontend->DebugInterface,
+                   __MODULE__ "|FRONTEND",
+                   FrontendDebugCallback,
+                   Frontend,
+                   &Frontend->DebugCallback);
+    if (!NT_SUCCESS(status))
+        goto fail1;
+
+    status = MacConnect(__FrontendGetMac(Frontend));
+    if (!NT_SUCCESS(status))
+        goto fail2;
+
+    status = ReceiverConnect(__FrontendGetReceiver(Frontend));
+    if (!NT_SUCCESS(status))
+        goto fail3;
+
+    status = TransmitterConnect(__FrontendGetTransmitter(Frontend));
+    if (!NT_SUCCESS(status))
+        goto fail4;
+
+    status = NotifierConnect(__FrontendGetNotifier(Frontend));
+    if (!NT_SUCCESS(status))
+        goto fail5;
+
+    Attempt = 0;
+    do {
+        PXENBUS_STORE_TRANSACTION   Transaction;
+
+        status = STORE(TransactionStart,
+                       Frontend->StoreInterface,
+                       &Transaction);
+        if (!NT_SUCCESS(status))
+            break;
+
+        status = NotifierStoreWrite(__FrontendGetNotifier(Frontend),
+                                    Transaction);
+        if (!NT_SUCCESS(status))
+            goto abort;
+
+        status = ReceiverStoreWrite(__FrontendGetReceiver(Frontend),
+                                    Transaction);
+        if (!NT_SUCCESS(status))
+            goto abort;
+
+        status = TransmitterStoreWrite(__FrontendGetTransmitter(Frontend),
+                                       Transaction);
+        if (!NT_SUCCESS(status))
+            goto abort;
+
+        status = STORE(TransactionEnd,
+                       Frontend->StoreInterface,
+                       Transaction,
+                       TRUE);
+        if (status != STATUS_RETRY || ++Attempt > 10)
+            break;
+
+        continue;
+
+abort:
+        (VOID) STORE(TransactionEnd,
+                     Frontend->StoreInterface,
+                     Transaction,
+                     FALSE);
+        break;
+    } while (status == STATUS_RETRY);
+
+    if (!NT_SUCCESS(status))
+        goto fail6;
+
+    status = STORE(Printf,
+                   Frontend->StoreInterface,
+                   NULL,
+                   __FrontendGetPath(Frontend),
+                   "state",
+                   "%u",
+                   XenbusStateConnected);
+    if (!NT_SUCCESS(status))
+        goto fail7;
+
+    State = XenbusStateInitWait;
+    status = __FrontendWaitForStateChange(Frontend, Path, &State);
+    if (!NT_SUCCESS(status))
+        goto fail8;
+
+    status = STATUS_UNSUCCESSFUL;
+    if (State != XenbusStateConnected)
+        goto fail9;
+
+    Trace("<====\n");
+    return STATUS_SUCCESS;
+
+fail9:
+    Error("fail9\n");
+
+fail8:
+    Error("fail8\n");
+
+fail7:
+    Error("fail7\n");
+
+fail6:
+    Error("fail7\n");
+
+    NotifierDisconnect(__FrontendGetNotifier(Frontend));
+
+fail5:
+    Error("fail5\n");
+
+    TransmitterDisconnect(__FrontendGetTransmitter(Frontend));
+
+fail4:
+    Error("fail4\n");
+
+    ReceiverDisconnect(__FrontendGetReceiver(Frontend));
+
+fail3:
+    Error("fail3\n");
+
+    MacDisconnect(__FrontendGetMac(Frontend));
+
+fail2:
+    Error("fail2\n");
+
+    DEBUG(Deregister,
+          Frontend->DebugInterface,
+          Frontend->DebugCallback);
+    Frontend->DebugCallback = NULL;
+
+fail1:
+    Error("fail1 (%08x)\n", status);
+
+    DEBUG(Release, Frontend->DebugInterface);
+    Frontend->DebugInterface = NULL;
+
+    Trace("<====\n");
+    return status;
+}
+
+static FORCEINLINE VOID
+__FrontendDisconnect(
+    IN  PXENVIF_FRONTEND    Frontend
+    )
+{
+    Trace("====>\n");
+
+    NotifierDisconnect(__FrontendGetNotifier(Frontend));
+    TransmitterDisconnect(__FrontendGetTransmitter(Frontend));
+    ReceiverDisconnect(__FrontendGetReceiver(Frontend));
+    MacDisconnect(__FrontendGetMac(Frontend));
+
+    DEBUG(Deregister,
+          Frontend->DebugInterface,
+          Frontend->DebugCallback);
+    Frontend->DebugCallback = NULL;
+
+    DEBUG(Release, Frontend->DebugInterface);
+    Frontend->DebugInterface = NULL;
+
+    Trace("<====\n");
+}
+
+static FORCEINLINE NTSTATUS
+__FrontendEnable(
+    IN  PXENVIF_FRONTEND    Frontend
+    )
+{
+    NTSTATUS                status;
+
+    Trace("====>\n");
+
+    status = MacEnable(__FrontendGetMac(Frontend));
+    if (!NT_SUCCESS(status))
+        goto fail1;
+
+    status = ReceiverEnable(__FrontendGetReceiver(Frontend));
+    if (!NT_SUCCESS(status))
+        goto fail2;
+
+    status = TransmitterEnable(__FrontendGetTransmitter(Frontend));
+    if (!NT_SUCCESS(status))
+        goto fail3;
+
+    status = NotifierEnable(__FrontendGetNotifier(Frontend));
+    if (!NT_SUCCESS(status))
+        goto fail4;
+
+    Trace("<====\n");
+    return STATUS_SUCCESS;
+
+fail4:
+    Error("fail4\n");
+
+    TransmitterDisable(__FrontendGetTransmitter(Frontend));
+
+fail3:
+    Error("fail3\n");
+
+    ReceiverDisable(__FrontendGetReceiver(Frontend));
+
+fail2:
+    Error("fail2\n");
+
+    MacDisable(__FrontendGetMac(Frontend));
+
+fail1:
+    Error("fail1 (%08x)\n", status);
+
+    return status;
+}
+
+static FORCEINLINE VOID
+__FrontendDisable(
+    IN  PXENVIF_FRONTEND    Frontend
+    )
+{
+    Trace("====>\n");
+
+    NotifierDisable(__FrontendGetNotifier(Frontend));
+    TransmitterDisable(__FrontendGetTransmitter(Frontend));
+    ReceiverDisable(__FrontendGetReceiver(Frontend));
+    MacDisable(__FrontendGetMac(Frontend));
+
+    Trace("<====\n");
+}
+
+NTSTATUS
+FrontendSetState(
+    IN  PXENVIF_FRONTEND        Frontend,
+    IN  XENVIF_FRONTEND_STATE   State
+    )
+{
+    BOOLEAN                     Failed;
+    KIRQL                       Irql;
+
+    KeAcquireSpinLock(&Frontend->Lock, &Irql);
+
+    Trace("%s: ====> '%s' -> '%s'\n",
+          __FrontendGetPath(Frontend),
+          FrontendStateName(Frontend->State),
+          FrontendStateName(State));
+
+    Failed = FALSE;
+    while (Frontend->State != State && !Failed) {
+        NTSTATUS    status;
+
+        switch (Frontend->State) {
+        case FRONTEND_CLOSED:
+            switch (State) {
+            case FRONTEND_PREPARED:
+            case FRONTEND_CONNECTED:
+            case FRONTEND_ENABLED:
+                status = __FrontendPrepare(Frontend);
+                if (NT_SUCCESS(status)) {
+                    Frontend->State = FRONTEND_PREPARED;
+                } else {
+                    Failed = TRUE;
+                }
+                break;
+
+            default:
+                ASSERT(FALSE);
+                break;
+            }
+            break;
+
+        case FRONTEND_PREPARED:
+            switch (State) {
+            case FRONTEND_CONNECTED:
+            case FRONTEND_ENABLED:
+                status = __FrontendConnect(Frontend);
+                if (NT_SUCCESS(status)) {
+                    Frontend->State = FRONTEND_CONNECTED;
+                } else {
+                    status = __FrontendClose(Frontend);
+                    if (NT_SUCCESS(status))
+                        Frontend->State = FRONTEND_CLOSED;
+                    else
+                        Frontend->State = FRONTEND_STATE_INVALID;
+
+                    Failed = TRUE;
+                }
+                break;
+
+            case FRONTEND_CLOSED:
+                status = __FrontendClose(Frontend);
+                if (NT_SUCCESS(status)) {
+                    Frontend->State = FRONTEND_CLOSED;
+                } else {
+                    Frontend->State = FRONTEND_STATE_INVALID;
+                    Failed = TRUE;
+                }
+
+                break;
+
+            default:
+                ASSERT(FALSE);
+                break;
+            }
+            break;
+
+        case FRONTEND_CONNECTED:
+            switch (State) {
+            case FRONTEND_ENABLED:
+                status = __FrontendEnable(Frontend);
+                if (NT_SUCCESS(status)) {
+                    Frontend->State = FRONTEND_ENABLED;
+                } else {
+                    status = __FrontendClose(Frontend);
+                    if (NT_SUCCESS(status))
+                        Frontend->State = FRONTEND_CLOSED;
+                    else
+                        Frontend->State = FRONTEND_STATE_INVALID;
+
+                    Failed = TRUE;
+                }
+                break;
+
+            case FRONTEND_PREPARED:
+            case FRONTEND_CLOSED:
+                status = __FrontendClose(Frontend);
+                if (NT_SUCCESS(status)) {
+                    Frontend->State = FRONTEND_CLOSED;
+                } else {
+                    Frontend->State = FRONTEND_STATE_INVALID;
+                    Failed = TRUE;
+                }
+
+                __FrontendDisconnect(Frontend);
+
+                break;
+
+            default:
+                ASSERT(FALSE);
+                break;
+            }
+            break;
+
+        case FRONTEND_ENABLED:
+            switch (State) {
+            case FRONTEND_CONNECTED:
+            case FRONTEND_PREPARED:
+            case FRONTEND_CLOSED:
+                __FrontendDisable(Frontend);
+                Frontend->State = FRONTEND_CONNECTED;
+                break;
+
+            default:
+                ASSERT(FALSE);
+                break;
+            }
+            break;
+
+        case FRONTEND_STATE_INVALID:
+            Failed = TRUE;
+            break;
+
+        default:
+            ASSERT(FALSE);
+            break;
+        }
+
+        Trace("%s in state '%s'\n",
+              __FrontendGetPath(Frontend),
+              FrontendStateName(Frontend->State));
+    }
+
+    KeReleaseSpinLock(&Frontend->Lock, Irql);
+
+    Trace("%s: <=====\n", __FrontendGetPath(Frontend));
+
+    return (!Failed) ? STATUS_SUCCESS : STATUS_UNSUCCESSFUL;
+}
+
+PCHAR
+FrontendGetAddress(
+    IN  PXENVIF_FRONTEND    Frontend
+    )
+{
+    return __FrontendGetAddress(Frontend);
+}
+
+static FORCEINLINE NTSTATUS
+__FrontendResume(
+    IN  PXENVIF_FRONTEND    Frontend
+    )
+{
+    NTSTATUS                status;
+
+    ASSERT3U(KeGetCurrentIrql(), ==, DISPATCH_LEVEL);
+
+    status = FrontendSetState(Frontend, FRONTEND_PREPARED);
+    if (!NT_SUCCESS(status))
+        goto fail1;
+
+    status = FrontendSetState(Frontend, FRONTEND_CLOSED);
+    if (!NT_SUCCESS(status))
+        goto fail2;
+
+    return STATUS_SUCCESS;
+
+fail2:
+    Error("fail2\n");
+
+    STORE(Release, Frontend->StoreInterface);
+    Frontend->StoreInterface = NULL;
+
+    Frontend->State = FRONTEND_CLOSED;
+
+fail1:
+    Error("fail1 (%08x)\n", status);
+
+    return status;
+}
+
+static FORCEINLINE VOID
+__FrontendSuspend(
+    IN  PXENVIF_FRONTEND    Frontend
+    )
+{
+    UNREFERENCED_PARAMETER(Frontend);
+
+    ASSERT3U(KeGetCurrentIrql(), ==, DISPATCH_LEVEL);
+}
+
+static DECLSPEC_NOINLINE VOID
+FrontendSuspendCallbackLate(
+    IN  PVOID           Argument
+    )
+{
+    PXENVIF_FRONTEND    Frontend = Argument;
+    NTSTATUS            status;
+
+    __FrontendSuspend(Frontend);
+
+    status = __FrontendResume(Frontend);
+    ASSERT(NT_SUCCESS(status));
+}
+
+NTSTATUS
+FrontendResume(
+    IN  PXENVIF_FRONTEND    Frontend
+    )
+{
+    KIRQL                   Irql;
+    NTSTATUS                status;
+
+    Trace("====>\n");
+
+    KeRaiseIrql(DISPATCH_LEVEL, &Irql);
+    status = __FrontendResume(Frontend);
+    KeLowerIrql(Irql);
+
+    if (!NT_SUCCESS(status))
+        goto fail1;
+
+    Frontend->SuspendInterface = __FrontendGetSuspendInterface(Frontend);
+
+    SUSPEND(Acquire, Frontend->SuspendInterface);
+
+    status = SUSPEND(Register,
+                     Frontend->SuspendInterface,
+                     SUSPEND_CALLBACK_LATE,
+                     FrontendSuspendCallbackLate,
+                     Frontend,
+                     &Frontend->SuspendCallbackLate);
+    if (!NT_SUCCESS(status))
+        goto fail2;
+
+    Trace("<====\n");
+
+    return STATUS_SUCCESS;
+    
+fail2:
+    Error("fail2\n");
+
+    SUSPEND(Release, Frontend->SuspendInterface);
+    Frontend->SuspendInterface = NULL;
+
+    KeRaiseIrql(DISPATCH_LEVEL, &Irql);
+    __FrontendSuspend(Frontend);
+    KeLowerIrql(Irql);
+
+fail1:
+    Error("fail1 (%08x)\n", status);
+
+    return status;
+}
+
+VOID
+FrontendSuspend(
+    IN  PXENVIF_FRONTEND    Frontend
+    )
+{
+    KIRQL                   Irql;
+
+    Trace("====>\n");
+
+    ASSERT3U(KeGetCurrentIrql(), ==, PASSIVE_LEVEL);
+
+    // FrontendResume() may have failed
+    if (Frontend->SuspendInterface == NULL)
+        goto done;
+
+    SUSPEND(Deregister,
+            Frontend->SuspendInterface,
+            Frontend->SuspendCallbackLate);
+    Frontend->SuspendCallbackLate = NULL;
+
+    SUSPEND(Release, Frontend->SuspendInterface);
+    Frontend->SuspendInterface = NULL;
+
+    KeRaiseIrql(DISPATCH_LEVEL, &Irql);
+    __FrontendSuspend(Frontend);
+    KeLowerIrql(Irql);
+
+done:
+    Trace("<====\n");
+}
+
+NTSTATUS
+FrontendInitialize(
+    IN  PXENVIF_PDO         Pdo,
+    OUT PXENVIF_FRONTEND    *Frontend
+    )
+{
+    PCHAR                   Name;
+    ULONG                   Length;
+    PCHAR                   Path;
+    PCHAR                   Prefix;
+    NTSTATUS                status;
+
+    Trace("====>\n");
+
+    Name = PdoGetName(Pdo);
+
+    Length = sizeof ("devices/vif/") + (ULONG)strlen(Name);
+    Path = __FrontendAllocate(Length);
+
+    status = STATUS_NO_MEMORY;
+    if (Path == NULL)
+        goto fail1;
+
+    status = RtlStringCbPrintfA(Path, 
+                                Length,
+                                "device/vif/%s", 
+                                Name);
+    if (!NT_SUCCESS(status))
+        goto fail2;
+
+    Length = sizeof ("attr/eth") + (ULONG)strlen(Name);
+    Prefix = __FrontendAllocate(Length);
+
+    status = STATUS_NO_MEMORY;
+    if (Prefix == NULL)
+        goto fail3;
+
+    status = RtlStringCbPrintfA(Prefix, 
+                                Length,
+                                "attr/eth%s", 
+                                Name);
+    if (!NT_SUCCESS(status))
+        goto fail4;
+
+    *Frontend = __FrontendAllocate(sizeof (XENVIF_FRONTEND));
+
+    status = STATUS_NO_MEMORY;
+    if (*Frontend == NULL)
+        goto fail5;
+
+    (*Frontend)->Pdo = Pdo;
+    (*Frontend)->Path = Path;
+    (*Frontend)->Prefix = Prefix;
+    (*Frontend)->BackendDomain = DOMID_INVALID;
+
+    KeInitializeSpinLock(&(*Frontend)->Lock);
+
+    (*Frontend)->State = FRONTEND_CLOSED;
+
+    status = ThreadCreate(FrontendEject, *Frontend, &(*Frontend)->EjectThread);
+    if (!NT_SUCCESS(status))
+        goto fail6;
+
+    status = MacInitialize(*Frontend, &(*Frontend)->Mac);
+    if (!NT_SUCCESS(status))
+        goto fail7;
+
+    status = NotifierInitialize(*Frontend, &(*Frontend)->Notifier);
+    if (!NT_SUCCESS(status))
+        goto fail8;
+
+    status = ReceiverInitialize(*Frontend, 1, &(*Frontend)->Receiver);
+    if (!NT_SUCCESS(status))
+        goto fail9;
+
+    status = TransmitterInitialize(*Frontend, 1, &(*Frontend)->Transmitter);
+    if (!NT_SUCCESS(status))
+        goto fail10;
+
+    status = ThreadCreate(FrontendMib, *Frontend, &(*Frontend)->MibThread);
+    if (!NT_SUCCESS(status))
+        goto fail11;
+
+    status = NotifyUnicastIpAddressChange(AF_UNSPEC,
+                                          FrontendIpAddressChange,
+                                          *Frontend,
+                                          TRUE,
+                                          &(*Frontend)->Handle);
+    if (!NT_SUCCESS(status))
+        goto fail12;
+
+    Trace("<====\n");
+
+    return STATUS_SUCCESS;
+
+fail12:
+    Error("fail12\n");
+
+    ThreadAlert((*Frontend)->MibThread);
+    ThreadJoin((*Frontend)->MibThread);
+    (*Frontend)->MibThread = NULL;
+
+fail11:
+    Error("fail11\n");
+
+    TransmitterTeardown(__FrontendGetTransmitter(*Frontend));
+    (*Frontend)->Transmitter = NULL;
+
+fail10:
+    Error("fail10\n");
+
+    ReceiverTeardown(__FrontendGetReceiver(*Frontend));
+    (*Frontend)->Receiver = NULL;
+
+fail9:
+    Error("fail9\n");
+
+    NotifierTeardown(__FrontendGetNotifier(*Frontend));
+    (*Frontend)->Notifier = NULL;
+
+fail8:
+    Error("fail8\n");
+
+    MacTeardown(__FrontendGetMac(*Frontend));
+    (*Frontend)->Mac = NULL;
+
+fail7:
+    Error("fail7\n");
+
+    ThreadAlert((*Frontend)->EjectThread);
+    ThreadJoin((*Frontend)->EjectThread);
+    (*Frontend)->EjectThread = NULL;
+
+fail6:
+    Error("fail6\n");
+
+    (*Frontend)->State = FRONTEND_STATE_INVALID;
+    RtlZeroMemory(&(*Frontend)->Lock, sizeof (KSPIN_LOCK));
+
+    (*Frontend)->BackendDomain = 0;
+    (*Frontend)->Prefix = NULL;
+    (*Frontend)->Path = NULL;
+    (*Frontend)->Pdo = NULL;
+
+    ASSERT(IsZeroMemory(*Frontend, sizeof (XENVIF_FRONTEND)));
+
+    __FrontendFree(*Frontend);
+    *Frontend = NULL;
+
+fail5:
+    Error("fail5\n");
+
+fail4:
+    Error("fail4\n");
+
+    __FrontendFree(Prefix);
+
+fail3:
+    Error("fail3\n");
+
+fail2:
+    Error("fail2\n");
+
+    __FrontendFree(Path);
+
+fail1:
+    Error("fail1 (%08x)\n", status);
+
+    return status;
+}
+
+VOID
+FrontendTeardown(
+    IN  PXENVIF_FRONTEND    Frontend
+    )
+{
+    Trace("====>\n");
+
+    ASSERT(Frontend->State != FRONTEND_ENABLED);
+    ASSERT(Frontend->State != FRONTEND_CONNECTED);
+
+    if (Frontend->State == FRONTEND_PREPARED) {
+        ASSERT(Frontend->Handle != NULL);
+        CancelMibChangeNotify2(Frontend->Handle);
+        Frontend->Handle = NULL;
+
+        ASSERT(Frontend->Watch != NULL);
+        (VOID) STORE(Unwatch,
+                     Frontend->StoreInterface,
+                     Frontend->Watch);
+        Frontend->Watch = NULL;
+
+        // Release cached information about the backend
+        ASSERT(Frontend->BackendPath != NULL);
+        STORE(Free,
+              Frontend->StoreInterface,
+              Frontend->BackendPath);
+        Frontend->BackendPath = NULL;
+
+        Frontend->BackendDomain = DOMID_INVALID;
+
+        Frontend->State = FRONTEND_CLOSED;
+    }
+
+    ASSERT3U(Frontend->State, ==, FRONTEND_CLOSED);
+
+    ASSERT(Frontend->Handle != NULL);
+    CancelMibChangeNotify2(Frontend->Handle);
+    Frontend->Handle = NULL;
+
+    ThreadAlert(Frontend->MibThread);
+    ThreadJoin(Frontend->MibThread);
+    Frontend->MibThread = NULL;
+
+    TransmitterTeardown(__FrontendGetTransmitter(Frontend));
+    Frontend->Transmitter = NULL;
+
+    ReceiverTeardown(__FrontendGetReceiver(Frontend));
+    Frontend->Receiver = NULL;
+
+    NotifierTeardown(__FrontendGetNotifier(Frontend));
+    Frontend->Notifier = NULL;
+
+    MacTeardown(__FrontendGetMac(Frontend));
+    Frontend->Mac = NULL;
+
+    ThreadAlert(Frontend->EjectThread);
+    ThreadJoin(Frontend->EjectThread);
+    Frontend->EjectThread = NULL;
+
+    Frontend->State = FRONTEND_STATE_INVALID;
+    RtlZeroMemory(&Frontend->Lock, sizeof (KSPIN_LOCK));
+
+    Frontend->BackendDomain = 0;
+
+    __FrontendFree(Frontend->Prefix);
+    Frontend->Prefix = NULL;
+
+    __FrontendFree(Frontend->Path);
+    Frontend->Path = NULL;
+
+    Frontend->Pdo = NULL;
+
+    ASSERT(IsZeroMemory(Frontend, sizeof (XENVIF_FRONTEND)));
+
+    __FrontendFree(Frontend);
+
+    Trace("<====\n");
+}
+
+VOID
+FrontendRemoveFailed(
+    IN PXENVIF_FRONTEND Frontend
+    )
+{
+    KIRQL               Irql;
+    ULONG               Length;
+    PCHAR               Path;
+    NTSTATUS            status;
+
+    KeAcquireSpinLock(&Frontend->Lock, &Irql);
+    if (!Frontend->EjectRequested)
+        goto done;
+
+    Frontend->EjectRequested = FALSE;
+
+    Info("%s: device eject failed\n", __FrontendGetPath(Frontend));
+
+    Length = sizeof ("error/") + (ULONG)strlen(__FrontendGetPath(Frontend));
+    Path = __FrontendAllocate(Length);
+
+    status = STATUS_NO_MEMORY;
+    if (Path == NULL)
+        goto fail1;
+
+    status = RtlStringCbPrintfA(Path, 
+                                Length,
+                                "error/%s", 
+                                __FrontendGetPath(Frontend));
+    if (!NT_SUCCESS(status))
+        goto fail2;
+
+    (VOID) STORE(Printf,
+                 Frontend->StoreInterface,
+                 NULL,
+                 Path,
+                 "error",
+                 "UNPLUG FAILED: device is still in use");
+
+    __FrontendFree(Path);
+
+done:        
+    KeReleaseSpinLock(&Frontend->Lock, Irql);
+    return;
+
+fail2:
+    Error("fail2\n");
+
+    __FrontendFree(Path);
+
+fail1:
+    Error("fail1 (%08x)\n", status);
+
+    KeReleaseSpinLock(&Frontend->Lock, Irql);
+}
diff --git a/src/xenvif/frontend.h b/src/xenvif/frontend.h
new file mode 100644 (file)
index 0000000..99439b5
--- /dev/null
@@ -0,0 +1,182 @@
+/* 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 _XENVIF_FRONTEND_H
+#define _XENVIF_FRONTEND_H
+
+#include <ntddk.h>
+#include <evtchn_interface.h>
+#include <debug_interface.h>
+#include <store_interface.h>
+#include <gnttab_interface.h>
+#include <vif_interface.h>
+
+#include "pdo.h"
+#include "ethernet.h"
+
+typedef struct _XENVIF_FRONTEND XENVIF_FRONTEND, *PXENVIF_FRONTEND;
+
+typedef enum _XENVIF_FRONTEND_STATE {
+    FRONTEND_STATE_INVALID,
+    FRONTEND_CLOSED,
+    FRONTEND_PREPARED,
+    FRONTEND_CONNECTED,
+    FRONTEND_ENABLED
+} XENVIF_FRONTEND_STATE, *PXENVIF_FRONTEND_STATE;
+
+extern NTSTATUS
+FrontendInitialize(
+    IN  PXENVIF_PDO         Pdo,
+    OUT PXENVIF_FRONTEND    *Frontend
+    );
+
+extern VOID
+FrontendTeardown(
+    IN  PXENVIF_FRONTEND    Frontend
+    );
+
+extern VOID
+FrontendRemoveFailed(
+    IN PXENVIF_FRONTEND Frontend
+    );
+
+extern NTSTATUS
+FrontendSetState(
+    IN  PXENVIF_FRONTEND        Frontend,
+    IN  XENVIF_FRONTEND_STATE   State
+    );
+
+extern NTSTATUS
+FrontendResume(
+    IN  PXENVIF_FRONTEND    Frontend
+    );
+
+extern VOID
+FrontendSuspend(
+    IN  PXENVIF_FRONTEND    Frontend
+    );
+
+extern NTSTATUS
+FrontendGetNetworkAddress(
+    IN  PXENVIF_FRONTEND    Frontend,
+    OUT PCHAR               *Buffer
+    );
+
+extern NTSTATUS
+FrontendGetNetLuidIndex(
+    IN  PXENVIF_FRONTEND    Frontend,
+    OUT PULONG              LuidIndex
+    );
+
+extern PCHAR
+FrontendGetPrefix(
+    IN  PXENVIF_FRONTEND    Frontend
+    );
+
+extern PCHAR
+FrontendGetPath(
+    IN  PXENVIF_FRONTEND    Frontend
+    );
+
+extern PCHAR
+FrontendGetBackendPath(
+    IN  PXENVIF_FRONTEND    Frontend
+    );
+
+extern USHORT
+FrontendGetBackendDomain(
+    IN  PXENVIF_FRONTEND    Frontend
+    );
+
+#include "notifier.h"
+
+extern PXENVIF_NOTIFIER
+FrontendGetNotifier(
+    IN  PXENVIF_FRONTEND    Frontend
+    );
+
+#include "mac.h"
+
+extern PXENVIF_MAC
+FrontendGetMac(
+    IN  PXENVIF_FRONTEND    Frontend
+    );
+
+#include "receiver.h"
+
+extern PXENVIF_RECEIVER
+FrontendGetReceiver(
+    IN  PXENVIF_FRONTEND    Frontend
+    );
+
+#include "transmitter.h"
+
+extern PXENVIF_TRANSMITTER
+FrontendGetTransmitter(
+    IN  PXENVIF_FRONTEND    Frontend
+    );
+
+extern PXENBUS_EVTCHN_INTERFACE
+FrontendGetEvtchnInterface(
+    IN  PXENVIF_FRONTEND    Frontend
+    );
+
+extern PXENBUS_DEBUG_INTERFACE
+FrontendGetDebugInterface(
+    IN  PXENVIF_FRONTEND    Frontend
+    );
+
+extern PXENBUS_SUSPEND_INTERFACE
+FrontendGetSuspendInterface(
+    IN  PXENVIF_FRONTEND    Frontend
+    );
+
+extern PXENBUS_STORE_INTERFACE
+FrontendGetStoreInterface(
+    IN  PXENVIF_FRONTEND    Frontend
+    );
+
+extern PXENBUS_GNTTAB_INTERFACE
+FrontendGetGnttabInterface(
+    IN  PXENVIF_FRONTEND    Frontend
+    );
+
+extern PXENVIF_VIF_INTERFACE
+FrontendGetVifInterface(
+    IN  PXENVIF_FRONTEND    Frontend
+    );
+
+extern PCHAR
+FrontendGetAddress(
+    IN  PXENVIF_FRONTEND    Frontend
+    );
+
+#endif  // _XENVIF_FRONTEND_H
diff --git a/src/xenvif/log.h b/src/xenvif/log.h
new file mode 100644 (file)
index 0000000..42efa8b
--- /dev/null
@@ -0,0 +1,136 @@
+/* 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 _XENVIF_LOG_H
+#define _XENVIF_LOG_H
+
+#include <ntddk.h>
+#include <stdarg.h>
+
+#pragma warning(disable:4127)   // conditional expression is constant
+
+static __inline VOID
+__Error(
+    IN  const CHAR  *Prefix,
+    IN  const CHAR  *Format,
+    ...
+    )
+{
+    va_list         Arguments;
+
+    va_start(Arguments, Format);
+
+#pragma prefast(suppress:6001) // Using uninitialized memory
+    vDbgPrintExWithPrefix(Prefix,
+                          DPFLTR_IHVDRIVER_ID,
+                          DPFLTR_ERROR_LEVEL,
+                          Format,
+                          Arguments);
+    va_end(Arguments);
+}
+
+#define Error(...)  \
+        __Error(__MODULE__ "|" __FUNCTION__ ": ", __VA_ARGS__)
+
+static __inline VOID
+__Warning(
+    IN  const CHAR  *Prefix,
+    IN  const CHAR  *Format,
+    ...
+    )
+{
+    va_list         Arguments;
+
+    va_start(Arguments, Format);
+
+#pragma prefast(suppress:6001) // Using uninitialized memory
+    vDbgPrintExWithPrefix(Prefix,
+                          DPFLTR_IHVDRIVER_ID,
+                          DPFLTR_WARNING_LEVEL,
+                          Format,
+                          Arguments);
+    va_end(Arguments);
+}
+
+#define Warning(...)  \
+        __Warning(__MODULE__ "|" __FUNCTION__ ": ", __VA_ARGS__)
+
+#if DBG
+static __inline VOID
+__Trace(
+    IN  const CHAR  *Prefix,
+    IN  const CHAR  *Format,
+    ...
+    )
+{
+    va_list         Arguments;
+
+    va_start(Arguments, Format);
+
+#pragma prefast(suppress:6001) // Using uninitialized memory
+    vDbgPrintExWithPrefix(Prefix,
+                          DPFLTR_IHVDRIVER_ID,
+                          DPFLTR_TRACE_LEVEL,
+                          Format,
+                          Arguments);
+    va_end(Arguments);
+}
+
+#define Trace(...)  \
+        __Trace(__MODULE__ "|" __FUNCTION__ ": ", __VA_ARGS__)
+#else   // DBG
+#define Trace(...)  (VOID)(__VA_ARGS__)
+#endif  // DBG
+
+static __inline VOID
+__Info(
+    IN  const CHAR  *Prefix,
+    IN  const CHAR  *Format,
+    ...
+    )
+{
+    va_list         Arguments;
+
+    va_start(Arguments, Format);
+
+#pragma prefast(suppress:6001) // Using uninitialized memory
+    vDbgPrintExWithPrefix(Prefix,
+                          DPFLTR_IHVDRIVER_ID,
+                          DPFLTR_INFO_LEVEL,
+                          Format,
+                          Arguments);
+    va_end(Arguments);
+}
+
+#define Info(...)  \
+        __Info(__MODULE__ "|"  __FUNCTION__ ": ", __VA_ARGS__)
+
+#endif  // _XENVIF_LOG_H
diff --git a/src/xenvif/mac.c b/src/xenvif/mac.c
new file mode 100644 (file)
index 0000000..4583511
--- /dev/null
@@ -0,0 +1,875 @@
+/* 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 <ntstrsafe.h>
+#include <stdlib.h>
+#include <util.h>
+#include <ethernet.h>
+#include <store_interface.h>
+
+#include "pdo.h"
+#include "frontend.h"
+#include "mac.h"
+#include "thread.h"
+#include "log.h"
+#include "assert.h"
+
+struct _XENVIF_MAC {
+    PXENVIF_FRONTEND            Frontend;
+    KSPIN_LOCK                  Lock;
+    BOOLEAN                     Connected;
+    BOOLEAN                     Enabled;
+    KEVENT                      Event;
+    ULONG                       MaximumFrameSize;
+    ETHERNET_ADDRESS            PermanentAddress;
+    ETHERNET_ADDRESS            CurrentAddress;
+    ETHERNET_ADDRESS            BroadcastAddress;
+    ETHERNET_ADDRESS            MulticastAddress[MAXIMUM_MULTICAST_ADDRESS_COUNT];
+    ULONG                       MulticastAddressCount;
+    XENVIF_MAC_FILTER_LEVEL     FilterLevel[ETHERNET_ADDRESS_TYPE_COUNT];
+
+    PXENBUS_STORE_INTERFACE     StoreInterface;
+    PXENBUS_DEBUG_INTERFACE     DebugInterface;
+
+    PXENBUS_DEBUG_CALLBACK      DebugCallback;
+    PXENBUS_STORE_WATCH         Watch;
+};
+
+#define MAC_POOL    'CAM'
+
+static FORCEINLINE PVOID
+__MacAllocate(
+    IN  ULONG   Length
+    )
+{
+    return __AllocateNonPagedPoolWithTag(Length, MAC_POOL);
+}
+
+static FORCEINLINE VOID
+__MacFree(
+    IN  PVOID   Buffer
+    )
+{
+    __FreePoolWithTag(Buffer, MAC_POOL);
+}
+
+static FORCEINLINE NTSTATUS
+__MacParseNetworkAddress(
+    IN  PCHAR               Buffer,
+    OUT PETHERNET_ADDRESS   Address
+    )
+{
+    ULONG                   Length;
+    NTSTATUS                status;
+
+    Length = 0;
+    for (;;) {
+        CHAR    Character;
+        UCHAR   Byte;
+
+        Character = *Buffer++;
+        if (Character == '\0')
+            break;
+
+        if (Character >= '0' && Character <= '9')
+            Byte = Character - '0';
+        else if (Character >= 'A' && Character <= 'F')
+            Byte = 0x0A + Character - 'A';
+        else if (Character >= 'a' && Character <= 'f')
+            Byte = 0x0A + Character - 'a';
+        else
+            break;
+
+        Byte <<= 4;
+
+        Character = *Buffer++;
+        if (Character == '\0')
+            break;
+
+        if (Character >= '0' && Character <= '9')
+            Byte += Character - '0';
+        else if (Character >= 'A' && Character <= 'F')
+            Byte += 0x0A + Character - 'A';
+        else if (Character >= 'a' && Character <= 'f')
+            Byte += 0x0A + Character - 'a';
+        else
+            break;
+
+        Address->Byte[Length++] = Byte;
+
+        // Skip over any separator
+        if (*Buffer == ':' || *Buffer == '-')
+            Buffer++;
+    }
+
+    status = STATUS_INVALID_PARAMETER;
+    if (Length != ETHERNET_ADDRESS_LENGTH)
+        goto fail1;
+
+    return STATUS_SUCCESS;
+
+fail1:
+    Error("fail1 (%08x)\n", status);
+
+    return status;
+}
+
+static FORCEINLINE VOID
+__MacSetPermanentAddress(
+    IN  PXENVIF_MAC         Mac,
+    IN  PETHERNET_ADDRESS   Address
+    )
+{
+    PXENVIF_FRONTEND        Frontend;
+
+    ASSERT(!(Address->Byte[0] & 0x01));
+
+    Frontend = Mac->Frontend;
+
+    Mac->PermanentAddress = *Address;
+
+    STORE(Printf,
+          Mac->StoreInterface,
+          NULL,
+          FrontendGetPrefix(Frontend),
+          "mac/unicast/permanent",
+          "%02x:%02x:%02x:%02x:%02x:%02x",
+          Mac->PermanentAddress.Byte[0],
+          Mac->PermanentAddress.Byte[1],
+          Mac->PermanentAddress.Byte[2],
+          Mac->PermanentAddress.Byte[3],
+          Mac->PermanentAddress.Byte[4],
+          Mac->PermanentAddress.Byte[5]);
+}
+
+static FORCEINLINE PETHERNET_ADDRESS
+__MacGetPermanentAddress(
+    IN  PXENVIF_MAC         Mac
+    )
+{
+    return &Mac->PermanentAddress;
+}
+
+PETHERNET_ADDRESS
+MacGetPermanentAddress(
+    IN  PXENVIF_MAC         Mac
+    )
+{
+    return __MacGetPermanentAddress(Mac);
+}
+
+static FORCEINLINE VOID
+__MacSetCurrentAddress(
+    IN  PXENVIF_MAC         Mac,
+    IN  PETHERNET_ADDRESS   Address
+    )
+{
+    PXENVIF_FRONTEND        Frontend;
+
+    ASSERT(!(Address->Byte[0] & 0x01));
+
+    Frontend = Mac->Frontend;
+
+    Mac->CurrentAddress = *Address;
+
+    STORE(Printf,
+          Mac->StoreInterface,
+          NULL,
+          FrontendGetPrefix(Frontend),
+          "mac/unicast/current",
+          "%02x:%02x:%02x:%02x:%02x:%02x",
+          Mac->CurrentAddress.Byte[0],
+          Mac->CurrentAddress.Byte[1],
+          Mac->CurrentAddress.Byte[2],
+          Mac->CurrentAddress.Byte[3],
+          Mac->CurrentAddress.Byte[4],
+          Mac->CurrentAddress.Byte[5]);
+}
+
+static FORCEINLINE PETHERNET_ADDRESS
+__MacGetCurrentAddress(
+    IN  PXENVIF_MAC         Mac
+    )
+{
+    return &Mac->CurrentAddress;
+}
+
+PETHERNET_ADDRESS
+MacGetCurrentAddress(
+    IN  PXENVIF_MAC         Mac
+    )
+{
+    return __MacGetCurrentAddress(Mac);
+}
+
+static VOID
+MacDebugCallback(
+    IN  PVOID   Argument,
+    IN  BOOLEAN Crashing
+    )
+{
+    PXENVIF_MAC Mac = Argument;
+    ULONG       Index;
+
+    UNREFERENCED_PARAMETER(Crashing);
+
+    DEBUG(Printf,
+          Mac->DebugInterface,
+          Mac->DebugCallback,
+          "MaximumFrameSize = %u\n",
+          Mac->MaximumFrameSize);
+
+    DEBUG(Printf,
+          Mac->DebugInterface,
+          Mac->DebugCallback,
+          "PermanentAddress = %02x:%02x:%02x:%02x:%02x:%02x\n",
+          Mac->PermanentAddress.Byte[0],
+          Mac->PermanentAddress.Byte[1],
+          Mac->PermanentAddress.Byte[2],
+          Mac->PermanentAddress.Byte[3],
+          Mac->PermanentAddress.Byte[4],
+          Mac->PermanentAddress.Byte[5]);
+
+    DEBUG(Printf,
+          Mac->DebugInterface,
+          Mac->DebugCallback,
+          "CurrentAddress = %02x:%02x:%02x:%02x:%02x:%02x\n",
+          Mac->CurrentAddress.Byte[0],
+          Mac->CurrentAddress.Byte[1],
+          Mac->CurrentAddress.Byte[2],
+          Mac->CurrentAddress.Byte[3],
+          Mac->CurrentAddress.Byte[4],
+          Mac->CurrentAddress.Byte[5]);
+
+    for (Index = 0; Index < Mac->MulticastAddressCount; Index++)
+        DEBUG(Printf,
+              Mac->DebugInterface,
+              Mac->DebugCallback,
+              "MulticastAddress[%u] = %02x:%02x:%02x:%02x:%02x:%02x\n",
+              Index,
+              Mac->MulticastAddress[Index].Byte[0],
+              Mac->MulticastAddress[Index].Byte[1],
+              Mac->MulticastAddress[Index].Byte[2],
+              Mac->MulticastAddress[Index].Byte[3],
+              Mac->MulticastAddress[Index].Byte[4],
+              Mac->MulticastAddress[Index].Byte[5]);
+
+    DEBUG(Printf,
+          Mac->DebugInterface,
+          Mac->DebugCallback,
+          "FilterLevel[ETHERNET_ADDRESS_UNICAST] = %s\n",
+          (Mac->FilterLevel[ETHERNET_ADDRESS_UNICAST] == MAC_FILTER_ALL) ? "All" :
+          (Mac->FilterLevel[ETHERNET_ADDRESS_UNICAST] == MAC_FILTER_MATCHING) ? "Matching" :
+          "None");
+
+    DEBUG(Printf,
+          Mac->DebugInterface,
+          Mac->DebugCallback,
+          "FilterLevel[ETHERNET_ADDRESS_MULTICAST] = %s\n",
+          (Mac->FilterLevel[ETHERNET_ADDRESS_MULTICAST] == MAC_FILTER_ALL) ? "All" :
+          (Mac->FilterLevel[ETHERNET_ADDRESS_MULTICAST] == MAC_FILTER_MATCHING) ? "Matching" :
+          "None");
+
+    DEBUG(Printf,
+          Mac->DebugInterface,
+          Mac->DebugCallback,
+          "FilterLevel[ETHERNET_ADDRESS_BROADCAST] = %s\n",
+          (Mac->FilterLevel[ETHERNET_ADDRESS_BROADCAST] == MAC_FILTER_ALL) ? "All" :
+          (Mac->FilterLevel[ETHERNET_ADDRESS_BROADCAST] == MAC_FILTER_MATCHING) ? "Matching" :
+          "None");
+}
+
+NTSTATUS
+MacInitialize(
+    IN  PXENVIF_FRONTEND    Frontend,
+    OUT PXENVIF_MAC         *Mac
+    )
+{
+    NTSTATUS                status;
+
+    *Mac = __MacAllocate(sizeof (XENVIF_MAC));
+
+    status = STATUS_NO_MEMORY;
+    if (*Mac == NULL)
+        goto fail1;
+
+    (*Mac)->Frontend = Frontend;
+
+    KeInitializeSpinLock(&(*Mac)->Lock);
+    KeInitializeEvent(&(*Mac)->Event, NotificationEvent, FALSE);
+
+    return STATUS_SUCCESS;
+
+fail1:
+    Error("fail1 (%08x)\n");
+
+    return status;
+}
+
+NTSTATUS
+MacConnect(
+    IN  PXENVIF_MAC     Mac
+    )
+{
+    PXENVIF_FRONTEND    Frontend;
+    PCHAR               Buffer;
+    ULONG64             Mtu;
+    ETHERNET_ADDRESS    Address;
+    NTSTATUS            status;
+
+    Frontend = Mac->Frontend;
+
+    Mac->StoreInterface = FrontendGetStoreInterface(Frontend);
+
+    STORE(Acquire, Mac->StoreInterface);
+
+    status = STORE(Read,
+                   Mac->StoreInterface,
+                   NULL,
+                   FrontendGetPath(Frontend),
+                   "mtu",
+                   &Buffer);
+    if (!NT_SUCCESS(status)) {
+        Mtu = ETHERNET_MTU;
+    } else {
+        Mtu = strtol(Buffer, NULL, 10);
+
+        STORE(Free,
+              Mac->StoreInterface,
+              Buffer);
+
+        Buffer = NULL;
+    }
+
+    status = STATUS_INVALID_PARAMETER;
+    if (Mtu < ETHERNET_MIN)
+        goto fail1;
+
+    Mac->MaximumFrameSize = (ULONG)Mtu + sizeof (ETHERNET_UNTAGGED_HEADER);
+
+    status = STORE(Read,
+                   Mac->StoreInterface,
+                   NULL,
+                   FrontendGetPath(Frontend),
+                   "mac",
+                   &Buffer);
+    if (!NT_SUCCESS(status))
+        goto fail2;
+
+    status = __MacParseNetworkAddress(Buffer, &Address);
+    if (!NT_SUCCESS(status))
+        goto fail3;
+
+    STORE(Free,
+          Mac->StoreInterface,
+          Buffer);
+
+    Buffer = NULL;
+
+    __MacSetPermanentAddress(Mac, &Address);
+
+    Buffer = FrontendGetAddress(Frontend);
+    if (Buffer != NULL) {
+        status = __MacParseNetworkAddress(Buffer, &Address);
+        if (!NT_SUCCESS(status))
+            goto fail4;
+
+        if (Address.Byte[0] & 0x01)
+            Address = *__MacGetPermanentAddress(Mac);
+    } else {
+        Address = *__MacGetPermanentAddress(Mac);
+    }
+
+    __MacSetCurrentAddress(Mac, &Address);
+
+    RtlFillMemory(Mac->BroadcastAddress.Byte, ETHERNET_ADDRESS_LENGTH, 0xFF);
+
+    Mac->DebugInterface = FrontendGetDebugInterface(Frontend);
+
+    DEBUG(Acquire, Mac->DebugInterface);
+
+    status = DEBUG(Register,
+                   Mac->DebugInterface,
+                   __MODULE__ "|MAC",
+                   MacDebugCallback,
+                   Mac,
+                   &Mac->DebugCallback);
+    if (!NT_SUCCESS(status))
+        goto fail5;
+
+    ASSERT(!Mac->Connected);
+    Mac->Connected = TRUE;
+
+    return STATUS_SUCCESS;
+
+fail5:
+    Error("fail5\n");
+
+    DEBUG(Release, Mac->DebugInterface);
+    Mac->DebugInterface = NULL;
+
+    RtlZeroMemory(Mac->BroadcastAddress.Byte, ETHERNET_ADDRESS_LENGTH);
+    RtlZeroMemory(Mac->CurrentAddress.Byte, ETHERNET_ADDRESS_LENGTH);
+
+fail4:
+    Error("fail4\n");
+
+    Buffer = NULL;
+
+    RtlZeroMemory(Mac->PermanentAddress.Byte, ETHERNET_ADDRESS_LENGTH);
+
+fail3:
+    Error("fail3\n");
+
+    if (Buffer != NULL) {
+        STORE(Free,
+              Mac->StoreInterface,
+              Buffer);
+
+        Buffer = NULL;
+    }
+
+fail2:
+    Error("fail2\n");
+
+    Mac->MaximumFrameSize = 0;
+
+fail1:
+    Error("fail1 (%08x)\n");
+
+    STORE(Release, Mac->StoreInterface);
+    Mac->StoreInterface = 0;
+
+    return status;
+}
+
+NTSTATUS
+MacEnable(
+    IN  PXENVIF_MAC     Mac
+    )
+{
+    PXENVIF_FRONTEND    Frontend;
+    NTSTATUS            status;
+
+    Frontend = Mac->Frontend;
+
+    ASSERT3U(KeGetCurrentIrql(), ==, DISPATCH_LEVEL);
+    KeAcquireSpinLockAtDpcLevel(&Mac->Lock);
+
+    status = STORE(Watch,
+                   Mac->StoreInterface,
+                   FrontendGetPath(Frontend),
+                   "disconnect",
+                   &Mac->Event,
+                   &Mac->Watch);
+    if (!NT_SUCCESS(status))
+        goto fail1;
+
+    ASSERT(!Mac->Enabled);
+    Mac->Enabled = TRUE;
+
+    KeReleaseSpinLockFromDpcLevel(&Mac->Lock);
+
+    return STATUS_SUCCESS;
+
+fail1:
+    Error("fail1 (%08x)\n");
+
+    KeReleaseSpinLockFromDpcLevel(&Mac->Lock);
+
+    return status;
+}
+
+VOID
+MacDisable(
+    IN  PXENVIF_MAC     Mac
+    )
+{
+    ASSERT3U(KeGetCurrentIrql(), ==, DISPATCH_LEVEL);
+    KeAcquireSpinLockAtDpcLevel(&Mac->Lock);
+
+    ASSERT(Mac->Enabled);
+    Mac->Enabled = FALSE;
+
+    (VOID) STORE(Unwatch,
+                 Mac->StoreInterface,
+                 Mac->Watch);
+    Mac->Watch = NULL;
+
+    KeReleaseSpinLockFromDpcLevel(&Mac->Lock);
+}
+
+VOID
+MacDisconnect(
+    IN  PXENVIF_MAC     Mac
+    )
+{
+    ASSERT(Mac->Connected);
+    Mac->Connected = FALSE;
+
+    DEBUG(Deregister,
+          Mac->DebugInterface,
+          Mac->DebugCallback);
+    Mac->DebugCallback = NULL;
+
+    DEBUG(Release, Mac->DebugInterface);
+    Mac->DebugInterface = NULL;
+
+    RtlZeroMemory(Mac->BroadcastAddress.Byte, ETHERNET_ADDRESS_LENGTH);
+    RtlZeroMemory(Mac->CurrentAddress.Byte, ETHERNET_ADDRESS_LENGTH);
+    RtlZeroMemory(Mac->PermanentAddress.Byte, ETHERNET_ADDRESS_LENGTH);
+
+    Mac->MaximumFrameSize = 0;
+
+    STORE(Release, Mac->StoreInterface);
+    Mac->StoreInterface = 0;
+}
+
+VOID
+MacTeardown(
+    IN  PXENVIF_MAC     Mac
+    )
+{
+    RtlZeroMemory(Mac->MulticastAddress,
+                  MAXIMUM_MULTICAST_ADDRESS_COUNT * sizeof (ETHERNET_ADDRESS));
+    Mac->MulticastAddressCount = 0;
+
+    RtlZeroMemory(&Mac->FilterLevel,
+                  ETHERNET_ADDRESS_TYPE_COUNT * sizeof (XENVIF_MAC_FILTER_LEVEL));
+
+    RtlZeroMemory(&Mac->Event, sizeof (KEVENT));
+    RtlZeroMemory(&Mac->Lock, sizeof (KSPIN_LOCK));
+
+    Mac->Frontend = NULL;
+
+    ASSERT(IsZeroMemory(Mac, sizeof (XENVIF_MAC)));
+    __MacFree(Mac);
+}
+
+ULONG
+MacGetLinkSpeed(
+    IN  PXENVIF_MAC     Mac
+    )
+{
+    PCHAR               Buffer;
+    ULONG               Speed;
+    NTSTATUS            status;
+
+    status = STORE(Read,
+                   Mac->StoreInterface,
+                   NULL,
+                   FrontendGetPath(Mac->Frontend),
+                   "speed",
+                   &Buffer);
+    if (!NT_SUCCESS(status)) {
+        Speed = 1;
+    } else {
+        Speed = (ULONG)strtol(Buffer, NULL, 10);
+
+        STORE(Free,
+              Mac->StoreInterface,
+              Buffer);
+    }
+
+    return Speed;
+}
+
+PKEVENT
+MacGetEvent(
+    IN  PXENVIF_MAC     Mac
+    )
+{
+    return &Mac->Event;
+}
+
+BOOLEAN
+MacGetLinkState(
+    IN  PXENVIF_MAC     Mac
+    )
+{
+    PCHAR               Buffer;
+    BOOLEAN             Disconnect;
+    NTSTATUS            status;
+
+    status = STORE(Read,
+                   Mac->StoreInterface,
+                   NULL,
+                   FrontendGetPath(Mac->Frontend),
+                   "disconnect",
+                   &Buffer);
+    if (!NT_SUCCESS(status)) {
+        Disconnect = FALSE;
+    } else {
+        Disconnect = (BOOLEAN)strtol(Buffer, NULL, 2);
+
+        STORE(Free,
+              Mac->StoreInterface,
+              Buffer);
+    }
+
+    return !Disconnect;
+}
+
+ULONG
+MacGetMaximumFrameSize(
+    IN  PXENVIF_MAC Mac
+    )
+{
+    return Mac->MaximumFrameSize;
+}
+
+NTSTATUS
+MacSetCurrentAddress(
+    IN  PXENVIF_MAC         Mac,
+    IN  PETHERNET_ADDRESS   Address
+    )
+{
+    PXENVIF_FRONTEND        Frontend;
+    KIRQL                   Irql;
+    NTSTATUS                status;
+
+    Frontend = Mac->Frontend;
+
+    KeAcquireSpinLock(&Mac->Lock, &Irql);
+
+    status = STATUS_INVALID_PARAMETER;
+    if (Address->Byte[0] & 0x01)
+        goto fail1;
+
+    __MacSetCurrentAddress(Mac, Address);
+
+    KeReleaseSpinLock(&Mac->Lock, Irql);
+
+    return STATUS_SUCCESS;
+
+fail1:
+    Error("fail1 (%08x)\n", status);
+
+    KeReleaseSpinLock(&Mac->Lock, Irql);
+
+    return status;
+}
+
+NTSTATUS
+MacSetMulticastAddresses(
+    IN  PXENVIF_MAC         Mac,
+    IN  ETHERNET_ADDRESS    Address[],
+    IN  ULONG               Count
+    )
+{
+    KIRQL                   Irql;
+    ULONG                   Index;
+    NTSTATUS                status;
+
+    KeAcquireSpinLock(&Mac->Lock, &Irql);
+
+    status = STATUS_BUFFER_OVERFLOW;
+    if (Count > MAXIMUM_MULTICAST_ADDRESS_COUNT)
+        goto fail1;
+
+    for (Index = 0; Index < Count; Index++) {
+        if (!(Address[Index].Byte[0] & 0x01))
+            goto fail2;
+    }
+
+    for (Index = 0; Index < Count; Index++)
+        Mac->MulticastAddress[Index] = Address[Index];
+
+    Mac->MulticastAddressCount = Count;
+
+    KeReleaseSpinLock(&Mac->Lock, Irql);
+
+    return STATUS_SUCCESS;
+
+fail2:
+    Error("fail2\n");
+
+fail1:
+    Error("fail1 (%08x)\n", status);
+
+    KeReleaseSpinLock(&Mac->Lock, Irql);
+
+    return status;
+}
+
+PETHERNET_ADDRESS
+MacGetMulticastAddresses(
+    IN      PXENVIF_MAC         Mac,
+    IN OUT  PULONG              Count
+    )
+{
+    *Count = Mac->MulticastAddressCount;
+
+    return Mac->MulticastAddress;
+}
+
+PETHERNET_ADDRESS
+MacGetBroadcastAddress(
+    IN  PXENVIF_MAC         Mac
+    )
+{
+    return &Mac->BroadcastAddress;
+}
+
+NTSTATUS
+MacSetFilterLevel(
+    IN  PXENVIF_MAC             Mac,
+    IN  ETHERNET_ADDRESS_TYPE   Type,
+    IN  XENVIF_MAC_FILTER_LEVEL Level
+    )
+{
+    KIRQL                       Irql;
+    NTSTATUS                    status;
+
+    ASSERT3U(Type, <, ETHERNET_ADDRESS_TYPE_COUNT);
+
+    KeAcquireSpinLock(&Mac->Lock, &Irql);
+
+    status = STATUS_INVALID_PARAMETER;
+    if (Level > MAC_FILTER_ALL || Level < MAC_FILTER_NONE)
+        goto fail1;
+
+    Mac->FilterLevel[Type] = Level;
+    KeReleaseSpinLock(&Mac->Lock, Irql);
+
+    return STATUS_SUCCESS;
+
+fail1:
+    Error("fail1 (%08x)\n", status);
+
+    KeReleaseSpinLock(&Mac->Lock, Irql);
+
+    return status;
+}
+
+XENVIF_MAC_FILTER_LEVEL
+MacGetFilterLevel(
+    IN  PXENVIF_MAC             Mac,
+    IN  ETHERNET_ADDRESS_TYPE   Type
+    )
+{
+    ASSERT3U(Type, <, ETHERNET_ADDRESS_TYPE_COUNT);
+
+    return Mac->FilterLevel[Type];
+}
+
+BOOLEAN
+MacApplyFilters(
+    IN  PXENVIF_MAC             Mac,
+    IN  PETHERNET_ADDRESS       DestinationAddress
+    )
+{
+    ETHERNET_ADDRESS_TYPE       Type;
+    BOOLEAN                     Allow;
+
+    Type = GET_ETHERNET_ADDRESS_TYPE(DestinationAddress);
+    Allow = FALSE;
+
+    ASSERT3U(KeGetCurrentIrql(), ==, DISPATCH_LEVEL);
+    KeAcquireSpinLockAtDpcLevel(&Mac->Lock);
+
+    switch (Type) {
+    case ETHERNET_ADDRESS_UNICAST:
+        switch (Mac->FilterLevel[ETHERNET_ADDRESS_UNICAST]) {
+        case MAC_FILTER_NONE:
+            break;
+
+        case MAC_FILTER_MATCHING:
+            if (RtlEqualMemory(&Mac->CurrentAddress,
+                               DestinationAddress,
+                               ETHERNET_ADDRESS_LENGTH))
+                Allow = TRUE;
+
+            break;
+
+        case MAC_FILTER_ALL:
+            Allow = TRUE;
+            break;
+
+        default:
+            ASSERT(FALSE);
+            break;
+        }
+        break;
+
+    case ETHERNET_ADDRESS_MULTICAST:
+        switch (Mac->FilterLevel[ETHERNET_ADDRESS_MULTICAST]) {
+        case MAC_FILTER_NONE:
+            break;
+
+        case MAC_FILTER_MATCHING: {
+            ULONG Index;
+
+            for (Index = 0; Index < Mac->MulticastAddressCount; Index++) {
+                if (RtlEqualMemory(&Mac->MulticastAddress[Index],
+                                   DestinationAddress,
+                                   ETHERNET_ADDRESS_LENGTH))
+                    Allow = TRUE;
+            }
+            break;
+        }
+
+        case MAC_FILTER_ALL:
+            Allow = TRUE;
+            break;
+
+        default:
+            ASSERT(FALSE);
+            break;
+        }
+        break;
+
+    case ETHERNET_ADDRESS_BROADCAST:
+        switch (Mac->FilterLevel[ETHERNET_ADDRESS_BROADCAST]) {
+        case MAC_FILTER_NONE:
+            break;
+
+        case MAC_FILTER_MATCHING:
+        case MAC_FILTER_ALL:
+            Allow = TRUE;
+            break;
+
+        default:
+            ASSERT(FALSE);
+            break;
+        }
+        break;
+
+    default:
+        ASSERT(FALSE);
+        break;
+    }
+
+    KeReleaseSpinLockFromDpcLevel(&Mac->Lock);
+
+    return Allow;
+}
diff --git a/src/xenvif/mac.h b/src/xenvif/mac.h
new file mode 100644 (file)
index 0000000..d9d26f7
--- /dev/null
@@ -0,0 +1,147 @@
+/* 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 _XENVIF_MAC_H
+#define _XENVIF_MAC_H
+
+#include <ntddk.h>
+#include <ethernet.h>
+#include <vif_interface.h>
+
+#include "frontend.h"
+
+typedef struct _XENVIF_MAC XENVIF_MAC, *PXENVIF_MAC;
+
+extern NTSTATUS
+MacInitialize(
+    IN  PXENVIF_FRONTEND    Frontend,
+    OUT PXENVIF_MAC         *Mac
+    );
+
+extern NTSTATUS
+MacConnect(
+    IN  PXENVIF_MAC    Mac
+    );
+
+extern NTSTATUS
+MacEnable(
+    IN  PXENVIF_MAC    Mac
+    );
+
+extern VOID
+MacDisable(
+    IN  PXENVIF_MAC    Mac
+    );
+
+extern VOID
+MacDisconnect(
+    IN  PXENVIF_MAC    Mac
+    );
+
+extern VOID
+MacTeardown(
+    IN  PXENVIF_MAC    Mac
+    );
+
+extern PKEVENT
+MacGetEvent(
+    IN  PXENVIF_MAC     Mac
+    );
+
+extern ULONG
+MacGetLinkSpeed(
+    IN  PXENVIF_MAC     Mac
+    );
+
+extern BOOLEAN
+MacGetLinkState(
+    IN  PXENVIF_MAC     Mac
+    );
+
+extern ULONG
+MacGetMaximumFrameSize(
+    IN  PXENVIF_MAC Mac
+    );
+
+extern PETHERNET_ADDRESS
+MacGetPermanentAddress(
+    IN  PXENVIF_MAC         Mac
+    );
+
+extern PETHERNET_ADDRESS
+MacGetCurrentAddress(
+    IN  PXENVIF_MAC         Mac
+    );
+
+extern NTSTATUS
+MacSetCurrentAddress(
+    IN  PXENVIF_MAC         Mac,
+    IN  PETHERNET_ADDRESS   Address
+    );
+
+extern PETHERNET_ADDRESS
+MacGetMulticastAddresses(
+    IN  PXENVIF_MAC         Mac,
+    OUT PULONG              Count
+    );
+
+extern NTSTATUS
+MacSetMulticastAddresses(
+    IN  PXENVIF_MAC         Mac,
+    IN  ETHERNET_ADDRESS    Address[],
+    IN  ULONG               Count
+    );
+
+extern PETHERNET_ADDRESS
+MacGetBroadcastAddress(
+    IN  PXENVIF_MAC         Mac
+    );
+
+extern NTSTATUS
+MacSetFilterLevel(
+    IN  PXENVIF_MAC             Mac,
+    IN  ETHERNET_ADDRESS_TYPE   Type,
+    IN  XENVIF_MAC_FILTER_LEVEL Level
+    );
+
+extern XENVIF_MAC_FILTER_LEVEL
+MacGetFilterLevel(
+    IN  PXENVIF_MAC             Mac,
+    IN  ETHERNET_ADDRESS_TYPE   Type
+    );
+
+extern BOOLEAN
+MacApplyFilters(
+    IN  PXENVIF_MAC         Mac,
+    IN  PETHERNET_ADDRESS   DestinationAddress
+    );
+
+#endif  // _XENVIF_MAC_H
diff --git a/src/xenvif/mrsw.h b/src/xenvif/mrsw.h
new file mode 100644 (file)
index 0000000..1e549c4
--- /dev/null
@@ -0,0 +1,287 @@
+/* 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 _XENVIF_MRSW_H
+#define _XENVIF_MRSW_H
+
+#include <ntddk.h>
+
+#include "assert.h"
+
+#pragma warning(disable:4127)   // conditional expression is constant
+
+typedef struct _XENVIF_MRSW_HOLDER {
+    PKTHREAD    Thread;
+    LONG        Level;
+} XENVIF_MRSW_HOLDER, *PXENVIF_MRSW_HOLDER;
+
+typedef struct _XENVIF_MRSW_LOCK {
+    volatile LONG64 Mask;
+    XENVIF_MRSW_HOLDER     Holder[64];
+    KEVENT          Event;
+} XENVIF_MRSW_LOCK, *PXENVIF_MRSW_LOCK;
+
+C_ASSERT(RTL_FIELD_SIZE(XENVIF_MRSW_LOCK, Holder) == RTL_FIELD_SIZE(XENVIF_MRSW_LOCK, Mask) * 8 * sizeof (XENVIF_MRSW_HOLDER));
+
+#define XENVIF_MRSW_EXCLUSIVE_SLOT  0
+
+static FORCEINLINE VOID
+InitializeMrswLock(
+    IN  PXENVIF_MRSW_LOCK   Lock
+    )
+{
+    LONG                    Slot;
+
+    RtlZeroMemory(Lock, sizeof (XENVIF_MRSW_LOCK));
+
+    for (Slot = 0; Slot < sizeof (Lock->Mask) * 8; Slot++)
+        Lock->Holder[Slot].Level = -1;
+
+    KeInitializeEvent(&Lock->Event, NotificationEvent, FALSE);
+}
+
+static FORCEINLINE BOOLEAN
+__ClaimExclusive(
+    IN  PXENVIF_MRSW_LOCK   Lock
+    )
+{
+    LONG64                  Old;
+    LONG64                  New;
+
+    Old = 0;
+    New = 1ll << XENVIF_MRSW_EXCLUSIVE_SLOT;
+
+    return (InterlockedCompareExchange64(&Lock->Mask, New, Old) == Old) ? TRUE : FALSE;
+}
+
+static FORCEINLINE KIRQL
+__drv_maxIRQL(APC_LEVEL)
+__drv_raisesIRQL(DISPATCH_LEVEL)
+__drv_savesIRQL
+__AcquireMrswLockExclusive(
+    IN  PXENVIF_MRSW_LOCK   Lock
+    )
+{
+    KIRQL                   Irql;
+    LONG                    Slot;
+    PKTHREAD                Self;
+    PXENVIF_MRSW_HOLDER     Holder;
+
+    ASSERT3U(KeGetCurrentIrql(), <, DISPATCH_LEVEL);
+    KeRaiseIrql(DISPATCH_LEVEL, &Irql);
+
+    Self = KeGetCurrentThread();
+
+    // Make sure we do not already hold the lock
+    for (Slot = 0; Slot < sizeof (Lock->Mask) * 8; Slot++)
+        ASSERT(Lock->Holder[Slot].Thread != Self);
+
+    for (;;) {
+        if (__ClaimExclusive(Lock))
+            break;
+
+        KeLowerIrql(Irql);
+
+        (VOID) KeWaitForSingleObject(&Lock->Event,
+                                     Executive,
+                                     KernelMode,
+                                     FALSE,
+                                     NULL);
+        KeClearEvent(&Lock->Event);
+
+        KeRaiseIrql(DISPATCH_LEVEL, &Irql);
+    }
+
+    Holder = &Lock->Holder[XENVIF_MRSW_EXCLUSIVE_SLOT];
+
+    ASSERT3P(Holder->Thread, ==, NULL);
+    Holder->Thread = Self;
+    Holder->Level = 0;
+
+    return Irql;
+}
+
+#define AcquireMrswLockExclusive(_Lock, _Irql)              \
+        do {                                                \
+            *(_Irql) = __AcquireMrswLockExclusive(_Lock);   \
+        } while (FALSE)
+
+static FORCEINLINE VOID
+__drv_maxIRQL(DISPATCH_LEVEL)
+__drv_requiresIRQL(DISPATCH_LEVEL)
+ReleaseMrswLockExclusive(
+    IN  PXENVIF_MRSW_LOCK           Lock,
+    IN  __drv_restoresIRQL KIRQL    Irql,
+    IN  BOOLEAN                     Shared
+    )
+{
+    LONG                            Slot;
+    PKTHREAD                        Self;
+    LONG64                          Old;
+    LONG64                          New;
+    PXENVIF_MRSW_HOLDER             Holder;
+
+    ASSERT3U(KeGetCurrentIrql(), ==, DISPATCH_LEVEL);
+
+    Slot = XENVIF_MRSW_EXCLUSIVE_SLOT + 1; // Choose any slot other than the exclusive slot
+
+    Old = 1ll << XENVIF_MRSW_EXCLUSIVE_SLOT;
+    New = (Shared) ? (1ll << Slot) : 0;
+
+    Old = InterlockedCompareExchange64(&Lock->Mask, New, Old);
+    ASSERT3U(Old, == , 1ll << XENVIF_MRSW_EXCLUSIVE_SLOT);
+
+    Self = KeGetCurrentThread();
+
+    ASSERT3P(Lock->Holder[XENVIF_MRSW_EXCLUSIVE_SLOT].Thread, ==, Self);
+
+    // If we are leaving the lock held shared then we need to transfer
+    // our identity information into the hew slot.
+    if (Shared)
+        Lock->Holder[Slot] = Lock->Holder[XENVIF_MRSW_EXCLUSIVE_SLOT];
+
+    Holder = &Lock->Holder[XENVIF_MRSW_EXCLUSIVE_SLOT];
+
+    Holder->Thread = NULL;
+    Holder->Level = -1;
+
+    KeLowerIrql(Irql);
+}
+
+static FORCEINLINE LONG
+__ClaimShared(
+    IN  PXENVIF_MRSW_LOCK   Lock
+    )
+{
+    LONG                    Slot;
+    LONG64                  Old;
+    LONG64                  New;
+
+    // Make sure the exclusive bit is set so that we don't find it
+    Old = Lock->Mask | (1ll << XENVIF_MRSW_EXCLUSIVE_SLOT);
+
+    Slot = __ffu((ULONG64)Old);
+    ASSERT(Slot >= 0);
+    ASSERT3U(Slot, != , XENVIF_MRSW_EXCLUSIVE_SLOT);
+
+    Old &= ~(1ll << XENVIF_MRSW_EXCLUSIVE_SLOT);
+    New = Old | (1ll << Slot);
+
+    return (InterlockedCompareExchange64(&Lock->Mask, New, Old) == Old) ? Slot : -1;
+}
+
+static FORCEINLINE VOID
+AcquireMrswLockShared(
+    IN  PXENVIF_MRSW_LOCK   Lock
+    )
+{
+    KIRQL                   Irql;
+    LONG                    Level;
+    LONG                    Slot;
+    PKTHREAD                Self;
+    PXENVIF_MRSW_HOLDER     Holder;
+
+    ASSERT3U(KeGetCurrentIrql(), <=, DISPATCH_LEVEL);
+    KeRaiseIrql(DISPATCH_LEVEL, &Irql);
+
+    Self = KeGetCurrentThread();
+
+    // Do we already hold the lock? If so, get the nesting level
+    Level = -1;
+    for (Slot = 0; Slot < sizeof (Lock->Mask) * 8; Slot++) {
+        if (Lock->Holder[Slot].Thread == Self && Lock->Holder[Slot].Level > Level)
+            Level = Lock->Holder[Slot].Level;
+    }
+    Level++;
+
+    for (;;) {
+        Slot = __ClaimShared(Lock);
+        if (Slot >= 0)
+            break;
+
+        _mm_pause();
+    }
+
+    Holder = &Lock->Holder[Slot];
+
+    Holder->Thread = Self;
+    Holder->Level = Level;
+
+    KeLowerIrql(Irql);
+}
+
+static FORCEINLINE VOID
+ReleaseMrswLockShared(
+    IN  PXENVIF_MRSW_LOCK   Lock
+    )
+{
+    KIRQL                   Irql;
+    PKTHREAD                Self;
+    LONG                    Level;
+    LONG                    Deepest;
+    LONG                    Slot;
+    LONG64                  Old;
+    LONG64                  New;
+    PXENVIF_MRSW_HOLDER     Holder;
+
+    ASSERT3U(KeGetCurrentIrql(), <=, DISPATCH_LEVEL);
+    KeRaiseIrql(DISPATCH_LEVEL, &Irql);
+
+    Self = KeGetCurrentThread();
+
+    Level = -1;
+    Deepest = -1;
+    for (Slot = 0; Slot < sizeof (Lock->Mask) * 8; Slot++) {
+        if (Lock->Holder[Slot].Thread == Self && Lock->Holder[Slot].Level > Level) {
+            Level = Lock->Holder[Slot].Level;
+            Deepest = Slot;
+        }
+    }
+    ASSERT(Level >= 0);
+
+    Slot = Deepest;
+    ASSERT3U(Slot, !=, XENVIF_MRSW_EXCLUSIVE_SLOT);
+
+    Holder = &Lock->Holder[Slot];
+
+    Holder->Thread = NULL;
+    Holder->Level = -1;
+
+    do {
+        Old = Lock->Mask;
+        New = Old & ~(1ll << Slot);
+    } while (InterlockedCompareExchange64(&Lock->Mask, New, Old) != Old);
+
+    KeSetEvent(&Lock->Event, IO_NO_INCREMENT, FALSE);
+    KeLowerIrql(Irql);
+}
+
+#endif  // _XENVIF_MRSW_H
diff --git a/src/xenvif/mutex.h b/src/xenvif/mutex.h
new file mode 100644 (file)
index 0000000..3556cf4
--- /dev/null
@@ -0,0 +1,82 @@
+/* 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 _XENVIF_MUTEX_H
+#define _XENVIF_MUTEX_H
+
+#include <ntddk.h>
+
+#include "assert.h"
+
+typedef struct _XENVIF_MUTEX {
+    PKTHREAD        Owner;
+    KEVENT          Event;
+} XENVIF_MUTEX, *PXENVIF_MUTEX;
+
+static FORCEINLINE VOID
+InitializeMutex(
+    IN  PXENVIF_MUTEX   Mutex
+    )
+{
+    RtlZeroMemory(Mutex, sizeof (XENVIF_MUTEX));
+
+    KeInitializeEvent(&Mutex->Event, SynchronizationEvent, TRUE);
+}
+
+static FORCEINLINE VOID
+__drv_maxIRQL(PASSIVE_LEVEL)
+AcquireMutex(
+    IN  PXENVIF_MUTEX   Mutex
+    )
+{
+    (VOID) KeWaitForSingleObject(&Mutex->Event,
+                                 Executive,
+                                 KernelMode,
+                                 FALSE,
+                                 NULL);
+
+    ASSERT3P(Mutex->Owner, ==, NULL);
+    Mutex->Owner = KeGetCurrentThread();
+}
+
+static FORCEINLINE VOID
+__drv_maxIRQL(PASSIVE_LEVEL)
+ReleaseMutex(
+    IN  PXENVIF_MUTEX   Mutex
+    )
+{
+    ASSERT3P(Mutex->Owner, ==, KeGetCurrentThread());
+    Mutex->Owner = NULL;
+
+    KeSetEvent(&Mutex->Event, IO_NO_INCREMENT, FALSE);
+}
+
+#endif  // _XENVIF_MUTEX_H
diff --git a/src/xenvif/names.h b/src/xenvif/names.h
new file mode 100644 (file)
index 0000000..9e81088
--- /dev/null
@@ -0,0 +1,309 @@
+/* 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 _XENVIF_NAMES_H_
+#define _XENVIF_NAMES_H_
+
+#include <ntddk.h>
+#include <xen.h>
+
+#include "types.h"
+
+static FORCEINLINE const CHAR *
+PowerTypeName(
+    IN  POWER_STATE_TYPE    Type
+    )
+{
+#define _POWER_TYPE_NAME(_Type) \
+        case _Type:             \
+            return #_Type;
+
+    switch (Type) {
+    _POWER_TYPE_NAME(SystemPowerState);
+    _POWER_TYPE_NAME(DevicePowerState);
+    default:
+        break;
+    }
+
+    return ("UNKNOWN");
+#undef  _POWER_ACTION_NAME
+}
+
+static FORCEINLINE const CHAR *
+PowerSystemStateName(
+    IN  SYSTEM_POWER_STATE State
+    )
+{
+#define _POWER_SYSTEM_STATE_NAME(_State)    \
+        case PowerSystem ## _State:         \
+            return #_State;
+
+    switch (State) {
+    _POWER_SYSTEM_STATE_NAME(Unspecified);
+    _POWER_SYSTEM_STATE_NAME(Working);
+    _POWER_SYSTEM_STATE_NAME(Sleeping1);
+    _POWER_SYSTEM_STATE_NAME(Sleeping2);
+    _POWER_SYSTEM_STATE_NAME(Sleeping3);
+    _POWER_SYSTEM_STATE_NAME(Hibernate);
+    _POWER_SYSTEM_STATE_NAME(Shutdown);
+    _POWER_SYSTEM_STATE_NAME(Maximum);
+    default:
+        break;
+    }
+
+    return ("UNKNOWN");
+#undef  _POWER_SYSTEM_STATE_NAME
+}
+
+static FORCEINLINE const CHAR *
+PowerDeviceStateName(
+    IN  DEVICE_POWER_STATE State
+    )
+{
+#define _POWER_DEVICE_STATE_NAME(_State)    \
+        case PowerDevice ## _State:         \
+            return #_State;
+
+    switch (State) {
+    _POWER_DEVICE_STATE_NAME(Unspecified);
+    _POWER_DEVICE_STATE_NAME(D0);
+    _POWER_DEVICE_STATE_NAME(D1);
+    _POWER_DEVICE_STATE_NAME(D2);
+    _POWER_DEVICE_STATE_NAME(D3);
+    _POWER_DEVICE_STATE_NAME(Maximum);
+    default:
+        break;
+    }
+
+    return ("UNKNOWN");
+#undef  _POWER_DEVICE_STATE_NAME
+}
+
+static FORCEINLINE const CHAR *
+PowerActionName(
+    IN  POWER_ACTION    Type
+    )
+{
+#define _POWER_ACTION_NAME(_Type)   \
+        case PowerAction ## _Type:  \
+            return #_Type;
+
+    switch (Type) {
+    _POWER_ACTION_NAME(None);
+    _POWER_ACTION_NAME(Reserved);
+    _POWER_ACTION_NAME(Sleep);
+    _POWER_ACTION_NAME(Hibernate);
+    _POWER_ACTION_NAME(Shutdown);
+    _POWER_ACTION_NAME(ShutdownReset);
+    _POWER_ACTION_NAME(ShutdownOff);
+    _POWER_ACTION_NAME(WarmEject);
+    default:
+        break;
+    }
+
+    return ("UNKNOWN");
+#undef  _POWER_ACTION_NAME
+}
+
+static FORCEINLINE const CHAR *
+PowerMinorFunctionName(
+    IN  ULONG   MinorFunction
+    )
+{
+#define _POWER_MINOR_FUNCTION_NAME(_Function)   \
+    case IRP_MN_ ## _Function:                  \
+        return #_Function;
+
+    switch (MinorFunction) {
+    _POWER_MINOR_FUNCTION_NAME(WAIT_WAKE);
+    _POWER_MINOR_FUNCTION_NAME(POWER_SEQUENCE);
+    _POWER_MINOR_FUNCTION_NAME(SET_POWER);
+    _POWER_MINOR_FUNCTION_NAME(QUERY_POWER);
+
+    default:
+        return "UNKNOWN";
+    }
+
+#undef  _POWER_MINOR_FUNCTION_NAME
+}
+
+static FORCEINLINE const CHAR *
+PnpDeviceStateName(
+    IN  DEVICE_PNP_STATE    State
+    )
+{
+#define _PNP_DEVICE_STATE_NAME(_State) \
+    case  _State:               \
+        return #_State;
+
+    switch (State) {
+    _PNP_DEVICE_STATE_NAME(Invalid);
+    _PNP_DEVICE_STATE_NAME(Present);
+    _PNP_DEVICE_STATE_NAME(Enumerated);
+    _PNP_DEVICE_STATE_NAME(Added);
+    _PNP_DEVICE_STATE_NAME(Started);
+    _PNP_DEVICE_STATE_NAME(StopPending);
+    _PNP_DEVICE_STATE_NAME(Stopped);
+    _PNP_DEVICE_STATE_NAME(RemovePending);
+    _PNP_DEVICE_STATE_NAME(SurpriseRemovePending);
+    _PNP_DEVICE_STATE_NAME(Deleted);
+    default:
+        break;
+    }
+
+    return "UNKNOWN";
+
+#undef  _STATE_NAME
+}
+
+static FORCEINLINE const CHAR *
+PnpMinorFunctionName(
+    IN  ULONG   Function
+    )
+{
+#define _PNP_MINOR_FUNCTION_NAME(_Function) \
+    case IRP_MN_ ## _Function:              \
+        return #_Function;
+
+    switch (Function) {
+    _PNP_MINOR_FUNCTION_NAME(START_DEVICE);
+    _PNP_MINOR_FUNCTION_NAME(QUERY_REMOVE_DEVICE);
+    _PNP_MINOR_FUNCTION_NAME(REMOVE_DEVICE);
+    _PNP_MINOR_FUNCTION_NAME(CANCEL_REMOVE_DEVICE);
+    _PNP_MINOR_FUNCTION_NAME(STOP_DEVICE);
+    _PNP_MINOR_FUNCTION_NAME(QUERY_STOP_DEVICE);
+    _PNP_MINOR_FUNCTION_NAME(CANCEL_STOP_DEVICE);
+    _PNP_MINOR_FUNCTION_NAME(QUERY_DEVICE_RELATIONS);
+    _PNP_MINOR_FUNCTION_NAME(QUERY_INTERFACE);
+    _PNP_MINOR_FUNCTION_NAME(QUERY_CAPABILITIES);
+    _PNP_MINOR_FUNCTION_NAME(QUERY_RESOURCES);
+    _PNP_MINOR_FUNCTION_NAME(QUERY_RESOURCE_REQUIREMENTS);
+    _PNP_MINOR_FUNCTION_NAME(QUERY_DEVICE_TEXT);
+    _PNP_MINOR_FUNCTION_NAME(FILTER_RESOURCE_REQUIREMENTS);
+    _PNP_MINOR_FUNCTION_NAME(READ_CONFIG);
+    _PNP_MINOR_FUNCTION_NAME(WRITE_CONFIG);
+    _PNP_MINOR_FUNCTION_NAME(EJECT);
+    _PNP_MINOR_FUNCTION_NAME(SET_LOCK);
+    _PNP_MINOR_FUNCTION_NAME(QUERY_ID);
+    _PNP_MINOR_FUNCTION_NAME(QUERY_PNP_DEVICE_STATE);
+    _PNP_MINOR_FUNCTION_NAME(QUERY_BUS_INFORMATION);
+    _PNP_MINOR_FUNCTION_NAME(DEVICE_USAGE_NOTIFICATION);
+    _PNP_MINOR_FUNCTION_NAME(SURPRISE_REMOVAL);
+    _PNP_MINOR_FUNCTION_NAME(QUERY_LEGACY_BUS_INFORMATION);
+    default:
+        break;
+    }
+
+    return "UNKNOWN";
+
+#undef  _PNP_MINOR_FUNCTION_NAME
+}
+
+static FORCEINLINE const CHAR *
+PartialResourceDescriptorTypeName(
+    IN  UCHAR   Type
+    )
+{
+#define _PARTIAL_RESOURCE_DESCRIPTOR_TYPE_NAME(_Type)   \
+    case CmResourceType ## _Type:                       \
+        return #_Type;
+
+    switch (Type) {
+    _PARTIAL_RESOURCE_DESCRIPTOR_TYPE_NAME(Null);
+    _PARTIAL_RESOURCE_DESCRIPTOR_TYPE_NAME(Port);
+    _PARTIAL_RESOURCE_DESCRIPTOR_TYPE_NAME(Interrupt);
+    _PARTIAL_RESOURCE_DESCRIPTOR_TYPE_NAME(Memory);
+    _PARTIAL_RESOURCE_DESCRIPTOR_TYPE_NAME(Dma);
+    _PARTIAL_RESOURCE_DESCRIPTOR_TYPE_NAME(DeviceSpecific);
+    _PARTIAL_RESOURCE_DESCRIPTOR_TYPE_NAME(BusNumber);
+    _PARTIAL_RESOURCE_DESCRIPTOR_TYPE_NAME(MemoryLarge);
+    _PARTIAL_RESOURCE_DESCRIPTOR_TYPE_NAME(ConfigData);
+    _PARTIAL_RESOURCE_DESCRIPTOR_TYPE_NAME(DevicePrivate);
+    default:
+        break;
+    }
+
+    return "UNKNOWN";
+
+#undef  _PARTIAL_RESOURCE_DESCRIPTOR_TYPE_NAME
+}
+
+static FORCEINLINE const PCHAR
+XenbusStateName(
+    IN  XenbusState   State
+    )
+{
+#define _STATE_NAME(_State)     \
+    case  XenbusState ## _State:  \
+        return #_State;
+
+    switch (State) {
+    _STATE_NAME(Unknown);
+    _STATE_NAME(Initialising);
+    _STATE_NAME(InitWait);
+    _STATE_NAME(Initialised);
+    _STATE_NAME(Connected);
+    _STATE_NAME(Closing);
+    _STATE_NAME(Closed);
+    _STATE_NAME(Reconfiguring);
+    _STATE_NAME(Reconfigured);
+    default:
+        break;
+    }
+
+    return "INVALID";
+
+#undef  _STATE_NAME
+}
+
+static FORCEINLINE const CHAR *
+DeviceUsageTypeName(
+    IN  DEVICE_USAGE_NOTIFICATION_TYPE  Type
+    )
+{
+#define _DEVICE_USAGE_TYPE_NAME(_Type)  \
+    case DeviceUsageType ## _Type:      \
+        return #_Type;
+
+    switch (Type) {
+    _DEVICE_USAGE_TYPE_NAME(Paging);
+    _DEVICE_USAGE_TYPE_NAME(Hibernation);
+    _DEVICE_USAGE_TYPE_NAME(DumpFile);
+    default:
+        break;
+    }
+
+    return "UNKNOWN";
+
+#undef  _DEVICE_USAGE_TYPE_NAME
+}
+
+#endif // _XENVIF_NAMES_H_
diff --git a/src/xenvif/notifier.c b/src/xenvif/notifier.c
new file mode 100644 (file)
index 0000000..ed1f44e
--- /dev/null
@@ -0,0 +1,440 @@
+/* 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 <ntstrsafe.h>
+#include <util.h>
+#include <evtchn_interface.h>
+#include <store_interface.h>
+
+#include "pdo.h"
+#include "frontend.h"
+#include "notifier.h"
+#include "receiver.h"
+#include "transmitter.h"
+#include "log.h"
+#include "assert.h"
+
+struct _XENVIF_NOTIFIER {
+    PXENVIF_FRONTEND            Frontend;
+    PXENBUS_EVTCHN_DESCRIPTOR   Evtchn;
+    KDPC                        Dpc;
+    ULONG                       Events;
+    ULONG                       Dpcs;
+    BOOLEAN                     Connected;
+    KSPIN_LOCK                  Lock;
+    BOOLEAN                     Enabled;
+
+    PXENBUS_EVTCHN_INTERFACE    EvtchnInterface;
+    PXENBUS_DEBUG_INTERFACE     DebugInterface;
+    PXENBUS_STORE_INTERFACE     StoreInterface;
+
+    PXENBUS_DEBUG_CALLBACK      DebugCallback;
+};
+
+#define NOTIFIER_POOL    'ITON'
+
+static FORCEINLINE PVOID
+__NotifierAllocate(
+    IN  ULONG   Length
+    )
+{
+    return __AllocateNonPagedPoolWithTag(Length, NOTIFIER_POOL);
+}
+
+static FORCEINLINE VOID
+__NotifierFree(
+    IN  PVOID   Buffer
+    )
+{
+    __FreePoolWithTag(Buffer, NOTIFIER_POOL);
+}
+
+#pragma warning(push)
+#pragma warning(disable:6011)   // Dereferencing NULL pointer
+
+static FORCEINLINE VOID
+__NotifierNotify(
+    IN  PXENVIF_NOTIFIER    Notifier
+    )
+{
+    PXENVIF_FRONTEND        Frontend;
+
+    Frontend = Notifier->Frontend;
+
+    TransmitterNotify(FrontendGetTransmitter(Frontend));
+    ReceiverNotify(FrontendGetReceiver(Frontend));
+}
+
+static FORCEINLINE BOOLEAN
+__NotifierUnmask(
+    IN  PXENVIF_NOTIFIER    Notifier
+    )
+{
+    BOOLEAN                 Pending;
+
+    KeAcquireSpinLockAtDpcLevel(&Notifier->Lock);
+
+    Pending = (Notifier->Connected) ?
+              EVTCHN(Unmask,
+                     Notifier->EvtchnInterface,
+                     Notifier->Evtchn,
+                     FALSE) :
+              FALSE;
+
+    KeReleaseSpinLockFromDpcLevel(&Notifier->Lock);
+
+    return Pending;
+}
+
+
+KDEFERRED_ROUTINE   NotifierDpc;
+
+VOID
+NotifierDpc(
+    IN  PKDPC           Dpc,
+    IN  PVOID           Context,
+    IN  PVOID           Argument1,
+    IN  PVOID           Argument2
+    )
+{
+    PXENVIF_NOTIFIER    Notifier = Context;
+    BOOLEAN             Pending;
+
+    UNREFERENCED_PARAMETER(Dpc);
+    UNREFERENCED_PARAMETER(Argument1);
+    UNREFERENCED_PARAMETER(Argument2);
+
+    ASSERT(Notifier != NULL);
+
+    do {
+        if (Notifier->Enabled)
+            __NotifierNotify(Notifier);
+
+        Pending = __NotifierUnmask(Notifier);
+    } while (Pending);
+}
+
+#pragma warning(pop)
+
+KSERVICE_ROUTINE    NotifierEvtchnCallback;
+
+BOOLEAN
+NotifierEvtchnCallback(
+    IN  PKINTERRUPT         InterruptObject,
+    IN  PVOID               Argument
+    )
+{
+    PXENVIF_NOTIFIER        Notifier = Argument;
+
+    UNREFERENCED_PARAMETER(InterruptObject);
+
+    ASSERT(Notifier != NULL);
+
+    Notifier->Events++;
+
+    if (KeInsertQueueDpc(&Notifier->Dpc, NULL, NULL))
+        Notifier->Dpcs++;
+
+    return TRUE;
+}
+
+static VOID
+NotifierDebugCallback(
+    IN  PVOID           Argument,
+    IN  BOOLEAN         Crashing
+    )
+{
+    PXENVIF_NOTIFIER    Notifier = Argument;
+
+    UNREFERENCED_PARAMETER(Crashing);
+
+    DEBUG(Printf,
+          Notifier->DebugInterface,
+          Notifier->DebugCallback,
+          "Events = %lu Dpcs = %lu\n",
+          Notifier->Events,
+          Notifier->Dpcs);
+}
+
+NTSTATUS
+NotifierInitialize(
+    IN  PXENVIF_FRONTEND    Frontend,
+    OUT PXENVIF_NOTIFIER    *Notifier
+    )
+{
+    NTSTATUS                status;
+
+    *Notifier = __NotifierAllocate(sizeof (XENVIF_NOTIFIER));
+
+    status = STATUS_NO_MEMORY;
+    if (*Notifier == NULL)
+        goto fail1;
+
+    (*Notifier)->Frontend = Frontend;
+
+    KeInitializeSpinLock(&(*Notifier)->Lock);
+    KeInitializeDpc(&(*Notifier)->Dpc,
+                    NotifierDpc,
+                    *Notifier);
+
+    return STATUS_SUCCESS;
+
+fail1:
+    Error("fail1 (%08x)\n", status);
+
+    return status;
+}
+
+NTSTATUS
+NotifierConnect(
+    IN  PXENVIF_NOTIFIER    Notifier
+    )
+{
+    PXENVIF_FRONTEND        Frontend = Notifier->Frontend;
+    BOOLEAN                 Pending;
+    NTSTATUS                status;
+
+    ASSERT3U(KeGetCurrentIrql(), ==, DISPATCH_LEVEL);
+    KeAcquireSpinLockAtDpcLevel(&Notifier->Lock);
+
+    ASSERT(!Notifier->Connected);
+
+    Notifier->EvtchnInterface = FrontendGetEvtchnInterface(Frontend);
+
+    EVTCHN(Acquire, Notifier->EvtchnInterface);
+
+    Notifier->Evtchn = EVTCHN(Open,
+                              Notifier->EvtchnInterface,
+                              EVTCHN_UNBOUND,
+                              NotifierEvtchnCallback,
+                              Notifier,
+                              FrontendGetBackendDomain(Frontend),
+                              TRUE);
+
+    status = STATUS_UNSUCCESSFUL;
+    if (Notifier->Evtchn == NULL)
+        goto fail1;
+
+    Pending = EVTCHN(Unmask,
+                     Notifier->EvtchnInterface,
+                     Notifier->Evtchn,
+                     FALSE);
+    if (Pending)
+        EVTCHN(Trigger,
+               Notifier->EvtchnInterface,
+               Notifier->Evtchn);
+
+    Notifier->DebugInterface = FrontendGetDebugInterface(Frontend);
+
+    DEBUG(Acquire, Notifier->DebugInterface);
+
+    status = DEBUG(Register,
+                   Notifier->DebugInterface,
+                   __MODULE__ "|NOTIFIER",
+                   NotifierDebugCallback,
+                   Notifier,
+                   &Notifier->DebugCallback);
+    if (!NT_SUCCESS(status))
+        goto fail2;
+
+    Notifier->StoreInterface = FrontendGetStoreInterface(Frontend);
+
+    STORE(Acquire, Notifier->StoreInterface);
+
+    Notifier->Connected = TRUE;
+    KeReleaseSpinLockFromDpcLevel(&Notifier->Lock);
+
+    return STATUS_SUCCESS;
+
+fail2:
+    Error("fail2\n");
+
+    DEBUG(Release, Notifier->DebugInterface);
+
+    EVTCHN(Close,
+           Notifier->EvtchnInterface,
+           Notifier->Evtchn);
+    Notifier->Evtchn = NULL;
+
+    Notifier->Events = 0;
+
+fail1:
+    Error("fail1 (%08x)\n", status);
+
+    EVTCHN(Release, Notifier->EvtchnInterface);
+
+    KeReleaseSpinLockFromDpcLevel(&Notifier->Lock);
+
+    return status;
+}
+
+NTSTATUS
+NotifierStoreWrite(
+    IN  PXENVIF_NOTIFIER            Notifier,
+    IN  PXENBUS_STORE_TRANSACTION   Transaction
+    )
+{
+    PXENVIF_FRONTEND                Frontend = Notifier->Frontend;
+    ULONG                           Port;
+    NTSTATUS                        status;
+
+    Port = EVTCHN(Port,
+                  Notifier->EvtchnInterface,
+                  Notifier->Evtchn);
+
+    status = STORE(Printf,
+                   Notifier->StoreInterface,
+                   Transaction,
+                   FrontendGetPath(Frontend),
+                   "event-channel",
+                   "%u",
+                   Port);
+
+    if (!NT_SUCCESS(status))
+        goto fail1;
+
+    return STATUS_SUCCESS;
+
+fail1:
+    Error("fail1 (%08x)\n", status);
+
+    return status;
+}
+
+NTSTATUS
+NotifierEnable(
+    IN  PXENVIF_NOTIFIER    Notifier
+    )
+{
+    ASSERT(!Notifier->Enabled);
+    Notifier->Enabled = TRUE;
+
+    if (KeInsertQueueDpc(&Notifier->Dpc, NULL, NULL))
+        Notifier->Dpcs++;
+
+    return STATUS_SUCCESS;
+}
+
+VOID
+NotifierDisable(
+    IN  PXENVIF_NOTIFIER    Notifier
+    )
+{
+    ASSERT(Notifier->Enabled);
+    Notifier->Enabled = FALSE;
+}
+
+VOID
+NotifierDisconnect(
+    IN  PXENVIF_NOTIFIER    Notifier
+    )
+{
+    KeAcquireSpinLockAtDpcLevel(&Notifier->Lock);
+
+    ASSERT(Notifier->Connected);
+    Notifier->Connected = FALSE;
+
+    STORE(Release, Notifier->StoreInterface);
+    Notifier->StoreInterface = NULL;
+
+    DEBUG(Deregister,
+          Notifier->DebugInterface,
+          Notifier->DebugCallback);
+    Notifier->DebugCallback = NULL;
+
+    DEBUG(Release, Notifier->DebugInterface);
+    Notifier->DebugInterface = NULL;
+
+    EVTCHN(Close,
+           Notifier->EvtchnInterface,
+           Notifier->Evtchn);
+    Notifier->Evtchn = NULL;
+
+    Notifier->Events = 0;
+
+    EVTCHN(Release, Notifier->EvtchnInterface);
+    Notifier->EvtchnInterface = NULL;
+
+    KeReleaseSpinLockFromDpcLevel(&Notifier->Lock);
+}
+
+VOID
+NotifierTeardown(
+    IN  PXENVIF_NOTIFIER    Notifier
+    )
+{
+    KeFlushQueuedDpcs();
+
+    Notifier->Dpcs = 0;
+    Notifier->Frontend = NULL;
+
+    RtlZeroMemory(&Notifier->Dpc, sizeof (KDPC));
+    RtlZeroMemory(&Notifier->Lock, sizeof (KSPIN_LOCK));
+
+    ASSERT(IsZeroMemory(Notifier, sizeof (XENVIF_NOTIFIER)));
+
+    __NotifierFree(Notifier);
+}
+
+VOID
+NotifierSend(
+    IN  PXENVIF_NOTIFIER    Notifier
+    )
+{
+    KIRQL                   Irql;
+
+    KeAcquireSpinLock(&Notifier->Lock, &Irql);
+
+    if (Notifier->Connected)
+        (VOID) EVTCHN(Send,
+                      Notifier->EvtchnInterface,
+                      Notifier->Evtchn);
+
+    KeReleaseSpinLock(&Notifier->Lock, Irql);
+}
+
+VOID
+NotifierTrigger(
+    IN  PXENVIF_NOTIFIER    Notifier
+    )
+{
+    KIRQL                   Irql;
+
+    KeAcquireSpinLock(&Notifier->Lock, &Irql);
+
+    if (Notifier->Connected)
+        (VOID) EVTCHN(Trigger,
+                      Notifier->EvtchnInterface,
+                      Notifier->Evtchn);
+
+    KeReleaseSpinLock(&Notifier->Lock, Irql);
+}
diff --git a/src/xenvif/notifier.h b/src/xenvif/notifier.h
new file mode 100644 (file)
index 0000000..b75bfc3
--- /dev/null
@@ -0,0 +1,89 @@
+/* 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 _XENVIF_NOTIFIER_H
+#define _XENVIF_NOTIFIER_H
+
+#include <ntddk.h>
+#include <store_interface.h>
+
+#include "frontend.h"
+
+typedef struct _XENVIF_NOTIFIER XENVIF_NOTIFIER, *PXENVIF_NOTIFIER;
+
+extern NTSTATUS
+NotifierInitialize(
+    IN  PXENVIF_FRONTEND    Frontend,
+    OUT PXENVIF_NOTIFIER    *Notifier
+    );
+
+extern NTSTATUS
+NotifierConnect(
+    IN  PXENVIF_NOTIFIER    Notifier
+    );
+
+extern NTSTATUS
+NotifierStoreWrite(
+    IN  PXENVIF_NOTIFIER            Notifier,
+    IN  PXENBUS_STORE_TRANSACTION   Transaction
+    );
+
+extern NTSTATUS
+NotifierEnable(
+    IN  PXENVIF_NOTIFIER    Notifier
+    );
+
+extern VOID
+NotifierDisable(
+    IN  PXENVIF_NOTIFIER    Notifier
+    );
+
+extern VOID
+NotifierDisconnect(
+    IN  PXENVIF_NOTIFIER    Notifier
+    );
+
+extern VOID
+NotifierTeardown(
+    IN  PXENVIF_NOTIFIER    Notifier
+    );
+
+extern VOID
+NotifierSend(
+    IN  PXENVIF_NOTIFIER    Notifier
+    );
+
+extern VOID
+NotifierTrigger(
+    IN  PXENVIF_NOTIFIER    Notifier
+    );
+
+#endif  // _XENVIF_NOTIFIER_H
diff --git a/src/xenvif/parse.c b/src/xenvif/parse.c
new file mode 100644 (file)
index 0000000..85d477f
--- /dev/null
@@ -0,0 +1,509 @@
+/* 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 <ntstrsafe.h>
+#include <util.h>
+#include <ethernet.h>
+#include <tcpip.h>
+#include <llc.h>
+#include <ipx.h>
+#include <vif_interface.h>
+
+#include "parse.h"
+#include "log.h"
+#include "assert.h"
+
+static FORCEINLINE NTSTATUS
+__ParseTcpHeader(
+    IN      PUCHAR                      StartVa,
+    IN      ULONG                       Offset,
+    IN      BOOLEAN                     (*Pullup)(PVOID, PUCHAR, PXENVIF_PACKET_PAYLOAD, ULONG),
+    IN      PVOID                       Argument,
+    IN OUT  PXENVIF_HEADER_STATISTICS   Statistics,
+    IN OUT  PXENVIF_PACKET_PAYLOAD      Payload,
+    OUT     PXENVIF_PACKET_INFO         Info
+    )
+{
+    PTCP_HEADER                         Header;
+
+    Info->TcpHeader.Offset = Offset;
+
+    if (!Pullup(Argument, StartVa + Offset, Payload, sizeof (TCP_HEADER)))
+        goto fail1;
+
+    Header = (PTCP_HEADER)(StartVa + Offset);
+    Offset += sizeof (TCP_HEADER);
+
+    Info->TcpHeader.Length = Offset - Info->TcpHeader.Offset;
+    Statistics->Tcp++;
+
+    // Check for malformned header
+    if (TCP_HEADER_LENGTH(Header) < Info->TcpHeader.Length)
+        goto fail2;
+
+    if (TCP_HEADER_LENGTH(Header) > Info->TcpHeader.Length) {
+        ULONG Extra;
+
+        Info->TcpOptions.Offset = Offset;
+
+        Extra = TCP_HEADER_LENGTH(Header) - Info->TcpHeader.Length;
+
+        if (!Pullup(Argument, StartVa + Offset, Payload, Extra))
+            goto fail3;
+
+        Offset += Extra;
+
+        Info->TcpOptions.Length = Offset - Info->TcpOptions.Offset;
+        Statistics->TcpOptions++;
+    }
+
+    Info->Length += Info->TcpHeader.Length + Info->TcpOptions.Length;
+
+    return STATUS_SUCCESS;
+
+fail3:
+    Info->TcpOptions.Offset = 0;
+
+fail2:
+    Info->TcpHeader.Length = 0;
+
+fail1:
+    Info->TcpHeader.Offset = 0;
+
+    return STATUS_UNSUCCESSFUL;
+}
+
+static FORCEINLINE NTSTATUS
+__ParseUdpHeader(
+    IN      PUCHAR                      StartVa,
+    IN      ULONG                       Offset,
+    IN      BOOLEAN                     (*Pullup)(PVOID, PUCHAR, PXENVIF_PACKET_PAYLOAD, ULONG),
+    IN      PVOID                       Argument,
+    IN OUT  PXENVIF_HEADER_STATISTICS   Statistics,
+    IN OUT  PXENVIF_PACKET_PAYLOAD      Payload,
+    OUT     PXENVIF_PACKET_INFO         Info
+    )
+{
+    Info->UdpHeader.Offset = Offset;
+
+    if (!Pullup(Argument, StartVa + Offset, Payload, sizeof (UDP_HEADER)))
+        goto fail1;
+
+    Offset += sizeof (UDP_HEADER);
+
+    Info->UdpHeader.Length = Offset - Info->UdpHeader.Offset;
+    Statistics->Udp++;
+
+    Info->Length += Info->UdpHeader.Length;
+
+    return STATUS_SUCCESS;
+
+fail1:
+    Info->UdpHeader.Offset = 0;
+
+    return STATUS_UNSUCCESSFUL;
+}
+
+static FORCEINLINE NTSTATUS
+__ParseIpVersion4Header(
+    IN      PUCHAR                      StartVa,
+    IN      ULONG                       Offset,
+    IN      BOOLEAN                     (*Pullup)(PVOID, PUCHAR, PXENVIF_PACKET_PAYLOAD, ULONG),
+    IN      PVOID                       Argument,
+    IN OUT  PXENVIF_HEADER_STATISTICS   Statistics,
+    IN OUT  PXENVIF_PACKET_PAYLOAD      Payload,
+    OUT     PXENVIF_PACKET_INFO         Info
+    )
+{
+    PIPV4_HEADER                        Header;
+    USHORT                              PacketLength;
+    USHORT                              FragmentOffsetAndFlags;
+    NTSTATUS                            status;
+
+    Info->IpHeader.Offset = Offset;
+
+    if (!Pullup(Argument, StartVa + Offset, Payload, sizeof (IPV4_HEADER)))
+        goto fail1;
+
+    Header = (PIPV4_HEADER)(StartVa + Offset);
+    Offset += sizeof (IPV4_HEADER);
+
+    Info->IpHeader.Length = Offset - Info->IpHeader.Offset;
+    Statistics->IpVersion4++;
+
+    if (Header->Version != 4)
+        goto fail2;
+
+    PacketLength = NTOHS(Header->PacketLength);
+    if (PacketLength > Info->IpHeader.Length + Payload->Length)
+        goto fail3;
+
+    if (IPV4_HEADER_LENGTH(Header) < Info->IpHeader.Length)
+        goto fail4;
+
+    if (IPV4_HEADER_LENGTH(Header) > Info->IpHeader.Length) {
+        ULONG Extra;
+
+        Info->IpOptions.Offset = Offset;
+
+        Extra = IPV4_HEADER_LENGTH(Header) - Info->IpHeader.Length;
+
+        if (!Pullup(Argument, StartVa + Offset, Payload, Extra))
+            goto fail5;
+
+        Offset += Extra;
+
+        Info->IpOptions.Length = Offset - Info->IpOptions.Offset;
+        Statistics->IpOptions++;
+    }
+
+    Info->Length += Info->IpHeader.Length + Info->IpOptions.Length;
+
+    FragmentOffsetAndFlags = NTOHS(Header->FragmentOffsetAndFlags);
+    
+    status = STATUS_SUCCESS;
+    if (IPV4_IS_A_FRAGMENT(FragmentOffsetAndFlags))
+        goto done;
+
+    switch (Header->Protocol) {
+    case IPPROTO_TCP:
+        status = __ParseTcpHeader(StartVa, Offset, Pullup, Argument, Statistics, Payload, Info);
+        break;
+
+    case IPPROTO_UDP:
+        status = __ParseUdpHeader(StartVa, Offset, Pullup, Argument, Statistics, Payload, Info);
+        break;
+
+    default:
+        status = STATUS_SUCCESS;
+        break;
+    }
+
+done:
+    return status;
+
+fail5:
+    Info->IpOptions.Offset = 0;
+
+fail4:
+fail3:
+fail2:
+    Info->IpHeader.Length = 0;
+
+fail1:
+    Info->IpHeader.Offset = 0;
+
+    return STATUS_UNSUCCESSFUL;
+}
+
+static FORCEINLINE NTSTATUS
+__ParseIpVersion6Header(
+    IN      PUCHAR                      StartVa,
+    IN      ULONG                       Offset,
+    IN      BOOLEAN                     (*Pullup)(PVOID, PUCHAR, PXENVIF_PACKET_PAYLOAD, ULONG),
+    IN      PVOID                       Argument,
+    IN OUT  PXENVIF_HEADER_STATISTICS   Statistics,
+    IN OUT  PXENVIF_PACKET_PAYLOAD      Payload,
+    OUT     PXENVIF_PACKET_INFO         Info
+    )
+{
+    PIPV6_HEADER                        Header;
+    USHORT                              PayloadLength;
+    BOOLEAN                             IsAFragment;
+    UCHAR                               NextHeader;
+    ULONG                               Count;
+    BOOLEAN                             Finished;
+    NTSTATUS                            status;
+
+    Info->IpHeader.Offset = Offset;
+
+    if (!Pullup(Argument, StartVa + Offset, Payload, sizeof (IPV6_HEADER)))
+        goto fail1;
+
+    Header = (PIPV6_HEADER)(StartVa + Offset);
+    Offset += sizeof (IPV6_HEADER);
+
+    Info->IpHeader.Length = Offset - Info->IpHeader.Offset;
+    Statistics->IpVersion6++;
+
+    if (Header->Version != 6)
+        goto fail2;
+
+    PayloadLength = NTOHS(Header->PayloadLength);
+    if (PayloadLength > Payload->Length)
+        goto fail3;
+
+    Info->IpOptions.Offset = Offset;
+
+    IsAFragment = FALSE;
+
+    NextHeader = Header->NextHeader;
+    Count = 0;
+    Finished = FALSE;
+
+    while (!Finished) {
+        Count++;
+        ASSERT3U(Count, <, 100);
+
+        switch (NextHeader) {
+        case IPPROTO_FRAGMENT: {
+            PIPV6_FRAGMENT_HEADER   Fragment;
+            USHORT                  FragmentOffsetAndFlags;
+
+            if (!Pullup(Argument, StartVa + Offset, Payload, sizeof (IPV6_FRAGMENT_HEADER)))
+                goto fail4;
+
+            Fragment = (PIPV6_FRAGMENT_HEADER)(StartVa + Offset);
+            Offset += sizeof (IPV6_FRAGMENT_HEADER);
+
+            FragmentOffsetAndFlags = NTOHS(Fragment->OffsetAndFlags);
+            IsAFragment = IPV6_IS_A_FRAGMENT(FragmentOffsetAndFlags) ? TRUE : FALSE;
+
+            NextHeader = Fragment->NextHeader;
+            break;
+        }
+        case IPPROTO_HOP_OPTIONS:
+        case IPPROTO_DST_OPTIONS:
+        case IPPROTO_ROUTING: {
+            PIPV6_OPTION_HEADER Option;
+            ULONG               Extra;
+
+            if (!Pullup(Argument, StartVa + Offset, Payload, sizeof (IPV6_OPTION_HEADER)))
+                goto fail4;
+
+            Option = (PIPV6_OPTION_HEADER)(StartVa + Offset);
+            Offset += sizeof (IPV6_OPTION_HEADER);
+
+            Extra = ((Option->Length + 1) * 8) - sizeof (IPV6_OPTION_HEADER);
+
+            if (!Pullup(Argument, StartVa + Offset, Payload, Extra))
+                goto fail5;
+
+            Offset += Extra;
+
+            NextHeader = Option->NextHeader;
+            break;
+        }
+        default:
+            Finished = TRUE;
+            break;
+        }
+    }
+
+    Info->IpOptions.Length = (ULONG)(Offset - Info->IpOptions.Offset);
+    if (Info->IpOptions.Length == 0)
+        Info->IpOptions.Offset = 0;
+    else
+        Statistics->IpOptions++;
+
+    Info->Length += Info->IpHeader.Length + Info->IpOptions.Length;
+
+    status = STATUS_SUCCESS;
+    if (IsAFragment)
+        goto done;
+    
+    switch (NextHeader) {
+    case IPPROTO_TCP:
+        status = __ParseTcpHeader(StartVa, Offset, Pullup, Argument, Statistics, Payload, Info);
+        break;
+
+    case IPPROTO_UDP:
+        status = __ParseUdpHeader(StartVa, Offset, Pullup, Argument, Statistics, Payload, Info);
+        break;
+
+    default:
+        status = STATUS_SUCCESS;
+        break;
+    }
+
+done:
+    return status;
+
+fail5:
+fail4:
+    Info->IpOptions.Offset = 0;
+
+fail3:
+fail2:
+    Info->IpHeader.Length = 0;
+
+fail1:
+    Info->IpHeader.Offset = 0;
+
+    return STATUS_UNSUCCESSFUL;
+}
+
+static FORCEINLINE NTSTATUS
+__ParseLLCSnapHeader(
+    IN      PUCHAR                      StartVa,
+    IN      ULONG                       Offset,
+    IN      BOOLEAN                     (*Pullup)(PVOID, PUCHAR, PXENVIF_PACKET_PAYLOAD, ULONG),
+    IN      PVOID                       Argument,
+    IN OUT  PXENVIF_HEADER_STATISTICS   Statistics,
+    IN OUT  PXENVIF_PACKET_PAYLOAD      Payload,
+    OUT     PXENVIF_PACKET_INFO         Info
+    )
+{
+    PLLC_SNAP_HEADER                    Header;
+
+    Info->LLCSnapHeader.Offset = Offset;
+
+    if (!Pullup(Argument, StartVa + Offset, Payload, sizeof (LLC_U_HEADER)))
+        goto fail1;
+
+    Header = (PLLC_SNAP_HEADER)(StartVa + Offset);
+    Offset += sizeof (LLC_U_HEADER);
+
+    if ((Header->DestinationSAP & LLC_SAP_MASK) == 0xAA &&
+        (Header->SourceSAP & LLC_SAP_MASK) == 0xAA &&
+        Header->Control == LLC_U_FRAME) {
+        ULONG Extra;
+
+        Extra = sizeof (LLC_SNAP_HEADER) - sizeof (LLC_U_HEADER);
+
+        if (!Pullup(Argument, StartVa + Offset, Payload, Extra))
+            goto fail2;
+
+        Offset += Extra;
+    }
+
+    Info->LLCSnapHeader.Length = Offset - Info->LLCSnapHeader.Offset;
+    Statistics->LLC++;
+
+    Info->Length += Info->LLCSnapHeader.Length;
+
+    return STATUS_SUCCESS;
+
+fail2:
+fail1:
+    Info->LLCSnapHeader.Offset = 0;
+
+    return STATUS_UNSUCCESSFUL;
+}
+
+static FORCEINLINE NTSTATUS
+__ParseEthernetHeader(
+    IN      PUCHAR                      StartVa,
+    IN      ULONG                       Offset,
+    IN      BOOLEAN                     (*Pullup)(PVOID, PUCHAR, PXENVIF_PACKET_PAYLOAD, ULONG),
+    IN      PVOID                       Argument,
+    IN OUT  PXENVIF_HEADER_STATISTICS   Statistics,
+    IN OUT  PXENVIF_PACKET_PAYLOAD      Payload,
+    OUT     PXENVIF_PACKET_INFO         Info
+    )
+{
+    PETHERNET_HEADER                    Header;
+    USHORT                              TypeOrLength;
+    BOOLEAN                             IsLLC;
+    NTSTATUS                            status;
+
+    Info->EthernetHeader.Offset = Offset;
+
+    if (!Pullup(Argument, StartVa + Offset, Payload, sizeof (ETHERNET_UNTAGGED_HEADER)))
+        goto fail1;
+
+    Header = (PETHERNET_HEADER)(StartVa + Offset);
+    Offset += sizeof (ETHERNET_UNTAGGED_HEADER);
+
+    IsLLC = FALSE;
+
+    TypeOrLength = NTOHS(Header->Untagged.TypeOrLength);
+    if (TypeOrLength == ETHERTYPE_TPID) {
+        ULONG Extra;
+
+        Extra = sizeof (ETHERNET_TAGGED_HEADER) - sizeof (ETHERNET_UNTAGGED_HEADER);
+
+        if (!Pullup(Argument, StartVa + Offset, Payload, Extra))
+            goto fail2;
+
+        Offset += Extra;
+
+        TypeOrLength = NTOHS(Header->Tagged.TypeOrLength);
+        Statistics->Tagged++;
+    }
+
+    if (TypeOrLength <= ETHERNET_MTU)
+        IsLLC = TRUE;
+
+    Info->EthernetHeader.Length = Offset - Info->EthernetHeader.Offset;
+    Info->Length += Info->EthernetHeader.Length;
+
+    if (IsLLC) {
+        status = __ParseLLCSnapHeader(StartVa, Offset, Pullup, Argument, Statistics, Payload, Info);
+    } else {
+        switch (TypeOrLength) {
+        case ETHERTYPE_IPV4:
+            status = __ParseIpVersion4Header(StartVa, Offset, Pullup, Argument, Statistics, Payload, Info);
+            break;
+
+        case ETHERTYPE_IPV6:
+            status = __ParseIpVersion6Header(StartVa, Offset, Pullup, Argument, Statistics, Payload, Info);
+            break;
+
+        default:
+            status = STATUS_SUCCESS;
+            break;
+        }
+    }
+
+    return status;
+
+fail2:
+fail1:
+    Info->EthernetHeader.Offset = 0;
+
+    return STATUS_UNSUCCESSFUL;
+}
+
+NTSTATUS
+ParsePacket(
+    IN      PUCHAR                      StartVa,
+    IN      BOOLEAN                     (*Pullup)(PVOID, PUCHAR, PXENVIF_PACKET_PAYLOAD, ULONG),
+    IN      PVOID                       Argument,
+    IN OUT  PXENVIF_HEADER_STATISTICS   Statistics,
+    IN OUT  PXENVIF_PACKET_PAYLOAD      Payload,
+    OUT     PXENVIF_PACKET_INFO         Info
+    )
+{
+    NTSTATUS                            status;
+
+    ASSERT(IsZeroMemory(Info, sizeof (XENVIF_PACKET_INFO)));
+
+    status = __ParseEthernetHeader(StartVa, 0, Pullup, Argument, Statistics, Payload, Info);
+    if (!NT_SUCCESS(status))
+        goto fail1;
+
+    return STATUS_SUCCESS;
+
+fail1:
+    return status;
+}
diff --git a/src/xenvif/parse.h b/src/xenvif/parse.h
new file mode 100644 (file)
index 0000000..41c257f
--- /dev/null
@@ -0,0 +1,64 @@
+/* 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 _XENVIF_PARSE_H
+#define _XENVIF_PARSE_H
+
+#include <vif_interface.h>
+
+typedef struct _XENVIF_HEADER_STATISTICS {
+    ULONGLONG   Tagged;
+    ULONGLONG   LLC;
+    ULONGLONG   IpVersion4;
+    ULONGLONG   IpVersion6;
+    ULONGLONG   IpOptions;
+    ULONGLONG   Tcp;
+    ULONGLONG   TcpOptions;
+    ULONGLONG   Udp;
+} XENVIF_HEADER_STATISTICS, *PXENVIF_HEADER_STATISTICS;
+
+typedef struct _XENVIF_PACKET_PAYLOAD {
+    PMDL    Mdl;
+    ULONG   Offset;
+    ULONG   Length;
+} XENVIF_PACKET_PAYLOAD, *PXENVIF_PACKET_PAYLOAD;
+
+extern NTSTATUS
+ParsePacket(
+    IN      PUCHAR                      StartVa,
+    IN      BOOLEAN                     (*Pullup)(PVOID, PUCHAR, PXENVIF_PACKET_PAYLOAD, ULONG),
+    IN      PVOID                       Argument,
+    IN OUT  PXENVIF_HEADER_STATISTICS   Statistics,
+    IN OUT  PXENVIF_PACKET_PAYLOAD      Payload,
+    OUT     PXENVIF_PACKET_INFO         Info
+    );
+
+#endif  // _XENVIF_PARSE_H
diff --git a/src/xenvif/pdo.c b/src/xenvif/pdo.c
new file mode 100644 (file)
index 0000000..b1fa5dc
--- /dev/null
@@ -0,0 +1,2271 @@
+/* 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 <util.h>
+#include <xen.h>
+#include <store_interface.h>
+#include <emulated_interface.h>
+
+#include "names.h"
+#include "fdo.h"
+#include "pdo.h"
+#include "frontend.h"
+#include "vif.h"
+#include "driver.h"
+#include "registry.h"
+#include "thread.h"
+#include "log.h"
+#include "assert.h"
+
+#define PDO_REVISION    0x02
+
+#define PDO_POOL 'ODP'
+
+typedef enum _PDO_RESOURCE_TYPE {
+    MEMORY_RESOURCE = 0,
+    INTERRUPT_RESOURCE,
+    RESOURCE_COUNT
+} PDO_RESOURCE_TYPE, *PPDO_RESOURCE_TYPE;
+
+typedef struct _PDO_RESOURCE {
+    CM_PARTIAL_RESOURCE_DESCRIPTOR Raw;
+    CM_PARTIAL_RESOURCE_DESCRIPTOR Translated;
+} PDO_RESOURCE, *PPDO_RESOURCE;
+
+struct _XENVIF_PDO {
+    PXENVIF_DX                  Dx;
+
+    PXENVIF_THREAD              SystemPowerThread;
+    PIRP                        SystemPowerIrp;
+    PXENVIF_THREAD              DevicePowerThread;
+    PIRP                        DevicePowerIrp;
+
+    PXENVIF_FDO                 Fdo;
+    BOOLEAN                     Missing;
+    const CHAR                  *Reason;
+    ULONG                       LuidIndex;
+    PCHAR                       Address;
+
+    PXENVIF_FRONTEND            Frontend;
+    XENVIF_VIF_INTERFACE        VifInterface;
+
+    PXENBUS_SUSPEND_INTERFACE   SuspendInterface;
+
+    PXENBUS_SUSPEND_CALLBACK    SuspendCallbackLate;
+};
+
+static FORCEINLINE PVOID
+__PdoAllocate(
+    IN  ULONG   Length
+    )
+{
+    return __AllocateNonPagedPoolWithTag(Length, PDO_POOL);
+}
+
+static FORCEINLINE VOID
+__PdoFree(
+    IN  PVOID   Buffer
+    )
+{
+    __FreePoolWithTag(Buffer, PDO_POOL);
+}
+
+static FORCEINLINE VOID
+__PdoSetDevicePnpState(
+    IN  PXENVIF_PDO         Pdo,
+    IN  DEVICE_PNP_STATE    State
+    )
+{
+    PXENVIF_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  PXENVIF_PDO         Pdo,
+    IN  DEVICE_PNP_STATE    State
+    )
+{
+    __PdoSetDevicePnpState(Pdo, State);
+}
+
+static FORCEINLINE VOID
+__PdoRestoreDevicePnpState(
+    IN  PXENVIF_PDO         Pdo,
+    IN  DEVICE_PNP_STATE    State
+    )
+{
+    PXENVIF_DX              Dx = Pdo->Dx;
+
+    if (Dx->DevicePnpState == State)
+        Dx->DevicePnpState = Dx->PreviousDevicePnpState;
+}
+
+static FORCEINLINE DEVICE_PNP_STATE
+__PdoGetDevicePnpState(
+    IN  PXENVIF_PDO Pdo
+    )
+{
+    PXENVIF_DX      Dx = Pdo->Dx;
+
+    return Dx->DevicePnpState;
+}
+
+DEVICE_PNP_STATE
+PdoGetDevicePnpState(
+    IN  PXENVIF_PDO Pdo
+    )
+{
+    return __PdoGetDevicePnpState(Pdo);
+}
+
+static FORCEINLINE VOID
+__PdoSetSystemPowerState(
+    IN  PXENVIF_PDO         Pdo,
+    IN  SYSTEM_POWER_STATE  State
+    )
+{
+    PXENVIF_DX              Dx = Pdo->Dx;
+
+    Dx->SystemPowerState = State;
+}
+
+static FORCEINLINE SYSTEM_POWER_STATE
+__PdoGetSystemPowerState(
+    IN  PXENVIF_PDO Pdo
+    )
+{
+    PXENVIF_DX      Dx = Pdo->Dx;
+
+    return Dx->SystemPowerState;
+}
+
+static FORCEINLINE VOID
+__PdoSetDevicePowerState(
+    IN  PXENVIF_PDO         Pdo,
+    IN  DEVICE_POWER_STATE  State
+    )
+{
+    PXENVIF_DX              Dx = Pdo->Dx;
+
+    Dx->DevicePowerState = State;
+}
+
+static FORCEINLINE DEVICE_POWER_STATE
+__PdoGetDevicePowerState(
+    IN  PXENVIF_PDO         Pdo
+    )
+{
+    PXENVIF_DX      Dx = Pdo->Dx;
+
+    return Dx->DevicePowerState;
+}
+
+static FORCEINLINE VOID
+__PdoSetMissing(
+    IN  PXENVIF_PDO Pdo,
+    IN  const CHAR  *Reason
+    )
+{
+    Pdo->Reason = Reason;
+    Pdo->Missing = TRUE;
+}
+
+VOID
+PdoSetMissing(
+    IN  PXENVIF_PDO Pdo,
+    IN  const CHAR  *Reason
+    )
+{
+    __PdoSetMissing(Pdo, Reason);
+}
+
+static FORCEINLINE BOOLEAN
+__PdoIsMissing(
+    IN  PXENVIF_PDO Pdo
+    )
+{
+    return Pdo->Missing;
+}
+
+BOOLEAN
+PdoIsMissing(
+    IN  PXENVIF_PDO Pdo
+    )
+{
+    return __PdoIsMissing(Pdo);
+}
+
+static FORCEINLINE VOID
+__PdoSetName(
+    IN  PXENVIF_PDO     Pdo,
+    IN  PANSI_STRING    Ansi
+    )
+{
+    PXENVIF_DX          Dx = Pdo->Dx;
+    NTSTATUS            status;
+
+    status = RtlStringCbPrintfA(Dx->Name, MAX_DEVICE_ID_LEN, "%Z", Ansi);
+    ASSERT(NT_SUCCESS(status));
+}
+
+static FORCEINLINE PCHAR
+__PdoGetName(
+    IN  PXENVIF_PDO Pdo
+    )
+{
+    PXENVIF_DX      Dx = Pdo->Dx;
+
+    return Dx->Name;
+}
+
+PCHAR
+PdoGetName(
+    IN  PXENVIF_PDO Pdo
+    )
+{
+    return __PdoGetName(Pdo);
+}
+
+static FORCEINLINE PDEVICE_OBJECT
+__PdoGetDeviceObject(
+    IN  PXENVIF_PDO Pdo
+    )
+{
+    PXENVIF_DX      Dx = Pdo->Dx;
+
+    return (Dx->DeviceObject);
+}
+    
+PDEVICE_OBJECT
+PdoGetDeviceObject(
+    IN  PXENVIF_PDO Pdo
+    )
+{
+    return __PdoGetDeviceObject(Pdo);
+}
+
+ULONG
+PdoGetLuidIndex(
+    IN  PXENVIF_PDO Pdo
+    )
+{
+    return Pdo->LuidIndex;
+}
+
+PCHAR
+PdoGetAddress(
+    IN  PXENVIF_PDO Pdo
+    )
+{
+    return Pdo->Address;
+}
+
+static FORCEINLINE VOID
+__PdoLink(
+    IN  PXENVIF_PDO Pdo,
+    IN  PXENVIF_FDO Fdo
+    )
+{
+    Pdo->Fdo = Fdo;
+
+    FdoAddPhysicalDeviceObject(Fdo, Pdo);
+}
+
+static FORCEINLINE VOID
+__PdoUnlink(
+    IN  PXENVIF_PDO Pdo
+    )
+{
+    PXENVIF_FDO     Fdo = Pdo->Fdo;
+
+    ASSERT(Fdo != NULL);
+
+    FdoRemovePhysicalDeviceObject(Fdo, Pdo);
+
+    Pdo->Fdo = NULL;
+}
+
+static FORCEINLINE PXENVIF_FDO
+__PdoGetFdo(
+    IN  PXENVIF_PDO Pdo
+    )
+{
+    return Pdo->Fdo;
+}
+
+static FORCEINLINE PXENVIF_FRONTEND
+__PdoGetFrontend(
+    IN  PXENVIF_PDO Pdo
+    )
+{
+    return Pdo->Frontend;
+}
+
+PXENVIF_FRONTEND
+PdoGetFrontend(
+    IN  PXENVIF_PDO Pdo
+    )
+{
+    return __PdoGetFrontend(Pdo);
+}
+
+static FORCEINLINE PXENBUS_EVTCHN_INTERFACE
+__PdoGetEvtchnInterface(
+    IN  PXENVIF_PDO Pdo
+    )
+{
+    return FdoGetEvtchnInterface(__PdoGetFdo(Pdo));
+}
+
+PXENBUS_EVTCHN_INTERFACE
+PdoGetEvtchnInterface(
+    IN  PXENVIF_PDO Pdo
+    )
+{
+    return __PdoGetEvtchnInterface(Pdo);
+}
+
+static FORCEINLINE PXENBUS_DEBUG_INTERFACE
+__PdoGetDebugInterface(
+    IN  PXENVIF_PDO Pdo
+    )
+{
+    return FdoGetDebugInterface(__PdoGetFdo(Pdo));
+}
+
+PXENBUS_DEBUG_INTERFACE
+PdoGetDebugInterface(
+    IN  PXENVIF_PDO Pdo
+    )
+{
+    return __PdoGetDebugInterface(Pdo);
+}
+
+static FORCEINLINE PXENBUS_GNTTAB_INTERFACE
+__PdoGetGnttabInterface(
+    IN  PXENVIF_PDO Pdo
+    )
+{
+    return FdoGetGnttabInterface(__PdoGetFdo(Pdo));
+}
+
+PXENBUS_GNTTAB_INTERFACE
+PdoGetGnttabInterface(
+    IN  PXENVIF_PDO Pdo
+    )
+{
+    return __PdoGetGnttabInterface(Pdo);
+}
+
+static FORCEINLINE PXENBUS_SUSPEND_INTERFACE
+__PdoGetSuspendInterface(
+    IN  PXENVIF_PDO Pdo
+    )
+{
+    return FdoGetSuspendInterface(__PdoGetFdo(Pdo));
+}
+
+PXENBUS_SUSPEND_INTERFACE
+PdoGetSuspendInterface(
+    IN  PXENVIF_PDO Pdo
+    )
+{
+    return __PdoGetSuspendInterface(Pdo);
+}
+
+static FORCEINLINE PXENBUS_STORE_INTERFACE
+__PdoGetStoreInterface(
+    IN  PXENVIF_PDO Pdo
+    )
+{
+    return FdoGetStoreInterface(__PdoGetFdo(Pdo));
+}
+
+PXENBUS_STORE_INTERFACE
+PdoGetStoreInterface(
+    IN  PXENVIF_PDO Pdo
+    )
+{
+    return __PdoGetStoreInterface(Pdo);
+}
+
+static FORCEINLINE PXENFILT_EMULATED_INTERFACE
+__PdoGetEmulatedInterface(
+    IN  PXENVIF_PDO Pdo
+    )
+{
+    return FdoGetEmulatedInterface(__PdoGetFdo(Pdo));
+}
+
+PXENFILT_EMULATED_INTERFACE
+PdoGetEmulatedInterface(
+    IN  PXENVIF_PDO Pdo
+    )
+{
+    return __PdoGetEmulatedInterface(Pdo);
+}
+
+static FORCEINLINE PXENVIF_VIF_INTERFACE
+__PdoGetVifInterface(
+    IN  PXENVIF_PDO Pdo
+    )
+{
+    return &Pdo->VifInterface;
+}
+
+PXENVIF_VIF_INTERFACE
+PdoGetVifInterface(
+    IN  PXENVIF_PDO Pdo
+    )
+{
+    return __PdoGetVifInterface(Pdo);
+}
+
+VOID
+PdoRequestEject(
+    IN  PXENVIF_PDO Pdo
+    )
+{
+    IoRequestDeviceEject(__PdoGetDeviceObject(Pdo));
+}
+
+static FORCEINLINE NTSTATUS
+__PdoD3ToD0(
+    IN  PXENVIF_PDO             Pdo
+    )
+{
+    POWER_STATE                 PowerState;
+    PXENFILT_EMULATED_INTERFACE EmulatedInterface;
+    BOOLEAN                     Present;
+    NTSTATUS                    status;
+
+    Trace("(%s) ====>\n", __PdoGetName(Pdo));
+
+    ASSERT3U(KeGetCurrentIrql(), ==, DISPATCH_LEVEL);
+    ASSERT3U(__PdoGetDevicePowerState(Pdo), ==, PowerDeviceD3);
+
+    EmulatedInterface = __PdoGetEmulatedInterface(Pdo);
+    if (EmulatedInterface != NULL) {
+        EMULATED(Acquire, EmulatedInterface);
+
+        Present = EMULATED(IsPresent,
+                           EmulatedInterface,
+                           "VIF",
+                           __PdoGetName(Pdo));
+
+        EMULATED(Release, EmulatedInterface);
+    } else {
+        // If we cannot confirm presence of the emulated device then
+        // we must assume it is present.
+        Present = TRUE;
+    }
+
+    status = STATUS_UNSUCCESSFUL;
+    if (Present)
+        goto fail1;
+
+    status = FrontendSetState(__PdoGetFrontend(Pdo), FRONTEND_CONNECTED);
+    if (!NT_SUCCESS(status))
+        goto fail2;
+
+    __PdoSetDevicePowerState(Pdo, PowerDeviceD0);
+
+    PowerState.DeviceState = PowerDeviceD0;
+    PoSetPowerState(__PdoGetDeviceObject(Pdo),
+                    DevicePowerState,
+                    PowerState);
+
+    Trace("(%s) <====\n", __PdoGetName(Pdo));
+
+    return STATUS_SUCCESS;
+
+fail2:
+    Error("fail2\n");
+
+fail1:
+    Error("fail1 (%08x)\n", status);
+
+    return status;
+}
+
+static FORCEINLINE VOID
+__PdoD0ToD3(
+    IN  PXENVIF_PDO     Pdo
+    )
+{
+    POWER_STATE         PowerState;
+
+    Trace("(%s) ====>\n", __PdoGetName(Pdo));
+
+    ASSERT3U(KeGetCurrentIrql(), ==, DISPATCH_LEVEL);
+    ASSERT3U(__PdoGetDevicePowerState(Pdo), ==, PowerDeviceD0);
+
+    PowerState.DeviceState = PowerDeviceD3;
+    PoSetPowerState(__PdoGetDeviceObject(Pdo),
+                    DevicePowerState,
+                    PowerState);
+
+    __PdoSetDevicePowerState(Pdo, PowerDeviceD3);
+
+    (VOID) FrontendSetState(__PdoGetFrontend(Pdo), FRONTEND_CLOSED);
+
+    Trace("(%s) <====\n", __PdoGetName(Pdo));
+}
+
+static DECLSPEC_NOINLINE VOID
+PdoSuspendCallbackLate(
+    IN  PVOID               Argument
+    )
+{
+    PXENVIF_PDO             Pdo = Argument;
+    NTSTATUS                status;
+
+    __PdoD0ToD3(Pdo);
+
+    status = __PdoD3ToD0(Pdo);
+    ASSERT(NT_SUCCESS(status));
+}
+
+static DECLSPEC_NOINLINE NTSTATUS
+PdoD3ToD0(
+    IN  PXENVIF_PDO Pdo
+    )
+{
+    KIRQL           Irql;
+    NTSTATUS        status;
+
+    ASSERT3U(KeGetCurrentIrql(), ==, PASSIVE_LEVEL);
+
+    KeRaiseIrql(DISPATCH_LEVEL, &Irql);
+    status = __PdoD3ToD0(Pdo);
+    KeLowerIrql(Irql);
+
+    if (!NT_SUCCESS(status))
+        goto fail1;
+
+    Pdo->SuspendInterface = __PdoGetSuspendInterface(Pdo);
+
+    SUSPEND(Acquire, Pdo->SuspendInterface);
+
+    status = SUSPEND(Register,
+                     Pdo->SuspendInterface,
+                     SUSPEND_CALLBACK_LATE,
+                     PdoSuspendCallbackLate,
+                     Pdo,
+                     &Pdo->SuspendCallbackLate);
+    if (!NT_SUCCESS(status))
+        goto fail2;
+
+    return STATUS_SUCCESS;
+
+fail2:
+    Error("fail2\n");
+
+    SUSPEND(Release, Pdo->SuspendInterface);
+    Pdo->SuspendInterface = NULL;
+
+    KeRaiseIrql(DISPATCH_LEVEL, &Irql);
+    __PdoD0ToD3(Pdo);
+    KeLowerIrql(Irql);
+
+fail1:
+    Error("fail1 (%08x)\n", status);
+
+    return status;
+}
+
+static DECLSPEC_NOINLINE VOID
+PdoD0ToD3(
+    IN  PXENVIF_PDO Pdo
+    )
+{
+    KIRQL           Irql;
+
+    ASSERT3U(KeGetCurrentIrql(), ==, PASSIVE_LEVEL);
+
+    SUSPEND(Deregister,
+            Pdo->SuspendInterface,
+            Pdo->SuspendCallbackLate);
+    Pdo->SuspendCallbackLate = NULL;
+
+    SUSPEND(Release, Pdo->SuspendInterface);
+    Pdo->SuspendInterface = NULL;
+
+    KeRaiseIrql(DISPATCH_LEVEL, &Irql);
+    __PdoD0ToD3(Pdo);
+    KeLowerIrql(Irql);
+}
+
+static DECLSPEC_NOINLINE VOID
+PdoS4ToS3(
+    IN  PXENVIF_PDO Pdo
+    )
+{
+    Trace("(%s) ====>\n", __PdoGetName(Pdo));
+
+    ASSERT3U(KeGetCurrentIrql(), ==, PASSIVE_LEVEL);
+    ASSERT3U(__PdoGetSystemPowerState(Pdo), ==, PowerSystemHibernate);
+
+    __PdoSetSystemPowerState(Pdo, PowerSystemSleeping3);
+
+    Trace("(%s) <====\n", __PdoGetName(Pdo));
+}
+
+static DECLSPEC_NOINLINE VOID
+PdoS3ToS4(
+    IN  PXENVIF_PDO Pdo
+    )
+{
+    Trace("(%s) ====>\n", __PdoGetName(Pdo));
+
+    ASSERT3U(KeGetCurrentIrql(), ==, PASSIVE_LEVEL);
+    ASSERT3U(__PdoGetSystemPowerState(Pdo), ==, PowerSystemSleeping3);
+
+    __PdoSetSystemPowerState(Pdo, PowerSystemHibernate);
+
+    Trace("(%s) <====\n", __PdoGetName(Pdo));
+}
+
+static DECLSPEC_NOINLINE VOID
+PdoParseResources(
+    IN  PXENVIF_PDO             Pdo,
+    IN  PCM_RESOURCE_LIST       RawResourceList,
+    IN  PCM_RESOURCE_LIST       TranslatedResourceList
+    )
+{
+    PCM_PARTIAL_RESOURCE_LIST   RawPartialList;
+    PCM_PARTIAL_RESOURCE_LIST   TranslatedPartialList;
+    ULONG                       Index;
+
+    UNREFERENCED_PARAMETER(Pdo);
+
+    ASSERT3U(RawResourceList->Count, ==, 1);
+    RawPartialList = &RawResourceList->List[0].PartialResourceList;
+
+    ASSERT3U(RawPartialList->Version, ==, 1);
+    ASSERT3U(RawPartialList->Revision, ==, 1);
+
+    ASSERT3U(TranslatedResourceList->Count, ==, 1);
+    TranslatedPartialList = &TranslatedResourceList->List[0].PartialResourceList;
+
+    ASSERT3U(TranslatedPartialList->Version, ==, 1);
+    ASSERT3U(TranslatedPartialList->Revision, ==, 1);
+
+    for (Index = 0; Index < TranslatedPartialList->Count; Index++) {
+        PCM_PARTIAL_RESOURCE_DESCRIPTOR RawPartialDescriptor;
+        PCM_PARTIAL_RESOURCE_DESCRIPTOR TranslatedPartialDescriptor;
+
+        RawPartialDescriptor = &RawPartialList->PartialDescriptors[Index];
+        TranslatedPartialDescriptor = &TranslatedPartialList->PartialDescriptors[Index];
+
+        Info("%s: [%d] %02x:%s\n",
+             __PdoGetName(Pdo),
+             Index,
+             TranslatedPartialDescriptor->Type,
+             PartialResourceDescriptorTypeName(TranslatedPartialDescriptor->Type));
+
+        switch (TranslatedPartialDescriptor->Type) {
+        case CmResourceTypeMemory:
+            Info("RAW: SharedDisposition=%02x Flags=%04x Start = %08x.%08x Length = %08x\n",
+                 RawPartialDescriptor->ShareDisposition,
+                 RawPartialDescriptor->Flags,
+                 RawPartialDescriptor->u.Memory.Start.HighPart,
+                 RawPartialDescriptor->u.Memory.Start.LowPart,
+                 RawPartialDescriptor->u.Memory.Length);
+
+            Info("TRANSLATED: SharedDisposition=%02x Flags=%04x Start = %08x.%08x Length = %08x\n",
+                 TranslatedPartialDescriptor->ShareDisposition,
+                 TranslatedPartialDescriptor->Flags,
+                 TranslatedPartialDescriptor->u.Memory.Start.HighPart,
+                 TranslatedPartialDescriptor->u.Memory.Start.LowPart,
+                 TranslatedPartialDescriptor->u.Memory.Length);
+            break;
+
+        case CmResourceTypeInterrupt:
+            Info("RAW: SharedDisposition=%02x Flags=%04x MessageCount=%04x Vector = %08x Affinity = %p\n",
+                 RawPartialDescriptor->ShareDisposition,
+                 RawPartialDescriptor->Flags,
+                 RawPartialDescriptor->u.MessageInterrupt.Raw.MessageCount,
+                 RawPartialDescriptor->u.MessageInterrupt.Raw.Vector,
+                 (PVOID)RawPartialDescriptor->u.MessageInterrupt.Raw.Affinity);
+
+            Info("TRANSLATED: SharedDisposition=%02x Flags=%04x Level = %08x Vector = %08x Affinity = %p\n",
+                 TranslatedPartialDescriptor->ShareDisposition,
+                 TranslatedPartialDescriptor->Flags,
+                 TranslatedPartialDescriptor->u.MessageInterrupt.Translated.Level,
+                 TranslatedPartialDescriptor->u.MessageInterrupt.Translated.Vector,
+                 (PVOID)TranslatedPartialDescriptor->u.MessageInterrupt.Translated.Affinity);
+            break;
+
+        default:
+            break;
+        }
+    }
+
+    Trace("<====\n");
+}
+
+static DECLSPEC_NOINLINE NTSTATUS
+PdoStartDevice(
+    IN  PXENVIF_PDO     Pdo,
+    IN  PIRP            Irp
+    )
+{
+    PIO_STACK_LOCATION  StackLocation;
+    HANDLE              Key;
+    PANSI_STRING        Ansi;
+    NTSTATUS            status;
+
+    StackLocation = IoGetCurrentIrpStackLocation(Irp);
+
+    PdoParseResources(Pdo,
+                      StackLocation->Parameters.StartDevice.AllocatedResources,
+                      StackLocation->Parameters.StartDevice.AllocatedResourcesTranslated);
+
+    status = RegistryOpenSoftwareKey(__PdoGetDeviceObject(Pdo),
+                                     KEY_READ,
+                                     &Key);
+    if (!NT_SUCCESS(status))
+        goto fail1;
+
+    status = RegistryQueryDwordValue(Key, "NetLuidIndex", &Pdo->LuidIndex);
+    if (!NT_SUCCESS(status))
+        goto fail2;
+
+    status = RegistryQuerySzValue(Key, "NetworkAddress", &Ansi);
+    if (NT_SUCCESS(status)) {
+        Pdo->Address = __PdoAllocate(Ansi[0].Length + sizeof (CHAR));
+
+        status = STATUS_NO_MEMORY;
+        if (Pdo->Address == NULL)
+            goto fail3;
+
+        RtlCopyMemory(Pdo->Address, Ansi[0].Buffer, Ansi[0].Length);
+
+        RegistryFreeSzValue(Ansi);
+    }
+
+    __PdoSetSystemPowerState(Pdo, PowerSystemHibernate);
+    PdoS4ToS3(Pdo);
+    __PdoSetSystemPowerState(Pdo, PowerSystemWorking);
+
+    status = PdoD3ToD0(Pdo);
+    if (!NT_SUCCESS(status))
+        goto fail4;
+
+    __PdoSetDevicePnpState(Pdo, Started);
+
+    RegistryCloseKey(Key);
+
+    Irp->IoStatus.Status = STATUS_SUCCESS;
+    IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+    return STATUS_SUCCESS;
+
+fail4:
+    Error("fail4\n");
+
+    __PdoSetSystemPowerState(Pdo, PowerSystemSleeping3);
+    PdoS3ToS4(Pdo);
+    __PdoSetSystemPowerState(Pdo, PowerSystemShutdown);
+
+fail3:
+    Error("fail3\n");
+
+    Pdo->LuidIndex = 0;
+
+fail2:
+    Error("fail2\n");
+
+    RegistryCloseKey(Key);
+
+fail1:
+    Error("fail1 (%08x)\n", status);
+
+
+    Irp->IoStatus.Status = status;
+    IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+    return status;
+}
+
+static DECLSPEC_NOINLINE NTSTATUS
+PdoQueryStopDevice(
+    IN  PXENVIF_PDO Pdo,
+    IN  PIRP        Irp
+    )
+{
+    NTSTATUS        status;
+
+    __PdoSetDevicePnpState(Pdo, StopPending);
+    status = STATUS_SUCCESS;
+
+    Irp->IoStatus.Status = status;
+    IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+    return status;
+}
+
+static DECLSPEC_NOINLINE NTSTATUS
+PdoCancelStopDevice(
+    IN  PXENVIF_PDO Pdo,
+    IN  PIRP        Irp
+    )
+{
+    NTSTATUS        status;
+
+    __PdoRestoreDevicePnpState(Pdo, StopPending);
+
+    status = STATUS_SUCCESS;
+
+    Irp->IoStatus.Status = status;
+    IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+    return status;
+}
+
+static DECLSPEC_NOINLINE NTSTATUS
+PdoStopDevice(
+    IN  PXENVIF_PDO     Pdo,
+    IN  PIRP            Irp
+    )
+{
+    NTSTATUS            status;
+
+    PdoD0ToD3(Pdo);
+
+    __PdoSetSystemPowerState(Pdo, PowerSystemSleeping3);
+    PdoS3ToS4(Pdo);
+    __PdoSetSystemPowerState(Pdo, PowerSystemShutdown);
+
+    Pdo->LuidIndex = 0;
+
+    if (Pdo->Address != NULL) {
+        __PdoFree(Pdo->Address);
+        Pdo->Address = NULL;
+    }
+
+    __PdoSetDevicePnpState(Pdo, Stopped);
+    status = STATUS_SUCCESS;
+
+    Irp->IoStatus.Status = status;
+    IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+    return status;
+}
+
+static DECLSPEC_NOINLINE NTSTATUS
+PdoQueryRemoveDevice(
+    IN  PXENVIF_PDO Pdo,
+    IN  PIRP        Irp
+    )
+{
+    NTSTATUS        status;
+
+    __PdoSetDevicePnpState(Pdo, RemovePending);
+    status = STATUS_SUCCESS;
+
+    Irp->IoStatus.Status = status;
+    IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+    return status;
+}
+
+static DECLSPEC_NOINLINE NTSTATUS
+PdoCancelRemoveDevice(
+    IN  PXENVIF_PDO Pdo,
+    IN  PIRP        Irp
+    )
+{
+    NTSTATUS        status;
+
+    FrontendRemoveFailed(__PdoGetFrontend(Pdo));
+
+    __PdoRestoreDevicePnpState(Pdo, RemovePending);
+
+    status = STATUS_SUCCESS;
+
+    Irp->IoStatus.Status = status;
+    IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+    return status;
+}
+
+static DECLSPEC_NOINLINE NTSTATUS
+PdoSurpriseRemoval(
+    IN  PXENVIF_PDO Pdo,
+    IN  PIRP        Irp
+    )
+{
+    NTSTATUS        status;
+
+    Warning("%s\n", __PdoGetName(Pdo));
+
+    __PdoSetDevicePnpState(Pdo, SurpriseRemovePending);
+    status = STATUS_SUCCESS;
+
+    Irp->IoStatus.Status = status;
+    IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+    return status;
+}
+
+static DECLSPEC_NOINLINE NTSTATUS
+PdoRemoveDevice(
+    IN  PXENVIF_PDO     Pdo,
+    IN  PIRP            Irp
+    )
+{
+    PXENVIF_FDO         Fdo = __PdoGetFdo(Pdo);
+    BOOLEAN             NeedInvalidate;
+    NTSTATUS            status;
+
+    if (__PdoGetDevicePowerState(Pdo) != PowerDeviceD0)
+        goto done;
+
+    PdoD0ToD3(Pdo);
+
+    __PdoSetSystemPowerState(Pdo, PowerSystemSleeping3);
+    PdoS3ToS4(Pdo);
+    __PdoSetSystemPowerState(Pdo, PowerSystemShutdown);
+
+done:
+    Pdo->LuidIndex = 0;
+
+    if (Pdo->Address != NULL) {
+        __PdoFree(Pdo->Address);
+        Pdo->Address = NULL;
+    }
+
+    NeedInvalidate = FALSE;
+
+    FdoAcquireMutex(Fdo);
+
+    if (__PdoIsMissing(Pdo) ||
+        __PdoGetDevicePnpState(Pdo) == SurpriseRemovePending)
+        __PdoSetDevicePnpState(Pdo, Deleted);
+    else
+        __PdoSetDevicePnpState(Pdo, Enumerated);
+
+    if (__PdoIsMissing(Pdo)) {
+        if (__PdoGetDevicePnpState(Pdo) == Deleted)
+            PdoDestroy(Pdo);
+        else
+            NeedInvalidate = TRUE;
+    }
+
+    FdoReleaseMutex(Fdo);
+
+    if (NeedInvalidate)
+        IoInvalidateDeviceRelations(FdoGetPhysicalDeviceObject(Fdo), 
+                                    BusRelations);
+
+    status = STATUS_SUCCESS;
+
+    Irp->IoStatus.Status = status;
+    IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+    return status;
+}
+
+static DECLSPEC_NOINLINE NTSTATUS
+PdoQueryDeviceRelations(
+    IN  PXENVIF_PDO     Pdo,
+    IN  PIRP            Irp
+    )
+{
+    PIO_STACK_LOCATION  StackLocation;
+    PDEVICE_RELATIONS   Relations;
+    NTSTATUS            status;
+
+    StackLocation = IoGetCurrentIrpStackLocation(Irp);
+
+    status = Irp->IoStatus.Status;
+
+    if (StackLocation->Parameters.QueryDeviceRelations.Type != TargetDeviceRelation)
+        goto done;
+
+    Relations = ExAllocatePoolWithTag(PagedPool, sizeof (DEVICE_RELATIONS), 'FIV');
+
+    status = STATUS_NO_MEMORY;
+    if (Relations == NULL)
+        goto done;
+
+    RtlZeroMemory(Relations, sizeof (DEVICE_RELATIONS));
+
+    Relations->Count = 1;
+    ObReferenceObject(__PdoGetDeviceObject(Pdo));
+    Relations->Objects[0] = __PdoGetDeviceObject(Pdo);
+
+    Irp->IoStatus.Information = (ULONG_PTR)Relations;
+    status = STATUS_SUCCESS;
+
+done:
+    Irp->IoStatus.Status = status;
+    IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+    return status;
+}
+
+static FORCEINLINE NTSTATUS
+__PdoDelegateIrp(
+    IN  PXENVIF_PDO Pdo,
+    IN  PIRP        Irp
+    )
+{
+    return FdoDelegateIrp(Pdo->Fdo, Irp);
+}
+
+static NTSTATUS
+PdoQueryVifInterface(
+    IN  PXENVIF_PDO         Pdo,
+    IN  PIRP                Irp
+    )
+{
+    PIO_STACK_LOCATION      StackLocation;
+    USHORT                  Size;
+    USHORT                  Version;
+    PINTERFACE              Interface;
+    NTSTATUS                status;
+
+    status = Irp->IoStatus.Status;        
+
+    StackLocation = IoGetCurrentIrpStackLocation(Irp);
+    Size = StackLocation->Parameters.QueryInterface.Size;
+    Version = StackLocation->Parameters.QueryInterface.Version;
+    Interface = StackLocation->Parameters.QueryInterface.Interface;
+
+    if (StackLocation->Parameters.QueryInterface.Version != VIF_INTERFACE_VERSION)
+        goto done;
+
+    status = STATUS_BUFFER_TOO_SMALL;        
+    if (StackLocation->Parameters.QueryInterface.Size < sizeof (INTERFACE))
+        goto done;
+
+    Interface->Size = sizeof (INTERFACE);
+    Interface->Version = VIF_INTERFACE_VERSION;
+    Interface->Context = __PdoGetVifInterface(Pdo);
+    Interface->InterfaceReference = NULL;
+    Interface->InterfaceDereference = NULL;
+
+    Irp->IoStatus.Information = 0;
+    status = STATUS_SUCCESS;
+
+done:
+    return status;
+}
+
+struct _INTERFACE_ENTRY {
+    const GUID  *Guid;
+    const CHAR  *Name;
+    NTSTATUS    (*Handler)(PXENVIF_PDO, PIRP);
+};
+
+#define DEFINE_HANDLER(_Guid, _Function)    \
+        { &GUID_ ## _Guid, #_Guid, (_Function) }
+
+struct _INTERFACE_ENTRY PdoInterfaceTable[] = {
+    DEFINE_HANDLER(VIF_INTERFACE, PdoQueryVifInterface),
+    { NULL, NULL, NULL }
+};
+
+static DECLSPEC_NOINLINE NTSTATUS
+PdoQueryInterface(
+    IN  PXENVIF_PDO         Pdo,
+    IN  PIRP                Irp
+    )
+{
+    PIO_STACK_LOCATION      StackLocation;
+    const GUID              *InterfaceType;
+    struct _INTERFACE_ENTRY *Entry;
+    NTSTATUS                status;
+
+    status = Irp->IoStatus.Status;        
+
+    if (status != STATUS_NOT_SUPPORTED)
+        goto done;
+
+    StackLocation = IoGetCurrentIrpStackLocation(Irp);
+    InterfaceType = StackLocation->Parameters.QueryInterface.InterfaceType;
+
+    for (Entry = PdoInterfaceTable; Entry->Guid != NULL; Entry++) {
+        if (IsEqualGUID(InterfaceType, Entry->Guid)) {
+            Trace("%s: %s\n",
+                  __PdoGetName(Pdo),
+                  Entry->Name);
+            status = Entry->Handler(Pdo, Irp);
+            goto done;
+        }
+    }
+
+    Trace("%s: (%08x-%04x-%04x-%02x%02x%02x%02x%02x%02x%02x%02x)\n",
+          __PdoGetName(Pdo),
+          InterfaceType->Data1,
+          InterfaceType->Data2,
+          InterfaceType->Data3,
+          InterfaceType->Data4[0],
+          InterfaceType->Data4[1],
+          InterfaceType->Data4[2],
+          InterfaceType->Data4[3],
+          InterfaceType->Data4[4],
+          InterfaceType->Data4[5],
+          InterfaceType->Data4[6],
+          InterfaceType->Data4[7]);
+
+    status = __PdoDelegateIrp(Pdo, Irp);
+
+done:
+    Irp->IoStatus.Status = status;
+    IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+    return status;
+}
+
+static DECLSPEC_NOINLINE NTSTATUS
+PdoQueryCapabilities(
+    IN  PXENVIF_PDO         Pdo,
+    IN  PIRP                Irp
+    )
+{
+    PIO_STACK_LOCATION      StackLocation;
+    PDEVICE_CAPABILITIES    Capabilities;
+    SYSTEM_POWER_STATE      SystemPowerState;
+    NTSTATUS                status;
+
+    UNREFERENCED_PARAMETER(Pdo);
+
+    StackLocation = IoGetCurrentIrpStackLocation(Irp);
+    Capabilities = StackLocation->Parameters.DeviceCapabilities.Capabilities;
+
+    status = STATUS_INVALID_PARAMETER;
+    if (Capabilities->Version != 1)
+        goto done;
+
+    Capabilities->DeviceD1 = 0;
+    Capabilities->DeviceD2 = 0;
+    Capabilities->LockSupported = 0;
+    Capabilities->EjectSupported = 1;
+    Capabilities->Removable = 1;
+    Capabilities->DockDevice = 0;
+    Capabilities->UniqueID = 1;
+    Capabilities->SilentInstall = 1;
+    Capabilities->RawDeviceOK = 0;
+    Capabilities->SurpriseRemovalOK = 1;
+    Capabilities->HardwareDisabled = 0;
+    Capabilities->NoDisplayInUI = 0;
+
+    Capabilities->Address = 0xffffffff;
+    Capabilities->UINumber = 0xffffffff;
+
+    for (SystemPowerState = 0; SystemPowerState < PowerSystemMaximum; SystemPowerState++) {
+        switch (SystemPowerState) {
+        case PowerSystemUnspecified:
+        case PowerSystemSleeping1:
+        case PowerSystemSleeping2:
+            break;
+
+        case PowerSystemWorking:
+            Capabilities->DeviceState[SystemPowerState] = PowerDeviceD0;
+            break;
+
+        default:
+            Capabilities->DeviceState[SystemPowerState] = PowerDeviceD3;
+            break;
+        }
+    }
+
+    Capabilities->SystemWake = PowerSystemUnspecified;
+    Capabilities->DeviceWake = PowerDeviceUnspecified;
+    Capabilities->D1Latency = 0;
+    Capabilities->D2Latency = 0;
+    Capabilities->D3Latency = 0;
+
+    status = STATUS_SUCCESS;
+
+done:
+    Irp->IoStatus.Status = status;
+    IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+    return status;
+}
+
+static DECLSPEC_NOINLINE NTSTATUS
+PdoQueryResourceRequirements(
+    IN  PXENVIF_PDO                 Pdo,
+    IN  PIRP                        Irp
+    )
+{
+    IO_RESOURCE_DESCRIPTOR          Memory;
+    IO_RESOURCE_DESCRIPTOR          Interrupt;
+    ULONG                           Count;
+    ULONG                           Size;
+    PIO_RESOURCE_REQUIREMENTS_LIST  Requirements;
+    PIO_RESOURCE_LIST               List;
+    NTSTATUS                        status;
+
+    UNREFERENCED_PARAMETER(Pdo);
+
+    RtlZeroMemory(&Memory, sizeof (IO_RESOURCE_DESCRIPTOR));
+    Memory.Type = CmResourceTypeMemory;
+    Memory.ShareDisposition = CmResourceShareDeviceExclusive;
+    Memory.Flags = CM_RESOURCE_MEMORY_READ_WRITE |
+                   CM_RESOURCE_MEMORY_PREFETCHABLE |
+                   CM_RESOURCE_MEMORY_CACHEABLE;
+
+    Memory.u.Memory.Length = PAGE_SIZE;
+    Memory.u.Memory.Alignment = PAGE_SIZE;
+    Memory.u.Memory.MinimumAddress.QuadPart = 0;
+    Memory.u.Memory.MaximumAddress.QuadPart = -1;
+
+    RtlZeroMemory(&Interrupt, sizeof (IO_RESOURCE_DESCRIPTOR));
+    Interrupt.Type = CmResourceTypeInterrupt;
+    Interrupt.ShareDisposition = CmResourceShareDeviceExclusive;
+    Interrupt.Flags = CM_RESOURCE_INTERRUPT_MESSAGE;
+
+    Count = 1; // Hard-code for now
+
+    Interrupt.u.Interrupt.MinimumVector = (ULONG)CM_RESOURCE_INTERRUPT_MESSAGE_TOKEN - Count + 1;
+    Interrupt.u.Interrupt.MaximumVector = (ULONG)CM_RESOURCE_INTERRUPT_MESSAGE_TOKEN;
+    Interrupt.u.Interrupt.AffinityPolicy = IrqPolicyOneCloseProcessor;
+    Interrupt.u.Interrupt.PriorityPolicy = IrqPriorityUndefined;
+
+    Size = sizeof (IO_RESOURCE_DESCRIPTOR) * 2;
+    Size += FIELD_OFFSET(IO_RESOURCE_LIST, Descriptors);
+    Size += FIELD_OFFSET(IO_RESOURCE_REQUIREMENTS_LIST, List);
+
+    Requirements = ExAllocatePoolWithTag(PagedPool, Size, 'FIV');
+
+    status = STATUS_NO_MEMORY;
+    if (Requirements == NULL)
+        goto fail1;
+
+    RtlZeroMemory(Requirements, Size);
+
+    Requirements->ListSize = Size;
+    Requirements->InterfaceType = PNPBus;
+    Requirements->BusNumber = 0;
+    Requirements->SlotNumber = 0;
+    Requirements->AlternativeLists = 1;
+
+    List = &Requirements->List[0];
+    List->Version = 1;
+    List->Revision = 1;
+    List->Count = 2;
+    List->Descriptors[0] = Memory;
+    List->Descriptors[1] = Interrupt;
+
+    Irp->IoStatus.Information = (ULONG_PTR)Requirements;
+
+    Irp->IoStatus.Status = STATUS_SUCCESS;
+    IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+    return STATUS_SUCCESS;
+
+fail1:
+    Error("fail1 (%08x)\n", status);
+
+    Irp->IoStatus.Status = status;
+    IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+    return status;
+}
+
+#define MAXTEXTLEN  128
+
+static DECLSPEC_NOINLINE NTSTATUS
+PdoQueryDeviceText(
+    IN  PXENVIF_PDO     Pdo,
+    IN  PIRP            Irp
+    )
+{
+    PIO_STACK_LOCATION  StackLocation;
+    PWCHAR              Buffer;
+    UNICODE_STRING      Text;
+    NTSTATUS            status;
+
+    StackLocation = IoGetCurrentIrpStackLocation(Irp);
+
+    switch (StackLocation->Parameters.QueryDeviceText.DeviceTextType) {
+    case DeviceTextDescription:
+        Trace("DeviceTextDescription\n");
+        break;
+
+    case DeviceTextLocationInformation:
+        Trace("DeviceTextLocationInformation\n");
+        break;
+
+    default:
+        Irp->IoStatus.Information = 0;
+        status = STATUS_NOT_SUPPORTED;
+        goto done;
+    }
+
+    Buffer = ExAllocatePoolWithTag(PagedPool, MAXTEXTLEN, 'FIV');
+
+    status = STATUS_NO_MEMORY;
+    if (Buffer == NULL)
+        goto done;
+
+    RtlZeroMemory(Buffer, MAXTEXTLEN);
+
+    Text.Buffer = Buffer;
+    Text.MaximumLength = MAXTEXTLEN;
+    Text.Length = 0;
+
+    switch (StackLocation->Parameters.QueryDeviceText.DeviceTextType) {
+    case DeviceTextDescription: {
+        status = RtlStringCbPrintfW(Buffer,
+                                    MAXTEXTLEN,
+                                    L"%hs %hs",
+                                    FdoGetName(Pdo->Fdo),
+                                    __PdoGetName(Pdo));
+        ASSERT(NT_SUCCESS(status));
+
+        Buffer += wcslen(Buffer);
+
+        break;
+    }
+    case DeviceTextLocationInformation:
+        status = RtlStringCbPrintfW(Buffer,
+                                    MAXTEXTLEN,
+                                    L"%hs",
+                                    __PdoGetName(Pdo));
+        ASSERT(NT_SUCCESS(status));
+
+        Buffer += wcslen(Buffer);
+
+        break;
+
+    default:
+        ASSERT(FALSE);
+        break;
+    }
+
+    Text.Length = (USHORT)((ULONG_PTR)Buffer - (ULONG_PTR)Text.Buffer);
+
+    ASSERT3U(KeGetCurrentIrql(), ==, PASSIVE_LEVEL);
+
+    Trace("%s: %wZ\n", __PdoGetName(Pdo), &Text);
+
+    Irp->IoStatus.Information = (ULONG_PTR)Text.Buffer;
+    status = STATUS_SUCCESS;
+
+done:
+    Irp->IoStatus.Status = status;
+    IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+    return status;
+}
+
+static DECLSPEC_NOINLINE NTSTATUS
+PdoReadConfig(
+    IN  PXENVIF_PDO Pdo,
+    IN  PIRP        Irp
+    )
+{
+    UNREFERENCED_PARAMETER(Pdo);
+
+    Irp->IoStatus.Status = STATUS_NOT_SUPPORTED;
+    IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+    return STATUS_NOT_SUPPORTED;
+}
+
+static DECLSPEC_NOINLINE NTSTATUS
+PdoWriteConfig(
+    IN  PXENVIF_PDO Pdo,
+    IN  PIRP        Irp
+    )
+{
+    UNREFERENCED_PARAMETER(Pdo);
+
+    Irp->IoStatus.Status = STATUS_NOT_SUPPORTED;
+    IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+    return STATUS_NOT_SUPPORTED;
+}
+
+static DECLSPEC_NOINLINE NTSTATUS
+PdoQueryId(
+    IN  PXENVIF_PDO     Pdo,
+    IN  PIRP            Irp
+    )
+{
+    PIO_STACK_LOCATION  StackLocation;
+    PWCHAR              Buffer;
+    UNICODE_STRING      Id;
+    ULONG               Type;
+    NTSTATUS            status;
+
+    StackLocation = IoGetCurrentIrpStackLocation(Irp);
+
+    switch (StackLocation->Parameters.QueryId.IdType) {
+    case BusQueryInstanceID:
+        Trace("BusQueryInstanceID\n");
+        break;
+
+    case BusQueryDeviceID:
+        Trace("BusQueryDeviceID\n");
+        break;
+
+    case BusQueryHardwareIDs:
+        Trace("BusQueryHardwareIDs\n");
+        break;
+
+    case BusQueryCompatibleIDs:
+        Trace("BusQueryCompatibleIDs\n");
+        break;
+
+    case BusQueryContainerID:
+        Trace("BusQueryContainerID\n");
+        break;
+
+    default:
+        Irp->IoStatus.Information = 0;
+        status = STATUS_NOT_SUPPORTED;
+        goto done;
+    }
+
+    Buffer = ExAllocatePoolWithTag(PagedPool, MAX_DEVICE_ID_LEN, 'FIV');
+
+    status = STATUS_NO_MEMORY;
+    if (Buffer == NULL)
+        goto done;
+
+    RtlZeroMemory(Buffer, MAX_DEVICE_ID_LEN);
+
+    Id.Buffer = Buffer;
+    Id.MaximumLength = MAX_DEVICE_ID_LEN - 1;
+    Id.Length = 0;
+
+    switch (StackLocation->Parameters.QueryId.IdType) {
+    case BusQueryInstanceID:
+    case BusQueryContainerID:
+        Type = REG_SZ;
+
+        status = RtlStringCbPrintfW(Buffer,
+                                    MAX_DEVICE_ID_LEN - 1,
+                                    L"%hs",
+                                    __PdoGetName(Pdo));
+        ASSERT(NT_SUCCESS(status));
+
+        Buffer += wcslen(Buffer);
+
+        break;
+
+    case BusQueryDeviceID:
+        Type = REG_SZ;
+
+        status = RtlStringCbPrintfW(Buffer,
+                                    MAX_DEVICE_ID_LEN - 1,
+                                    L"XENVIF\\DEVICE&REV_%02X",
+                                    PDO_REVISION);
+        ASSERT(NT_SUCCESS(status));
+
+        Buffer += wcslen(Buffer);
+
+        break;
+
+    case BusQueryHardwareIDs:
+    case BusQueryCompatibleIDs: {
+        ULONG   Length;
+
+        Type = REG_MULTI_SZ;
+
+        Length = MAX_DEVICE_ID_LEN;
+        status = RtlStringCbPrintfW(Buffer,
+                                    Length,
+                                    L"XENVIF\\DEVICE&REV_%02X",
+                                    PDO_REVISION);
+        ASSERT(NT_SUCCESS(status));
+
+        Buffer += wcslen(Buffer);
+        Buffer++;
+
+        Length = MAX_DEVICE_ID_LEN - (ULONG)((ULONG_PTR)Buffer - (ULONG_PTR)Id.Buffer); 
+        status = RtlStringCbPrintfW(Buffer,
+                                    Length,
+                                    L"XENDEVICE");
+        ASSERT(NT_SUCCESS(status));
+
+        Buffer += wcslen(Buffer);
+        Buffer++;
+
+        break;
+    }
+    default:
+        Type = REG_NONE;
+
+        ASSERT(FALSE);
+        break;
+    }
+
+    ASSERT3U(KeGetCurrentIrql(), ==, PASSIVE_LEVEL);
+
+    Id.Length = (USHORT)((ULONG_PTR)Buffer - (ULONG_PTR)Id.Buffer);
+    Buffer = Id.Buffer;
+
+    switch (Type) {
+    case REG_SZ:
+        Trace("- %ws\n", Buffer);
+        break;
+
+    case REG_MULTI_SZ:
+        do {
+            Trace("- %ws\n", Buffer);
+            Buffer += wcslen(Buffer);
+            Buffer++;
+        } while (*Buffer != L'\0');
+        break;
+
+    default:
+        ASSERT(FALSE);
+        break;
+    }
+
+    Irp->IoStatus.Information = (ULONG_PTR)Id.Buffer;
+    status = STATUS_SUCCESS;
+
+done:
+    Irp->IoStatus.Status = status;
+    IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+    return status;
+}
+
+static DECLSPEC_NOINLINE NTSTATUS
+PdoQueryBusInformation(
+    IN  PXENVIF_PDO         Pdo,
+    IN  PIRP                Irp
+    )
+{
+    PPNP_BUS_INFORMATION    Info;
+    NTSTATUS                status;
+
+    UNREFERENCED_PARAMETER(Pdo);
+
+    Info = ExAllocatePoolWithTag(PagedPool, sizeof (PNP_BUS_INFORMATION), 'FIV');
+
+    status = STATUS_NO_MEMORY;
+    if (Info == NULL)
+        goto done;
+
+    RtlZeroMemory(Info, sizeof (PNP_BUS_INFORMATION));
+
+    Info->BusTypeGuid = GUID_BUS_TYPE_INTERNAL;
+    Info->LegacyBusType = PNPBus;
+    Info->BusNumber = 0;
+
+    Irp->IoStatus.Information = (ULONG_PTR)Info;
+    status = STATUS_SUCCESS;
+
+done:
+    Irp->IoStatus.Status = status;
+    IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+    return status;
+}
+
+static DECLSPEC_NOINLINE NTSTATUS
+PdoDeviceUsageNotification(
+    IN  PXENVIF_PDO Pdo,
+    IN  PIRP        Irp
+    )
+{
+    NTSTATUS        status;
+
+    status = __PdoDelegateIrp(Pdo, Irp);
+
+    Irp->IoStatus.Status = status;
+    IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+    return status;
+}
+
+static DECLSPEC_NOINLINE NTSTATUS
+PdoEject(
+    IN  PXENVIF_PDO Pdo,
+    IN  PIRP        Irp
+    )
+{
+    PXENVIF_FDO     Fdo = __PdoGetFdo(Pdo);
+    NTSTATUS        status;
+
+    Trace("%s\n", __PdoGetName(Pdo));
+
+    FdoAcquireMutex(Fdo);
+
+    __PdoSetDevicePnpState(Pdo, Deleted);
+    __PdoSetMissing(Pdo, "device ejected");
+
+    PdoDestroy(Pdo);
+
+    FdoReleaseMutex(Fdo);
+
+    status = STATUS_SUCCESS;
+
+    Irp->IoStatus.Status = status;
+    IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+    return status;
+}
+
+static DECLSPEC_NOINLINE NTSTATUS
+PdoDispatchPnp(
+    IN  PXENVIF_PDO     Pdo,
+    IN  PIRP            Irp
+    )
+{
+    PIO_STACK_LOCATION  StackLocation;
+    UCHAR               MinorFunction;
+    NTSTATUS            status;
+
+    StackLocation = IoGetCurrentIrpStackLocation(Irp);
+    MinorFunction = StackLocation->MinorFunction;
+
+    Trace("====> (%s) (%02x:%s)\n",
+          __PdoGetName(Pdo),
+          MinorFunction, 
+          PnpMinorFunctionName(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_CANCEL_REMOVE_DEVICE:
+        status = PdoCancelRemoveDevice(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_QUERY_DEVICE_RELATIONS:
+        status = PdoQueryDeviceRelations(Pdo, Irp);
+        break;
+
+    case IRP_MN_QUERY_INTERFACE:
+        status = PdoQueryInterface(Pdo, Irp);
+        break;
+
+    case IRP_MN_QUERY_CAPABILITIES:
+        status = PdoQueryCapabilities(Pdo, Irp);
+        break;
+
+    case IRP_MN_QUERY_RESOURCE_REQUIREMENTS:
+        status = PdoQueryResourceRequirements(Pdo, Irp);
+        break;
+
+    case IRP_MN_QUERY_DEVICE_TEXT:
+        status = PdoQueryDeviceText(Pdo, Irp);
+        break;
+
+    case IRP_MN_READ_CONFIG:
+        status = PdoReadConfig(Pdo, Irp);
+        break;
+
+    case IRP_MN_WRITE_CONFIG:
+        status = PdoWriteConfig(Pdo, Irp);
+        break;
+
+    case IRP_MN_QUERY_ID:
+        status = PdoQueryId(Pdo, Irp);
+        break;
+
+    case IRP_MN_QUERY_BUS_INFORMATION:
+        status = PdoQueryBusInformation(Pdo, Irp);
+        break;
+
+    case IRP_MN_DEVICE_USAGE_NOTIFICATION:
+        status = PdoDeviceUsageNotification(Pdo, Irp);
+        break;
+
+    case IRP_MN_EJECT:
+        status = PdoEject(Pdo, Irp);
+        break;
+
+    default:
+        status = Irp->IoStatus.Status;
+        IoCompleteRequest(Irp, IO_NO_INCREMENT);
+        break;
+    }
+
+    Trace("<==== (%02x:%s)(%08x)\n",
+          MinorFunction, 
+          PnpMinorFunctionName(MinorFunction),
+          status);
+
+    return status;
+}
+
+static FORCEINLINE NTSTATUS
+__PdoSetDevicePower(
+    IN  PXENVIF_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:%s)\n",
+          __PdoGetName(Pdo),
+          PowerDeviceStateName(DeviceState), 
+          PowerActionName(PowerAction));
+
+    ASSERT3U(PowerAction, <, PowerActionShutdown);
+
+    if (__PdoGetDevicePowerState(Pdo) > DeviceState) {
+        Trace("%s: POWERING UP: %s -> %s\n",
+              __PdoGetName(Pdo),
+              PowerDeviceStateName(__PdoGetDevicePowerState(Pdo)),
+              PowerDeviceStateName(DeviceState));
+
+        ASSERT3U(DeviceState, ==, PowerDeviceD0);
+        status = PdoD3ToD0(Pdo);
+        ASSERT(NT_SUCCESS(status));
+    } else if (__PdoGetDevicePowerState(Pdo) < DeviceState) {
+        Trace("%s: POWERING DOWN: %s -> %s\n",
+              __PdoGetName(Pdo),
+              PowerDeviceStateName(__PdoGetDevicePowerState(Pdo)),
+              PowerDeviceStateName(DeviceState));
+
+        ASSERT3U(DeviceState, ==, PowerDeviceD3);
+        PdoD0ToD3(Pdo);
+    }
+
+    Irp->IoStatus.Status = STATUS_SUCCESS;
+    IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+    Trace("<==== (%s:%s)\n",
+          PowerDeviceStateName(DeviceState), 
+          PowerActionName(PowerAction));
+
+    return STATUS_SUCCESS;
+}
+
+static NTSTATUS
+PdoDevicePower(
+    IN  PXENVIF_THREAD  Self,
+    IN  PVOID           Context
+    )
+{
+    PXENVIF_PDO         Pdo = Context;
+    PKEVENT             Event;
+
+    Event = ThreadGetEvent(Self);
+
+    for (;;) {
+        PIRP    Irp;
+
+        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();
+
+        (VOID) __PdoSetDevicePower(Pdo, Irp);
+    }
+
+    return STATUS_SUCCESS;
+}
+
+static FORCEINLINE NTSTATUS
+__PdoSetSystemPower(
+    IN  PXENVIF_PDO         Pdo,
+    IN  PIRP                Irp
+    )
+{
+    PIO_STACK_LOCATION      StackLocation;
+    SYSTEM_POWER_STATE      SystemState;
+    POWER_ACTION            PowerAction;
+
+    StackLocation = IoGetCurrentIrpStackLocation(Irp);
+    SystemState = StackLocation->Parameters.Power.State.SystemState;
+    PowerAction = StackLocation->Parameters.Power.ShutdownType;
+
+    Trace("====> (%s) (%s:%s)\n",
+          __PdoGetName(Pdo),
+          PowerSystemStateName(SystemState), 
+          PowerActionName(PowerAction));
+
+    ASSERT3U(PowerAction, <, PowerActionShutdown);
+
+    if (__PdoGetSystemPowerState(Pdo) > SystemState) {
+        if (SystemState < PowerSystemHibernate &&
+            __PdoGetSystemPowerState(Pdo) >= PowerSystemHibernate) {
+            __PdoSetSystemPowerState(Pdo, PowerSystemHibernate);
+            PdoS4ToS3(Pdo);
+        }
+
+        Trace("%s: POWERING UP: %s -> %s\n",
+              __PdoGetName(Pdo),
+              PowerSystemStateName(__PdoGetSystemPowerState(Pdo)),
+              PowerSystemStateName(SystemState));
+    } else if (__PdoGetSystemPowerState(Pdo) < SystemState) {
+        Trace("%s: POWERING DOWN: %s -> %s\n",
+              __PdoGetName(Pdo),
+              PowerSystemStateName(__PdoGetSystemPowerState(Pdo)),
+              PowerSystemStateName(SystemState));
+
+        if (SystemState >= PowerSystemHibernate &&
+            __PdoGetSystemPowerState(Pdo) < PowerSystemHibernate) {
+            __PdoSetSystemPowerState(Pdo, PowerSystemSleeping3);
+            PdoS3ToS4(Pdo);
+        }
+    }
+
+    __PdoSetSystemPowerState(Pdo, SystemState);
+
+    Irp->IoStatus.Status = STATUS_SUCCESS;
+    IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+    Trace("<==== (%s:%s)\n",
+          PowerSystemStateName(SystemState), 
+          PowerActionName(PowerAction));
+
+    return STATUS_SUCCESS;
+}
+
+static NTSTATUS
+PdoSystemPower(
+    IN  PXENVIF_THREAD  Self,
+    IN  PVOID           Context
+    )
+{
+    PXENVIF_PDO         Pdo = Context;
+    PKEVENT             Event;
+
+    Event = ThreadGetEvent(Self);
+
+    for (;;) {
+        PIRP    Irp;
+
+        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();
+
+        (VOID) __PdoSetSystemPower(Pdo, Irp);
+    }
+
+    return STATUS_SUCCESS;
+}
+
+static DECLSPEC_NOINLINE NTSTATUS
+PdoSetPower(
+    IN  PXENVIF_PDO     Pdo,
+    IN  PIRP            Irp
+    )
+{
+    PIO_STACK_LOCATION  StackLocation;
+    POWER_STATE_TYPE    PowerType;
+    POWER_ACTION        PowerAction;
+    NTSTATUS            status;
+    
+    StackLocation = IoGetCurrentIrpStackLocation(Irp);
+    PowerType = StackLocation->Parameters.Power.Type;
+    PowerAction = StackLocation->Parameters.Power.ShutdownType;
+
+    if (PowerAction >= PowerActionShutdown) {
+        Irp->IoStatus.Status = STATUS_SUCCESS;
+
+        status = Irp->IoStatus.Status;
+        IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+        goto done;
+    }
+
+    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:
+        status = Irp->IoStatus.Status;
+        IoCompleteRequest(Irp, IO_NO_INCREMENT);
+        break;
+    }
+
+done:    
+    return status;
+}
+
+static DECLSPEC_NOINLINE NTSTATUS
+PdoQueryPower(
+    IN  PXENVIF_PDO     Pdo,
+    IN  PIRP            Irp
+    )
+{
+    NTSTATUS            status;
+    
+    UNREFERENCED_PARAMETER(Pdo);
+
+    Irp->IoStatus.Status = STATUS_SUCCESS;
+
+    status = Irp->IoStatus.Status;
+    IoCompleteRequest(Irp, IO_NO_INCREMENT);
+    
+    return status;
+}
+
+static DECLSPEC_NOINLINE NTSTATUS
+PdoDispatchPower(
+    IN  PXENVIF_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_SET_POWER:
+        status = PdoSetPower(Pdo, Irp);
+        break;
+
+    case IRP_MN_QUERY_POWER:
+        status = PdoQueryPower(Pdo, Irp);
+        break;
+
+    default:
+        status = Irp->IoStatus.Status;
+        IoCompleteRequest(Irp, IO_NO_INCREMENT);
+        break;
+    }
+
+    return status;
+}
+
+static DECLSPEC_NOINLINE NTSTATUS
+PdoDispatchDefault(
+    IN  PXENVIF_PDO Pdo,
+    IN  PIRP        Irp
+    )
+{
+    NTSTATUS        status;
+
+    UNREFERENCED_PARAMETER(Pdo);
+
+    status = Irp->IoStatus.Status;
+    IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+    return status;
+}
+
+NTSTATUS
+PdoDispatch(
+    IN  PXENVIF_PDO     Pdo,
+    IN  PIRP            Irp
+    )
+{
+    PIO_STACK_LOCATION  StackLocation;
+    NTSTATUS            status;
+
+    StackLocation = IoGetCurrentIrpStackLocation(Irp);
+
+    switch (StackLocation->MajorFunction) {
+    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;
+}
+
+VOID
+PdoResume(
+    IN  PXENVIF_PDO     Pdo
+    )
+{
+    FrontendResume(__PdoGetFrontend(Pdo));
+}
+
+VOID
+PdoSuspend(
+    IN  PXENVIF_PDO     Pdo
+    )
+{
+    FrontendSuspend(__PdoGetFrontend(Pdo));
+}
+
+NTSTATUS
+PdoCreate(
+    IN  PXENVIF_FDO     Fdo,
+    IN  PANSI_STRING    Name
+    )
+{
+    PDEVICE_OBJECT      PhysicalDeviceObject;
+    PXENVIF_DX          Dx;
+    PXENVIF_PDO         Pdo;
+    NTSTATUS            status;
+
+#pragma prefast(suppress:28197) // Possibly leaking memory 'PhysicalDeviceObject'
+    status = IoCreateDevice(DriverObject,
+                            sizeof(XENVIF_DX),
+                            NULL,
+                            FILE_DEVICE_UNKNOWN,
+                            FILE_DEVICE_SECURE_OPEN | FILE_AUTOGENERATED_DEVICE_NAME,
+                            FALSE,
+                            &PhysicalDeviceObject);
+    if (!NT_SUCCESS(status))
+        goto fail1;
+
+    Dx = (PXENVIF_DX)PhysicalDeviceObject->DeviceExtension;
+    RtlZeroMemory(Dx, sizeof (XENVIF_DX));
+
+    Dx->Type = PHYSICAL_DEVICE_OBJECT;
+    Dx->DeviceObject = PhysicalDeviceObject;
+    Dx->DevicePnpState = Present;
+    Dx->SystemPowerState = PowerSystemShutdown;
+    Dx->DevicePowerState = PowerDeviceD3;
+
+    Pdo = __PdoAllocate(sizeof (XENVIF_PDO));
+
+    status = STATUS_NO_MEMORY;
+    if (Pdo == NULL)
+        goto fail2;
+
+    Pdo->Dx = Dx;
+
+    status = ThreadCreate(PdoSystemPower, Pdo, &Pdo->SystemPowerThread);
+    if (!NT_SUCCESS(status))
+        goto fail3;
+
+    status = ThreadCreate(PdoDevicePower, Pdo, &Pdo->DevicePowerThread);
+    if (!NT_SUCCESS(status))
+        goto fail4;
+
+    __PdoSetName(Pdo, Name);
+
+    status = FrontendInitialize(Pdo, &Pdo->Frontend);
+    if (!NT_SUCCESS(status))
+        goto fail5;
+
+    status = VifInitialize(Pdo, &Pdo->VifInterface);
+    if (!NT_SUCCESS(status))
+        goto fail6;
+
+    Info("%p (XENVIF\\DEVICE&REV_%02X#%s)\n",
+         PhysicalDeviceObject,
+         PDO_REVISION,
+         __PdoGetName(Pdo));
+
+    Dx->Pdo = Pdo;
+    PhysicalDeviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
+
+    __PdoLink(Pdo, Fdo);
+
+    return STATUS_SUCCESS;
+
+fail6:
+    Error("fail6\n");
+
+    FrontendTeardown(Pdo->Frontend);
+    Pdo->Frontend = NULL;    
+
+fail5:
+    Error("fail5\n");
+
+    ThreadAlert(Pdo->DevicePowerThread);
+    ThreadJoin(Pdo->DevicePowerThread);
+    Pdo->DevicePowerThread = NULL;
+
+fail4:
+    Error("fail4\n");
+
+    ThreadAlert(Pdo->SystemPowerThread);
+    ThreadJoin(Pdo->SystemPowerThread);
+    Pdo->SystemPowerThread = NULL;
+
+fail3:
+    Error("fail3\n");
+
+    Pdo->Dx = NULL;
+
+    ASSERT(IsZeroMemory(Pdo, sizeof (XENVIF_PDO)));
+    __PdoFree(Pdo);
+
+fail2:
+    Error("fail2\n");
+
+    IoDeleteDevice(PhysicalDeviceObject);
+
+fail1:
+    Error("fail1 (%08x)\n", status);
+
+    return status;
+}
+
+VOID
+PdoDestroy(
+    IN  PXENVIF_PDO Pdo
+    )
+{
+    PXENVIF_DX      Dx = Pdo->Dx;
+    PDEVICE_OBJECT  PhysicalDeviceObject = Dx->DeviceObject;
+
+    ASSERT3U(__PdoGetDevicePnpState(Pdo), ==, Deleted);
+
+    ASSERT(__PdoIsMissing(Pdo));
+    Pdo->Missing = FALSE;
+
+    __PdoUnlink(Pdo);
+
+    Info("%p (XENVIF\\DEVICE&REV_%02X#%s) (%s)\n",
+         PhysicalDeviceObject,
+         PDO_REVISION,
+         __PdoGetName(Pdo),
+         Pdo->Reason);
+    Pdo->Reason = NULL;
+
+    Dx->Pdo = NULL;
+
+    VifTeardown(__PdoGetVifInterface(Pdo));
+
+    FrontendTeardown(__PdoGetFrontend(Pdo));
+    Pdo->Frontend = NULL;    
+
+    ThreadAlert(Pdo->DevicePowerThread);
+    ThreadJoin(Pdo->DevicePowerThread);
+    Pdo->DevicePowerThread = NULL;
+
+    ThreadAlert(Pdo->SystemPowerThread);
+    ThreadJoin(Pdo->SystemPowerThread);
+    Pdo->SystemPowerThread = NULL;
+
+    Pdo->Dx = NULL;
+
+    ASSERT(IsZeroMemory(Pdo, sizeof (XENVIF_PDO)));
+    __PdoFree(Pdo);
+
+    IoDeleteDevice(PhysicalDeviceObject);
+}
+
+
diff --git a/src/xenvif/pdo.h b/src/xenvif/pdo.h
new file mode 100644 (file)
index 0000000..84f2148
--- /dev/null
@@ -0,0 +1,158 @@
+/* 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 _XENVIF_PDO_H
+#define _XENVIF_PDO_H
+
+#include <ntddk.h>
+
+#include "driver.h"
+#include "types.h"
+
+extern VOID
+PdoSetDevicePnpState(
+    IN  PXENVIF_PDO         Pdo,
+    IN  DEVICE_PNP_STATE    State
+    );
+
+extern DEVICE_PNP_STATE
+PdoGetDevicePnpState(
+    IN  PXENVIF_PDO Pdo
+    );
+
+extern VOID
+PdoSetMissing(
+    IN  PXENVIF_PDO Pdo,
+    IN  const CHAR  *Reason
+    );
+
+extern BOOLEAN
+PdoIsMissing(
+    IN  PXENVIF_PDO Pdo
+    );
+
+extern BOOLEAN
+PdoIsEjected(
+    IN  PXENVIF_PDO Pdo
+    );
+
+extern PCHAR
+PdoGetName(
+    IN  PXENVIF_PDO Pdo
+    );
+
+extern PDEVICE_OBJECT
+PdoGetDeviceObject(
+    IN  PXENVIF_PDO Pdo
+    );
+
+extern ULONG
+PdoGetLuidIndex(
+    IN  PXENVIF_PDO Pdo
+    );
+
+extern PCHAR
+PdoGetAddress(
+    IN  PXENVIF_PDO Pdo
+    );
+
+extern NTSTATUS
+PdoCreate(
+    IN  PXENVIF_FDO     Fdo,
+    IN  PANSI_STRING    Name
+    );
+
+extern VOID
+PdoResume(
+    IN  PXENVIF_PDO     Pdo
+    );
+
+extern VOID
+PdoSuspend(
+    IN  PXENVIF_PDO     Pdo
+    );
+
+extern VOID
+PdoDestroy(
+    IN  PXENVIF_PDO Pdo
+    );
+
+#include "frontend.h"
+
+extern PXENVIF_FRONTEND
+PdoGetFrontend(
+    IN  PXENVIF_PDO Pdo
+    );
+
+extern PXENBUS_EVTCHN_INTERFACE
+PdoGetEvtchnInterface(
+    IN  PXENVIF_PDO     Pdo
+    );
+
+extern PXENBUS_DEBUG_INTERFACE
+PdoGetDebugInterface(
+    IN  PXENVIF_PDO     Pdo
+    );
+
+extern PXENBUS_STORE_INTERFACE
+PdoGetStoreInterface(
+    IN  PXENVIF_PDO     Pdo
+    );
+
+extern PXENBUS_GNTTAB_INTERFACE
+PdoGetGnttabInterface(
+    IN  PXENVIF_PDO     Pdo
+    );
+
+extern PXENBUS_SUSPEND_INTERFACE
+PdoGetSuspendInterface(
+    IN  PXENVIF_PDO     Pdo
+    );
+
+#include "vif.h"
+
+extern PXENVIF_VIF_INTERFACE
+PdoGetVifInterface(
+    IN  PXENVIF_PDO Pdo
+    );
+
+extern VOID
+PdoRequestEject(
+    IN  PXENVIF_PDO Pdo
+    );
+
+extern NTSTATUS
+PdoDispatch(
+    IN  PXENVIF_PDO Pdo,
+    IN  PIRP        Irp
+    );
+
+#endif  // _XENVIF_PDO_H
diff --git a/src/xenvif/pool.c b/src/xenvif/pool.c
new file mode 100644 (file)
index 0000000..c19ebf0
--- /dev/null
@@ -0,0 +1,568 @@
+/* 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 <ntstrsafe.h>
+#include <util.h>
+
+#include "pool.h"
+#include "thread.h"
+#include "log.h"
+#include "assert.h"
+
+#define POOL_POOL   'OBJE'
+
+typedef struct _OBJECT_HEADER {
+    ULONG       Magic;
+
+#define OBJECT_HEADER_MAGIC 0x02121996
+
+    LIST_ENTRY  ListEntry;
+} OBJECT_HEADER, *POBJECT_HEADER;
+
+#define MAXIMUM_SLOTS   6
+
+typedef struct _POOL_MAGAZINE {
+    PVOID   Slot[MAXIMUM_SLOTS];
+} POOL_MAGAZINE, *PPOOL_MAGAZINE;
+
+struct _XENVIF_POOL {
+    const CHAR      *Name;
+    ULONG           Size;
+    NTSTATUS        (*Ctor)(PVOID, PVOID);
+    VOID            (*Dtor)(PVOID, PVOID);
+    VOID            (*AcquireLock)(PVOID);
+    VOID            (*ReleaseLock)(PVOID);
+    PVOID           Argument;
+    PXENVIF_THREAD  Thread;
+    LIST_ENTRY      GetList;
+    PLIST_ENTRY     PutList;
+    POOL_MAGAZINE   Magazine[MAXIMUM_PROCESSORS];
+    LONG            Allocated;
+    LONG            MaximumAllocated;
+    LONG            Count;
+    LONG            MinimumCount;
+};
+
+static FORCEINLINE PVOID
+__PoolAllocate(
+    IN  ULONG   Length
+    )
+{
+    return __AllocateNonPagedPoolWithTag(Length, POOL_POOL);
+}
+
+static FORCEINLINE VOID
+__PoolFree(
+    IN  PVOID   Buffer
+    )
+{
+    __FreePoolWithTag(Buffer, POOL_POOL);
+}
+
+static FORCEINLINE VOID
+__PoolSwizzle(
+    IN  PXENVIF_POOL    Pool
+    )
+{
+    PLIST_ENTRY         ListEntry;
+
+    ListEntry = InterlockedExchangePointer(&Pool->PutList, NULL);
+
+    while (ListEntry != NULL) {
+        PLIST_ENTRY Next;
+
+        Next = ListEntry->Flink;
+        ListEntry->Flink = NULL;
+        ASSERT3P(ListEntry->Blink, ==, NULL);
+
+        InsertTailList(&Pool->GetList, ListEntry);
+
+        ListEntry = Next;
+    }
+}
+
+static FORCEINLINE PVOID
+__PoolGetShared(
+    IN  PXENVIF_POOL    Pool,
+    IN  BOOLEAN         Locked
+    )
+{
+    LONG                Count;
+    POBJECT_HEADER      Header;
+    PVOID               Object;
+    LONG                Allocated;
+    NTSTATUS            status;
+
+    Count = InterlockedDecrement(&Pool->Count);
+
+    if (Count >= 0) {
+        PLIST_ENTRY     ListEntry;
+
+        if (!Locked)
+            Pool->AcquireLock(Pool->Argument);
+
+        if (Count < Pool->MinimumCount)
+            Pool->MinimumCount = Count;
+
+        if (IsListEmpty(&Pool->GetList))
+            __PoolSwizzle(Pool);
+
+        ListEntry = RemoveHeadList(&Pool->GetList);
+        ASSERT(ListEntry != &Pool->GetList);
+
+        if (!Locked)
+            Pool->ReleaseLock(Pool->Argument);
+
+        RtlZeroMemory(ListEntry, sizeof (LIST_ENTRY));
+
+        Header = CONTAINING_RECORD(ListEntry, OBJECT_HEADER, ListEntry);
+        ASSERT3U(Header->Magic, ==, OBJECT_HEADER_MAGIC);
+
+        Object = Header + 1;
+        goto done;
+    }
+
+    (VOID) InterlockedIncrement(&Pool->Count);
+
+    Header = __PoolAllocate(sizeof (OBJECT_HEADER) + Pool->Size);
+
+    status = STATUS_NO_MEMORY;
+    if (Header == NULL)
+        goto fail1;
+
+    Header->Magic = OBJECT_HEADER_MAGIC;
+
+    Object = Header + 1;
+
+    status = Pool->Ctor(Pool->Argument, Object);
+    if (!NT_SUCCESS(status))
+        goto fail2;
+
+    Allocated = InterlockedIncrement(&Pool->Allocated);
+
+    if (Allocated > Pool->MaximumAllocated) {
+        if (!Locked)
+            Pool->AcquireLock(Pool->Argument);
+
+        if (Allocated > Pool->MaximumAllocated)
+            Pool->MaximumAllocated = Allocated;
+
+        if (!Locked)
+            Pool->ReleaseLock(Pool->Argument);
+    }
+
+done:
+    return Object;
+
+fail2:
+    Error("fail2\n");
+
+    Header->Magic = 0;
+
+    ASSERT(IsZeroMemory(Header, sizeof (OBJECT_HEADER)));
+    __PoolFree(Header);
+
+fail1:
+    Error("fail1 (%08x)\n", status);
+
+    return NULL;    
+}
+
+static FORCEINLINE VOID
+__PoolPutShared(
+    IN  PXENVIF_POOL    Pool,
+    IN  PVOID           Object,
+    IN  BOOLEAN         Locked
+    )
+{
+    POBJECT_HEADER      Header;
+    PLIST_ENTRY         Old;
+    PLIST_ENTRY         New;
+
+    ASSERT(Object != NULL);
+
+    Header = Object;
+    --Header;
+    ASSERT3U(Header->Magic, ==, OBJECT_HEADER_MAGIC);
+
+    ASSERT(IsZeroMemory(&Header->ListEntry, sizeof (LIST_ENTRY)));
+
+    if (!Locked) {
+        New = &Header->ListEntry;
+
+        do {
+            Old = Pool->PutList;
+            New->Flink = Old;
+        } while (InterlockedCompareExchangePointer(&Pool->PutList, New, Old) != Old);
+    } else {
+        InsertTailList(&Pool->GetList, &Header->ListEntry);
+    }
+
+    KeMemoryBarrier();
+
+    (VOID) InterlockedIncrement(&Pool->Count);
+}
+
+static FORCEINLINE PVOID
+__PoolGetMagazine(
+    IN  PXENVIF_POOL    Pool,
+    IN  ULONG           Cpu
+    )
+{
+    PPOOL_MAGAZINE      Magazine;
+    ULONG               Index;
+
+    Magazine = &Pool->Magazine[Cpu];
+
+    for (Index = 0; Index < MAXIMUM_SLOTS; Index++) {
+        PVOID   Object;
+
+        if (Magazine->Slot[Index] != NULL) {
+            Object = Magazine->Slot[Index];
+            Magazine->Slot[Index] = NULL;
+
+            return Object;
+        }
+    }
+
+    return NULL;
+}
+
+static FORCEINLINE BOOLEAN
+__PoolPutMagazine(
+    IN  PXENVIF_POOL    Pool,
+    IN  ULONG           Cpu,
+    IN  PVOID           Object
+    )
+{
+    PPOOL_MAGAZINE      Magazine;
+    ULONG               Index;
+
+    Magazine = &Pool->Magazine[Cpu];
+
+    for (Index = 0; Index < MAXIMUM_SLOTS; Index++) {
+        if (Magazine->Slot[Index] == NULL) {
+            Magazine->Slot[Index] = Object;
+            return TRUE;
+        }
+    }
+
+    return FALSE;
+}
+
+PVOID
+PoolGet(
+    IN  PXENVIF_POOL    Pool,
+    IN  BOOLEAN         Locked
+    )
+{
+    KIRQL               Irql;
+    ULONG               Cpu;
+    PVOID               Object;
+
+    KeRaiseIrql(DISPATCH_LEVEL, &Irql);
+    Cpu = KeGetCurrentProcessorNumber();
+
+    Object = __PoolGetMagazine(Pool, Cpu);
+    if (Object == NULL)
+        Object = __PoolGetShared(Pool, Locked);
+
+    KeLowerIrql(Irql);
+
+    return Object;
+}
+
+VOID
+PoolPut(
+    IN  PXENVIF_POOL    Pool,
+    IN  PVOID           Object,
+    IN  BOOLEAN         Locked
+    )
+{
+    KIRQL               Irql;
+    ULONG               Cpu;
+
+    KeRaiseIrql(DISPATCH_LEVEL, &Irql);
+    Cpu = KeGetCurrentProcessorNumber();
+
+    if (!__PoolPutMagazine(Pool, Cpu, Object))
+        __PoolPutShared(Pool, Object, Locked);
+
+    KeLowerIrql(Irql);
+}
+
+VOID
+PoolGetStatistics(
+    IN  PXENVIF_POOL    Pool,
+    OUT PULONG          Allocated,
+    OUT PULONG          MaximumAllocated,
+    OUT PULONG          Count,
+    OUT PULONG          MinimumCount
+    )
+{
+    *Allocated = Pool->Allocated;
+    *MaximumAllocated = Pool->MaximumAllocated;
+
+    *Count = Pool->Count;
+    *MinimumCount = Pool->MinimumCount;
+}
+
+static FORCEINLINE
+__PoolFlushMagazines(
+    IN  PXENVIF_POOL    Pool
+    )
+{
+    ULONG               Cpu;
+
+    for (Cpu = 0; Cpu < MAXIMUM_PROCESSORS; Cpu++) {
+        PVOID   Object;
+
+        while ((Object = __PoolGetMagazine(Pool, Cpu)) != NULL)
+            __PoolPutShared(Pool, Object, TRUE);
+    }
+}
+
+static FORCEINLINE VOID
+__PoolTrimShared(
+    IN      PXENVIF_POOL    Pool,
+    IN OUT  PLIST_ENTRY     List
+    )
+{
+    LONG                    Count;
+    LONG                    Excess;
+
+    Count = Pool->Count;
+
+    KeMemoryBarrier();
+
+    Excess = Pool->MinimumCount;
+
+    while (Excess != 0) {
+        PLIST_ENTRY     ListEntry;
+
+        Count = InterlockedDecrement(&Pool->Count);
+        if (Count < 0) {
+            Count = InterlockedIncrement(&Pool->Count);
+            break;
+        }
+
+        if (IsListEmpty(&Pool->GetList))
+            __PoolSwizzle(Pool);
+
+        ListEntry = RemoveHeadList(&Pool->GetList);
+        ASSERT(ListEntry != &Pool->GetList);
+
+        InsertTailList(List, ListEntry);
+
+        InterlockedDecrement(&Pool->Allocated);
+        --Excess;
+    }
+
+    Pool->MinimumCount = Count;
+}
+
+static FORCEINLINE VOID
+__PoolEmpty(
+    IN      PXENVIF_POOL    Pool,
+    IN OUT  PLIST_ENTRY     List
+    )
+{
+    while (!IsListEmpty(List)) {
+        PLIST_ENTRY     ListEntry;
+        POBJECT_HEADER  Header;
+        PVOID           Object;
+
+        ListEntry = RemoveHeadList(List);
+        RtlZeroMemory(ListEntry, sizeof (LIST_ENTRY));
+
+        Header = CONTAINING_RECORD(ListEntry, OBJECT_HEADER, ListEntry);
+        ASSERT3U(Header->Magic, ==, OBJECT_HEADER_MAGIC);
+
+        Object = Header + 1;
+
+        Pool->Dtor(Pool->Argument, Object);
+
+        Header->Magic = 0;
+
+        ASSERT(IsZeroMemory(Header, sizeof (OBJECT_HEADER)));
+        __PoolFree(Header);
+    }
+}
+
+#define TIME_US(_us)        ((_us) * 10)
+#define TIME_MS(_ms)        (TIME_US((_ms) * 1000))
+#define TIME_RELATIVE(_t)   (-(_t))
+
+#define POOL_PERIOD  1000
+
+static NTSTATUS
+PoolMonitor(
+    IN  PXENVIF_THREAD  Self,
+    IN  PVOID           Context
+    )
+{
+    PXENVIF_POOL        Pool = Context;
+    LARGE_INTEGER       Timeout;
+
+    Trace("====> (%s)\n", Pool->Name);
+
+    Timeout.QuadPart = TIME_RELATIVE(TIME_MS(POOL_PERIOD));
+
+    for (;;) {
+        PKEVENT     Event;
+        KIRQL       Irql;
+        LIST_ENTRY  List;
+
+        Event = ThreadGetEvent(Self);
+
+        (VOID) KeWaitForSingleObject(Event,
+                                     Executive,
+                                     KernelMode,
+                                     FALSE,
+                                     &Timeout);
+        KeClearEvent(Event);
+
+        if (ThreadIsAlerted(Self))
+            break;
+
+        InitializeListHead(&List);
+
+        KeRaiseIrql(DISPATCH_LEVEL, &Irql);
+        Pool->AcquireLock(Pool->Argument);
+        __PoolTrimShared(Pool, &List);
+        Pool->ReleaseLock(Pool->Argument);
+        KeLowerIrql(Irql);
+
+        __PoolEmpty(Pool, &List);
+        ASSERT(IsListEmpty(&List));
+    }
+
+    Trace("<==== (%s)\n", Pool->Name);
+
+    return STATUS_SUCCESS;
+}
+
+NTSTATUS
+PoolInitialize(
+    IN  const CHAR      *Name,
+    IN  ULONG           Size,
+    IN  NTSTATUS        (*Ctor)(PVOID, PVOID),
+    IN  VOID            (*Dtor)(PVOID, PVOID),
+    IN  VOID            (*AcquireLock)(PVOID),
+    IN  VOID            (*ReleaseLock)(PVOID),
+    IN  PVOID           Argument,
+    OUT PXENVIF_POOL    *Pool
+    )
+{
+    NTSTATUS            status;
+
+    *Pool = __PoolAllocate(sizeof (XENVIF_POOL));
+
+    status = STATUS_NO_MEMORY;
+    if (*Pool == NULL)
+        goto fail1;
+
+    (*Pool)->Name = Name;
+    (*Pool)->Size = Size;
+    (*Pool)->Ctor = Ctor;
+    (*Pool)->Dtor = Dtor;
+    (*Pool)->AcquireLock = AcquireLock;
+    (*Pool)->ReleaseLock = ReleaseLock;
+    (*Pool)->Argument = Argument;
+
+    InitializeListHead(&(*Pool)->GetList);
+
+    status = ThreadCreate(PoolMonitor, *Pool, &(*Pool)->Thread);
+    if (!NT_SUCCESS(status))
+        goto fail2;
+
+    return STATUS_SUCCESS;
+
+fail2:
+    Error("fail2\n");
+
+    RtlZeroMemory(&(*Pool)->GetList, sizeof (LIST_ENTRY));
+
+    (*Pool)->Argument = NULL;
+    (*Pool)->ReleaseLock = NULL;
+    (*Pool)->AcquireLock = NULL;
+    (*Pool)->Dtor = NULL;
+    (*Pool)->Ctor = NULL;
+    (*Pool)->Size = 0;
+    (*Pool)->Name = NULL;
+
+    ASSERT(IsZeroMemory(*Pool, sizeof (XENVIF_POOL)));
+    __PoolFree(*Pool);
+    *Pool = NULL;
+
+fail1:
+    Error("fail1 (%08x)\n", status);
+
+    return status;    
+}
+
+VOID
+PoolTeardown(
+    IN  PXENVIF_POOL    Pool
+    )
+{
+    LIST_ENTRY          List;
+
+    ThreadAlert(Pool->Thread);
+    ThreadJoin(Pool->Thread);
+    Pool->Thread = NULL;
+
+    InitializeListHead(&List);
+
+    __PoolFlushMagazines(Pool);
+
+    Pool->MinimumCount = Pool->Count;
+    __PoolTrimShared(Pool, &List);
+    __PoolEmpty(Pool, &List);
+
+    ASSERT3U(Pool->Count, ==, 0);
+    ASSERT3U(Pool->Allocated, ==, 0);
+    Pool->MaximumAllocated = 0;
+
+    RtlZeroMemory(&Pool->GetList, sizeof (LIST_ENTRY));
+
+    Pool->Argument = NULL;
+    Pool->ReleaseLock = NULL;
+    Pool->AcquireLock = NULL;
+    Pool->Dtor = NULL;
+    Pool->Ctor = NULL;
+    Pool->Size = 0;
+    Pool->Name = NULL;
+
+    ASSERT(IsZeroMemory(Pool, sizeof (XENVIF_POOL)));
+    __PoolFree(Pool);
+}
diff --git a/src/xenvif/pool.h b/src/xenvif/pool.h
new file mode 100644 (file)
index 0000000..9c1ab84
--- /dev/null
@@ -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 _XENVIF_POOL_H
+#define _XENVIF_POOL_H
+
+#include <ntddk.h>
+
+typedef struct _XENVIF_POOL XENVIF_POOL, *PXENVIF_POOL;
+
+extern NTSTATUS
+PoolInitialize(
+    IN  const CHAR      *Name,
+    IN  ULONG           Size,
+    IN  NTSTATUS        (*Ctor)(PVOID, PVOID),
+    IN  VOID            (*Dtor)(PVOID, PVOID),
+    IN  VOID            (*AcquireLock)(PVOID),
+    IN  VOID            (*ReleaseLock)(PVOID),
+    IN  PVOID           Argument,
+    OUT PXENVIF_POOL    *Pool
+    );
+
+extern VOID
+PoolTeardown(
+    IN  PXENVIF_POOL    Pool
+    );
+
+extern PVOID
+PoolGet(
+    IN  PXENVIF_POOL    Pool,
+    IN  BOOLEAN         Locked
+    );
+
+extern VOID
+PoolPut(
+    IN  PXENVIF_POOL    Pool,
+    IN  PVOID           Object,
+    IN  BOOLEAN         Locked
+    );
+
+extern VOID
+PoolGetStatistics(
+    IN  PXENVIF_POOL    Pool,
+    OUT PULONG          Allocated,
+    OUT PULONG          MaximumAllocated,
+    OUT PULONG          Count,
+    OUT PULONG          MinimumCount
+    );
+
+#endif  // _XENVIF_POOL_H
diff --git a/src/xenvif/receiver.c b/src/xenvif/receiver.c
new file mode 100644 (file)
index 0000000..e4636f3
--- /dev/null
@@ -0,0 +1,4070 @@
+/* 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 <ntstrsafe.h>
+#include <stdlib.h>
+#include <netioapi.h>
+#include <util.h>
+#include <xen.h>
+#include <debug_interface.h>
+#include <store_interface.h>
+#include <gnttab_interface.h>
+
+// This should be in public/io/netif.h
+#define _NETRXF_gso_prefix     (4)
+#define  NETRXF_gso_prefix     (1U<<_NETRXF_gso_prefix)
+
+#include "ethernet.h"
+#include "tcpip.h"
+#include "pdo.h"
+#include "frontend.h"
+#include "pool.h"
+#include "checksum.h"
+#include "parse.h"
+#include "mac.h"
+#include "vif.h"
+#include "receiver.h"
+#include "driver.h"
+#include "log.h"
+#include "assert.h"
+
+#define RECEIVER_POOL    'ECER'
+
+typedef struct _RECEIVER_TAG {
+    LIST_ENTRY  ListEntry;
+    ULONG       Next;
+    PVOID       Context;
+    ULONG       Reference;
+} RECEIVER_TAG, *PRECEIVER_TAG;
+
+typedef struct _RECEIVER_OFFLOAD_STATISTICS {
+    ULONGLONG   IpVersion4LargePacketSegment;
+    ULONGLONG   IpVersion6LargePacketSegment;
+    ULONGLONG   IpVersion4HeaderChecksumCalculated;
+    ULONGLONG   IpVersion4HeaderChecksumSucceeded;
+    ULONGLONG   IpVersion4HeaderChecksumFailed;
+    ULONGLONG   IpVersion4HeaderChecksumPresent;
+    ULONGLONG   IpVersion4TcpChecksumCalculated;
+    ULONGLONG   IpVersion4TcpChecksumSucceeded;
+    ULONGLONG   IpVersion4TcpChecksumFailed;
+    ULONGLONG   IpVersion4TcpChecksumPresent;
+    ULONGLONG   IpVersion6TcpChecksumCalculated;
+    ULONGLONG   IpVersion6TcpChecksumSucceeded;
+    ULONGLONG   IpVersion6TcpChecksumFailed;
+    ULONGLONG   IpVersion6TcpChecksumPresent;
+    ULONGLONG   IpVersion4UdpChecksumCalculated;
+    ULONGLONG   IpVersion4UdpChecksumSucceeded;
+    ULONGLONG   IpVersion4UdpChecksumFailed;
+    ULONGLONG   IpVersion4UdpChecksumPresent;
+    ULONGLONG   IpVersion6UdpChecksumCalculated;
+    ULONGLONG   IpVersion6UdpChecksumSucceeded;
+    ULONGLONG   IpVersion6UdpChecksumFailed;
+    ULONGLONG   IpVersion6UdpChecksumPresent;
+    ULONGLONG   TagRemoved;
+} RECEIVER_OFFLOAD_STATISTICS, *PRECEIVER_OFFLOAD_STATISTICS;
+
+#define PROTOCOL0_RING_SIZE (__CONST_RING_SIZE(netif_rx, PAGE_SIZE))
+#define MAXIMUM_TAG_COUNT   (PROTOCOL0_RING_SIZE * 2)
+
+#define TAG_INDEX_INVALID       0xFFFFFFFF
+
+typedef struct _RECEIVER_RING_PROTOCOL0 {
+    netif_rx_front_ring_t   Front;
+    netif_rx_sring_t        *Shared;
+    ULONG                   Reference;
+    ULONG                   HeadFreeTag;
+    RECEIVER_TAG            Tag[MAXIMUM_TAG_COUNT];
+    netif_rx_request_t      Pending[MAXIMUM_TAG_COUNT];
+    ULONG                   RequestsPosted;
+    ULONG                   RequestsPushed;
+    ULONG                   ResponsesProcessed;
+} RECEIVER_RING_PROTOCOL0, *PRECEIVER_RING_PROTOCOL0;
+
+#define PROTOCOL1_RING_SIZE     (__CONST_RING_SIZE(netif_tx, PAGE_SIZE))
+#define MAXIMUM_OPERATION_COUNT ((MAX_SKB_FRAGS + 1) * 2)
+
+typedef struct _RECEIVER_RING_PROTOCOL1 {
+    netif_tx_back_ring_t            Back;
+    netif_tx_sring_t                *Shared;
+    ULONG                           Reference;
+    LIST_ENTRY                      List;
+    XENBUS_GNTTAB_COPY_OPERATION    Operation[MAXIMUM_OPERATION_COUNT];
+    ULONG                           RequestsProcessed;
+    ULONG                           ResponsesPosted;
+    ULONG                           ResponsesPushed;
+} RECEIVER_RING_PROTOCOL1, *PRECEIVER_RING_PROTOCOL1;
+
+typedef NTSTATUS (*RECEIVER_RING_CONNECT)(PRECEIVER_RING);
+typedef NTSTATUS (*RECEIVER_RING_STORE_WRITE)(PRECEIVER_RING, PXENBUS_STORE_TRANSACTION);
+typedef NTSTATUS (*RECEIVER_RING_ENABLE)(PRECEIVER_RING);
+typedef VOID     (*RECEIVER_RING_POLL)(PRECEIVER_RING, PLIST_ENTRY, PULONG);
+typedef VOID     (*RECEIVER_RING_DISABLE)(PRECEIVER_RING);
+typedef VOID     (*RECEIVER_RING_DISCONNECT)(PRECEIVER_RING);
+typedef VOID     (*RECEIVER_RING_DEBUG_CALLBACK)(PRECEIVER_RING);
+typedef ULONG    (*RECEIVER_RING_GET_SIZE)(PRECEIVER_RING);
+
+typedef struct _RECEIVER_RING_OPERATIONS {
+    RECEIVER_RING_CONNECT           Connect;
+    RECEIVER_RING_STORE_WRITE       StoreWrite;
+    RECEIVER_RING_ENABLE            Enable;
+    RECEIVER_RING_POLL              Poll;
+    RECEIVER_RING_DISABLE           Disable;
+    RECEIVER_RING_DISCONNECT        Disconnect;
+    RECEIVER_RING_DEBUG_CALLBACK    DebugCallback;
+    RECEIVER_RING_GET_SIZE          GetSize;
+} RECEIVER_RING_OPERATIONS, *PRECEIVER_RING_OPERATIONS;
+
+#pragma warning(push)
+#pragma warning(disable:4201)   // nonstandard extension used : nameless struct/union
+
+typedef struct _RECEIVER_RING {
+    PXENVIF_RECEIVER                    Receiver;
+    LIST_ENTRY                          ListEntry;
+    KSPIN_LOCK                          Lock;
+    PXENVIF_POOL                        PacketPool;
+    PMDL                                Mdl;
+    union {
+        RECEIVER_RING_PROTOCOL0         Protocol0;
+        RECEIVER_RING_PROTOCOL1         Protocol1;
+    };
+    BOOLEAN                             Enabled;
+    BOOLEAN                             Stopped;
+    PRECEIVER_RING_OPERATIONS           Operations;
+    XENVIF_OFFLOAD_OPTIONS              OffloadOptions;
+    XENVIF_RECEIVER_PACKET_STATISTICS   PacketStatistics;
+    XENVIF_HEADER_STATISTICS            HeaderStatistics;
+    RECEIVER_OFFLOAD_STATISTICS         OffloadStatistics;
+    LIST_ENTRY                          PacketList;
+} RECEIVER_RING, *PRECEIVER_RING;
+
+#pragma warning(pop)
+
+struct _XENVIF_RECEIVER {
+    PXENVIF_FRONTEND            Frontend;
+    ULONG                       Protocol;
+    LIST_ENTRY                  List;
+    LONG                        Loaned;
+    LONG                        Returned;
+    KEVENT                      Event;
+
+    PXENBUS_DEBUG_INTERFACE     DebugInterface;
+    PXENBUS_STORE_INTERFACE     StoreInterface;
+    PXENBUS_GNTTAB_INTERFACE    GnttabInterface;
+    PXENVIF_VIF_INTERFACE       VifInterface;
+
+    PXENBUS_DEBUG_CALLBACK      DebugCallback;
+};
+
+#define REQ_ID_INTEGRITY_CHECK  0xF000
+
+static FORCEINLINE PVOID
+__ReceiverAllocate(
+    IN  ULONG   Length
+    )
+{
+    return __AllocateNonPagedPoolWithTag(Length, RECEIVER_POOL);
+}
+
+static FORCEINLINE VOID
+__ReceiverFree(
+    IN  PVOID   Buffer
+    )
+{
+    __FreePoolWithTag(Buffer, RECEIVER_POOL);
+}
+
+static NTSTATUS
+ReceiverPacketCtor(
+    IN  PVOID               Argument,
+    IN  PVOID               Object
+    )
+{
+    PXENVIF_RECEIVER_PACKET Packet = Object;
+    PMDL                           Mdl;
+    PUCHAR                     StartVa;
+    NTSTATUS                   status;
+
+    UNREFERENCED_PARAMETER(Argument);
+
+    ASSERT(IsZeroMemory(Packet, sizeof (XENVIF_RECEIVER_PACKET)));
+
+    Mdl = __AllocatePage();
+
+    status = STATUS_NO_MEMORY;
+    if (Mdl == NULL)
+       goto fail1;
+
+    StartVa = MmGetSystemAddressForMdlSafe(Mdl, NormalPagePriority);
+    RtlFillMemory(StartVa, PAGE_SIZE, 0xAA);
+
+    ASSERT3U(Mdl->ByteOffset, ==, 0);
+    Mdl->StartVa = StartVa;
+    Mdl->ByteCount = 0;
+
+    Packet->Mdl = *Mdl;
+    Packet->__Pfn = MmGetMdlPfnArray(Mdl)[0];
+
+    ExFreePool(Mdl);
+
+    return STATUS_SUCCESS;
+
+fail1:
+    Error("fail1 (%08x)\n", status);
+    
+    ASSERT(IsZeroMemory(Packet, sizeof (XENVIF_RECEIVER_PACKET)));
+
+    return status;
+}
+
+static VOID
+ReceiverPacketDtor(
+    IN  PVOID               Argument,
+    IN  PVOID               Object
+    )
+{
+    PXENVIF_RECEIVER_PACKET Packet = Object;
+    PMDL                    Mdl;
+
+    UNREFERENCED_PARAMETER(Argument);
+
+    Mdl = &Packet->Mdl;
+
+    Mdl->ByteCount = PAGE_SIZE;
+
+    __FreePage(Mdl);
+
+    RtlZeroMemory(Mdl, sizeof (MDL) + sizeof (PFN_NUMBER));
+
+    ASSERT(IsZeroMemory(Packet, sizeof (XENVIF_RECEIVER_PACKET)));
+}
+
+static FORCEINLINE PXENVIF_RECEIVER_PACKET
+__RingGetPacket(
+    IN  PRECEIVER_RING      Ring,
+    IN  BOOLEAN             Locked
+    )
+{
+    PXENVIF_RECEIVER_PACKET Packet;
+
+    Packet = PoolGet(Ring->PacketPool, Locked);
+
+    ASSERT(Packet == NULL || IsZeroMemory(Packet, FIELD_OFFSET(XENVIF_RECEIVER_PACKET, Mdl)));
+
+    return Packet;
+}
+
+static FORCEINLINE VOID
+__RingPutPacket(
+    IN  PRECEIVER_RING          Ring,
+    IN  PXENVIF_RECEIVER_PACKET Packet,
+    IN  BOOLEAN                 Locked
+    )
+{
+    PMDL                        Mdl = &Packet->Mdl;
+
+    RtlZeroMemory(Packet, FIELD_OFFSET(XENVIF_RECEIVER_PACKET, Mdl));
+
+    Mdl->MappedSystemVa = Mdl->StartVa;
+    Mdl->ByteOffset = 0;
+    Mdl->ByteCount = 0;
+    ASSERT3P(Mdl->Next, ==, NULL);
+
+    PoolPut(Ring->PacketPool, Packet, Locked);
+}
+
+static FORCEINLINE PMDL
+__RingGetMdl(
+    IN  PRECEIVER_RING      Ring,
+    IN  BOOLEAN             Locked
+    )
+{
+    PXENVIF_RECEIVER_PACKET Packet;
+
+    Packet = __RingGetPacket(Ring, Locked);
+    if (Packet == NULL)
+        return NULL;
+
+    return &Packet->Mdl;
+}
+
+static FORCEINLINE VOID
+__RingPutMdl(
+    IN  PRECEIVER_RING      Ring,
+    IN  PMDL                Mdl,
+    IN  BOOLEAN             Locked
+    )
+{
+    PXENVIF_RECEIVER_PACKET Packet;
+
+    Packet = CONTAINING_RECORD(Mdl, XENVIF_RECEIVER_PACKET, Mdl);
+    __RingPutPacket(Ring, Packet, Locked);
+}
+
+static DECLSPEC_NOINLINE VOID
+PacketProcessTag(
+    IN  PXENVIF_RECEIVER_PACKET         Packet,
+    IN  PXENVIF_OFFLOAD_OPTIONS         OffloadOptions,
+    IN  PRECEIVER_OFFLOAD_STATISTICS    OffloadStatistics
+    )
+{
+    PXENVIF_PACKET_INFO                 Info;
+    ULONG                               PayloadLength;
+    PUCHAR                              StartVa;
+    PETHERNET_HEADER                    EthernetHeader;
+    ULONG                               Offset;
+
+    Info = &Packet->Info;
+
+    PayloadLength = Packet->Length - Info->Length;
+
+    StartVa = MmGetSystemAddressForMdlSafe(&Packet->Mdl, NormalPagePriority);
+    StartVa += Packet->Offset;
+
+    ASSERT(Info->EthernetHeader.Length != 0);
+    EthernetHeader = (PETHERNET_HEADER)(StartVa + Info->EthernetHeader.Offset);
+
+    if (!ETHERNET_HEADER_IS_TAGGED(EthernetHeader) ||
+        OffloadOptions->OffloadTagManipulation == 0)
+        return;
+
+    Packet->TagControlInformation = NTOHS(EthernetHeader->Tagged.Tag.ControlInformation);
+
+    Offset = FIELD_OFFSET(ETHERNET_TAGGED_HEADER, Tag);
+    RtlMoveMemory((PUCHAR)EthernetHeader + sizeof (ETHERNET_TAG),
+                  (PUCHAR)EthernetHeader,
+                  Offset);
+
+    // Fix up the packet information
+    Packet->Offset += sizeof (ETHERNET_TAG);
+    Packet->Length -= sizeof (ETHERNET_TAG);
+
+    Info->EthernetHeader.Length -= sizeof (ETHERNET_TAG);
+
+    if (Info->IpHeader.Length != 0)
+        Info->IpHeader.Offset -= sizeof (ETHERNET_TAG);
+
+    if (Info->IpOptions.Length != 0)
+        Info->IpOptions.Offset -= sizeof (ETHERNET_TAG);
+
+    if (Info->UdpHeader.Length != 0)
+        Info->UdpHeader.Offset -= sizeof (ETHERNET_TAG);
+
+    if (Info->TcpHeader.Length != 0)
+        Info->TcpHeader.Offset -= sizeof (ETHERNET_TAG);
+
+    if (Info->TcpOptions.Length != 0)
+        Info->TcpOptions.Offset -= sizeof (ETHERNET_TAG);
+
+    Info->Length -= sizeof (ETHERNET_TAG);
+
+    StartVa = MmGetSystemAddressForMdlSafe(&Packet->Mdl, NormalPagePriority);
+    StartVa += Packet->Offset;
+
+    EthernetHeader = (PETHERNET_HEADER)(StartVa + Info->EthernetHeader.Offset);
+    ASSERT(!ETHERNET_HEADER_IS_TAGGED(EthernetHeader));
+
+    ASSERT3U(PayloadLength, ==, Packet->Length - Info->Length);
+
+    OffloadStatistics->TagRemoved++;
+}
+
+static DECLSPEC_NOINLINE VOID
+PacketProcessChecksum(
+    IN  PXENVIF_RECEIVER_PACKET         Packet,
+    IN  PXENVIF_OFFLOAD_OPTIONS         OffloadOptions,
+    IN  PRECEIVER_OFFLOAD_STATISTICS    OffloadStatistics
+    )
+{
+    PXENVIF_PACKET_INFO                 Info;
+    XENVIF_PACKET_PAYLOAD               Payload;
+    uint16_t                            flags;
+    PUCHAR                              StartVa;
+    PIP_HEADER                          IpHeader;
+    BOOLEAN                             IsAFragment;
+
+    Info = &Packet->Info;
+
+    Payload.Mdl = Packet->Mdl.Next;
+    Payload.Offset = 0;
+    Payload.Length = Packet->Length - Info->Length;
+
+    flags = (uint16_t)(ULONG_PTR)Packet->Cookie;
+
+    if (Info->IpHeader.Length == 0)
+        return;
+
+    StartVa = MmGetSystemAddressForMdlSafe(&Packet->Mdl, NormalPagePriority);
+    StartVa += Packet->Offset;
+
+    IpHeader = (PIP_HEADER)(StartVa + Info->IpHeader.Offset);
+
+    if (IpHeader->Version == 4) {
+        BOOLEAN OffloadChecksum;
+
+        if (OffloadOptions->OffloadIpVersion4HeaderChecksum)
+            OffloadChecksum = TRUE;
+        else
+            OffloadChecksum = FALSE;
+
+        // IP header checksums are always present and not validated
+
+        if (OffloadChecksum) {
+            USHORT  Embedded;
+            USHORT  Calculated;
+
+            Embedded = IpHeader->Version4.Checksum;
+
+            Calculated = ChecksumIpVersion4Header(StartVa, Info);
+            OffloadStatistics->IpVersion4HeaderChecksumCalculated++;
+
+            if (Embedded == Calculated) {
+                Packet->Flags.IpChecksumSucceeded = 1;
+                OffloadStatistics->IpVersion4HeaderChecksumSucceeded++;
+            } else {
+                Packet->Flags.IpChecksumFailed = 1;
+                OffloadStatistics->IpVersion4HeaderChecksumFailed++;
+            }
+        }
+
+        if (!OffloadChecksum ||
+            OffloadOptions->NeedChecksumValue ||
+            DriverParameters.ReceiverCalculateChecksums) {  // Checksum must be present
+            Packet->Flags.IpChecksumPresent = 1;
+            OffloadStatistics->IpVersion4HeaderChecksumPresent++;
+        } else {
+            IpHeader->Version4.Checksum = 0;
+        }
+
+        IsAFragment = IPV4_IS_A_FRAGMENT(NTOHS(IpHeader->Version4.FragmentOffsetAndFlags)) ? TRUE : FALSE;
+    } else {
+        ASSERT3U(IpHeader->Version, ==, 6);
+
+        IsAFragment = FALSE;  // No fragmentation in IPv6
+    }
+
+    if (Info->TcpHeader.Length != 0 && !IsAFragment) {
+        PTCP_HEADER     TcpHeader;
+        BOOLEAN         OffloadChecksum;
+
+        TcpHeader = (PTCP_HEADER)(StartVa + Info->TcpHeader.Offset);
+
+        if (IpHeader->Version == 4 && OffloadOptions->OffloadIpVersion4TcpChecksum)
+            OffloadChecksum = TRUE;
+        else if (IpHeader->Version == 6 && OffloadOptions->OffloadIpVersion6TcpChecksum)
+            OffloadChecksum = TRUE;
+        else
+            OffloadChecksum = FALSE;
+
+        if (OffloadChecksum) {
+            if (flags & NETRXF_data_validated) {    // Checksum may not be present but it is validated
+                Packet->Flags.TcpChecksumSucceeded = 1;
+
+                if (IpHeader->Version == 4)
+                    OffloadStatistics->IpVersion4TcpChecksumSucceeded++;
+                else
+                    OffloadStatistics->IpVersion6TcpChecksumSucceeded++;
+
+            } else {                                // Checksum is present but is not validated
+                USHORT  Embedded;
+                USHORT  Calculated;
+
+                ASSERT(~flags & NETRXF_csum_blank);
+
+                Embedded = TcpHeader->Checksum;
+
+                Calculated = ChecksumPseudoHeader(StartVa, Info);
+                Calculated = ChecksumTcpPacket(StartVa, Info, Calculated, &Payload);
+
+                if (IpHeader->Version == 4)
+                    OffloadStatistics->IpVersion4TcpChecksumCalculated++;
+                else
+                    OffloadStatistics->IpVersion6TcpChecksumCalculated++;
+
+                if (Embedded == Calculated) {
+                    Packet->Flags.TcpChecksumSucceeded = 1;
+
+                    if (IpHeader->Version == 4)
+                        OffloadStatistics->IpVersion4TcpChecksumSucceeded++;
+                    else
+                        OffloadStatistics->IpVersion6TcpChecksumSucceeded++;
+
+                } else {
+                    Packet->Flags.TcpChecksumFailed = 1;
+
+                    if (IpHeader->Version == 4)
+                        OffloadStatistics->IpVersion4TcpChecksumFailed++;
+                    else
+                        OffloadStatistics->IpVersion6TcpChecksumFailed++;
+                }
+            }
+        }
+        
+        if (!OffloadChecksum ||
+            OffloadOptions->NeedChecksumValue ||
+            DriverParameters.ReceiverCalculateChecksums) {  // Checksum must be present
+            if (flags & NETRXF_csum_blank) {                // Checksum is not present
+                USHORT  Calculated;
+
+                Calculated = ChecksumPseudoHeader(StartVa, Info);
+                Calculated = ChecksumTcpPacket(StartVa, Info, Calculated, &Payload);
+
+                if (IpHeader->Version == 4)
+                    OffloadStatistics->IpVersion4TcpChecksumCalculated++;
+                else
+                    OffloadStatistics->IpVersion6TcpChecksumCalculated++;
+
+                TcpHeader->Checksum = Calculated;
+            }
+
+            Packet->Flags.TcpChecksumPresent = 1;
+
+            if (IpHeader->Version == 4)
+                OffloadStatistics->IpVersion4TcpChecksumPresent++;
+            else
+                OffloadStatistics->IpVersion6TcpChecksumPresent++;
+        } else {
+            TcpHeader->Checksum = 0;
+        }
+
+    } else if (Info->UdpHeader.Length != 0 && !IsAFragment) {
+        PUDP_HEADER     UdpHeader;
+        BOOLEAN         OffloadChecksum;
+
+        UdpHeader = (PUDP_HEADER)(StartVa + Info->UdpHeader.Offset);
+
+        if (IpHeader->Version == 4 && OffloadOptions->OffloadIpVersion4UdpChecksum)
+            OffloadChecksum = TRUE;
+        else if (IpHeader->Version == 6 && OffloadOptions->OffloadIpVersion6UdpChecksum)
+            OffloadChecksum = TRUE;
+        else
+            OffloadChecksum = FALSE;
+
+        if (OffloadChecksum) {
+            if (flags & NETRXF_data_validated) {    // Checksum may not be present but it is validated
+                Packet->Flags.UdpChecksumSucceeded = 1;
+
+                if (IpHeader->Version == 4)
+                    OffloadStatistics->IpVersion4UdpChecksumSucceeded++;
+                else
+                    OffloadStatistics->IpVersion6UdpChecksumSucceeded++;
+
+            } else {                                // Checksum is present but is not validated
+                USHORT  Embedded;
+                USHORT  Calculated;
+
+                ASSERT(~flags & NETRXF_csum_blank);
+
+                Embedded = UdpHeader->Checksum;
+
+                Calculated = ChecksumPseudoHeader(StartVa, Info);
+                Calculated = ChecksumUdpPacket(StartVa, Info, Calculated, &Payload);
+
+                if (IpHeader->Version == 4)
+                    OffloadStatistics->IpVersion4UdpChecksumCalculated++;
+                else
+                    OffloadStatistics->IpVersion6UdpChecksumCalculated++;
+
+                if (Embedded == Calculated) {
+                    Packet->Flags.UdpChecksumSucceeded = 1;
+
+                    if (IpHeader->Version == 4)
+                        OffloadStatistics->IpVersion4UdpChecksumSucceeded++;
+                    else
+                        OffloadStatistics->IpVersion6UdpChecksumSucceeded++;
+
+                } else {
+                    Packet->Flags.UdpChecksumFailed = 1;
+
+                    if (IpHeader->Version == 4)
+                        OffloadStatistics->IpVersion4UdpChecksumFailed++;
+                    else
+                        OffloadStatistics->IpVersion6UdpChecksumFailed++;
+
+                }
+            }
+
+        }
+
+        if (!OffloadChecksum ||
+            OffloadOptions->NeedChecksumValue ||
+            DriverParameters.ReceiverCalculateChecksums) {  // Checksum must be present
+            if (flags & NETRXF_csum_blank) {                // Checksum is not present
+                USHORT  Calculated;
+
+                Calculated = ChecksumPseudoHeader(StartVa, Info);
+                Calculated = ChecksumUdpPacket(StartVa, Info, Calculated, &Payload);
+
+                if (IpHeader->Version == 4)
+                    OffloadStatistics->IpVersion4UdpChecksumCalculated++;
+                else
+                    OffloadStatistics->IpVersion6UdpChecksumCalculated++;
+
+                UdpHeader->Checksum = Calculated;
+            }
+
+            Packet->Flags.UdpChecksumPresent = 1;
+
+            if (IpHeader->Version == 4)
+                OffloadStatistics->IpVersion4UdpChecksumPresent++;
+            else
+                OffloadStatistics->IpVersion6UdpChecksumPresent++;
+        } else {
+            UdpHeader->Checksum = 0;
+        }
+    }
+}
+
+static FORCEINLINE VOID
+__drv_requiresIRQL(DISPATCH_LEVEL)
+__RingAcquireLock(
+    IN  PRECEIVER_RING  Ring
+    )
+{
+    ASSERT3U(KeGetCurrentIrql(), ==, DISPATCH_LEVEL);
+
+    KeAcquireSpinLockAtDpcLevel(&Ring->Lock);
+}
+
+static DECLSPEC_NOINLINE VOID
+RingAcquireLock(
+    IN  PRECEIVER_RING  Ring
+    )
+{
+    __RingAcquireLock(Ring);
+}
+
+static FORCEINLINE VOID
+__drv_requiresIRQL(DISPATCH_LEVEL)
+__RingReleaseLock(
+    IN  PRECEIVER_RING  Ring
+    )
+{
+    ASSERT3U(KeGetCurrentIrql(), ==, DISPATCH_LEVEL);
+
+#pragma prefast(disable:26110)
+    KeReleaseSpinLockFromDpcLevel(&Ring->Lock);
+}
+
+static DECLSPEC_NOINLINE VOID
+RingReleaseLock(
+    IN  PRECEIVER_RING  Ring
+    )
+{
+    __RingReleaseLock(Ring);
+}
+
+static FORCEINLINE VOID
+__RingStop(
+    IN  PRECEIVER_RING  Ring
+    )
+{
+    Info("<===>\n");
+
+    Ring->Stopped = TRUE;
+}
+
+static FORCEINLINE VOID
+__RingStart(
+    IN  PRECEIVER_RING  Ring
+    )
+{
+    Ring->Stopped = FALSE;
+}
+
+static FORCEINLINE BOOLEAN
+__RingIsStopped(
+    IN  PRECEIVER_RING  Ring
+    )
+{
+    return Ring->Stopped;
+}
+
+static FORCEINLINE VOID
+__RingReturnPacket(
+    IN  PRECEIVER_RING          Ring,
+    IN  PXENVIF_RECEIVER_PACKET Packet,
+    IN  BOOLEAN                 Locked
+    )
+{
+    PMDL                        Mdl;
+
+    Mdl = &Packet->Mdl;
+
+    while (Mdl != NULL) {
+        PMDL    Next;
+
+        Next = Mdl->Next;
+        Mdl->Next = NULL;
+
+        __RingPutMdl(Ring, Mdl, Locked);
+
+        Mdl = Next;
+    }
+
+    if (__RingIsStopped(Ring)) {
+        KIRQL   Irql;
+
+        KeRaiseIrql(DISPATCH_LEVEL, &Irql);
+
+        if (!Locked)
+            __RingAcquireLock(Ring);
+
+        if (__RingIsStopped(Ring)) {
+            PXENVIF_RECEIVER    Receiver;
+            PXENVIF_FRONTEND    Frontend;
+
+            __RingStart(Ring);
+
+            Receiver = Ring->Receiver;
+            Frontend = Receiver->Frontend;
+
+            NotifierTrigger(FrontendGetNotifier(Frontend));
+        }
+
+        if (!Locked)
+            __RingReleaseLock(Ring);
+
+        KeLowerIrql(Irql);
+    }
+}
+
+static BOOLEAN
+ReceiverPullup(
+    IN      PVOID                   Argument,
+    IN      PUCHAR                  DestinationVa,
+    IN OUT  PXENVIF_PACKET_PAYLOAD  Payload,
+    IN      ULONG                   Length
+    )
+{
+    PMDL                            Mdl;
+
+    Mdl = Payload->Mdl;
+    ASSERT3U(Payload->Offset, ==, 0);
+
+    if (Payload->Length < Length)
+        goto fail1;
+
+    Payload->Length -= Length;
+
+    while (Length != 0) {
+        PUCHAR  SourceVa;
+        ULONG   CopyLength;
+
+        SourceVa = MmGetSystemAddressForMdlSafe(Mdl, NormalPagePriority);
+        ASSERT(SourceVa != NULL);
+
+        CopyLength = __min(Mdl->ByteCount, Length);
+
+        RtlCopyMemory(DestinationVa, SourceVa, CopyLength);
+
+        DestinationVa += CopyLength;
+
+        Mdl->ByteOffset += CopyLength;
+        Mdl->MappedSystemVa = (PUCHAR)Mdl->MappedSystemVa + CopyLength;
+        Length -= CopyLength;
+
+        Mdl->ByteCount -= CopyLength;
+        if (Mdl->ByteCount == 0) {
+            PRECEIVER_RING  Ring = Argument;
+            PMDL            Next;
+
+            Next = Mdl->Next;
+            Mdl->Next = NULL;
+
+            __RingPutMdl(Ring, Mdl, TRUE);
+
+            Mdl = Next;
+        }
+    }
+
+    Payload->Mdl = Mdl;
+
+    return TRUE;
+
+fail1:
+    Error("fail1\n");
+
+    return FALSE;
+}
+
+static FORCEINLINE PXENVIF_RECEIVER_PACKET
+__RingBuildSegment(
+    IN  PRECEIVER_RING          Ring,
+    IN  PXENVIF_RECEIVER_PACKET Packet,
+    IN  ULONG                   SegmentSize,
+    IN  PXENVIF_PACKET_PAYLOAD  Payload
+    )
+{
+    PXENVIF_PACKET_INFO         Info;
+    PXENVIF_RECEIVER_PACKET     Segment;
+    PMDL                        Mdl;
+    PUCHAR                      InfoVa;
+    PUCHAR                      StartVa;
+    PIP_HEADER                  IpHeader;
+    PTCP_HEADER                 TcpHeader;
+    ULONG                       Seq;
+    NTSTATUS                    status;
+
+    Info = &Packet->Info;
+
+    InfoVa = MmGetSystemAddressForMdlSafe(&Packet->Mdl, NormalPagePriority);
+    InfoVa += Packet->Offset;
+
+    Segment = __RingGetPacket(Ring, TRUE);
+
+    status = STATUS_NO_MEMORY;
+    if (Segment == NULL)
+        goto fail1;
+
+    Segment->Offset = Packet->Offset;
+
+    Mdl = &Segment->Mdl;
+
+    StartVa = MmGetSystemAddressForMdlSafe(Mdl, NormalPagePriority);
+    StartVa += Segment->Offset;
+
+    Mdl->ByteCount = Segment->Offset;
+
+    // Copy in the header
+    RtlCopyMemory(StartVa, InfoVa, Info->Length);
+    Mdl->ByteCount += Info->Length;
+
+    Segment->Info = Packet->Info;
+    Segment->Cookie = Packet->Cookie;
+
+    // Adjust the info for the next segment
+    IpHeader = (PIP_HEADER)(InfoVa + Info->IpHeader.Offset);
+    if (IpHeader->Version == 4) {
+        USHORT  PacketID;
+        USHORT  PacketLength;
+
+        PacketID = NTOHS(IpHeader->Version4.PacketID);
+        IpHeader->Version4.PacketID = HTONS(PacketID + 1);
+
+        PacketLength = NTOHS(IpHeader->Version4.PacketLength);
+        IpHeader->Version4.PacketLength = HTONS(PacketLength - (USHORT)SegmentSize);
+    } else {
+        USHORT  PayloadLength;
+
+        ASSERT3U(IpHeader->Version, ==, 6);
+
+        PayloadLength = NTOHS(IpHeader->Version6.PayloadLength);
+        IpHeader->Version6.PayloadLength = HTONS(PayloadLength - (USHORT)SegmentSize);
+    }
+
+    TcpHeader = (PTCP_HEADER)(InfoVa + Info->TcpHeader.Offset);
+
+    Seq = NTOHL(TcpHeader->Seq);
+    TcpHeader->Seq = HTONL(Seq + SegmentSize);
+
+    TcpHeader->Flags &= ~TCP_CWR;
+
+    // Adjust the segment IP header
+    IpHeader = (PIP_HEADER)(StartVa + Info->IpHeader.Offset);
+    if (IpHeader->Version == 4) {
+        ULONG   Length;
+
+        Length = Info->IpHeader.Length +
+                 Info->IpOptions.Length + 
+                 Info->TcpHeader.Length + 
+                 Info->TcpOptions.Length + 
+                 SegmentSize;
+
+        IpHeader->Version4.PacketLength = HTONS((USHORT)Length);
+        IpHeader->Version4.Checksum = ChecksumIpVersion4Header(StartVa, Info);
+
+        Ring->OffloadStatistics.IpVersion4LargePacketSegment++;
+    } else {
+        ULONG   Length;
+
+        ASSERT3U(IpHeader->Version, ==, 6);
+
+        Length = Info->IpOptions.Length + 
+                 Info->TcpHeader.Length + 
+                 Info->TcpOptions.Length + 
+                 SegmentSize;
+
+        IpHeader->Version6.PayloadLength = HTONS((USHORT)Length);
+
+        Ring->OffloadStatistics.IpVersion6LargePacketSegment++;
+    }
+
+    // Adjust the segment TCP header
+    TcpHeader = (PTCP_HEADER)(StartVa + Info->TcpHeader.Offset);
+
+    TcpHeader->Flags &= ~(TCP_PSH | TCP_FIN);
+
+    // Copy in the payload
+    for (;;) {
+        ULONG   Length;
+
+        Mdl->Next = __RingGetMdl(Ring, TRUE);
+            
+        status = STATUS_NO_MEMORY;
+        if (Mdl->Next == NULL)
+            goto fail2;
+
+        Mdl = Mdl->Next;
+        StartVa = MmGetSystemAddressForMdlSafe(Mdl, NormalPagePriority);
+
+        Length = __min(SegmentSize - Segment->Length, PAGE_SIZE);
+        ASSERT(Length != 0);
+
+        ReceiverPullup(Ring, StartVa, Payload, Length);
+        Mdl->ByteCount += Length;
+        Segment->Length += Length;
+
+        ASSERT3U(Segment->Length, <=, SegmentSize);
+        if (Segment->Length == SegmentSize)
+            break;
+
+        ASSERT3U(Mdl->ByteCount, ==, PAGE_SIZE);
+    }
+
+    Segment->Length += Info->Length;
+
+    return Segment;
+
+fail2:
+    Error("fail2\n");
+
+    if (IpHeader->Version == 4) {
+        --Ring->OffloadStatistics.IpVersion4LargePacketSegment;
+    } else {
+        ASSERT3U(IpHeader->Version, ==, 6);
+        --Ring->OffloadStatistics.IpVersion6LargePacketSegment;
+    }
+
+    Mdl = Segment->Mdl.Next;
+    Segment->Mdl.Next = NULL;
+
+    while (Mdl != NULL) {
+        PMDL    Next;
+
+        Next = Mdl->Next;
+        Mdl->Next = NULL;
+
+        __RingPutMdl(Ring, Mdl, TRUE);
+
+        Mdl = Next;
+    }
+
+    __RingPutPacket(Ring, Segment, TRUE);
+
+fail1:
+    Error("fail1 (%08x)\n", status);
+    
+    return NULL;
+}
+
+static FORCEINLINE VOID
+__RingProcessLargePacket(
+    IN  PRECEIVER_RING          Ring,
+    IN  PXENVIF_RECEIVER_PACKET Packet,
+    OUT PLIST_ENTRY             List
+    )
+{
+    BOOLEAN                     Offload;
+    PXENVIF_PACKET_INFO         Info;
+    uint16_t                    flags;
+    XENVIF_PACKET_PAYLOAD       Payload;
+    PUCHAR                      InfoVa;
+    PIP_HEADER                  IpHeader;
+    ULONG                       Length;
+    NTSTATUS                    status;
+
+    Info = &Packet->Info;
+    
+    flags = (uint16_t)(ULONG_PTR)Packet->Cookie;
+    ASSERT(flags & NETRXF_csum_blank);
+    ASSERT(flags & NETRXF_data_validated);
+
+    Payload.Mdl = Packet->Mdl.Next;
+    Payload.Offset = 0;
+    Payload.Length = Packet->Length - Info->Length;
+
+    Packet->Mdl.Next = NULL;
+
+    InfoVa = MmGetSystemAddressForMdlSafe(&Packet->Mdl, NormalPagePriority);
+    InfoVa += Packet->Offset;
+
+    IpHeader = (PIP_HEADER)(InfoVa + Info->IpHeader.Offset);
+
+    if (IpHeader->Version == 4) {
+        Offload = (Ring->OffloadOptions.OffloadIpVersion4LargePacket) ? TRUE : FALSE;
+    } else {
+        ASSERT3U(IpHeader->Version, ==, 6);
+        Offload = (Ring->OffloadOptions.OffloadIpVersion6LargePacket) ? TRUE : FALSE;
+    }
+
+    if (IpHeader->Version == 4) {
+        USHORT  PacketLength;
+
+        PacketLength = NTOHS(IpHeader->Version4.PacketLength);
+        
+        Length = (ULONG)PacketLength -
+                 Info->TcpOptions.Length -
+                 Info->TcpHeader.Length -
+                 Info->IpOptions.Length - 
+                 Info->IpHeader.Length;
+    } else {
+        USHORT  PayloadLength;
+
+        ASSERT3U(IpHeader->Version, ==, 6);
+
+        PayloadLength = NTOHS(IpHeader->Version6.PayloadLength);
+
+        Length = (ULONG)PayloadLength -
+                 Info->TcpOptions.Length -
+                 Info->TcpHeader.Length -
+                 Info->IpOptions.Length;
+    }
+
+    while (Length > 0) {
+        ULONG                   SegmentSize;
+        PXENVIF_RECEIVER_PACKET Segment;
+
+        if (Offload &&
+            Ring->OffloadOptions.NeedLargePacketSplit == 0)
+            break;
+
+        SegmentSize = __min(Length, Packet->MaximumSegmentSize);
+
+        Segment = __RingBuildSegment(Ring, Packet, SegmentSize, &Payload);
+
+        status = STATUS_NO_MEMORY;
+        if (Segment == NULL)
+            goto fail1;
+
+        ASSERT3U(Length, >=, SegmentSize);
+        Length -= SegmentSize;
+
+        ASSERT(IsZeroMemory(&Segment->ListEntry, sizeof (LIST_ENTRY)));
+        InsertTailList(List, &Segment->ListEntry);
+
+        if (Offload) {
+            ASSERT(Ring->OffloadOptions.NeedLargePacketSplit);
+            break;
+        }
+    }
+
+    if (Length != 0) {
+        ASSERT(Payload.Mdl != NULL);
+
+        if (IpHeader->Version == 4) {
+            USHORT  PacketLength;
+
+            PacketLength = NTOHS(IpHeader->Version4.PacketLength);
+        
+            ASSERT3U(Length,
+                     ==,
+                     (ULONG)PacketLength -
+                     Info->TcpOptions.Length -
+                     Info->TcpHeader.Length -
+                     Info->IpOptions.Length - 
+                     Info->IpHeader.Length);
+
+            IpHeader->Version4.Checksum = ChecksumIpVersion4Header(InfoVa, Info);
+
+            Ring->OffloadStatistics.IpVersion4LargePacketSegment++;
+        } else {
+            USHORT  PayloadLength;
+
+            ASSERT3U(IpHeader->Version, ==, 6);
+
+            PayloadLength = NTOHS(IpHeader->Version6.PayloadLength);
+
+            ASSERT3U(Length,
+                     ==,
+                     (ULONG)PayloadLength -
+                     Info->TcpOptions.Length -
+                     Info->TcpHeader.Length -
+                     Info->IpOptions.Length);
+
+            Ring->OffloadStatistics.IpVersion6LargePacketSegment++;
+        }
+
+        Packet->Mdl.Next = Payload.Mdl;
+        Packet->Length = Info->Length + Payload.Length;
+
+        if (Payload.Length < Packet->MaximumSegmentSize)
+            Packet->MaximumSegmentSize = 0;
+
+        ASSERT(IsZeroMemory(&Packet->ListEntry, sizeof (LIST_ENTRY)));
+        InsertTailList(List, &Packet->ListEntry);
+    } else {
+        __RingPutPacket(Ring, Packet, TRUE);
+    }
+
+    return;
+
+fail1:
+    Error("fail1 (%08x)\n", status);
+
+    if (Payload.Length != 0) {
+        PMDL    Mdl = Payload.Mdl;
+
+        ASSERT(Mdl != NULL);
+
+        while (Mdl != NULL) {
+            PMDL    Next;
+
+            Next = Mdl->Next;
+            Mdl->Next = NULL;
+
+            __RingPutMdl(Ring, Mdl, TRUE);
+
+            Mdl = Next;
+        }
+    }
+
+    __RingPutPacket(Ring, Packet, TRUE);
+        
+    Ring->PacketStatistics.Drop++;
+}
+
+static FORCEINLINE VOID
+__RingProcessPacket(
+    IN  PRECEIVER_RING          Ring,
+    IN  PXENVIF_RECEIVER_PACKET Packet,
+    OUT PLIST_ENTRY             List
+    )
+{
+    PXENVIF_RECEIVER            Receiver;
+    PXENVIF_FRONTEND            Frontend;
+    ULONG                       Length;
+    USHORT                      MaximumSegmentSize;
+    PVOID                       Cookie;
+    XENVIF_PACKET_PAYLOAD       Payload;
+    PXENVIF_PACKET_INFO         Info;
+    PUCHAR                      StartVa;
+    PETHERNET_HEADER            EthernetHeader;
+    PETHERNET_ADDRESS           DestinationAddress;
+    ETHERNET_ADDRESS_TYPE       Type;
+    NTSTATUS                    status;
+
+    Receiver = Ring->Receiver;
+    Frontend = Receiver->Frontend;
+
+    ASSERT3U(Packet->Offset, ==, 0);
+    Length = Packet->Length;
+    MaximumSegmentSize = Packet->MaximumSegmentSize;
+    Cookie = Packet->Cookie;
+
+    // Clean the packet metadata since this structure now becomes payload
+    RtlZeroMemory(Packet, FIELD_OFFSET(XENVIF_RECEIVER_PACKET, Mdl));
+
+    Payload.Mdl = &Packet->Mdl;
+    Payload.Offset = 0;
+    Payload.Length = Length;
+
+    // Get a new packet structure that will just contain the header after parsing
+    Packet = __RingGetPacket(Ring, TRUE);
+
+    status = STATUS_NO_MEMORY;
+    if (Packet == NULL) {
+        Ring->PacketStatistics.FrontendError++;
+        goto fail1;
+    }
+
+    // Copy in the extracted metadata
+    Packet->Offset = DriverParameters.ReceiverIpAlignOffset;
+    Packet->Length = Length;
+    Packet->MaximumSegmentSize = MaximumSegmentSize;
+    Packet->Cookie = Cookie;
+
+    StartVa = MmGetSystemAddressForMdlSafe(&Packet->Mdl, NormalPagePriority);
+    StartVa += Packet->Offset;
+
+    Packet->Mdl.ByteCount = Packet->Offset;
+
+    Info = &Packet->Info;
+
+    status = ParsePacket(StartVa, ReceiverPullup, Ring, &Ring->HeaderStatistics, &Payload, Info);
+    if (!NT_SUCCESS(status)) {
+        Ring->PacketStatistics.FrontendError++;
+        goto fail2;
+    }
+
+    ASSERT3U(Packet->Length, ==, Info->Length + Payload.Length);
+
+    Packet->Mdl.ByteCount += Info->Length;
+
+    // Certain HCK tests (e.g. the NDISTest 2c_Priority test) are sufficiently brain-dead
+    // that they cannot cope with multi-fragment packets, or at least packets where
+    // headers are in different fragments. All these tests seem to use IPX packets and,
+    // in practice, little else uses LLC so pull up all LLC packets into a single fragment.
+    if (Info->LLCSnapHeader.Length != 0) {
+        ULONG   PayloadLength = Payload.Length;
+
+        ASSERT3U(Packet->Length, <=, PAGE_SIZE);
+
+        status = ReceiverPullup(Ring, StartVa + Info->Length, &Payload, PayloadLength);
+        ASSERT(NT_SUCCESS(status));
+
+        ASSERT3U(Payload.Length, ==, 0);
+        Packet->Mdl.ByteCount += PayloadLength;
+    }
+
+    if (Payload.Length != 0) {
+        PMDL    Mdl = Payload.Mdl;
+
+        ASSERT(Mdl != NULL);
+
+        Packet->Mdl.Next = Mdl;
+    }
+
+    ASSERT(Info->EthernetHeader.Length != 0);
+    EthernetHeader = (PETHERNET_HEADER)(StartVa + Info->EthernetHeader.Offset);
+
+    DestinationAddress = &EthernetHeader->DestinationAddress;
+
+    status = STATUS_UNSUCCESSFUL;
+    if (!MacApplyFilters(FrontendGetMac(Frontend),
+                         DestinationAddress))
+        goto fail3;
+
+    Type = GET_ETHERNET_ADDRESS_TYPE(DestinationAddress);
+
+    switch (Type) {
+    case ETHERNET_ADDRESS_UNICAST:
+        Ring->PacketStatistics.Unicast++;
+        Ring->PacketStatistics.UnicastBytes += Packet->Length;
+        break;
+            
+    case ETHERNET_ADDRESS_MULTICAST:
+        Ring->PacketStatistics.Multicast++;
+        Ring->PacketStatistics.MulticastBytes += Packet->Length;
+        break;
+
+    case ETHERNET_ADDRESS_BROADCAST:
+        Ring->PacketStatistics.Broadcast++;
+        Ring->PacketStatistics.BroadcastBytes += Packet->Length;
+        break;
+
+    default:
+        ASSERT(FALSE);
+        break;
+    }
+
+    if (Packet->MaximumSegmentSize != 0) {
+        __RingProcessLargePacket(Ring, Packet, List);
+    } else {
+        ASSERT(IsZeroMemory(&Packet->ListEntry, sizeof (LIST_ENTRY)));
+        InsertTailList(List, &Packet->ListEntry);
+    }
+
+    return;
+
+fail3:
+    Packet->Mdl.Next = NULL;
+    __RingPutPacket(Ring, Packet, TRUE);
+
+fail2:
+fail1:
+    if (Payload.Length != 0) {
+        PMDL    Mdl = Payload.Mdl;
+
+        ASSERT(Mdl != NULL);
+
+        while (Mdl != NULL) {
+            PMDL    Next;
+
+            Next = Mdl->Next;
+            Mdl->Next = NULL;
+
+            __RingPutMdl(Ring, Mdl, TRUE);
+
+            Mdl = Next;
+        }
+    }
+        
+    Ring->PacketStatistics.Drop++;
+}
+
+static VOID
+RingProcessPackets(
+    IN      PRECEIVER_RING  Ring,
+    OUT     PLIST_ENTRY     List,
+    OUT     PULONG          Count
+    )
+{
+    PLIST_ENTRY             ListEntry;
+
+    while (!IsListEmpty(&Ring->PacketList)) {
+        PXENVIF_RECEIVER_PACKET Packet;
+
+        ListEntry = RemoveHeadList(&Ring->PacketList);
+        ASSERT3P(ListEntry, !=, &Ring->PacketList);
+
+        RtlZeroMemory(ListEntry, sizeof (LIST_ENTRY));
+
+        Packet = CONTAINING_RECORD(ListEntry, XENVIF_RECEIVER_PACKET, ListEntry);
+        __RingProcessPacket(Ring, Packet, List);
+    }
+
+    for (ListEntry = List->Flink;
+         ListEntry != List;
+         ListEntry = ListEntry->Flink) {
+        PXENVIF_RECEIVER_PACKET Packet;
+
+        Packet = CONTAINING_RECORD(ListEntry, XENVIF_RECEIVER_PACKET, ListEntry);
+
+        PacketProcessTag(Packet,
+                         &Ring->OffloadOptions,
+                         &Ring->OffloadStatistics);
+
+        PacketProcessChecksum(Packet,
+                              &Ring->OffloadOptions,
+                              &Ring->OffloadStatistics);
+
+        Packet->Cookie = Ring;
+
+        (*Count)++;
+    }
+}
+
+static FORCEINLINE PRECEIVER_TAG
+__Protocol0GetTag(
+    IN  PRECEIVER_RING          Ring
+    )
+{
+    PRECEIVER_RING_PROTOCOL0    Protocol0;
+    ULONG                       Index;
+    PRECEIVER_TAG               Tag;
+
+    Protocol0 = &Ring->Protocol0;
+
+    Index = Protocol0->HeadFreeTag;
+    ASSERT3U(Index, <, MAXIMUM_TAG_COUNT);
+
+    Tag = &Protocol0->Tag[Index];
+    Protocol0->HeadFreeTag = Tag->Next;
+    Tag->Next = TAG_INDEX_INVALID;
+
+    return Tag;
+}
+
+static FORCEINLINE
+__Protocol0PutTag(
+    IN  PRECEIVER_RING          Ring,
+    IN  PRECEIVER_TAG           Tag
+    )
+{
+    PRECEIVER_RING_PROTOCOL0    Protocol0;
+    ULONG                       Index;
+
+    Protocol0 = &Ring->Protocol0;
+
+    ASSERT3P(Tag->Context, ==, NULL);
+
+    Index = (ULONG)(Tag - &Protocol0->Tag[0]);
+    ASSERT3U(Index, <, MAXIMUM_TAG_COUNT);
+
+    ASSERT3U(Tag->Next, ==, TAG_INDEX_INVALID);
+    Tag->Next = Protocol0->HeadFreeTag;
+    Protocol0->HeadFreeTag = Index;
+}
+
+static FORCEINLINE PRECEIVER_TAG
+__Protocol0PreparePacket(
+    IN  PRECEIVER_RING  Ring,
+    IN  PMDL            Mdl
+    )
+{
+    PXENVIF_RECEIVER    Receiver;
+    PXENVIF_FRONTEND    Frontend;
+    PRECEIVER_TAG       Tag;
+
+    Receiver = Ring->Receiver;
+    Frontend = Receiver->Frontend;
+
+    Tag = __Protocol0GetTag(Ring);
+
+    Tag->Context = Mdl;
+
+    GNTTAB(PermitForeignAccess,
+           Receiver->GnttabInterface,
+           Tag->Reference,
+           FrontendGetBackendDomain(Frontend),
+           GNTTAB_ENTRY_FULL_PAGE,
+           MmGetMdlPfnArray(Mdl)[0],
+           FALSE);
+
+    return Tag;
+}
+
+static VOID
+Protocol0ReleaseTag(
+    IN  PRECEIVER_RING  Ring,
+    IN  PRECEIVER_TAG   Tag
+    )
+{
+    PXENVIF_RECEIVER    Receiver;
+
+    Receiver = Ring->Receiver;
+
+    GNTTAB(RevokeForeignAccess,
+           Receiver->GnttabInterface,
+           Tag->Reference);
+
+    __Protocol0PutTag(Ring, Tag);
+}
+
+static FORCEINLINE VOID
+__Protocol0PushRequests(
+    IN  PRECEIVER_RING          Ring
+    )
+{
+    PRECEIVER_RING_PROTOCOL0    Protocol0;
+    BOOLEAN                     Notify;
+
+    Protocol0 = &Ring->Protocol0;
+
+    if (Protocol0->RequestsPosted == Protocol0->RequestsPushed)
+        return;
+
+#pragma warning (push)
+#pragma warning (disable:4244)
+
+    // Make the requests visible to the backend
+    RING_PUSH_REQUESTS_AND_CHECK_NOTIFY(&Protocol0->Front, Notify);
+
+#pragma warning (pop)
+
+    if (Notify) {
+        PXENVIF_RECEIVER    Receiver;
+        PXENVIF_FRONTEND    Frontend;
+
+        Receiver = Ring->Receiver;
+        Frontend = Receiver->Frontend;
+
+        NotifierSend(FrontendGetNotifier(Frontend));
+    }
+
+    Protocol0->RequestsPushed = Protocol0->RequestsPosted;
+}
+
+static VOID
+Protocol0Fill(
+    IN  PRECEIVER_RING          Ring
+    )
+{
+    PRECEIVER_RING_PROTOCOL0    Protocol0;
+    RING_IDX                    req_prod;
+    RING_IDX                    rsp_cons;
+
+    Protocol0 = &Ring->Protocol0;
+
+    req_prod = Protocol0->Front.req_prod_pvt;
+    rsp_cons = Protocol0->Front.rsp_cons;
+
+    while (req_prod - rsp_cons < RING_SIZE(&Protocol0->Front)) {
+        PXENVIF_RECEIVER_PACKET Packet;
+        PRECEIVER_TAG           Tag;
+        netif_rx_request_t      *req;
+        uint16_t                id;
+
+        Packet = __RingGetPacket(Ring, TRUE);
+
+        if (Packet == NULL) {
+            __RingStop(Ring);
+            break;
+        }
+
+        Tag = __Protocol0PreparePacket(Ring, &Packet->Mdl);
+        ASSERT(Tag != NULL);
+
+        req = RING_GET_REQUEST(&Protocol0->Front, req_prod);
+        req_prod++;
+        Protocol0->RequestsPosted++;
+
+        id = (USHORT)(Tag - &Protocol0->Tag[0]);
+        ASSERT3U(id, <, MAXIMUM_TAG_COUNT);
+
+        req->id = id | REQ_ID_INTEGRITY_CHECK;
+        req->gref = Tag->Reference;
+
+        // Store a copy of the request in case we need to fake a response ourselves
+        ASSERT(IsZeroMemory(&Protocol0->Pending[id], sizeof (netif_rx_request_t)));
+        Protocol0->Pending[id] = *req;
+    }
+
+    Protocol0->Front.req_prod_pvt = req_prod;
+
+    __Protocol0PushRequests(Ring);
+}
+
+#define PROTOCOL0_BATCH(_Ring) (RING_SIZE(&(_Ring)->Protocol0.Front) / 4)
+
+static DECLSPEC_NOINLINE VOID
+Protocol0Poll(
+    IN  PRECEIVER_RING          Ring
+    )
+{
+    PXENVIF_RECEIVER            Receiver;
+    PXENVIF_FRONTEND            Frontend;
+    PRECEIVER_RING_PROTOCOL0    Protocol0;
+
+    Receiver = Ring->Receiver;
+    Frontend = Receiver->Frontend;
+
+    Protocol0 = &Ring->Protocol0;
+
+    for (;;) {
+        BOOLEAN                 Error;
+        PXENVIF_RECEIVER_PACKET Packet;
+        uint16_t                flags;
+        USHORT                  MaximumSegmentSize;
+        PMDL                    TailMdl;
+        RING_IDX                rsp_prod;
+        RING_IDX                rsp_cons;
+
+        Error = FALSE;
+        Packet = NULL;
+        flags = 0;
+        MaximumSegmentSize = 0;
+        TailMdl = NULL;
+
+        KeMemoryBarrier();
+
+        rsp_prod = Protocol0->Shared->rsp_prod;
+        rsp_cons = Protocol0->Front.rsp_cons;
+
+        KeMemoryBarrier();
+
+        if (rsp_cons == rsp_prod)
+            break;
+
+        while (rsp_cons != rsp_prod) {
+            netif_rx_response_t *rsp;
+            uint16_t            id;
+            netif_rx_request_t  *req;
+            PRECEIVER_TAG       Tag;
+            PMDL                Mdl;
+            RING_IDX            req_prod;
+
+            rsp = RING_GET_RESPONSE(&Protocol0->Front, rsp_cons);
+            rsp_cons++;
+            Protocol0->ResponsesProcessed++;
+
+            ASSERT3U((rsp->id & REQ_ID_INTEGRITY_CHECK), ==, REQ_ID_INTEGRITY_CHECK);
+            id = rsp->id & ~REQ_ID_INTEGRITY_CHECK;
+
+            ASSERT3U(id, <, MAXIMUM_TAG_COUNT);
+            req = &Protocol0->Pending[id];
+
+            ASSERT3U(req->id, ==, rsp->id);
+            RtlZeroMemory(req, sizeof (netif_rx_request_t));
+
+            Tag = &Protocol0->Tag[id];
+
+            Mdl = Tag->Context;
+            Tag->Context = NULL;
+
+            Protocol0ReleaseTag(Ring, Tag);
+            Tag = NULL;
+
+            ASSERT(Mdl != NULL);
+
+            if (rsp->status < 0)
+                Error = TRUE;
+
+            if (rsp->flags & NETRXF_gso_prefix) {
+                __RingPutMdl(Ring, Mdl, TRUE);
+
+                flags = NETRXF_gso_prefix;
+                MaximumSegmentSize = rsp->offset;
+
+                ASSERT(rsp->flags & NETRXF_more_data);
+                continue;
+            } else {
+                Mdl->ByteOffset = rsp->offset;
+                Mdl->MappedSystemVa = (PUCHAR)Mdl->StartVa + rsp->offset;
+                Mdl->ByteCount = rsp->status;
+            }
+
+            if (Packet == NULL) {   // SOP
+                Packet = CONTAINING_RECORD(Mdl, XENVIF_RECEIVER_PACKET, Mdl);
+
+                ASSERT3P(TailMdl, ==, NULL);
+                TailMdl = Mdl;
+
+                ASSERT3U((flags & ~NETRXF_gso_prefix), ==, 0);
+                flags |= rsp->flags;
+
+                Packet->Length = Mdl->ByteCount;
+            } else {
+                ASSERT3P(Mdl->Next, ==, NULL);
+
+                ASSERT(TailMdl != NULL);
+                TailMdl->Next = Mdl;
+                TailMdl = Mdl;
+
+                flags |= rsp->flags;
+
+                Packet->Length += Mdl->ByteCount;
+            }
+
+            if (~rsp->flags & NETRXF_more_data) {  // EOP
+                ASSERT(Packet != NULL);
+                ASSERT3P(Packet->Cookie, ==, NULL);
+
+                if (Error) {
+                    Ring->PacketStatistics.BackendError++;
+
+                    __RingReturnPacket(Ring, Packet, TRUE);
+                } else {
+                    if (flags & NETRXF_gso_prefix) {
+                        ASSERT(MaximumSegmentSize != 0);
+                        Packet->MaximumSegmentSize = MaximumSegmentSize;
+                    }
+
+                    Packet->Cookie = (PVOID)(flags & (NETRXF_csum_blank | NETRXF_data_validated));
+
+                    ASSERT(IsZeroMemory(&Packet->ListEntry, sizeof (LIST_ENTRY)));
+                    InsertTailList(&Ring->PacketList, &Packet->ListEntry);
+                }
+
+                Error = FALSE;
+                Packet = NULL;
+                flags = 0;
+                MaximumSegmentSize = 0;
+                TailMdl = NULL;
+            }
+
+            KeMemoryBarrier();
+
+            req_prod = Protocol0->Front.req_prod_pvt;
+
+            if (req_prod - rsp_cons < PROTOCOL0_BATCH(Ring) &&
+                !__RingIsStopped(Ring)) {
+                Protocol0->Front.rsp_cons = rsp_cons;
+                Protocol0Fill(Ring);
+            }
+        }
+        ASSERT(!Error);
+        ASSERT3P(Packet, ==, NULL);
+        ASSERT3U(flags, ==, 0);
+        ASSERT3U(MaximumSegmentSize, ==, 0);
+        ASSERT3P(TailMdl, ==, NULL);
+
+        KeMemoryBarrier();
+
+        Protocol0->Front.rsp_cons = rsp_cons;
+        Protocol0->Shared->rsp_event = rsp_cons + 1;
+    }
+
+    if (!__RingIsStopped(Ring))
+        Protocol0Fill(Ring);
+}
+
+static FORCEINLINE VOID
+__Protocol0Empty(
+    IN  PRECEIVER_RING          Ring
+    )
+{
+    PRECEIVER_RING_PROTOCOL0    Protocol0;
+    RING_IDX                    rsp_cons;
+    RING_IDX                    rsp_prod;
+    uint16_t                    id;
+
+    Protocol0 = &Ring->Protocol0;
+
+    KeMemoryBarrier();
+
+    // Clean up any unprocessed responses
+    rsp_prod = Protocol0->Shared->rsp_prod;
+    rsp_cons = Protocol0->Front.rsp_cons;
+
+    KeMemoryBarrier();
+
+    while (rsp_cons != rsp_prod) {
+        netif_rx_response_t *rsp;
+        netif_rx_request_t  *req;
+        PRECEIVER_TAG       Tag;
+        PMDL                Mdl;
+
+        rsp = RING_GET_RESPONSE(&Protocol0->Front, rsp_cons);
+        rsp_cons++;
+        Protocol0->ResponsesProcessed++;
+
+        ASSERT3U((rsp->id & REQ_ID_INTEGRITY_CHECK), ==, REQ_ID_INTEGRITY_CHECK);
+        id = rsp->id & ~REQ_ID_INTEGRITY_CHECK;
+
+        ASSERT3U(id, <, MAXIMUM_TAG_COUNT);
+        req = &Protocol0->Pending[id];
+
+        ASSERT3U(req->id, ==, rsp->id);
+        RtlZeroMemory(req, sizeof (netif_rx_request_t));
+
+        Tag = &Protocol0->Tag[id];
+
+        Mdl = Tag->Context;
+        Tag->Context = NULL;
+
+        Protocol0ReleaseTag(Ring, Tag);
+
+        __RingPutMdl(Ring, Mdl, TRUE);
+
+        RtlZeroMemory(rsp, sizeof (netif_rx_response_t));
+    }
+
+    Protocol0->Front.rsp_cons = rsp_cons;
+
+    // Clean up any pending requests
+    for (id = 0; id < MAXIMUM_TAG_COUNT; id++) {
+        netif_rx_request_t  *req;
+        PRECEIVER_TAG       Tag;
+        PMDL                Mdl;
+
+        req = &Protocol0->Pending[id];
+        if (req->id == 0)
+            continue;
+
+        --Protocol0->RequestsPosted;
+        --Protocol0->RequestsPushed;
+
+        ASSERT3U((req->id & REQ_ID_INTEGRITY_CHECK), ==, REQ_ID_INTEGRITY_CHECK);
+        ASSERT3U((req->id & ~REQ_ID_INTEGRITY_CHECK), ==, id);
+
+        RtlZeroMemory(req, sizeof (netif_rx_request_t));
+
+        Tag = &Protocol0->Tag[id];
+
+        Mdl = Tag->Context;
+        Tag->Context = NULL;
+
+        Protocol0ReleaseTag(Ring, Tag);
+
+        __RingPutMdl(Ring, Mdl, TRUE);
+
+        RtlZeroMemory(req, sizeof (netif_rx_request_t));
+    }
+}
+
+static DECLSPEC_NOINLINE VOID
+Protocol0DebugCallback(
+    IN  PRECEIVER_RING       Ring
+    )
+{
+    PXENVIF_RECEIVER         Receiver;
+    PRECEIVER_RING_PROTOCOL0 Protocol0;
+
+    Receiver = Ring->Receiver;
+    Protocol0 = &Ring->Protocol0;
+
+    // Dump front ring
+    DEBUG(Printf,
+          Receiver->DebugInterface,
+          Receiver->DebugCallback,
+          "FRONT: req_prod_pvt = %u rsp_cons = %u nr_ents = %u sring = %p\n",
+          Protocol0->Front.req_prod_pvt,
+          Protocol0->Front.rsp_cons,
+          Protocol0->Front.nr_ents,
+          Protocol0->Front.sring);
+
+    // Dump shared ring
+    DEBUG(Printf,
+          Receiver->DebugInterface,
+          Receiver->DebugCallback,
+          "SHARED: req_prod = %u req_event = %u rsp_prod = %u rsp_event = %u\n",
+          Protocol0->Shared->req_prod,
+          Protocol0->Shared->req_event,
+          Protocol0->Shared->rsp_prod,
+          Protocol0->Shared->rsp_event);
+
+    DEBUG(Printf,
+          Receiver->DebugInterface,
+          Receiver->DebugCallback,
+          "RequestsPosted = %u RequestsPushed = %u ResponsesProcessed = %u\n",
+          Protocol0->RequestsPosted,
+          Protocol0->RequestsPushed,
+          Protocol0->ResponsesProcessed);
+}
+
+static DECLSPEC_NOINLINE NTSTATUS
+Protocol0Connect(
+    IN  PRECEIVER_RING          Ring
+    )
+{
+    PXENVIF_RECEIVER            Receiver;
+    PXENVIF_FRONTEND            Frontend;
+    PRECEIVER_RING_PROTOCOL0    Protocol0;
+    ULONG                       Index;
+    PFN_NUMBER                  Pfn;
+    NTSTATUS                    status;
+
+    Receiver = Ring->Receiver;
+    Frontend = Receiver->Frontend;
+
+    Ring->Mdl = __AllocatePage();
+
+    status = STATUS_NO_MEMORY;
+    if (Ring->Mdl == NULL)
+       goto fail1;
+
+    Protocol0 = &Ring->Protocol0;
+    ASSERT(IsZeroMemory(Protocol0, sizeof (RECEIVER_RING_PROTOCOL0)));
+
+    Protocol0->Shared = MmGetSystemAddressForMdlSafe(Ring->Mdl, NormalPagePriority);
+    ASSERT(Protocol0->Shared != NULL);
+
+    SHARED_RING_INIT(Protocol0->Shared);
+    FRONT_RING_INIT(&Protocol0->Front, Protocol0->Shared, PAGE_SIZE);
+    ASSERT3P(Protocol0->Front.sring, ==, Protocol0->Shared);
+
+    Receiver->GnttabInterface = FrontendGetGnttabInterface(Frontend);
+
+    GNTTAB(Acquire, Receiver->GnttabInterface);
+
+    status = GNTTAB(Get,
+                    Receiver->GnttabInterface,
+                    &Protocol0->Reference);
+    if (!NT_SUCCESS(status))
+        goto fail2;
+
+    Protocol0->HeadFreeTag = TAG_INDEX_INVALID;
+    for (Index = 0; Index < MAXIMUM_TAG_COUNT; Index++) {
+        PRECEIVER_TAG   Tag = &Protocol0->Tag[Index];
+
+        status = GNTTAB(Get,
+                        Receiver->GnttabInterface,
+                        &Tag->Reference);
+        if (!NT_SUCCESS(status))
+            goto fail3;
+
+        Tag->Next = Protocol0->HeadFreeTag;
+        Protocol0->HeadFreeTag = Index;
+    }
+
+    Pfn = MmGetMdlPfnArray(Ring->Mdl)[0];
+    
+    GNTTAB(PermitForeignAccess,
+           Receiver->GnttabInterface,
+           Protocol0->Reference,
+           FrontendGetBackendDomain(Frontend),
+           GNTTAB_ENTRY_FULL_PAGE,
+           Pfn,
+           FALSE);
+
+    return STATUS_SUCCESS;
+
+fail3:
+    Error("fail3\n");
+
+    while (Protocol0->HeadFreeTag != TAG_INDEX_INVALID) {
+        PRECEIVER_TAG   Tag = &Protocol0->Tag[Protocol0->HeadFreeTag];
+
+        Protocol0->HeadFreeTag = Tag->Next;
+        Tag->Next = 0;
+
+        GNTTAB(Put,
+               Receiver->GnttabInterface,
+               Tag->Reference);
+        Tag->Reference = 0;
+    }
+    Protocol0->HeadFreeTag = 0;
+
+    GNTTAB(Put,
+           Receiver->GnttabInterface,
+           Protocol0->Reference);
+    Protocol0->Reference = 0;
+
+fail2:
+    Error("fail2\n");
+
+    GNTTAB(Release, Receiver->GnttabInterface);
+    Receiver->GnttabInterface = NULL;
+
+    RtlZeroMemory(&Protocol0->Front, sizeof (netif_rx_front_ring_t));
+    RtlZeroMemory(Protocol0->Shared, PAGE_SIZE);
+
+    Protocol0->Shared = NULL;
+    ASSERT(IsZeroMemory(Protocol0, sizeof (RECEIVER_RING_PROTOCOL0)));
+
+    __FreePage(Ring->Mdl);
+    Ring->Mdl = NULL;
+
+
+fail1:
+    Error("fail1 (%08x)\n", status);
+
+    return status;
+}
+
+static DECLSPEC_NOINLINE NTSTATUS
+Protocol0StoreWrite(
+    IN  PRECEIVER_RING              Ring,
+    IN  PXENBUS_STORE_TRANSACTION   Transaction
+    )
+{
+    PXENVIF_RECEIVER                Receiver;
+    PXENVIF_FRONTEND                Frontend;
+    PRECEIVER_RING_PROTOCOL0        Protocol0;
+    NTSTATUS                        status;
+
+    Receiver = Ring->Receiver;
+    Frontend = Receiver->Frontend;
+
+    Protocol0 = &Ring->Protocol0;
+
+    status = STORE(Printf,
+                   Receiver->StoreInterface,
+                   Transaction,
+                   FrontendGetPath(Frontend),
+                   "rx-ring-ref",
+                   "%u",
+                   Protocol0->Reference);
+
+    if (!NT_SUCCESS(status))
+        goto fail1;
+
+    return STATUS_SUCCESS;
+
+fail1:
+    Error("fail1 (%08x)\n", status);
+
+    return status;
+}
+
+static DECLSPEC_NOINLINE NTSTATUS
+Protocol0Enable(
+    IN  PRECEIVER_RING          Ring
+    )
+{
+    PXENVIF_RECEIVER            Receiver;
+    PXENVIF_FRONTEND            Frontend;
+    PRECEIVER_RING_PROTOCOL0    Protocol0;
+    NTSTATUS                    status;
+
+    Receiver = Ring->Receiver;
+    Frontend = Receiver->Frontend;
+
+    Protocol0 = &Ring->Protocol0;
+
+    Protocol0Fill(Ring);
+
+    status = STATUS_INSUFFICIENT_RESOURCES;
+    if (RING_FREE_REQUESTS(&Protocol0->Front) != 0)
+        goto fail1;
+
+    return STATUS_SUCCESS;
+
+fail1:
+    Error("fail1 (%08x)\n", status);
+
+    return status;
+}
+
+static FORCEINLINE ULONG
+Protocol0GetSize(
+    IN  PRECEIVER_RING  Ring
+    )
+{
+    UNREFERENCED_PARAMETER(Ring);
+
+    return PROTOCOL0_RING_SIZE;
+}
+
+static DECLSPEC_NOINLINE VOID
+Protocol0Disable(
+    IN  PRECEIVER_RING  Ring
+    )
+{    
+    UNREFERENCED_PARAMETER(Ring);
+}
+
+static DECLSPEC_NOINLINE VOID
+Protocol0Disconnect(
+    IN  PRECEIVER_RING          Ring
+    )
+{
+    PXENVIF_RECEIVER            Receiver;
+    PXENVIF_FRONTEND            Frontend;
+    PRECEIVER_RING_PROTOCOL0    Protocol0;
+    ULONG                       Count;
+
+    Receiver = Ring->Receiver;
+    Frontend = Receiver->Frontend;
+
+    Protocol0 = &Ring->Protocol0;
+
+    __Protocol0Empty(Ring);
+
+    ASSERT3U(Protocol0->ResponsesProcessed, ==, Protocol0->RequestsPushed);
+    ASSERT3U(Protocol0->RequestsPushed, ==, Protocol0->RequestsPosted);
+
+    Protocol0->ResponsesProcessed = 0;
+    Protocol0->RequestsPushed = 0;
+    Protocol0->RequestsPosted = 0;
+
+    GNTTAB(RevokeForeignAccess,
+           Receiver->GnttabInterface,
+           Protocol0->Reference);
+
+    Count = 0;
+    while (Protocol0->HeadFreeTag != TAG_INDEX_INVALID) {
+        ULONG           Index = Protocol0->HeadFreeTag;
+        PRECEIVER_TAG   Tag = &Protocol0->Tag[Index];
+
+        Protocol0->HeadFreeTag = Tag->Next;
+        Tag->Next = 0;
+
+        GNTTAB(Put,
+               Receiver->GnttabInterface,
+               Tag->Reference);
+        Tag->Reference = 0;
+
+        Count++;
+    }
+    ASSERT3U(Count, ==, MAXIMUM_TAG_COUNT);
+
+    Protocol0->HeadFreeTag = 0;
+
+    GNTTAB(Put,
+           Receiver->GnttabInterface,
+           Protocol0->Reference);
+    Protocol0->Reference = 0;
+
+    GNTTAB(Release, Receiver->GnttabInterface);
+    Receiver->GnttabInterface = NULL;
+
+    RtlZeroMemory(&Protocol0->Front, sizeof (netif_rx_front_ring_t));
+    RtlZeroMemory(Protocol0->Shared, PAGE_SIZE);
+
+    Protocol0->Shared = NULL;
+    ASSERT(IsZeroMemory(Protocol0, sizeof (RECEIVER_RING_PROTOCOL0)));
+
+    __FreePage(Ring->Mdl);
+    Ring->Mdl = NULL;
+}
+
+static RECEIVER_RING_OPERATIONS  Protocol0Operations = {
+    Protocol0Connect,
+    Protocol0StoreWrite,
+    Protocol0Enable,
+    Protocol0Poll,
+    Protocol0Disable,
+    Protocol0Disconnect,
+    Protocol0DebugCallback,
+    Protocol0GetSize
+};
+
+static DECLSPEC_NOINLINE NTSTATUS
+Protocol1Connect(
+    IN  PRECEIVER_RING          Ring
+    )
+{
+    PXENVIF_RECEIVER            Receiver;
+    PXENVIF_FRONTEND            Frontend;
+    PRECEIVER_RING_PROTOCOL1    Protocol1;
+    ULONG                       Index;
+    PFN_NUMBER                  Pfn;
+    NTSTATUS                    status;
+
+    Receiver = Ring->Receiver;
+    Frontend = Receiver->Frontend;
+
+    Ring->Mdl = __AllocatePage();
+
+    status = STATUS_NO_MEMORY;
+    if (Ring->Mdl == NULL)
+       goto fail1;
+
+    Protocol1 = &Ring->Protocol1;
+    ASSERT(IsZeroMemory(Protocol1, sizeof (RECEIVER_RING_PROTOCOL1)));
+
+    Protocol1->Shared = MmGetSystemAddressForMdlSafe(Ring->Mdl, NormalPagePriority);
+    ASSERT(Protocol1->Shared != NULL);
+
+    SHARED_RING_INIT(Protocol1->Shared);
+    BACK_RING_INIT(&Protocol1->Back, Protocol1->Shared, PAGE_SIZE);
+    ASSERT3P(Protocol1->Back.sring, ==, Protocol1->Shared);
+
+    Receiver->GnttabInterface = FrontendGetGnttabInterface(Frontend);
+
+    GNTTAB(Acquire, Receiver->GnttabInterface);
+
+    status = GNTTAB(Get,
+                    Receiver->GnttabInterface,
+                    &Protocol1->Reference);
+    if (!NT_SUCCESS(status))
+        goto fail2;
+
+    InitializeListHead(&Protocol1->List);
+    for (Index = 0; Index < MAXIMUM_OPERATION_COUNT; Index++) {
+        PXENBUS_GNTTAB_COPY_OPERATION   Operation = &Protocol1->Operation[Index];
+
+        InsertTailList(&Protocol1->List, &Operation->ListEntry);
+    }
+
+    Pfn = (PFN_NUMBER)(MmGetPhysicalAddress(Protocol1->Shared).QuadPart >> PAGE_SHIFT);
+    
+    GNTTAB(PermitForeignAccess,
+           Receiver->GnttabInterface,
+           Protocol1->Reference,
+           FrontendGetBackendDomain(Frontend),
+           GNTTAB_ENTRY_FULL_PAGE,
+           Pfn,
+           FALSE);
+
+    return STATUS_SUCCESS;
+
+fail2:
+    Error("fail2\n");
+
+    GNTTAB(Release, Receiver->GnttabInterface);
+    Receiver->GnttabInterface = NULL;
+
+    RtlZeroMemory(&Protocol1->Back, sizeof (netif_tx_back_ring_t));
+    RtlZeroMemory(Protocol1->Shared, PAGE_SIZE);
+
+    Protocol1->Shared = NULL;
+    ASSERT(IsZeroMemory(Protocol1, sizeof (RECEIVER_RING_PROTOCOL1)));
+
+    __FreePage(Ring->Mdl);
+    Ring->Mdl = NULL;
+
+fail1:
+    Error("fail1 (%08x)\n", status);
+
+    return status;
+}
+
+static FORCEINLINE PXENBUS_GNTTAB_COPY_OPERATION
+__Protocol1GetOperation(
+    IN  PRECEIVER_RING              Ring
+    )
+{
+    PRECEIVER_RING_PROTOCOL1        Protocol1;
+    PLIST_ENTRY                     ListEntry;
+    PXENBUS_GNTTAB_COPY_OPERATION   Operation;
+
+    Protocol1 = &Ring->Protocol1;
+
+    ListEntry = RemoveHeadList(&Protocol1->List);
+    ASSERT3P(ListEntry, !=, &Protocol1->List);
+
+    RtlZeroMemory(ListEntry, sizeof (LIST_ENTRY));
+
+    Operation = CONTAINING_RECORD(ListEntry, XENBUS_GNTTAB_COPY_OPERATION, ListEntry);
+
+    return Operation;
+}
+
+static FORCEINLINE VOID
+__Protocol1PutOperation(
+    IN  PRECEIVER_RING                  Ring,
+    IN  PXENBUS_GNTTAB_COPY_OPERATION   Operation
+    )
+{
+    PRECEIVER_RING_PROTOCOL1            Protocol1;
+
+    Protocol1 = &Ring->Protocol1;
+
+    ASSERT(IsZeroMemory(&Operation->ListEntry, sizeof (LIST_ENTRY)));
+
+    RtlZeroMemory(Operation, sizeof (XENBUS_GNTTAB_COPY_OPERATION));
+    InsertHeadList(&Protocol1->List, &Operation->ListEntry);
+}
+
+static DECLSPEC_NOINLINE NTSTATUS
+Protocol1Enable(
+    IN  PRECEIVER_RING  Ring
+    )
+{
+    UNREFERENCED_PARAMETER(Ring);
+
+    return STATUS_SUCCESS;
+}
+
+static FORCEINLINE ULONG
+Protocol1GetSize(
+    IN  PRECEIVER_RING  Ring
+    )
+{
+    UNREFERENCED_PARAMETER(Ring);
+
+    return PROTOCOL1_RING_SIZE;
+}
+
+static DECLSPEC_NOINLINE NTSTATUS
+Protocol1StoreWrite(
+    IN  PRECEIVER_RING              Ring,
+    IN  PXENBUS_STORE_TRANSACTION   Transaction
+    )
+{
+    PXENVIF_RECEIVER                Receiver;
+    PXENVIF_FRONTEND                Frontend;
+    PRECEIVER_RING_PROTOCOL1        Protocol1;
+    NTSTATUS                        status;
+
+    Receiver = Ring->Receiver;
+    Frontend = Receiver->Frontend;
+
+    Protocol1 = &Ring->Protocol1;
+
+    status = STORE(Printf,
+                   Receiver->StoreInterface,
+                   Transaction,
+                   FrontendGetPath(Frontend),
+                   "rx-ring-ref",
+                   "%u",
+                   Protocol1->Reference);
+
+    if (!NT_SUCCESS(status))
+        goto fail1;
+
+    return STATUS_SUCCESS;
+
+fail1:
+    Error("fail1 (%08x)\n", status);
+
+    return status;
+}
+
+static FORCEINLINE NTSTATUS
+__Protocol1BuildOperations(
+    IN      PRECEIVER_RING          Ring,
+    IN      netif_tx_request_t      *req,
+    IN      ULONG                   nr_reqs,
+    OUT     PXENVIF_RECEIVER_PACKET *Packet,
+    IN OUT  PLIST_ENTRY             List,
+    OUT     PULONG                  Count
+    )
+{
+    PXENVIF_RECEIVER                Receiver;
+    PXENVIF_FRONTEND                Frontend;
+    ULONG                           Index;
+    ULONG                           Length;
+    PMDL                            Mdl;
+    ULONG                           MdlOffset;
+    ULONG                           MdlByteCount;
+    uint16_t                        flags;
+    netif_tx_request_t              current;
+    PXENBUS_GNTTAB_COPY_OPERATION   Operation;
+    NTSTATUS                        status;
+
+    ASSERT(req != NULL);
+    ASSERT(nr_reqs != 0);
+    ASSERT(~req[nr_reqs - 1].flags & NETTXF_more_data);
+
+    Receiver = Ring->Receiver;
+    Frontend = Receiver->Frontend;
+
+    // The first request specifies the total packet length. Sample it and
+    // then adjust it to match all other requests in specifying it's fragment
+    // size.
+    Length = req[0].size;
+    Index = 1;
+
+    if (req[0].flags & NETTXF_extra_info) {
+        struct netif_extra_info *extra;
+
+        ASSERT3U(nr_reqs, >=, 2);
+        extra = (struct netif_extra_info *)&req[1];
+
+        ASSERT3U(extra->type, ==, XEN_NETIF_EXTRA_TYPE_GSO);
+        ASSERT(extra->u.gso.type == XEN_NETIF_GSO_TYPE_TCPV4 ||
+               extra->u.gso.type == XEN_NETIF_GSO_TYPE_TCPV6);
+
+        Index++;
+    }
+
+    while (Index < nr_reqs) {
+        ASSERT3U(req[0].size, >, req[Index].size);
+        req[0].size = req[0].size - req[Index].size;
+
+        Index++;
+    }
+
+    *Packet = __RingGetPacket(Ring, TRUE);
+
+    status = STATUS_NO_MEMORY;
+    if (*Packet == NULL)
+        goto fail1;
+
+    (*Packet)->Offset = 0;
+    (*Packet)->Length = Length;
+
+    Mdl = &(*Packet)->Mdl;
+
+    ASSERT3P((*Packet)->Cookie, ==, NULL);
+
+    // Unfortunately NETTXF_... don't match NETRXF_...
+    flags = 0;
+    if (req[0].flags & NETTXF_csum_blank)
+        flags |= NETRXF_csum_blank;
+    if (req[0].flags & NETTXF_data_validated)
+        flags |= NETRXF_data_validated;
+
+    (*Packet)->Cookie = (PVOID)flags;
+
+    ASSERT3U(Mdl->ByteCount, ==, 0);
+
+    MdlOffset = 0;
+    MdlByteCount = PAGE_SIZE;   // Remaining byte count
+
+    Index = 0;
+    *Count = 0;
+
+    current = req[Index];
+
+    while (Length != 0) {
+        USHORT  SourceLength;
+        USHORT  DestLength;
+        USHORT  CopyLength;
+
+        if (current.size == 0) {
+            Index++;
+
+            if (current.flags & NETTXF_extra_info) {
+                struct netif_extra_info *extra;
+
+                ASSERT3U(Index, ==, 1);
+
+                extra = (struct netif_extra_info *)&req[Index];
+
+                ASSERT3U(extra->type, ==, XEN_NETIF_EXTRA_TYPE_GSO);
+
+                (*Packet)->MaximumSegmentSize = extra->u.gso.size;
+                ASSERT((*Packet)->MaximumSegmentSize != 0);
+
+                Index++;
+            }
+
+            current = req[Index];
+        }
+        ASSERT(current.size != 0);
+
+        if (MdlByteCount == 0) {
+            PXENVIF_RECEIVER_PACKET NextPacket;
+            PMDL                    NextMdl;
+
+            NextPacket = __RingGetPacket(Ring, TRUE);
+
+            status = STATUS_NO_MEMORY;
+            if (NextPacket == NULL)
+                goto fail2;
+
+            NextMdl = &NextPacket->Mdl;
+            
+            ASSERT3P(Mdl->Next, ==, NULL);
+            Mdl->Next = NextMdl;
+            Mdl = NextMdl;
+
+            MdlOffset = 0;
+            MdlByteCount = PAGE_SIZE;
+        }
+
+        Operation = __Protocol1GetOperation(Ring);
+
+        Operation->RemoteDomain = FrontendGetBackendDomain(Frontend);
+        Operation->RemoteReference = current.gref;
+        Operation->RemoteOffset = current.offset;
+
+        SourceLength = current.size;
+
+        ASSERT3U(MdlOffset, <, PAGE_SIZE);
+        Operation->Pfn = MmGetMdlPfnArray(Mdl)[0];
+        Operation->Offset = (uint16_t)MdlOffset;
+
+        DestLength = (USHORT)__min(MdlByteCount, PAGE_SIZE - MdlOffset);
+
+        CopyLength = __min(SourceLength, DestLength);
+
+        Operation->Length = CopyLength;
+
+        Mdl->ByteCount += CopyLength;
+
+        current.offset = current.offset + CopyLength;
+        ASSERT3U(current.size, >=, CopyLength);
+        current.size = current.size - CopyLength;
+
+        MdlOffset += CopyLength;
+        ASSERT3U(MdlByteCount, >=, CopyLength);
+        MdlByteCount -= CopyLength;
+
+        ASSERT3U(Length, >=, CopyLength);
+        Length -= CopyLength;
+
+        ASSERT(IsZeroMemory(&Operation->ListEntry, sizeof (LIST_ENTRY)));
+        InsertTailList(List, &Operation->ListEntry);
+        (*Count)++;
+    }
+    ASSERT3U(current.size, ==, 0);
+    ASSERT(IMPLY(current.flags & NETTXF_extra_info, Index == nr_reqs - 2));
+    ASSERT(IMPLY(~current.flags & NETTXF_extra_info, Index == nr_reqs - 1));
+
+    return STATUS_SUCCESS;
+
+fail2:
+    Error("fail2\n");
+
+    (*Packet)->Cookie = NULL;
+    __RingReturnPacket(Ring, *Packet, TRUE);
+            
+fail1:
+    Error("fail1 (%08x)\n", status);
+
+    return status;
+}
+
+static FORCEINLINE VOID
+__Protocol1DisposeOperations(
+    IN      PRECEIVER_RING  Ring,
+    IN OUT  PLIST_ENTRY     List,
+    IN      ULONG           Count
+    )
+{
+    while (!IsListEmpty(List)) {
+        PLIST_ENTRY                     ListEntry;
+        PXENBUS_GNTTAB_COPY_OPERATION   Operation;
+
+        ListEntry = RemoveTailList(List);
+        ASSERT3P(ListEntry, !=, List);
+
+        --Count;
+
+        RtlZeroMemory(ListEntry, sizeof (LIST_ENTRY));
+
+        Operation = CONTAINING_RECORD(ListEntry, XENBUS_GNTTAB_COPY_OPERATION, ListEntry);
+
+        __Protocol1PutOperation(Ring, Operation);
+    }
+    ASSERT3U(Count, ==, 0);
+}
+
+static FORCEINLINE VOID
+__Protocol1PostResponses(
+    IN  PRECEIVER_RING          Ring,
+    IN  netif_tx_request_t      *req,
+    IN  ULONG                   nr_reqs,
+    IN  NTSTATUS                status
+    )
+{
+    PRECEIVER_RING_PROTOCOL1    Protocol1;
+    RING_IDX                    rsp_prod;
+
+    Protocol1 = &Ring->Protocol1;
+
+    rsp_prod = Protocol1->Back.rsp_prod_pvt;
+
+    while (nr_reqs != 0) {
+        netif_tx_response_t *rsp;
+
+        rsp = RING_GET_RESPONSE(&Protocol1->Back, rsp_prod);
+        rsp_prod++;
+        Protocol1->ResponsesPosted++;
+
+        rsp->id = req->id;
+        rsp->status = NT_SUCCESS(status) ? NETIF_RSP_OKAY : NETIF_RSP_ERROR;
+
+        if (req->flags & NETTXF_extra_info) {
+            rsp = RING_GET_RESPONSE(&Protocol1->Back, rsp_prod);
+            rsp_prod++;
+            Protocol1->ResponsesPosted++;
+
+            rsp->status = NETIF_RSP_NULL;
+
+            req++;
+            --nr_reqs;
+        }
+
+        req++;
+        --nr_reqs;
+    }
+
+    Protocol1->Back.rsp_prod_pvt = rsp_prod;
+}
+
+static FORCEINLINE VOID
+__Protocol1PushResponses(
+    IN  PRECEIVER_RING          Ring
+    )
+{
+    PRECEIVER_RING_PROTOCOL1    Protocol1;
+    BOOLEAN                     Notify;
+
+    Protocol1 = &Ring->Protocol1;
+
+    if (Protocol1->ResponsesPosted == Protocol1->ResponsesPushed)
+        return;
+
+#pragma warning (push)
+#pragma warning (disable:4244)
+
+    // Make the responses visible to the backend
+    RING_PUSH_RESPONSES_AND_CHECK_NOTIFY(&Protocol1->Back, Notify);
+
+#pragma warning (pop)
+
+    if (Notify) {
+        PXENVIF_RECEIVER    Receiver;
+        PXENVIF_FRONTEND    Frontend;
+
+        Receiver = Ring->Receiver;
+        Frontend = Receiver->Frontend;
+
+        NotifierSend(FrontendGetNotifier(Frontend));
+    }
+
+    Protocol1->ResponsesPushed = Protocol1->ResponsesPosted;
+}
+
+static DECLSPEC_NOINLINE VOID
+Protocol1Poll(
+    IN  PRECEIVER_RING          Ring
+    )
+{
+    PXENVIF_RECEIVER            Receiver;
+    PRECEIVER_RING_PROTOCOL1    Protocol1;
+
+    Receiver = Ring->Receiver;
+
+    Protocol1 = &Ring->Protocol1;
+
+    for (;;) {
+        RING_IDX    req_prod;
+        RING_IDX    req_cons;
+        RING_IDX    rsp_prod;
+
+        if (__RingIsStopped(Ring))
+            break;
+
+        KeMemoryBarrier();
+
+        req_prod = Protocol1->Shared->req_prod;
+        req_cons = Protocol1->Back.req_cons;
+
+        KeMemoryBarrier();
+
+        if (req_cons == req_prod)
+            break;
+
+        while (req_cons != req_prod) {
+            netif_tx_request_t              req[MAX_SKB_FRAGS + 2];
+            ULONG                           nr_reqs;
+            PXENVIF_RECEIVER_PACKET         Packet;
+            LIST_ENTRY                      List;
+            ULONG                           Count;
+            NTSTATUS                        status;
+
+            RtlZeroMemory(req, sizeof (req));
+
+            nr_reqs = 0;
+            for (;;) {
+                uint16_t    flags;
+
+                ASSERT3U(nr_reqs, <, MAX_SKB_FRAGS + 2);
+
+                req[nr_reqs] = *RING_GET_REQUEST(&Protocol1->Back, req_cons + nr_reqs);
+
+                flags = req[nr_reqs].flags;
+                nr_reqs++;
+
+                if (flags & NETTXF_extra_info) {
+                    req[nr_reqs] = *RING_GET_REQUEST(&Protocol1->Back, req_cons + nr_reqs);
+
+                    nr_reqs++;
+                }
+
+                if (~flags & NETTXF_more_data)
+                    break;
+            }
+
+            InitializeListHead(&List);
+
+            status = __Protocol1BuildOperations(Ring, req, nr_reqs, &Packet, &List, &Count);
+            if (!NT_SUCCESS(status)) {
+                __RingStop(Ring);
+                break;
+            }
+
+            req_cons += nr_reqs;
+            Protocol1->RequestsProcessed += nr_reqs;
+
+            Protocol1->Back.req_cons = req_cons;
+
+            status = GNTTAB(Copy,
+                            Receiver->GnttabInterface,
+                            &List,
+                            Count);
+
+            __Protocol1DisposeOperations(Ring, &List, Count);
+
+            if (NT_SUCCESS(status)) {
+                ASSERT(IsZeroMemory(&Packet->ListEntry, sizeof (LIST_ENTRY)));
+                InsertTailList(&Ring->PacketList, &Packet->ListEntry);
+            } else {
+                Ring->PacketStatistics.BackendError++;
+
+                Packet->Cookie = NULL;
+                __RingReturnPacket(Ring, Packet, TRUE);
+            }
+
+            __Protocol1PostResponses(Ring,
+                                     req,
+                                     nr_reqs,
+                                     status);
+
+            rsp_prod = Protocol1->Back.rsp_prod_pvt;
+            ASSERT3U(rsp_prod, ==, req_cons);
+
+            KeMemoryBarrier();
+        }
+
+        Protocol1->Shared->req_event = req_cons + 1;
+
+        KeMemoryBarrier();
+
+        __Protocol1PushResponses(Ring);
+    }
+}
+
+static DECLSPEC_NOINLINE VOID
+Protocol1Disable(
+    IN  PRECEIVER_RING  Ring
+    )
+{    
+    UNREFERENCED_PARAMETER(Ring);
+}
+
+static DECLSPEC_NOINLINE VOID
+Protocol1Disconnect(
+    IN  PRECEIVER_RING          Ring
+    )
+{
+    PXENVIF_RECEIVER            Receiver;
+    PRECEIVER_RING_PROTOCOL1    Protocol1;
+    ULONG                       Count;
+
+    Receiver = Ring->Receiver;
+
+    Protocol1 = &Ring->Protocol1;
+
+    ASSERT3U(Protocol1->ResponsesPushed, ==, Protocol1->ResponsesPosted);
+    ASSERT3U(Protocol1->ResponsesPosted, ==, Protocol1->RequestsProcessed);
+
+    Protocol1->RequestsProcessed = 0;
+    Protocol1->ResponsesPushed = 0;
+    Protocol1->ResponsesPosted = 0;
+
+    GNTTAB(RevokeForeignAccess,
+           Receiver->GnttabInterface,
+           Protocol1->Reference);
+
+    Count = MAXIMUM_OPERATION_COUNT;
+    while (!IsListEmpty(&Protocol1->List)) {
+        PLIST_ENTRY                     ListEntry;
+
+        ListEntry = RemoveHeadList(&Protocol1->List);
+        ASSERT3P(ListEntry, !=, &Protocol1->List);
+
+        --Count;
+
+        RtlZeroMemory(ListEntry, sizeof (LIST_ENTRY));
+    }
+    ASSERT3U(Count, ==, 0);
+
+    RtlZeroMemory(&Protocol1->List, sizeof (LIST_ENTRY));
+
+    GNTTAB(Put,
+           Receiver->GnttabInterface,
+           Protocol1->Reference);
+    Protocol1->Reference = 0;
+
+    GNTTAB(Release, Receiver->GnttabInterface);
+    Receiver->GnttabInterface = NULL;
+
+    RtlZeroMemory(&Protocol1->Back, sizeof (netif_tx_back_ring_t));
+    RtlZeroMemory(Protocol1->Shared, PAGE_SIZE);
+
+    Protocol1->Shared = NULL;
+    ASSERT(IsZeroMemory(Protocol1, sizeof (RECEIVER_RING_PROTOCOL1)));
+
+    __FreePage(Ring->Mdl);
+    Ring->Mdl = NULL;
+}
+
+static DECLSPEC_NOINLINE VOID
+Protocol1DebugCallback(
+    IN  PRECEIVER_RING       Ring
+    )
+{
+    PXENVIF_RECEIVER         Receiver;
+    PRECEIVER_RING_PROTOCOL1 Protocol1;
+
+    Receiver = Ring->Receiver;
+    Protocol1 = &Ring->Protocol1;
+
+    // Dump back ring
+    DEBUG(Printf,
+          Receiver->DebugInterface,
+          Receiver->DebugCallback,
+          "BACK: rsp_prod_pvt = %u req_cons = %u nr_ents = %u sring = %p\n",
+          Protocol1->Back.rsp_prod_pvt,
+          Protocol1->Back.req_cons,
+          Protocol1->Back.nr_ents,
+          Protocol1->Back.sring);
+
+    // Dump shared ring
+    DEBUG(Printf,
+          Receiver->DebugInterface,
+          Receiver->DebugCallback,
+          "SHARED: req_prod = %u req_event = %u rsp_prod = %u rsp_event = %u\n",
+          Protocol1->Shared->req_prod,
+          Protocol1->Shared->req_event,
+          Protocol1->Shared->rsp_prod,
+          Protocol1->Shared->rsp_event);
+
+    DEBUG(Printf,
+          Receiver->DebugInterface,
+          Receiver->DebugCallback,
+          "RequestsProcessed = %u ResponsesPosted = %u ResponsesPushed = %u\n",
+          Protocol1->RequestsProcessed,
+          Protocol1->ResponsesPosted,
+          Protocol1->ResponsesPushed);
+}
+
+static RECEIVER_RING_OPERATIONS  Protocol1Operations = {
+    Protocol1Connect,
+    Protocol1StoreWrite,
+    Protocol1Enable,
+    Protocol1Poll,
+    Protocol1Disable,
+    Protocol1Disconnect,
+    Protocol1DebugCallback,
+    Protocol1GetSize
+};
+
+static PRECEIVER_RING_OPERATIONS RingOperations[] = {
+    &Protocol0Operations,
+    &Protocol1Operations
+};
+
+#define SUPPORTED_PROTOCOLS \
+        (sizeof (RingOperations) / sizeof (RingOperations[0]))
+
+C_ASSERT((RECEIVER_MINIMUM_PROTOCOL + SUPPORTED_PROTOCOLS - 1) == RECEIVER_MAXIMUM_PROTOCOL);
+
+static FORCEINLINE VOID
+__RingDebugCallback(
+    IN  PRECEIVER_RING          Ring
+    )
+{
+    PXENVIF_RECEIVER            Receiver;
+    ULONG                       Allocated;
+    ULONG                       MaximumAllocated;
+    ULONG                       Count;
+    ULONG                       MinimumCount;
+    PRECEIVER_RING_OPERATIONS   Operations;
+
+    Receiver = Ring->Receiver;
+
+    DEBUG(Printf,
+          Receiver->DebugInterface,
+          Receiver->DebugCallback,
+          "0x%p [%s][%s]\n",
+          Ring,
+          (Ring->Enabled) ? "ENABLED" : "DISABLED",
+          (__RingIsStopped(Ring)) ? "STOPPED" : "RUNNING");
+
+    PoolGetStatistics(Ring->PacketPool,
+                      &Allocated,
+                      &MaximumAllocated,
+                      &Count,
+                      &MinimumCount);
+
+    DEBUG(Printf,
+          Receiver->DebugInterface,
+          Receiver->DebugCallback,
+          "PACKET POOL: Allocated = %u (Maximum = %u)\n",
+          Allocated,
+          MaximumAllocated);
+
+    DEBUG(Printf,
+          Receiver->DebugInterface,
+          Receiver->DebugCallback,
+          "PACKET POOL: Count = %u (Minimum = %u)\n",
+          Count,
+          MinimumCount);
+
+    Operations = Ring->Operations;
+    if (Operations != NULL)
+        Operations->DebugCallback(Ring);
+
+    DEBUG(Printf,
+          Receiver->DebugInterface,
+          Receiver->DebugCallback,
+          "OffloadOptions = %02x\n",
+          Ring->OffloadOptions.Value);
+
+    if (Ring->OffloadOptions.OffloadTagManipulation)
+        DEBUG(Printf,
+              Receiver->DebugInterface,
+              Receiver->DebugCallback,
+              "- TAG MANIPULATION\n");
+
+    if (Ring->OffloadOptions.OffloadIpVersion4LargePacket)
+        DEBUG(Printf,
+              Receiver->DebugInterface,
+              Receiver->DebugCallback,
+              "- IPV4 LARGE PACKET\n");
+
+    if (Ring->OffloadOptions.OffloadIpVersion6LargePacket)
+        DEBUG(Printf,
+              Receiver->DebugInterface,
+              Receiver->DebugCallback,
+              "- IPV6 LARGE PACKET\n");
+
+    if (Ring->OffloadOptions.OffloadIpVersion4HeaderChecksum)
+        DEBUG(Printf,
+              Receiver->DebugInterface,
+              Receiver->DebugCallback,
+              "- IPV4 HEADER CHECKSUM\n");
+
+    if (Ring->OffloadOptions.OffloadIpVersion4TcpChecksum)
+        DEBUG(Printf,
+              Receiver->DebugInterface,
+              Receiver->DebugCallback,
+              "- IPV4 TCP CHECKSUM\n");
+
+    if (Ring->OffloadOptions.OffloadIpVersion4UdpChecksum)
+        DEBUG(Printf,
+              Receiver->DebugInterface,
+              Receiver->DebugCallback,
+              "- IPV4 UDP CHECKSUM\n");
+
+    if (Ring->OffloadOptions.OffloadIpVersion6TcpChecksum)
+        DEBUG(Printf,
+              Receiver->DebugInterface,
+              Receiver->DebugCallback,
+              "- IPV6 TCP CHECKSUM\n");
+
+    if (Ring->OffloadOptions.OffloadIpVersion6UdpChecksum)
+        DEBUG(Printf,
+              Receiver->DebugInterface,
+              Receiver->DebugCallback,
+              "- IPV6 UDP CHECKSUM\n");
+
+    if (Ring->OffloadOptions.NeedChecksumValue)
+        DEBUG(Printf,
+              Receiver->DebugInterface,
+              Receiver->DebugCallback,
+              "- NEED CHECKSUM VALUE\n");
+
+    DEBUG(Printf,
+          Receiver->DebugInterface,
+          Receiver->DebugCallback,
+          "PacketStatistics:\n");
+
+    DEBUG(Printf,
+          Receiver->DebugInterface,
+          Receiver->DebugCallback,
+          "- Drop = %u\n",
+          Ring->PacketStatistics.Drop);
+
+    DEBUG(Printf,
+          Receiver->DebugInterface,
+          Receiver->DebugCallback,
+          "- BackendError = %u\n",
+          Ring->PacketStatistics.BackendError);
+
+    DEBUG(Printf,
+          Receiver->DebugInterface,
+          Receiver->DebugCallback,
+          "- FrontendError = %u\n",
+          Ring->PacketStatistics.FrontendError);
+
+    DEBUG(Printf,
+          Receiver->DebugInterface,
+          Receiver->DebugCallback,
+          "- Unicast = %u\n",
+          Ring->PacketStatistics.Unicast);
+
+    DEBUG(Printf,
+          Receiver->DebugInterface,
+          Receiver->DebugCallback,
+          "- UnicastBytes = %u\n",
+          Ring->PacketStatistics.UnicastBytes);
+
+    DEBUG(Printf,
+          Receiver->DebugInterface,
+          Receiver->DebugCallback,
+          "- Multicast = %u\n",
+          Ring->PacketStatistics.Multicast);
+
+    DEBUG(Printf,
+          Receiver->DebugInterface,
+          Receiver->DebugCallback,
+          "- MulticastBytes = %u\n",
+          Ring->PacketStatistics.MulticastBytes);
+
+    DEBUG(Printf,
+          Receiver->DebugInterface,
+          Receiver->DebugCallback,
+          "- Broadcast = %u\n",
+          Ring->PacketStatistics.Broadcast);
+
+    DEBUG(Printf,
+          Receiver->DebugInterface,
+          Receiver->DebugCallback,
+          "- BroadcastBytes = %u\n",
+          Ring->PacketStatistics.BroadcastBytes);
+
+    DEBUG(Printf,
+          Receiver->DebugInterface,
+          Receiver->DebugCallback,
+          "HeaderStatistics:\n");
+
+    DEBUG(Printf,
+          Receiver->DebugInterface,
+          Receiver->DebugCallback,
+          "- Tagged = %u\n",
+          Ring->HeaderStatistics.Tagged);
+
+    DEBUG(Printf,
+          Receiver->DebugInterface,
+          Receiver->DebugCallback,
+          "- LLC = %u\n",
+          Ring->HeaderStatistics.LLC);
+
+    DEBUG(Printf,
+          Receiver->DebugInterface,
+          Receiver->DebugCallback,
+          "- Ip Version4 = %u\n",
+          Ring->HeaderStatistics.IpVersion4);
+
+    DEBUG(Printf,
+          Receiver->DebugInterface,
+          Receiver->DebugCallback,
+          "- Ip Version6 = %u\n",
+          Ring->HeaderStatistics.IpVersion6);
+
+    DEBUG(Printf,
+          Receiver->DebugInterface,
+          Receiver->DebugCallback,
+          "- Ip Options = %u\n",
+          Ring->HeaderStatistics.IpOptions);
+
+    DEBUG(Printf,
+          Receiver->DebugInterface,
+          Receiver->DebugCallback,
+          "- Tcp = %u\n",
+          Ring->HeaderStatistics.Tcp);
+
+    DEBUG(Printf,
+          Receiver->DebugInterface,
+          Receiver->DebugCallback,
+          "- Tcp Options = %u\n",
+          Ring->HeaderStatistics.TcpOptions);
+
+    DEBUG(Printf,
+          Receiver->DebugInterface,
+          Receiver->DebugCallback,
+          "- Udp = %u\n",
+          Ring->HeaderStatistics.Udp);
+
+    DEBUG(Printf,
+          Receiver->DebugInterface,
+          Receiver->DebugCallback,
+          "OffloadStatistics:\n");
+
+    DEBUG(Printf,
+          Receiver->DebugInterface,
+          Receiver->DebugCallback,
+          "- IpVersion4LargePacketSegment = %u\n",
+          Ring->OffloadStatistics.IpVersion4LargePacketSegment);
+
+    DEBUG(Printf,
+          Receiver->DebugInterface,
+          Receiver->DebugCallback,
+          "- IpVersion6LargePacketSegment = %u\n",
+          Ring->OffloadStatistics.IpVersion6LargePacketSegment);
+
+    DEBUG(Printf,
+          Receiver->DebugInterface,
+          Receiver->DebugCallback,
+          "- IpVersion4HeaderChecksumCalculated = %u\n",
+          Ring->OffloadStatistics.IpVersion4HeaderChecksumCalculated);
+
+    DEBUG(Printf,
+          Receiver->DebugInterface,
+          Receiver->DebugCallback,
+          "- IpVersion4HeaderChecksumSucceeded = %u\n",
+          Ring->OffloadStatistics.IpVersion4HeaderChecksumSucceeded);
+
+    DEBUG(Printf,
+          Receiver->DebugInterface,
+          Receiver->DebugCallback,
+          "- IpVersion4HeaderChecksumFailed = %u\n",
+          Ring->OffloadStatistics.IpVersion4HeaderChecksumFailed);
+
+    DEBUG(Printf,
+          Receiver->DebugInterface,
+          Receiver->DebugCallback,
+          "- IpVersion4HeaderChecksumPresent = %u\n",
+          Ring->OffloadStatistics.IpVersion4HeaderChecksumPresent);
+
+    DEBUG(Printf,
+          Receiver->DebugInterface,
+          Receiver->DebugCallback,
+          "- IpVersion4TcpChecksumCalculated = %u\n",
+          Ring->OffloadStatistics.IpVersion4TcpChecksumCalculated);
+
+    DEBUG(Printf,
+          Receiver->DebugInterface,
+          Receiver->DebugCallback,
+          "- IpVersion4TcpChecksumSucceeded = %u\n",
+          Ring->OffloadStatistics.IpVersion4TcpChecksumSucceeded);
+
+    DEBUG(Printf,
+          Receiver->DebugInterface,
+          Receiver->DebugCallback,
+          "- IpVersion4TcpChecksumFailed = %u\n",
+          Ring->OffloadStatistics.IpVersion4TcpChecksumFailed);
+
+    DEBUG(Printf,
+          Receiver->DebugInterface,
+          Receiver->DebugCallback,
+          "- IpVersion4TcpChecksumPresent = %u\n",
+          Ring->OffloadStatistics.IpVersion4TcpChecksumPresent);
+
+    DEBUG(Printf,
+          Receiver->DebugInterface,
+          Receiver->DebugCallback,
+          "- IpVersion6TcpChecksumCalculated = %u\n",
+          Ring->OffloadStatistics.IpVersion6TcpChecksumCalculated);
+
+    DEBUG(Printf,
+          Receiver->DebugInterface,
+          Receiver->DebugCallback,
+          "- IpVersion6TcpChecksumSucceeded = %u\n",
+          Ring->OffloadStatistics.IpVersion6TcpChecksumSucceeded);
+
+    DEBUG(Printf,
+          Receiver->DebugInterface,
+          Receiver->DebugCallback,
+          "- IpVersion6TcpChecksumFailed = %u\n",
+          Ring->OffloadStatistics.IpVersion6TcpChecksumFailed);
+
+    DEBUG(Printf,
+          Receiver->DebugInterface,
+          Receiver->DebugCallback,
+          "- IpVersion6TcpChecksumPresent = %u\n",
+          Ring->OffloadStatistics.IpVersion6TcpChecksumPresent);
+
+    DEBUG(Printf,
+          Receiver->DebugInterface,
+          Receiver->DebugCallback,
+          "- IpVersion4UdpChecksumCalculated = %u\n",
+          Ring->OffloadStatistics.IpVersion4UdpChecksumCalculated);
+
+    DEBUG(Printf,
+          Receiver->DebugInterface,
+          Receiver->DebugCallback,
+          "- IpVersion4UdpChecksumSucceeded = %u\n",
+          Ring->OffloadStatistics.IpVersion4UdpChecksumSucceeded);
+
+    DEBUG(Printf,
+          Receiver->DebugInterface,
+          Receiver->DebugCallback,
+          "- IpVersion4UdpChecksumFailed = %u\n",
+          Ring->OffloadStatistics.IpVersion4UdpChecksumFailed);
+
+    DEBUG(Printf,
+          Receiver->DebugInterface,
+          Receiver->DebugCallback,
+          "- IpVersion4UdpChecksumPresent = %u\n",
+          Ring->OffloadStatistics.IpVersion4UdpChecksumPresent);
+
+    DEBUG(Printf,
+          Receiver->DebugInterface,
+          Receiver->DebugCallback,
+          "- IpVersion6UdpChecksumCalculated = %u\n",
+          Ring->OffloadStatistics.IpVersion6UdpChecksumCalculated);
+
+    DEBUG(Printf,
+          Receiver->DebugInterface,
+          Receiver->DebugCallback,
+          "- IpVersion6UdpChecksumSucceeded = %u\n",
+          Ring->OffloadStatistics.IpVersion6UdpChecksumSucceeded);
+
+    DEBUG(Printf,
+          Receiver->DebugInterface,
+          Receiver->DebugCallback,
+          "- IpVersion6UdpChecksumFailed = %u\n",
+          Ring->OffloadStatistics.IpVersion6UdpChecksumFailed);
+
+    DEBUG(Printf,
+          Receiver->DebugInterface,
+          Receiver->DebugCallback,
+          "- IpVersion6UdpChecksumPresent = %u\n",
+          Ring->OffloadStatistics.IpVersion6UdpChecksumPresent);
+
+    DEBUG(Printf,
+          Receiver->DebugInterface,
+          Receiver->DebugCallback,
+          "- TagRemoved = %u\n",
+          Ring->OffloadStatistics.TagRemoved);
+}
+
+static FORCEINLINE NTSTATUS
+__RingInitialize(
+    IN  PXENVIF_RECEIVER    Receiver,
+    OUT PRECEIVER_RING      *Ring
+    )
+{
+    NTSTATUS                status;
+
+    *Ring = __ReceiverAllocate(sizeof (RECEIVER_RING));
+
+    status = STATUS_NO_MEMORY;
+    if (Ring == NULL)
+        goto fail1;
+
+    KeInitializeSpinLock(&(*Ring)->Lock);
+
+    status = PoolInitialize("ReceiverPacket",
+                            sizeof (XENVIF_RECEIVER_PACKET),
+                            ReceiverPacketCtor,
+                            ReceiverPacketDtor,
+                            RingAcquireLock,
+                            RingReleaseLock,
+                            *Ring,
+                            &(*Ring)->PacketPool);
+    if (!NT_SUCCESS(status))
+        goto fail2;
+
+    InitializeListHead(&(*Ring)->PacketList);
+
+    (*Ring)->Receiver = Receiver;
+
+    return STATUS_SUCCESS;
+
+fail2:
+    RtlZeroMemory(&(*Ring)->Lock, sizeof (KSPIN_LOCK));
+
+    ASSERT(IsZeroMemory(*Ring, sizeof (RECEIVER_RING)));
+    __ReceiverFree(*Ring);
+    *Ring = NULL;
+
+fail1:
+    Error("fail1 (%08x)\n", status);
+
+    return status;
+}
+
+static FORCEINLINE NTSTATUS
+__RingConnect(
+    IN  PRECEIVER_RING          Ring
+    )
+{
+    PXENVIF_RECEIVER            Receiver;
+    PRECEIVER_RING_OPERATIONS   Operations;
+    NTSTATUS                    status;
+
+    Receiver = Ring->Receiver;
+
+    Info("Protocol %d\n", Receiver->Protocol);
+
+    ASSERT3P(Ring->Operations, ==, NULL);
+    ASSERT3U(Receiver->Protocol - RECEIVER_MINIMUM_PROTOCOL, <=, SUPPORTED_PROTOCOLS);
+    Ring->Operations = Operations = RingOperations[Receiver->Protocol - RECEIVER_MINIMUM_PROTOCOL];
+    ASSERT(Operations != NULL);
+
+    status = Operations->Connect(Ring);
+    if (!NT_SUCCESS(status))
+        goto fail1;
+
+    return STATUS_SUCCESS;
+
+fail1:
+    Error("fail1 (%08x)\n", status);
+
+    Ring->Operations = NULL;
+
+    return status;
+}
+
+static FORCEINLINE NTSTATUS
+__RingStoreWrite(
+    IN  PRECEIVER_RING              Ring,
+    IN  PXENBUS_STORE_TRANSACTION   Transaction
+    )
+{
+    PRECEIVER_RING_OPERATIONS       Operations;
+    NTSTATUS                        status;
+
+    Operations = Ring->Operations;
+    ASSERT(Operations != NULL);
+
+    status = Operations->StoreWrite(Ring, Transaction);
+    if (!NT_SUCCESS(status))
+        goto fail1;
+
+    return STATUS_SUCCESS;
+
+fail1:
+    Error("fail1 (%08x)\n", status);
+
+    return status;
+}
+
+static FORCEINLINE NTSTATUS
+__RingEnable(
+    IN  PRECEIVER_RING          Ring
+    )
+{
+    PRECEIVER_RING_OPERATIONS   Operations;
+    NTSTATUS                    status;
+
+    __RingAcquireLock(Ring);
+
+    ASSERT(!Ring->Enabled);
+
+    Operations = Ring->Operations;
+    ASSERT(Operations != NULL);
+
+    status = Operations->Enable(Ring);
+    if (!NT_SUCCESS(status))
+        goto fail1;
+
+    Ring->Enabled = TRUE;
+
+    __RingReleaseLock(Ring);
+
+    return STATUS_SUCCESS;
+
+fail1:
+    Error("fail1 (%08x)\n", status);
+
+    __RingReleaseLock(Ring);
+
+    return status;
+}
+
+static FORCEINLINE ULONG
+__RingGetSize(
+    IN  PRECEIVER_RING  Ring
+    )
+{
+    PRECEIVER_RING_OPERATIONS   Operations;
+
+    Operations = Ring->Operations;
+    ASSERT(Operations != NULL);
+
+    return Operations->GetSize(Ring);
+}
+
+static FORCEINLINE VOID
+__RingPoll(
+    IN  PRECEIVER_RING          Ring
+    )
+{
+    PRECEIVER_RING_OPERATIONS   Operations;
+
+    if (!(Ring->Enabled))
+        return;
+
+    Operations = Ring->Operations;
+    ASSERT(Operations != NULL);
+
+    Operations->Poll(Ring);
+}
+
+static FORCEINLINE VOID
+__RingDisable(
+    IN  PRECEIVER_RING          Ring
+    )
+{    
+    PRECEIVER_RING_OPERATIONS   Operations;
+
+    __RingAcquireLock(Ring);
+
+    ASSERT(Ring->Enabled);
+
+    Ring->Enabled = FALSE;
+    Ring->Stopped = FALSE;
+
+    Operations = Ring->Operations;
+    ASSERT(Operations != NULL);
+
+    Operations->Disable(Ring);
+
+    __RingReleaseLock(Ring);
+}
+
+static FORCEINLINE VOID
+__RingDisconnect(
+    IN  PRECEIVER_RING          Ring
+    )
+{
+    PRECEIVER_RING_OPERATIONS   Operations;
+
+    Operations = Ring->Operations;
+    ASSERT(Operations != NULL);
+
+    Operations->Disconnect(Ring);
+
+    Ring->Operations = NULL;
+}
+
+static FORCEINLINE VOID
+__RingTeardown(
+    IN  PRECEIVER_RING  Ring
+    )
+{
+    Ring->Receiver = NULL;
+
+    Ring->OffloadOptions.Value = 0;
+
+    RtlZeroMemory(&Ring->HeaderStatistics, sizeof (XENVIF_HEADER_STATISTICS));
+    RtlZeroMemory(&Ring->OffloadStatistics, sizeof (RECEIVER_OFFLOAD_STATISTICS));
+    RtlZeroMemory(&Ring->PacketStatistics, sizeof (XENVIF_RECEIVER_PACKET_STATISTICS));
+
+    ASSERT(IsListEmpty(&Ring->PacketList));
+    RtlZeroMemory(&Ring->PacketList, sizeof (LIST_ENTRY));
+
+    PoolTeardown(Ring->PacketPool);
+    Ring->PacketPool = NULL;
+
+    RtlZeroMemory(&Ring->Lock, sizeof (KSPIN_LOCK));
+
+    ASSERT(IsZeroMemory(Ring, sizeof (RECEIVER_RING)));
+    __ReceiverFree(Ring);
+}
+
+static FORCEINLINE VOID
+__RingNotify(
+    IN  PRECEIVER_RING  Ring
+    )
+{
+    PXENVIF_RECEIVER    Receiver;
+    PXENVIF_FRONTEND    Frontend;
+    LIST_ENTRY          List;
+    ULONG               Count;
+
+    Receiver = Ring->Receiver;
+    Frontend = Receiver->Frontend;
+
+    InitializeListHead(&List);
+    Count = 0;
+
+    __RingAcquireLock(Ring);
+
+    __RingPoll(Ring);
+
+    RingProcessPackets(Ring, &List, &Count);
+    ASSERT(EQUIV(IsListEmpty(&List), Count == 0));
+    ASSERT(IsListEmpty(&Ring->PacketList));
+
+    // We need to bump Loaned before dropping the lock to avoid VifDisable()
+    // returning prematurely.
+    if (!IsListEmpty(&List))
+        __InterlockedAdd(&Receiver->Loaned, Count);
+
+    __RingReleaseLock(Ring);
+
+    if (!IsListEmpty(&List))
+        VifReceivePackets(Receiver->VifInterface, &List);
+
+    ASSERT(IsListEmpty(&List));
+}
+
+static FORCEINLINE VOID
+__RingSetOffloadOptions(
+    IN  PRECEIVER_RING          Ring,
+    IN  XENVIF_OFFLOAD_OPTIONS  Options
+    )
+{
+    KIRQL                       Irql;
+
+    KeRaiseIrql(DISPATCH_LEVEL, &Irql);
+
+    __RingAcquireLock(Ring);
+    Ring->OffloadOptions = Options;
+    __RingReleaseLock(Ring);
+
+    KeLowerIrql(Irql);
+}
+
+static FORCEINLINE VOID
+__RingAddPacketStatistics(
+    IN      PRECEIVER_RING                      Ring,
+    IN OUT  PXENVIF_RECEIVER_PACKET_STATISTICS  Statistics
+    )
+{
+    // Don't bother locking
+
+    Statistics->Drop += Ring->PacketStatistics.Drop;
+    Statistics->BackendError += Ring->PacketStatistics.BackendError;
+    Statistics->FrontendError += Ring->PacketStatistics.FrontendError;
+    Statistics->Unicast += Ring->PacketStatistics.Unicast;
+    Statistics->UnicastBytes += Ring->PacketStatistics.UnicastBytes;
+    Statistics->Multicast += Ring->PacketStatistics.Multicast;
+    Statistics->MulticastBytes += Ring->PacketStatistics.MulticastBytes;
+    Statistics->Broadcast += Ring->PacketStatistics.Broadcast;
+    Statistics->BroadcastBytes += Ring->PacketStatistics.BroadcastBytes;
+}
+
+static VOID
+ReceiverDebugCallback(
+    IN  PVOID           Argument,
+    IN  BOOLEAN         Crashing
+    )
+{
+    PXENVIF_RECEIVER    Receiver = Argument;
+    PLIST_ENTRY         ListEntry;
+
+    UNREFERENCED_PARAMETER(Crashing);
+
+    for (ListEntry = Receiver->List.Flink;
+         ListEntry != &Receiver->List;
+         ListEntry = ListEntry->Flink) {
+        PRECEIVER_RING   Ring;
+
+        Ring = CONTAINING_RECORD(ListEntry, RECEIVER_RING, ListEntry);
+
+        __RingDebugCallback(Ring);
+    }    
+
+    DEBUG(Printf,
+          Receiver->DebugInterface,
+          Receiver->DebugCallback,
+          "Loaned = %d Returned = %d\n",
+          Receiver->Loaned,
+          Receiver->Returned);
+}
+
+NTSTATUS
+ReceiverInitialize(
+    IN  PXENVIF_FRONTEND    Frontend,
+    IN  ULONG               Count,
+    OUT PXENVIF_RECEIVER    *Receiver
+    )
+{
+    ULONG                   Done;
+    NTSTATUS                status;
+
+    *Receiver = __ReceiverAllocate(sizeof (XENVIF_RECEIVER));
+
+    status = STATUS_NO_MEMORY;
+    if (*Receiver == NULL)
+        goto fail1;
+
+    InitializeListHead(&(*Receiver)->List);
+    KeInitializeEvent(&(*Receiver)->Event, NotificationEvent, FALSE);
+
+    Done = 0;
+    while (Done < Count) {
+        PRECEIVER_RING   Ring;
+
+        status = __RingInitialize(*Receiver, &Ring);
+        if (!NT_SUCCESS(status))
+            goto fail2;
+
+        InsertTailList(&(*Receiver)->List, &Ring->ListEntry);
+        Done++;
+    }
+
+    (*Receiver)->Frontend = Frontend;
+
+    return STATUS_SUCCESS;
+
+fail2:
+    Error("fail2\n");
+
+    while (!IsListEmpty(&(*Receiver)->List)) {
+        PLIST_ENTRY     ListEntry;
+        PRECEIVER_RING  Ring;
+
+        ListEntry = RemoveTailList(&(*Receiver)->List);
+        ASSERT3P(ListEntry, !=, &(*Receiver)->List);
+
+        RtlZeroMemory(ListEntry, sizeof (LIST_ENTRY));
+
+        Ring = CONTAINING_RECORD(ListEntry, RECEIVER_RING, ListEntry);
+
+        __RingTeardown(Ring);
+
+        --Done;
+    }
+    ASSERT3U(Done, ==, 0);
+
+    RtlZeroMemory(&(*Receiver)->Event, sizeof (KEVENT));
+    RtlZeroMemory(&(*Receiver)->List, sizeof (LIST_ENTRY));
+
+    ASSERT(IsZeroMemory(*Receiver, sizeof (XENVIF_RECEIVER)));
+    __ReceiverFree(*Receiver);
+
+fail1:
+    Error("fail1 (%08x)\n", status);
+
+    return status;
+}
+
+static FORCEINLINE VOID
+__ReceiverSetGsoFeatureFlag(
+    IN  PXENVIF_RECEIVER        Receiver
+    )
+{
+    PXENVIF_FRONTEND            Frontend;
+
+    Frontend = Receiver->Frontend;
+
+    (VOID) STORE(Printf,
+                 Receiver->StoreInterface,
+                 NULL,
+                 FrontendGetPath(Frontend),
+                 "feature-gso-tcpv4-prefix",
+                 "%u",
+                 (Receiver->Protocol == 0) ? TRUE : FALSE);
+
+    (VOID) STORE(Printf,
+                 Receiver->StoreInterface,
+                 NULL,
+                 FrontendGetPath(Frontend),
+                 "feature-gso-tcpv6-prefix",
+                 "%u",
+                 (Receiver->Protocol == 0) ? TRUE : FALSE);
+
+    (VOID) STORE(Printf,
+                 Receiver->StoreInterface,
+                 NULL,
+                 FrontendGetPath(Frontend),
+                 "feature-gso-tcpv4",
+                 "%u",
+                 (Receiver->Protocol == 1) ? TRUE : FALSE);
+
+    (VOID) STORE(Printf,
+                 Receiver->StoreInterface,
+                 NULL,
+                 FrontendGetPath(Frontend),
+                 "feature-gso-tcpv6",
+                 "%u",
+                 (Receiver->Protocol == 1) ? TRUE : FALSE);
+}
+
+NTSTATUS
+ReceiverConnect(
+    IN  PXENVIF_RECEIVER    Receiver
+    )
+{
+    PXENVIF_FRONTEND        Frontend;
+    PLIST_ENTRY             ListEntry;
+    PCHAR                   Buffer;
+    ULONG                   MinimumProtocol;
+    ULONG                   MaximumProtocol;
+    NTSTATUS                status;
+
+    Frontend = Receiver->Frontend;
+
+    Receiver->StoreInterface = FrontendGetStoreInterface(Frontend);
+
+    STORE(Acquire, Receiver->StoreInterface);
+
+    status = STORE(Read,
+                   Receiver->StoreInterface,
+                   NULL,
+                   FrontendGetBackendPath(Frontend),
+                   "min-rx-protocol",
+                   &Buffer);
+    if (!NT_SUCCESS(status)) {
+        MinimumProtocol = 0;
+    } else {
+        MinimumProtocol = (ULONG)strtol(Buffer, NULL, 10);
+
+        STORE(Free,
+              Receiver->StoreInterface,
+              Buffer);
+    }
+
+    status = STORE(Read,
+                   Receiver->StoreInterface,
+                   NULL,
+                   FrontendGetBackendPath(Frontend),
+                   "max-rx-protocol",
+                   &Buffer);
+    if (!NT_SUCCESS(status)) {
+        MaximumProtocol = 0;
+    } else {
+        MaximumProtocol = (ULONG)strtol(Buffer, NULL, 10);
+
+        STORE(Free,
+              Receiver->StoreInterface,
+              Buffer);
+    }
+
+    MinimumProtocol = __max(MinimumProtocol, RECEIVER_MINIMUM_PROTOCOL);
+    MaximumProtocol = __min(MaximumProtocol, DriverParameters.ReceiverMaximumProtocol);
+
+    status = STATUS_NOT_SUPPORTED;
+    if (MaximumProtocol < MinimumProtocol)
+        goto fail1;
+
+    Receiver->Protocol = MaximumProtocol;
+
+    for (ListEntry = Receiver->List.Flink;
+         ListEntry != &Receiver->List;
+         ListEntry = ListEntry->Flink) {
+        PRECEIVER_RING  Ring;
+
+        Ring = CONTAINING_RECORD(ListEntry, RECEIVER_RING, ListEntry);
+
+        status = __RingConnect(Ring);
+        if (!NT_SUCCESS(status))
+            goto fail2;
+    }    
+
+    __ReceiverSetGsoFeatureFlag(Receiver);
+
+    Receiver->DebugInterface = FrontendGetDebugInterface(Frontend);
+
+    DEBUG(Acquire, Receiver->DebugInterface);
+
+    status = DEBUG(Register,
+                   Receiver->DebugInterface,
+                   __MODULE__ "|RECEIVER",
+                   ReceiverDebugCallback,
+                   Receiver,
+                   &Receiver->DebugCallback);
+    if (!NT_SUCCESS(status))
+        goto fail3;
+
+    return STATUS_SUCCESS;
+
+fail3:
+    Error("fail3\n");
+
+    DEBUG(Release, Receiver->DebugInterface);
+    Receiver->DebugInterface = NULL;
+
+    ListEntry = &Receiver->List;
+
+fail2:
+    Error("fail2\n");
+
+    ListEntry = ListEntry->Blink;
+
+    while (ListEntry != &Receiver->List) {
+        PLIST_ENTRY      Prev = ListEntry->Blink;
+        PRECEIVER_RING   Ring;
+
+        Ring = CONTAINING_RECORD(ListEntry, RECEIVER_RING, ListEntry);
+
+        __RingDisconnect(Ring);
+
+        ListEntry = Prev;
+    }
+
+fail1:
+    Error("fail1 (%08x)\n", status);
+
+    Receiver->Protocol = 0;
+
+    STORE(Release, Receiver->StoreInterface);
+    Receiver->StoreInterface = NULL;
+
+    return status;
+}
+
+NTSTATUS
+ReceiverStoreWrite(
+    IN  PXENVIF_RECEIVER            Receiver,
+    IN  PXENBUS_STORE_TRANSACTION   Transaction
+    )
+{
+    PXENVIF_FRONTEND                Frontend;
+    PLIST_ENTRY                     ListEntry;
+    NTSTATUS                        status;
+
+    Frontend = Receiver->Frontend;
+
+    status = STORE(Printf,
+                   Receiver->StoreInterface,
+                   Transaction,
+                   FrontendGetPath(Frontend),
+                   "request-rx-copy",
+                   "%u",
+                   TRUE);
+    if (!NT_SUCCESS(status))
+        goto fail1;
+
+    status = STORE(Printf,
+                   Receiver->StoreInterface,
+                   Transaction,
+                   FrontendGetPath(Frontend),
+                   "feature-sg",
+                   "%u",
+                   TRUE);
+    if (!NT_SUCCESS(status))
+        goto fail2;
+
+    status = STORE(Printf,
+                   Receiver->StoreInterface,
+                   Transaction,
+                   FrontendGetPath(Frontend),
+                   "feature-no-csum-offload",
+                   "%u",
+                   FALSE);
+    if (!NT_SUCCESS(status))
+        goto fail3;
+
+    status = STORE(Printf,
+                   Receiver->StoreInterface,
+                   Transaction,
+                   FrontendGetPath(Frontend),
+                   "feature-ipv6-csum-offload",
+                   "%u",
+                   TRUE);
+    if (!NT_SUCCESS(status))
+        goto fail4;
+
+    status = STORE(Printf,
+                   Receiver->StoreInterface,
+                   Transaction,
+                   FrontendGetPath(Frontend),
+                   "feature-rx-notify",
+                   "%u",
+                   TRUE);
+    if (!NT_SUCCESS(status))
+        goto fail5;
+
+    status = STORE(Printf,
+                   Receiver->StoreInterface,
+                   Transaction,
+                   FrontendGetPath(Frontend),
+                   "rx-protocol",
+                   "%u",
+                   Receiver->Protocol);
+    if (!NT_SUCCESS(status))
+        goto fail6;
+
+    for (ListEntry = Receiver->List.Flink;
+         ListEntry != &Receiver->List;
+         ListEntry = ListEntry->Flink) {
+        PRECEIVER_RING   Ring;
+
+        Ring = CONTAINING_RECORD(ListEntry, RECEIVER_RING, ListEntry);
+
+        status = __RingStoreWrite(Ring, Transaction);
+        if (!NT_SUCCESS(status))
+            goto fail7;
+    }    
+
+    return STATUS_SUCCESS;
+
+fail7:
+    Error("fail7\n");
+
+fail6:
+    Error("fail6\n");
+
+fail5:
+    Error("fail5\n");
+
+fail4:
+    Error("fail4\n");
+
+fail3:
+    Error("fail3\n");
+
+fail2:
+    Error("fail2\n");
+
+fail1:
+    Error("fail1 (%08x)\n", status);
+
+    return status;
+}
+
+NTSTATUS
+ReceiverEnable(
+    IN  PXENVIF_RECEIVER    Receiver
+    )
+{
+    PXENVIF_FRONTEND        Frontend;
+    PLIST_ENTRY             ListEntry;
+    NTSTATUS                status;
+
+    Frontend = Receiver->Frontend;
+
+    Receiver->VifInterface = FrontendGetVifInterface(Frontend);
+
+    for (ListEntry = Receiver->List.Flink;
+         ListEntry != &Receiver->List;
+         ListEntry = ListEntry->Flink) {
+        PRECEIVER_RING   Ring;
+
+        Ring = CONTAINING_RECORD(ListEntry, RECEIVER_RING, ListEntry);
+
+        status = __RingEnable(Ring);
+        if (!NT_SUCCESS(status))
+            goto fail1;
+    }    
+
+    return STATUS_SUCCESS;
+
+fail1:
+    Error("fail1 (%08x)\n", status);
+
+    ListEntry = ListEntry->Blink;
+
+    while (ListEntry != &Receiver->List) {
+        PRECEIVER_RING   Ring;
+
+        Ring = CONTAINING_RECORD(ListEntry, RECEIVER_RING, ListEntry);
+
+        __RingDisable(Ring);
+    }
+
+    Receiver->VifInterface = NULL;
+
+    return status;
+}
+
+VOID
+ReceiverDisable(
+    IN  PXENVIF_RECEIVER    Receiver
+    )
+{
+    PLIST_ENTRY             ListEntry;
+
+    for (ListEntry = Receiver->List.Blink;
+         ListEntry != &Receiver->List;
+         ListEntry = ListEntry->Blink) {
+        PRECEIVER_RING   Ring;
+
+        Ring = CONTAINING_RECORD(ListEntry, RECEIVER_RING, ListEntry);
+
+        __RingDisable(Ring);
+    }
+
+    Receiver->VifInterface = NULL;
+}
+
+VOID
+ReceiverDisconnect(
+    IN  PXENVIF_RECEIVER    Receiver
+    )
+{
+    PLIST_ENTRY             ListEntry;
+
+    DEBUG(Deregister,
+          Receiver->DebugInterface,
+          Receiver->DebugCallback);
+    Receiver->DebugCallback = NULL;
+
+    DEBUG(Release, Receiver->DebugInterface);
+    Receiver->DebugInterface = NULL;
+
+    for (ListEntry = Receiver->List.Blink; ListEntry != &Receiver->List; ListEntry = ListEntry->Blink) {
+        PRECEIVER_RING   Ring;
+
+        Ring = CONTAINING_RECORD(ListEntry, RECEIVER_RING, ListEntry);
+
+        __RingDisconnect(Ring);
+    }
+
+    Receiver->Protocol = 0;
+
+    STORE(Release, Receiver->StoreInterface);
+    Receiver->StoreInterface = NULL;
+}
+
+VOID
+ReceiverTeardown(
+    IN  PXENVIF_RECEIVER    Receiver
+    )
+{
+    ASSERT3U(Receiver->Returned, ==, Receiver->Loaned);
+    Receiver->Loaned = 0;
+    Receiver->Returned = 0;
+
+    Receiver->Frontend = NULL;
+
+    while (!IsListEmpty(&Receiver->List)) {
+        PLIST_ENTRY     ListEntry;
+        PRECEIVER_RING  Ring;
+
+        ListEntry = RemoveTailList(&Receiver->List);
+        ASSERT3P(ListEntry, !=, &Receiver->List);
+        RtlZeroMemory(ListEntry, sizeof (LIST_ENTRY));
+
+        Ring = CONTAINING_RECORD(ListEntry, RECEIVER_RING, ListEntry);
+
+        __RingTeardown(Ring);
+    }
+
+    RtlZeroMemory(&Receiver->Event, sizeof (KEVENT));
+    RtlZeroMemory(&Receiver->List, sizeof (LIST_ENTRY));
+
+    ASSERT(IsZeroMemory(Receiver, sizeof (XENVIF_RECEIVER)));
+    __ReceiverFree(Receiver);
+}
+
+NTSTATUS
+ReceiverSetOffloadOptions(
+    IN  PXENVIF_RECEIVER        Receiver,
+    IN  XENVIF_OFFLOAD_OPTIONS  Options
+    )
+{
+    PLIST_ENTRY                 ListEntry;
+
+    if (DriverParameters.ReceiverAllowGsoPackets == 0) {
+        Options.OffloadIpVersion4LargePacket = 0;
+        Options.OffloadIpVersion6LargePacket = 0;
+    }
+
+    for (ListEntry = Receiver->List.Flink;
+         ListEntry != &Receiver->List;
+         ListEntry = ListEntry->Flink) {
+        PRECEIVER_RING   Ring;
+
+        Ring = CONTAINING_RECORD(ListEntry, RECEIVER_RING, ListEntry);
+
+        __RingSetOffloadOptions(Ring, Options);
+    }    
+
+    return STATUS_SUCCESS;
+}
+
+VOID
+ReceiverGetPacketStatistics(
+    IN  PXENVIF_RECEIVER                    Receiver,
+    OUT PXENVIF_RECEIVER_PACKET_STATISTICS  Statistics
+    )
+{
+    PLIST_ENTRY                             ListEntry;
+
+    RtlZeroMemory(Statistics, sizeof (XENVIF_RECEIVER_PACKET_STATISTICS));
+
+    for (ListEntry = Receiver->List.Flink;
+         ListEntry != &Receiver->List;
+         ListEntry = ListEntry->Flink) {
+        PRECEIVER_RING   Ring;
+
+        Ring = CONTAINING_RECORD(ListEntry, RECEIVER_RING, ListEntry);
+
+        __RingAddPacketStatistics(Ring, Statistics);
+    }    
+}
+
+ULONG
+ReceiverGetRingSize(
+    IN  PXENVIF_RECEIVER    Receiver
+    )
+{
+    PLIST_ENTRY             ListEntry;
+    PRECEIVER_RING          Ring;
+
+    // Use the first ring
+    ListEntry = Receiver->List.Flink;
+    Ring = CONTAINING_RECORD(ListEntry, RECEIVER_RING, ListEntry);
+
+    return __RingGetSize(Ring);
+}
+
+VOID
+ReceiverReturnPacket(
+    IN  PXENVIF_RECEIVER        Receiver,
+    IN  PXENVIF_RECEIVER_PACKET Packet
+    )
+{
+    PRECEIVER_RING              Ring;
+    LONG                        Loaned;
+    LONG                        Returned;
+
+    Ring = Packet->Cookie;
+    Packet->Cookie = NULL;
+
+    __RingReturnPacket(Ring, Packet, FALSE);
+
+    Returned = InterlockedIncrement(&Receiver->Returned);
+
+    // Make sure Loaned is not sampled before Returned
+    KeMemoryBarrier();
+
+    Loaned = Receiver->Loaned;
+
+    ASSERT3S(Loaned - Returned, >=, 0);
+
+    KeSetEvent(&Receiver->Event, 0, FALSE);
+}
+
+VOID
+ReceiverWaitForPackets(
+    IN  PXENVIF_RECEIVER    Receiver
+    )
+{
+    LONG                    Loaned;
+
+    ASSERT3U(KeGetCurrentIrql(), <, DISPATCH_LEVEL);
+
+    Loaned = Receiver->Loaned;
+
+    while (Receiver->Returned != Loaned) {
+        Trace("waiting for packets\n");
+
+        (VOID) KeWaitForSingleObject(&Receiver->Event,
+                                     Executive,
+                                     KernelMode,
+                                     FALSE,
+                                     NULL);
+        KeClearEvent(&Receiver->Event);
+
+        ASSERT3U(Loaned, ==, Receiver->Loaned);
+
+        KeMemoryBarrier();
+    }
+}
+
+VOID
+ReceiverNotify(
+    IN  PXENVIF_RECEIVER    Receiver
+    )
+{
+    PLIST_ENTRY             ListEntry;
+
+    for (ListEntry = Receiver->List.Flink;
+         ListEntry != &Receiver->List;
+         ListEntry = ListEntry->Flink) {
+        PRECEIVER_RING   Ring;
+
+        Ring = CONTAINING_RECORD(ListEntry, RECEIVER_RING, ListEntry);
+
+        __RingNotify(Ring);
+    }    
+}
diff --git a/src/xenvif/receiver.h b/src/xenvif/receiver.h
new file mode 100644 (file)
index 0000000..ac1d8b8
--- /dev/null
@@ -0,0 +1,118 @@
+/* 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 _XENVIF_RECEIVER_H
+#define _XENVIF_RECEIVER_H
+
+#include <ntddk.h>
+#include <ifdef.h>
+#include <vif_interface.h>
+
+#include "frontend.h"
+
+typedef struct _XENVIF_RECEIVER XENVIF_RECEIVER, *PXENVIF_RECEIVER;
+
+#define RECEIVER_MINIMUM_PROTOCOL   0
+#define RECEIVER_MAXIMUM_PROTOCOL   1
+
+extern NTSTATUS
+ReceiverInitialize(
+    IN  PXENVIF_FRONTEND    Frontend,
+    IN  ULONG               Count,
+    OUT PXENVIF_RECEIVER    *Receiver
+    );
+
+extern NTSTATUS
+ReceiverConnect(
+    IN  PXENVIF_RECEIVER    Receiver
+    );
+
+extern NTSTATUS
+ReceiverStoreWrite(
+    IN  PXENVIF_RECEIVER            Receiver,
+    IN  PXENBUS_STORE_TRANSACTION   Transaction
+    );
+
+extern NTSTATUS
+ReceiverEnable(
+    IN  PXENVIF_RECEIVER    Receiver
+    );
+
+extern VOID
+ReceiverDisable(
+    IN  PXENVIF_RECEIVER    Receiver
+    );
+
+extern VOID
+ReceiverDisconnect(
+    IN  PXENVIF_RECEIVER    Receiver
+    );
+
+extern VOID
+ReceiverTeardown(
+    IN  PXENVIF_RECEIVER    Receiver
+    );
+
+extern VOID
+ReceiverNotify(
+    IN  PXENVIF_RECEIVER    Receiver
+    );
+
+extern VOID
+ReceiverWaitForPackets(
+    IN  PXENVIF_RECEIVER    Receiver
+    );
+
+extern VOID
+ReceiverGetPacketStatistics(
+    IN  PXENVIF_RECEIVER                    Receiver,
+    OUT PXENVIF_RECEIVER_PACKET_STATISTICS  Statistics
+    );
+
+extern ULONG
+ReceiverGetRingSize(
+    IN  PXENVIF_RECEIVER    Receiver
+    );
+
+extern VOID
+ReceiverReturnPacket(
+    IN  PXENVIF_RECEIVER        Receiver,
+    IN  PXENVIF_RECEIVER_PACKET Packet
+    );
+
+extern NTSTATUS
+ReceiverSetOffloadOptions(
+    IN  PXENVIF_RECEIVER        Receiver,
+    IN  XENVIF_OFFLOAD_OPTIONS  Options
+    );
+
+
+#endif  // _XENVIF_RECEIVER_H
diff --git a/src/xenvif/registry.c b/src/xenvif/registry.c
new file mode 100644 (file)
index 0000000..08a1cb4
--- /dev/null
@@ -0,0 +1,821 @@
+/* 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 <util.h>
+
+#include "registry.h"
+#include "log.h"
+#include "assert.h"
+
+#define REGISTRY_POOL 'GERX'
+
+static UNICODE_STRING   RegistryPath;
+
+static FORCEINLINE PVOID
+__RegistryAllocate(
+    IN  ULONG   Length
+    )
+{
+    return __AllocateNonPagedPoolWithTag(Length, REGISTRY_POOL);
+}
+
+static FORCEINLINE VOID
+__RegistryFree(
+    IN  PVOID   Buffer
+    )
+{
+    __FreePoolWithTag(Buffer, REGISTRY_POOL);
+}
+
+NTSTATUS
+RegistryInitialize(
+    IN PUNICODE_STRING  Path
+    )
+{
+    NTSTATUS            status;
+
+    ASSERT3P(RegistryPath.Buffer, ==, NULL);
+
+    status = RtlUpcaseUnicodeString(&RegistryPath, Path, TRUE);
+    if (!NT_SUCCESS(status))
+        goto fail1;
+
+    return STATUS_SUCCESS;
+
+fail1:
+    Error("fail1 (%08x)\n", status);
+
+    return status;
+}
+
+VOID
+RegistryTeardown(
+    VOID
+    )
+{
+    RtlFreeUnicodeString(&RegistryPath);
+    RegistryPath.Buffer = NULL;
+    RegistryPath.MaximumLength = RegistryPath.Length = 0;
+}
+
+NTSTATUS
+RegistryOpenSubKey(
+    IN  PCHAR           Name,
+    IN  ACCESS_MASK     DesiredAccess,
+    OUT PHANDLE         Key
+    )
+{
+    ANSI_STRING         Ansi;
+    UNICODE_STRING      Unicode;
+    OBJECT_ATTRIBUTES   Attributes;
+    HANDLE              ServiceKey;
+    NTSTATUS            status;
+
+    RtlInitAnsiString(&Ansi, Name);
+
+    status = RtlAnsiStringToUnicodeString(&Unicode, &Ansi, TRUE);
+    if (!NT_SUCCESS(status))
+        goto fail1;
+
+    InitializeObjectAttributes(&Attributes,
+                               &RegistryPath,
+                               OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
+                               NULL,
+                               NULL);
+
+    status = ZwOpenKey(&ServiceKey,
+                       KEY_ALL_ACCESS,
+                       &Attributes);
+    if (!NT_SUCCESS(status))
+        goto fail2;
+
+    InitializeObjectAttributes(&Attributes,
+                               &Unicode,
+                               OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
+                               ServiceKey,
+                               NULL);
+
+    status = ZwOpenKey(Key,
+                       DesiredAccess,
+                       &Attributes);
+    if (!NT_SUCCESS(status))
+        goto fail3;
+
+    ZwClose(ServiceKey);
+
+    RtlFreeUnicodeString(&Unicode);
+
+    return STATUS_SUCCESS;
+
+fail3:
+    ZwClose(ServiceKey);
+
+fail2:
+    RtlFreeUnicodeString(&Unicode);
+
+fail1:
+    return status;
+}
+
+NTSTATUS
+RegistryCreateSubKey(
+    IN  PCHAR           Name
+    )
+{
+    ANSI_STRING         Ansi;
+    UNICODE_STRING      Unicode;
+    OBJECT_ATTRIBUTES   Attributes;
+    HANDLE              ServiceKey;
+    HANDLE              Key;
+    NTSTATUS            status;
+
+    RtlInitAnsiString(&Ansi, Name);
+
+    status = RtlAnsiStringToUnicodeString(&Unicode, &Ansi, TRUE);
+    if (!NT_SUCCESS(status))
+        goto fail1;
+
+    InitializeObjectAttributes(&Attributes,
+                               &RegistryPath,
+                               OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
+                               NULL,
+                               NULL);
+
+    status = ZwOpenKey(&ServiceKey,
+                       KEY_ALL_ACCESS,
+                       &Attributes);
+    if (!NT_SUCCESS(status))
+        goto fail2;
+
+    InitializeObjectAttributes(&Attributes,
+                               &Unicode,
+                               OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
+                               ServiceKey,
+                               NULL);
+
+    status = ZwCreateKey(&Key,
+                         KEY_ALL_ACCESS,
+                         &Attributes,
+                         0,
+                         NULL,
+                         REG_OPTION_VOLATILE,
+                         NULL
+                         );
+    if (!NT_SUCCESS(status))
+        goto fail3;
+
+    ZwClose(Key);
+
+    ZwClose(ServiceKey);
+
+    RtlFreeUnicodeString(&Unicode);
+
+    return STATUS_SUCCESS;
+
+fail3:
+    ZwClose(ServiceKey);
+
+fail2:
+    RtlFreeUnicodeString(&Unicode);
+
+fail1:
+    return status;
+}
+
+NTSTATUS
+RegistryDeleteSubKey(
+    IN  PCHAR           Name
+    )
+{
+    ANSI_STRING         Ansi;
+    UNICODE_STRING      Unicode;
+    OBJECT_ATTRIBUTES   Attributes;
+    HANDLE              ServiceKey;
+    HANDLE              Key;
+    NTSTATUS            status;
+
+    RtlInitAnsiString(&Ansi, Name);
+
+    status = RtlAnsiStringToUnicodeString(&Unicode, &Ansi, TRUE);
+    if (!NT_SUCCESS(status))
+        goto fail1;
+
+    InitializeObjectAttributes(&Attributes,
+                               &RegistryPath,
+                               OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
+                               NULL,
+                               NULL);
+
+    status = ZwOpenKey(&ServiceKey,
+                       KEY_ALL_ACCESS,
+                       &Attributes);
+    if (!NT_SUCCESS(status))
+        goto fail2;
+
+    InitializeObjectAttributes(&Attributes,
+                               &Unicode,
+                               OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
+                               ServiceKey,
+                               NULL);
+
+    status = ZwOpenKey(&Key,
+                       KEY_ALL_ACCESS,
+                       &Attributes);
+    if (!NT_SUCCESS(status))
+        goto fail3;
+
+    status = ZwDeleteKey(Key);
+    if (!NT_SUCCESS(status))
+        goto fail4;
+
+    ZwClose(Key);
+
+    ZwClose(ServiceKey);
+
+    RtlFreeUnicodeString(&Unicode);
+
+    return STATUS_SUCCESS;
+
+fail4:
+    ZwClose(Key);
+
+fail3:
+    ZwClose(ServiceKey);
+
+fail2:
+    RtlFreeUnicodeString(&Unicode);
+
+fail1:
+    return status;
+}
+
+
+NTSTATUS
+RegistryOpenSoftwareKey(
+    IN  PDEVICE_OBJECT  DeviceObject,
+    IN  ACCESS_MASK     DesiredAccess,
+    OUT PHANDLE         Key
+    )
+{
+    NTSTATUS            status;
+
+    status = IoOpenDeviceRegistryKey(DeviceObject,
+                                     PLUGPLAY_REGKEY_DRIVER,
+                                     DesiredAccess,
+                                     Key);
+    if (!NT_SUCCESS(status))
+        goto fail1;
+
+    return STATUS_SUCCESS;
+
+fail1:
+    return status;
+}
+
+NTSTATUS
+RegistryOpenHardwareKey(
+    IN  PDEVICE_OBJECT  DeviceObject,
+    IN  ACCESS_MASK     DesiredAccess,
+    OUT PHANDLE         Key
+    )
+{
+    NTSTATUS            status;
+
+    status = IoOpenDeviceRegistryKey(DeviceObject,
+                                     PLUGPLAY_REGKEY_DEVICE,
+                                     DesiredAccess,
+                                     Key);
+    if (!NT_SUCCESS(status))
+        goto fail1;
+
+    return STATUS_SUCCESS;
+
+fail1:
+    return status;
+}
+
+NTSTATUS
+RegistryQueryDwordValue(
+    IN  HANDLE                      Key,
+    IN  PCHAR                       Name,
+    OUT PULONG                      Value
+    )
+{
+    ANSI_STRING                     Ansi;
+    UNICODE_STRING                  Unicode;
+    PKEY_VALUE_PARTIAL_INFORMATION  Partial;
+    ULONG                           Size;
+    NTSTATUS                        status;
+
+    RtlInitAnsiString(&Ansi, Name);
+
+    status = RtlAnsiStringToUnicodeString(&Unicode, &Ansi, TRUE);
+    if (!NT_SUCCESS(status))
+        goto fail1;
+        
+    status = ZwQueryValueKey(Key,
+                             &Unicode,
+                             KeyValuePartialInformation,
+                             NULL,
+                             0,
+                             &Size);
+    if (status != STATUS_BUFFER_TOO_SMALL)
+        goto fail2;
+
+    Partial = __RegistryAllocate(Size);
+
+    status = STATUS_NO_MEMORY;
+    if (Partial == NULL)
+        goto fail3;
+
+    status = ZwQueryValueKey(Key,
+                             &Unicode,
+                             KeyValuePartialInformation,
+                             Partial,
+                             Size,
+                             &Size);
+    if (!NT_SUCCESS(status))
+        goto fail4;
+
+    status = STATUS_INVALID_PARAMETER;
+    if (Partial->Type != REG_DWORD ||
+        Partial->DataLength != sizeof (ULONG))
+        goto fail5;
+
+    *Value = *(PULONG)Partial->Data;            
+
+    __RegistryFree(Partial);
+
+    RtlFreeUnicodeString(&Unicode);
+
+    return STATUS_SUCCESS;
+
+fail5:
+fail4:
+    __RegistryFree(Partial);
+
+fail3:
+fail2:
+    RtlFreeUnicodeString(&Unicode);
+
+fail1:
+    return status;
+}
+
+NTSTATUS
+RegistryUpdateDwordValue(
+    IN  HANDLE                      Key,
+    IN  PCHAR                       Name,
+    IN  ULONG                       Value
+    )
+{
+    ANSI_STRING                     Ansi;
+    UNICODE_STRING                  Unicode;
+    PKEY_VALUE_PARTIAL_INFORMATION  Partial;
+    NTSTATUS                        status;
+
+    RtlInitAnsiString(&Ansi, Name);
+
+    status = RtlAnsiStringToUnicodeString(&Unicode, &Ansi, TRUE);
+    if (!NT_SUCCESS(status))
+        goto fail1;
+        
+    Partial = __RegistryAllocate(FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data) +
+                                 sizeof (ULONG));
+
+    status = STATUS_NO_MEMORY;
+    if (Partial == NULL)
+        goto fail2;
+
+    Partial->TitleIndex = 0;
+    Partial->Type = REG_DWORD;
+    Partial->DataLength = sizeof (ULONG);
+    *(PULONG)Partial->Data = Value;            
+
+    status = ZwSetValueKey(Key,
+                           &Unicode,
+                           Partial->TitleIndex,
+                           Partial->Type,
+                           Partial->Data,
+                           Partial->DataLength);
+    if (!NT_SUCCESS(status))
+        goto fail3;
+
+    __RegistryFree(Partial);
+
+    RtlFreeUnicodeString(&Unicode);
+
+    return STATUS_SUCCESS;
+
+fail3:
+    __RegistryFree(Partial);
+
+fail2:
+    RtlFreeUnicodeString(&Unicode);
+
+fail1:
+
+    return status;
+}
+
+static PANSI_STRING
+RegistrySzToAnsi(
+    IN  PWCHAR      Buffer
+    )
+{
+    PANSI_STRING    Ansi;
+    ULONG           Length;
+    UNICODE_STRING  Unicode;
+    NTSTATUS        status;
+
+    Ansi = __RegistryAllocate(sizeof (ANSI_STRING) * 2);
+
+    status = STATUS_NO_MEMORY;
+    if (Ansi == NULL)
+        goto fail1;
+
+    Length = (ULONG)wcslen(Buffer);
+    Ansi[0].MaximumLength = (USHORT)(Length + 1) * sizeof (CHAR);
+    Ansi[0].Buffer = __RegistryAllocate(Ansi[0].MaximumLength);
+
+    status = STATUS_NO_MEMORY;
+    if (Ansi[0].Buffer == NULL)
+        goto fail2;
+
+    RtlInitUnicodeString(&Unicode, Buffer);
+    status = RtlUnicodeStringToAnsiString(&Ansi[0], &Unicode, FALSE);
+    ASSERT(NT_SUCCESS(status));
+
+    Ansi[0].Length = (USHORT)Length * sizeof (CHAR);
+
+    return Ansi;
+
+fail2:
+    __RegistryFree(Ansi);
+
+fail1:
+    return NULL;
+}
+
+static PANSI_STRING
+RegistryMultiSzToAnsi(
+    IN  PWCHAR      Buffer
+    )
+{
+    PANSI_STRING    Ansi;
+    LONG            Index;
+    LONG            Count;
+    NTSTATUS        status;
+
+    Index = 0;
+    Count = 0;
+    for (;;) {
+        ULONG   Length;
+
+        Length = (ULONG)wcslen(&Buffer[Index]);
+        if (Length == 0)
+            break;
+
+        Index += Length + 1;
+        Count++;
+    }
+
+    Ansi = __RegistryAllocate(sizeof (ANSI_STRING) * (Count + 1));
+
+    status = STATUS_NO_MEMORY;
+    if (Ansi == NULL)
+        goto fail1;
+
+    for (Index = 0; Index < Count; Index++) {
+        ULONG           Length;
+        UNICODE_STRING  Unicode;
+
+        Length = (ULONG)wcslen(Buffer);
+        Ansi[Index].MaximumLength = (USHORT)(Length + 1) * sizeof (CHAR);
+        Ansi[Index].Buffer = __RegistryAllocate(Ansi[Index].MaximumLength);
+
+        status = STATUS_NO_MEMORY;
+        if (Ansi[Index].Buffer == NULL)
+            goto fail2;
+
+        RtlInitUnicodeString(&Unicode, Buffer);
+
+        status = RtlUnicodeStringToAnsiString(&Ansi[Index], &Unicode, FALSE);
+        ASSERT(NT_SUCCESS(status));
+
+        Ansi[Index].Length = (USHORT)Length * sizeof (CHAR);
+        Buffer += Length + 1;
+    }
+
+    return Ansi;
+
+fail2:
+    while (--Index >= 0)
+        __RegistryFree(Ansi[Index].Buffer);
+
+    __RegistryFree(Ansi);
+
+fail1:
+    return NULL;
+}
+
+NTSTATUS
+RegistryQuerySzValue(
+    IN  HANDLE                      Key,
+    IN  PCHAR                       Name,
+    OUT PANSI_STRING                *Array
+    )
+{
+    ANSI_STRING                     Ansi;
+    UNICODE_STRING                  Unicode;
+    PKEY_VALUE_PARTIAL_INFORMATION  Value;
+    ULONG                           Size;
+    NTSTATUS                        status;
+
+    RtlInitAnsiString(&Ansi, Name);
+
+    status = RtlAnsiStringToUnicodeString(&Unicode, &Ansi, TRUE);
+    if (!NT_SUCCESS(status))
+        goto fail1;
+        
+    status = ZwQueryValueKey(Key,
+                             &Unicode,
+                             KeyValuePartialInformation,
+                             NULL,
+                             0,
+                             &Size);
+    if (status != STATUS_BUFFER_TOO_SMALL)
+        goto fail2;
+
+    Value = __RegistryAllocate(Size);
+
+    status = STATUS_NO_MEMORY;
+    if (Value == NULL)
+        goto fail3;
+
+    status = ZwQueryValueKey(Key,
+                             &Unicode,
+                             KeyValuePartialInformation,
+                             Value,
+                             Size,
+                             &Size);
+    if (!NT_SUCCESS(status))
+        goto fail4;
+
+    switch (Value->Type) {
+    case REG_SZ:
+        status = STATUS_NO_MEMORY;
+        *Array = RegistrySzToAnsi((PWCHAR)Value->Data);
+        break;
+
+    case REG_MULTI_SZ:
+        status = STATUS_NO_MEMORY;
+        *Array = RegistryMultiSzToAnsi((PWCHAR)Value->Data);
+        break;
+
+    default:
+        status = STATUS_INVALID_PARAMETER;
+        *Array = NULL;
+        break;
+    }
+
+    if (*Array == NULL)
+        goto fail5;
+
+    __RegistryFree(Value);
+
+    RtlFreeUnicodeString(&Unicode);
+
+    return STATUS_SUCCESS;
+
+fail5:
+fail4:
+    __RegistryFree(Value);
+
+fail3:
+fail2:
+    RtlFreeUnicodeString(&Unicode);
+
+fail1:
+    return status;
+}
+
+static PKEY_VALUE_PARTIAL_INFORMATION
+RegistryAnsiToSz(
+    PANSI_STRING                    Ansi
+    )
+{
+    ULONG                           Length;
+    PKEY_VALUE_PARTIAL_INFORMATION  Partial;
+    UNICODE_STRING                  Unicode;
+    NTSTATUS                        status;
+
+    Length = Ansi->Length + 1;
+    Partial = __RegistryAllocate(FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data) +
+                                 Length * sizeof (WCHAR));
+
+    status = STATUS_NO_MEMORY;
+    if (Partial == NULL)
+        goto fail1;
+
+    Partial->TitleIndex = 0;
+    Partial->Type = REG_SZ;
+    Partial->DataLength = Length * sizeof (WCHAR);
+
+    Unicode.MaximumLength = (UCHAR)Partial->DataLength;
+    Unicode.Buffer = (PWCHAR)Partial->Data;
+    Unicode.Length = 0;
+
+    status = RtlAnsiStringToUnicodeString(&Unicode, Ansi, FALSE);
+    if (!NT_SUCCESS(status))
+        goto fail2;
+
+    return Partial;
+
+fail2:
+    __RegistryFree(Partial);
+
+fail1:
+    return NULL;
+}
+
+static PKEY_VALUE_PARTIAL_INFORMATION
+RegistryAnsiToMultiSz(
+    PANSI_STRING                    Ansi
+    )
+{
+    ULONG                           Length;
+    ULONG                           Index;
+    PKEY_VALUE_PARTIAL_INFORMATION  Partial;
+    UNICODE_STRING                  Unicode;
+    NTSTATUS                        status;
+
+    Length = 1;
+    for (Index = 0; Ansi[Index].Buffer != NULL; Index++)
+        Length += Ansi[Index].Length + 1;
+
+    Partial = __RegistryAllocate(FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data) +
+                               Length * sizeof (WCHAR));
+
+    status = STATUS_NO_MEMORY;
+    if (Partial == NULL)
+        goto fail1;
+
+    Partial->TitleIndex = 0;
+    Partial->Type = REG_MULTI_SZ;
+    Partial->DataLength = Length * sizeof (WCHAR);
+
+    Unicode.MaximumLength = (USHORT)Partial->DataLength;
+    Unicode.Buffer = (PWCHAR)Partial->Data;
+    Unicode.Length = 0;
+
+    for (Index = 0; Ansi[Index].Buffer != NULL; Index++) {
+        status = RtlAnsiStringToUnicodeString(&Unicode, &Ansi[Index], FALSE);
+        if (!NT_SUCCESS(status))
+            goto fail2;
+
+        Length = Unicode.Length / sizeof (WCHAR);
+
+        ASSERT3U(Unicode.MaximumLength, >=, (Length + 1) * sizeof (WCHAR));
+        Unicode.MaximumLength -= (USHORT)((Length + 1) * sizeof (WCHAR));
+        Unicode.Buffer += Length + 1;
+        Unicode.Length = 0;
+    }
+    *Unicode.Buffer = L'\0';
+
+    return Partial;
+
+fail2:
+    __RegistryFree(Partial);
+
+fail1:
+    return NULL;
+}
+
+NTSTATUS
+RegistryUpdateSzValue(
+    IN  HANDLE                      Key,
+    IN  PCHAR                       Name,
+    IN  ULONG                       Type,
+    ...
+    )
+{
+    ANSI_STRING                     Ansi;
+    UNICODE_STRING                  Unicode;
+    va_list                         Arguments;
+    PKEY_VALUE_PARTIAL_INFORMATION  Partial;
+    NTSTATUS                        status;
+
+    RtlInitAnsiString(&Ansi, Name);
+
+    status = RtlAnsiStringToUnicodeString(&Unicode, &Ansi, TRUE);
+    if (!NT_SUCCESS(status))
+        goto fail1;
+        
+    va_start(Arguments, Type);
+    switch (Type) {
+    case REG_SZ: {
+        PANSI_STRING    Argument;
+
+        Argument = va_arg(Arguments, PANSI_STRING);
+
+        status = STATUS_NO_MEMORY;
+        Partial = RegistryAnsiToSz(Argument);        
+        break;
+    }
+    case REG_MULTI_SZ: {
+        PANSI_STRING    Argument;
+
+        Argument = va_arg(Arguments, PANSI_STRING);
+
+        status = STATUS_NO_MEMORY;
+        Partial = RegistryAnsiToMultiSz(Argument);        
+        break;
+    }
+    default:
+        status = STATUS_INVALID_PARAMETER;
+        Partial = NULL;
+        break;
+    }
+    va_end(Arguments);
+
+    if (Partial == NULL)
+        goto fail2;
+
+    status = ZwSetValueKey(Key,
+                           &Unicode,
+                           Partial->TitleIndex,
+                           Partial->Type,
+                           Partial->Data,
+                           Partial->DataLength);
+    if (!NT_SUCCESS(status))
+        goto fail3;
+
+    __RegistryFree(Partial);
+
+    RtlFreeUnicodeString(&Unicode);
+
+    return STATUS_SUCCESS;
+
+fail3:
+    __RegistryFree(Partial);
+
+fail2:
+    RtlFreeUnicodeString(&Unicode);
+
+fail1:
+    return status;
+}
+
+VOID
+RegistryFreeSzValue(
+    IN  PANSI_STRING    Array
+    )
+{
+    ULONG               Index;
+
+    if (Array == NULL)
+        return;
+
+    for (Index = 0; Array[Index].Buffer != NULL; Index++)
+        __RegistryFree(Array[Index].Buffer);
+
+    __RegistryFree(Array);
+}
+
+VOID
+RegistryCloseKey(
+    IN  HANDLE  Key
+    )
+{
+    ZwClose(Key);
+}
diff --git a/src/xenvif/registry.h b/src/xenvif/registry.h
new file mode 100644 (file)
index 0000000..f806cd2
--- /dev/null
@@ -0,0 +1,117 @@
+/* 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 _XENVIF_REGISTRY_H
+#define _XENVIF_REGISTRY_H
+
+#include <ntddk.h>
+
+extern NTSTATUS
+RegistryInitialize(
+    IN PUNICODE_STRING  Path
+    );
+
+extern VOID
+RegistryTeardown(
+    VOID
+    );
+
+extern NTSTATUS
+RegistryOpenSubKey(
+    IN  PCHAR       Name,
+    IN  ACCESS_MASK DesiredAccess,
+    OUT PHANDLE     Key
+    );
+
+extern NTSTATUS
+RegistryCreateSubKey(
+    IN  PCHAR       Name
+    );
+
+extern NTSTATUS
+RegistryDeleteSubKey(
+    IN  PCHAR       Name
+    );
+
+extern NTSTATUS
+RegistryOpenSoftwareKey(
+    IN  PDEVICE_OBJECT  DeviceObject,
+    IN  ACCESS_MASK     DesiredAccess,
+    OUT PHANDLE         Key
+    );
+
+extern NTSTATUS
+RegistryOpenHardwareKey(
+    IN  PDEVICE_OBJECT  DeviceObject,
+    IN  ACCESS_MASK     DesiredAccess,
+    OUT PHANDLE         Key
+    );
+
+extern NTSTATUS
+RegistryQueryDwordValue(
+    IN  HANDLE          Key,
+    IN  PCHAR           Name,
+    OUT PULONG          Value
+    );
+    
+extern NTSTATUS
+RegistryUpdateDwordValue(
+    IN  HANDLE          Key,
+    IN  PCHAR           Name,
+    IN  ULONG           Value
+    );
+    
+extern NTSTATUS
+RegistryQuerySzValue(
+    IN  HANDLE          Key,
+    IN  PCHAR           Name,
+    OUT PANSI_STRING    *Array
+    );
+
+extern VOID
+RegistryFreeSzValue(
+    IN  PANSI_STRING    Array
+    );
+
+extern NTSTATUS
+RegistryUpdateSzValue(
+    IN  HANDLE          Key,
+    IN  PCHAR           Name,
+    IN  ULONG           Type,
+    ...
+    );
+
+extern VOID
+RegistryCloseKey(
+    IN  HANDLE  Key
+    );
+
+#endif  // _XENVIF_REGISTRY_H
diff --git a/src/xenvif/thread.c b/src/xenvif/thread.c
new file mode 100644 (file)
index 0000000..3762ea8
--- /dev/null
@@ -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 <util.h>
+
+#include "thread.h"
+#include "log.h"
+#include "assert.h"
+
+#define THREAD_POOL 'ERHT'
+
+struct _XENVIF_THREAD {
+    XENVIF_THREAD_FUNCTION  Function;
+    PVOID                   Context;
+    KEVENT                  Event;
+    BOOLEAN                 Alerted;
+    LONG                    References;
+    PKTHREAD                Thread;
+};
+
+static FORCEINLINE PVOID
+__ThreadAllocate(
+    IN  ULONG   Length
+    )
+{
+    return __AllocateNonPagedPoolWithTag(Length, THREAD_POOL);
+}
+
+static FORCEINLINE VOID
+__ThreadFree(
+    IN  PVOID   Buffer
+    )
+{
+    __FreePoolWithTag(Buffer, THREAD_POOL);
+}
+
+static FORCEINLINE VOID
+__ThreadWake(
+    IN  PXENVIF_THREAD  Thread
+    )
+{
+    KeSetEvent(&Thread->Event, IO_NO_INCREMENT, FALSE);
+}
+
+VOID
+ThreadWake(
+    IN  PXENVIF_THREAD  Thread
+    )
+{
+    __ThreadWake(Thread);
+}
+
+static FORCEINLINE VOID
+__ThreadAlert(
+    IN  PXENVIF_THREAD  Thread
+    )
+{
+    Thread->Alerted = TRUE;
+    __ThreadWake(Thread);
+}
+
+VOID
+ThreadAlert(
+    IN  PXENVIF_THREAD  Thread
+    )
+{
+    __ThreadAlert(Thread);
+}
+
+KSTART_ROUTINE  ThreadFunction;
+
+VOID
+ThreadFunction(
+    IN  PVOID       Argument
+    )
+{
+    PXENVIF_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  XENVIF_THREAD_FUNCTION  Function,
+    IN  PVOID                   Context,
+    OUT PXENVIF_THREAD          *Thread
+    )
+{
+    HANDLE                      Handle;
+    NTSTATUS                    status;
+
+    ASSERT3U(KeGetCurrentIrql(), ==, PASSIVE_LEVEL);
+
+    (*Thread) = __ThreadAllocate(sizeof (XENVIF_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  PXENVIF_THREAD  Thread
+    )
+{
+    return &Thread->Event;
+}
+
+BOOLEAN
+ThreadIsAlerted(
+    IN  PXENVIF_THREAD  Thread
+    )
+{
+    return Thread->Alerted;
+}
+
+VOID
+ThreadJoin(
+    IN  PXENVIF_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/xenvif/thread.h b/src/xenvif/thread.h
new file mode 100644 (file)
index 0000000..b48cad3
--- /dev/null
@@ -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 _XENVIF_THREAD_H
+#define _XENVIF_THREAD_H
+
+#include <ntddk.h>
+
+typedef struct _XENVIF_THREAD XENVIF_THREAD, *PXENVIF_THREAD;
+
+typedef NTSTATUS (*XENVIF_THREAD_FUNCTION)(PXENVIF_THREAD, PVOID);
+
+extern NTSTATUS
+ThreadCreate(
+    IN  XENVIF_THREAD_FUNCTION  Function,
+    IN  PVOID                   Context,
+    OUT PXENVIF_THREAD          *Thread
+    );
+
+extern PKEVENT
+ThreadGetEvent(
+    IN  PXENVIF_THREAD  Self
+    );
+
+extern BOOLEAN
+ThreadIsAlerted(
+    IN  PXENVIF_THREAD  Self
+    );
+
+extern VOID
+ThreadWake(
+    IN  PXENVIF_THREAD  Thread
+    );
+
+extern VOID
+ThreadAlert(
+    IN  PXENVIF_THREAD  Thread
+    );
+
+extern VOID
+ThreadJoin(
+    IN  PXENVIF_THREAD  Thread
+    );
+
+#endif  // _XENVIF_THREAD_H
+
diff --git a/src/xenvif/transmitter.c b/src/xenvif/transmitter.c
new file mode 100644 (file)
index 0000000..a0c4ecd
--- /dev/null
@@ -0,0 +1,4018 @@
+/* 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 <ntstrsafe.h>
+#include <stdlib.h>
+#include <netioapi.h>
+#include <util.h>
+#include <xen.h>
+#include <debug_interface.h>
+#include <store_interface.h>
+#include <gnttab_interface.h>
+
+#include "ethernet.h"
+#include "tcpip.h"
+#include "pdo.h"
+#include "frontend.h"
+#include "pool.h"
+#include "checksum.h"
+#include "parse.h"
+#include "transmitter.h"
+#include "mac.h"
+#include "vif.h"
+#include "thread.h"
+#include "log.h"
+#include "assert.h"
+
+#define TRANSMITTER_POOL    'NART'
+
+typedef struct _TRANSMITTER_BUFFER {
+    PMDL        Mdl;
+    PVOID       Context;
+    ULONG       Reference;
+} TRANSMITTER_BUFFER, *PTRANSMITTER_BUFFER;
+
+typedef enum _TRANSMITTER_TAG_TYPE {
+    TAG_TYPE_INVALID = 0,
+    TAG_PACKET,
+    TAG_BUFFER
+} TRANSMITTER_TAG_TYPE, *PTRANSMITTER_TAG_TYPE;
+
+typedef struct _TRANSMITTER_TAG {
+    LIST_ENTRY              ListEntry;
+    ULONG                   Next;
+    TRANSMITTER_TAG_TYPE    Type;
+    PVOID                   Context;
+    ULONG                   Reference;
+    ULONG                   Offset;
+    ULONG                   Length;
+} TRANSMITTER_TAG, *PTRANSMITTER_TAG;
+
+typedef struct _TRANSMITTER_OFFLOAD_STATISTICS {
+    ULONGLONG   TagManipulation;
+    ULONGLONG   IpVersion4LargePacket;
+    ULONGLONG   IpVersion6LargePacket;
+    ULONGLONG   IpVersion4HeaderChecksum;
+    ULONGLONG   IpVersion4TcpChecksum;
+    ULONGLONG   IpVersion6TcpChecksum;
+    ULONGLONG   IpVersion4UdpChecksum;
+    ULONGLONG   IpVersion6UdpChecksum;
+} TRANSMITTER_OFFLOAD_STATISTICS, *PTRANSMITTER_OFFLOAD_STATISTICS;
+
+typedef struct _TRANSMITTER_STATE {
+    PXENVIF_TRANSMITTER_PACKET  Packet;
+    XENVIF_SEND_INFO            Send;
+    PUCHAR                      StartVa;
+    XENVIF_PACKET_INFO          Info;
+    XENVIF_PACKET_PAYLOAD       Payload;
+    LIST_ENTRY                  List;
+    ULONG                       Count;
+} TRANSMITTER_STATE, *PTRANSMITTER_STATE;
+
+#define TRANSMITTER_RING_SIZE   (__CONST_RING_SIZE(netif_tx, PAGE_SIZE))
+#define MAXIMUM_TAG_COUNT       (TRANSMITTER_RING_SIZE * 2)
+
+#define TAG_INDEX_INVALID       0xFFFFFFFF
+
+#define REQ_ID_INTEGRITY_CHECK  0xF000
+
+typedef struct _TRANSMITTER_PACKET_LIST {
+    PXENVIF_TRANSMITTER_PACKET  HeadPacket;
+    PXENVIF_TRANSMITTER_PACKET  *TailPacket;
+} TRANSMITTER_PACKET_LIST, *PTRANSMITTER_PACKET_LIST;
+
+typedef struct _TRANSMITTER_RING {
+    PXENVIF_TRANSMITTER                     Transmitter;
+    LIST_ENTRY                              ListEntry;
+    PXENVIF_POOL                            BufferPool;
+    PMDL                                    Mdl;
+    netif_tx_front_ring_t                   Front;
+    netif_tx_sring_t                        *Shared;
+    ULONG                                   Reference;
+    BOOLEAN                                 Connected;
+    BOOLEAN                                 Enabled;
+    BOOLEAN                                 Stopped;
+    PXENVIF_TRANSMITTER_PACKET              Lock;
+    PKTHREAD                                LockThread;
+    TRANSMITTER_PACKET_LIST                 Queued;
+    TRANSMITTER_STATE                       State;
+    ULONG                                   PacketsQueued;
+    ULONG                                   PacketsGranted;
+    ULONG                                   PacketsCopied;
+    ULONG                                   PacketsFaked;
+    ULONG                                   PacketsUnprepared;
+    ULONG                                   PacketsPrepared;
+    ULONG                                   HeadFreeTag;
+    TRANSMITTER_TAG                         Tag[MAXIMUM_TAG_COUNT];
+    netif_tx_request_t                      Pending[MAXIMUM_TAG_COUNT];
+    ULONG                                   RequestsPosted;
+    ULONG                                   RequestsPushed;
+    ULONG                                   ResponsesProcessed;
+    ULONG                                   PacketsSent;
+    TRANSMITTER_PACKET_LIST                 Completed;
+    ULONG                                   PacketsCompleted;
+    PSOCKADDR_INET                          AddressTable;
+    ULONG                                   AddressCount;
+    ULONG                                   AddressIndex;
+    PXENVIF_THREAD                          Thread;
+    XENVIF_TRANSMITTER_PACKET_STATISTICS    PacketStatistics;
+    XENVIF_HEADER_STATISTICS                HeaderStatistics;
+    TRANSMITTER_OFFLOAD_STATISTICS          OffloadStatistics;
+} TRANSMITTER_RING, *PTRANSMITTER_RING;
+
+struct _XENVIF_TRANSMITTER {
+    PXENVIF_FRONTEND                    Frontend;
+    LIST_ENTRY                          List;
+    XENVIF_TRANSMITTER_PACKET_METADATA  Metadata;
+
+    PXENBUS_DEBUG_INTERFACE             DebugInterface;
+    PXENBUS_STORE_INTERFACE             StoreInterface;
+    PXENBUS_GNTTAB_INTERFACE            GnttabInterface;
+    PXENVIF_VIF_INTERFACE               VifInterface;
+
+    PXENBUS_DEBUG_CALLBACK              DebugCallback;
+};
+
+static FORCEINLINE PVOID
+__TransmitterAllocate(
+    IN  ULONG   Length
+    )
+{
+    return __AllocateNonPagedPoolWithTag(Length, TRANSMITTER_POOL);
+}
+
+static FORCEINLINE VOID
+__TransmitterFree(
+    IN  PVOID   Buffer
+    )
+{
+    __FreePoolWithTag(Buffer, TRANSMITTER_POOL);
+}
+
+static NTSTATUS
+TransmitterBufferCtor(
+    IN  PVOID   Argument,
+    IN  PVOID   Object
+    )
+{
+    PTRANSMITTER_BUFFER Buffer = Object;
+    PMDL               Mdl;
+    PUCHAR             MdlMappedSystemVa;
+    NTSTATUS           status;
+
+    UNREFERENCED_PARAMETER(Argument);
+
+    ASSERT(IsZeroMemory(Buffer, sizeof (TRANSMITTER_BUFFER)));
+
+    Mdl = __AllocatePage();
+
+    status = STATUS_NO_MEMORY;
+    if (Mdl == NULL)
+       goto fail1;
+
+    MdlMappedSystemVa = MmGetSystemAddressForMdlSafe(Mdl, NormalPagePriority);
+    RtlFillMemory(MdlMappedSystemVa, PAGE_SIZE, 0xAA);
+
+    Mdl->ByteCount = 0;
+    Buffer->Mdl = Mdl;
+
+    return STATUS_SUCCESS;
+
+fail1:
+    Error("fail1 (%08x)\n", status);
+
+    ASSERT(IsZeroMemory(Buffer, sizeof (TRANSMITTER_BUFFER)));
+
+    return status;
+}
+
+static VOID
+TransmitterBufferDtor(
+    IN  PVOID           Argument,
+    IN  PVOID           Object
+    )
+{
+    PTRANSMITTER_BUFFER Buffer = Object;
+    PMDL                Mdl;
+
+    UNREFERENCED_PARAMETER(Argument);
+
+    Mdl = Buffer->Mdl;
+    Buffer->Mdl = NULL;
+
+    Mdl->ByteCount = PAGE_SIZE;
+
+    __FreePage(Mdl);
+    ExFreePool(Mdl);
+
+    ASSERT(IsZeroMemory(Buffer, sizeof (TRANSMITTER_BUFFER)));
+}
+
+static FORCEINLINE PTRANSMITTER_BUFFER
+__TransmitterGetBuffer(
+    IN  PTRANSMITTER_RING   Ring
+    )
+{
+    PTRANSMITTER_BUFFER     Buffer;
+
+    Buffer = PoolGet(Ring->BufferPool, TRUE);
+
+    ASSERT(IMPLY(Buffer != NULL, Buffer->Mdl->ByteCount == 0));
+
+    return Buffer;
+}
+
+static FORCEINLINE VOID
+__TransmitterPutBuffer(
+    IN  PTRANSMITTER_RING   Ring,
+    IN  PTRANSMITTER_BUFFER Buffer
+    )
+{
+    ASSERT3U(Buffer->Reference, ==, 0);
+    ASSERT3P(Buffer->Context, ==, NULL);
+
+    Buffer->Mdl->ByteCount = 0;
+
+    PoolPut(Ring->BufferPool, Buffer, TRUE);
+}
+
+static FORCEINLINE PTRANSMITTER_TAG
+__TransmitterGetTag(
+    IN  PTRANSMITTER_RING   Ring
+    )
+{
+    ULONG                   Index;
+    PTRANSMITTER_TAG        Tag;
+
+    Index = Ring->HeadFreeTag;
+    ASSERT3U(Index, <, MAXIMUM_TAG_COUNT);
+
+    Tag = &Ring->Tag[Index];
+    Ring->HeadFreeTag = Tag->Next;
+    Tag->Next = TAG_INDEX_INVALID;
+
+    return Tag;
+}
+
+static FORCEINLINE
+__TransmitterPutTag(
+    IN  PTRANSMITTER_RING   Ring,
+    IN  PTRANSMITTER_TAG    Tag
+    )
+{
+    ULONG                   Index;
+
+    ASSERT3U(Tag->Length, ==, 0);
+    ASSERT3U(Tag->Offset, ==, 0);
+    ASSERT3U(Tag->Type, ==, TAG_TYPE_INVALID);
+    ASSERT3P(Tag->Context, ==, NULL);
+
+    Index = (ULONG)(Tag - &Ring->Tag[0]);
+    ASSERT3U(Index, <, MAXIMUM_TAG_COUNT);
+
+    ASSERT3U(Tag->Next, ==, TAG_INDEX_INVALID);
+    Tag->Next = Ring->HeadFreeTag;
+    Ring->HeadFreeTag = Index;
+}
+
+static FORCEINLINE VOID
+__RingDebugCallback(
+    IN  PTRANSMITTER_RING   Ring
+    )
+{
+    PXENVIF_TRANSMITTER     Transmitter;
+    ULONG                   Allocated;
+    ULONG                   MaximumAllocated;
+    ULONG                   Count;
+    ULONG                   MinimumCount;
+
+    Transmitter = Ring->Transmitter;
+
+    DEBUG(Printf,
+          Transmitter->DebugInterface,
+          Transmitter->DebugCallback,
+          "0x%p [%s]\n",
+          Ring,
+          (Ring->Enabled) ? "ENABLED" : "DISABLED");
+
+    PoolGetStatistics(Ring->BufferPool,
+                      &Allocated,
+                      &MaximumAllocated,
+                      &Count,
+                      &MinimumCount);
+
+    DEBUG(Printf,
+          Transmitter->DebugInterface,
+          Transmitter->DebugCallback,
+          "BUFFER POOL: Allocated = %u (Maximum = %u)\n",
+          Allocated,
+          MaximumAllocated);
+
+    DEBUG(Printf,
+          Transmitter->DebugInterface,
+          Transmitter->DebugCallback,
+          "BUFFER POOL: Count = %u (Minimum = %u)\n",
+          Count,
+          MinimumCount);
+
+    // Dump front ring
+    DEBUG(Printf,
+          Transmitter->DebugInterface,
+          Transmitter->DebugCallback,
+          "FRONT: req_prod_pvt = %u rsp_cons = %u nr_ents = %u sring = %p\n",
+          Ring->Front.req_prod_pvt,
+          Ring->Front.rsp_cons,
+          Ring->Front.nr_ents,
+          Ring->Front.sring);
+
+    // Dump shared ring
+    DEBUG(Printf,
+          Transmitter->DebugInterface,
+          Transmitter->DebugCallback,
+          "SHARED: req_prod = %u req_event = %u rsp_prod = %u rsp_event = %u\n",
+          Ring->Shared->req_prod,
+          Ring->Shared->req_event,
+          Ring->Shared->rsp_prod,
+          Ring->Shared->rsp_event);
+
+    DEBUG(Printf,
+          Transmitter->DebugInterface,
+          Transmitter->DebugCallback,
+          "RequestsPosted = %u RequestsPushed = %u ResponsesProcessed = %u\n",
+          Ring->RequestsPosted,
+          Ring->RequestsPushed,
+          Ring->ResponsesProcessed);
+
+    DEBUG(Printf,
+          Transmitter->DebugInterface,
+          Transmitter->DebugCallback,
+          "State:\n");
+
+    DEBUG(Printf,
+          Transmitter->DebugInterface,
+          Transmitter->DebugCallback,
+          "- Packet = %p\n",
+          Ring->State.Packet);
+
+    DEBUG(Printf,
+          Transmitter->DebugInterface,
+          Transmitter->DebugCallback,
+          "- Count = %u\n",
+          Ring->State.Count);
+
+    DEBUG(Printf,
+          Transmitter->DebugInterface,
+          Transmitter->DebugCallback,
+          "PacketsGranted = %u PacketsCopied = %u PacketsFaked = %u\n",
+          Ring->PacketsGranted,
+          Ring->PacketsCopied,
+          Ring->PacketsFaked);
+
+    DEBUG(Printf,
+          Transmitter->DebugInterface,
+          Transmitter->DebugCallback,
+          "PacketsQueued = %u PacketsPrepared = %u PacketsUnprepared = %u PacketsSent = %u PacketsCompleted = %u\n",
+          Ring->PacketsQueued,
+          Ring->PacketsPrepared,
+          Ring->PacketsUnprepared,
+          Ring->PacketsSent,
+          Ring->PacketsCompleted);
+
+    DEBUG(Printf,
+          Transmitter->DebugInterface,
+          Transmitter->DebugCallback,
+          "PacketStatistics:\n");
+
+    DEBUG(Printf,
+          Transmitter->DebugInterface,
+          Transmitter->DebugCallback,
+          "- Drop = %u\n",
+          Ring->PacketStatistics.Drop);
+
+    DEBUG(Printf,
+          Transmitter->DebugInterface,
+          Transmitter->DebugCallback,
+          "- BackendError = %u\n",
+          Ring->PacketStatistics.BackendError);
+
+    DEBUG(Printf,
+          Transmitter->DebugInterface,
+          Transmitter->DebugCallback,
+          "- FrontendError = %u\n",
+          Ring->PacketStatistics.FrontendError);
+
+    DEBUG(Printf,
+          Transmitter->DebugInterface,
+          Transmitter->DebugCallback,
+          "- Unicast = %u\n",
+          Ring->PacketStatistics.Unicast);
+
+    DEBUG(Printf,
+          Transmitter->DebugInterface,
+          Transmitter->DebugCallback,
+          "- UnicastBytes = %u\n",
+          Ring->PacketStatistics.UnicastBytes);
+
+    DEBUG(Printf,
+          Transmitter->DebugInterface,
+          Transmitter->DebugCallback,
+          "- Multicast = %u\n",
+          Ring->PacketStatistics.Multicast);
+
+    DEBUG(Printf,
+          Transmitter->DebugInterface,
+          Transmitter->DebugCallback,
+          "- MulticastBytes = %u\n",
+          Ring->PacketStatistics.MulticastBytes);
+
+    DEBUG(Printf,
+          Transmitter->DebugInterface,
+          Transmitter->DebugCallback,
+          "- Broadcast = %u\n",
+          Ring->PacketStatistics.Broadcast);
+
+    DEBUG(Printf,
+          Transmitter->DebugInterface,
+          Transmitter->DebugCallback,
+          "- BroadcastBytes = %u\n",
+          Ring->PacketStatistics.BroadcastBytes);
+
+    DEBUG(Printf,
+          Transmitter->DebugInterface,
+          Transmitter->DebugCallback,
+          "HeaderStatistics:\n");
+
+    DEBUG(Printf,
+          Transmitter->DebugInterface,
+          Transmitter->DebugCallback,
+          "- Tagged = %u\n",
+          Ring->HeaderStatistics.Tagged);
+
+    DEBUG(Printf,
+          Transmitter->DebugInterface,
+          Transmitter->DebugCallback,
+          "- LLC = %u\n",
+          Ring->HeaderStatistics.LLC);
+
+    DEBUG(Printf,
+          Transmitter->DebugInterface,
+          Transmitter->DebugCallback,
+          "- Ip Version4 = %u\n",
+          Ring->HeaderStatistics.IpVersion4);
+
+    DEBUG(Printf,
+          Transmitter->DebugInterface,
+          Transmitter->DebugCallback,
+          "- Ip Version6 = %u\n",
+          Ring->HeaderStatistics.IpVersion6);
+
+    DEBUG(Printf,
+          Transmitter->DebugInterface,
+          Transmitter->DebugCallback,
+          "- Ip Options = %u\n",
+          Ring->HeaderStatistics.IpOptions);
+
+    DEBUG(Printf,
+          Transmitter->DebugInterface,
+          Transmitter->DebugCallback,
+          "- Tcp = %u\n",
+          Ring->HeaderStatistics.Tcp);
+
+    DEBUG(Printf,
+          Transmitter->DebugInterface,
+          Transmitter->DebugCallback,
+          "- Tcp Options = %u\n",
+          Ring->HeaderStatistics.TcpOptions);
+
+    DEBUG(Printf,
+          Transmitter->DebugInterface,
+          Transmitter->DebugCallback,
+          "- Udp = %u\n",
+          Ring->HeaderStatistics.Udp);
+
+    DEBUG(Printf,
+          Transmitter->DebugInterface,
+          Transmitter->DebugCallback,
+          "OffloadStatistics:\n");
+
+    DEBUG(Printf,
+          Transmitter->DebugInterface,
+          Transmitter->DebugCallback,
+          "- TagManipulation = %u\n",
+          Ring->OffloadStatistics.TagManipulation);
+
+    DEBUG(Printf,
+          Transmitter->DebugInterface,
+          Transmitter->DebugCallback,
+          "- IpVersion4LargePacket = %u\n",
+          Ring->OffloadStatistics.IpVersion4LargePacket);
+
+    DEBUG(Printf,
+          Transmitter->DebugInterface,
+          Transmitter->DebugCallback,
+          "- IpVersion6LargePacket = %u\n",
+          Ring->OffloadStatistics.IpVersion6LargePacket);
+
+    DEBUG(Printf,
+          Transmitter->DebugInterface,
+          Transmitter->DebugCallback,
+          "- IpVersion4HeaderChecksum = %u\n",
+          Ring->OffloadStatistics.IpVersion4HeaderChecksum);
+
+    DEBUG(Printf,
+          Transmitter->DebugInterface,
+          Transmitter->DebugCallback,
+          "- IpVersion4TcpChecksum = %u\n",
+          Ring->OffloadStatistics.IpVersion4TcpChecksum);
+
+    DEBUG(Printf,
+          Transmitter->DebugInterface,
+          Transmitter->DebugCallback,
+          "- IpVersion6TcpChecksum = %u\n",
+          Ring->OffloadStatistics.IpVersion6TcpChecksum);
+
+    DEBUG(Printf,
+          Transmitter->DebugInterface,
+          Transmitter->DebugCallback,
+          "- IpVersion4UdpChecksum = %u\n",
+          Ring->OffloadStatistics.IpVersion4UdpChecksum);
+
+    DEBUG(Printf,
+          Transmitter->DebugInterface,
+          Transmitter->DebugCallback,
+          "- IpVersion6UdpChecksum = %u\n",
+          Ring->OffloadStatistics.IpVersion6UdpChecksum);
+
+    if (Ring->AddressCount != 0) {
+        ULONG   Index;
+
+        for (Index = 0; Index < Ring->AddressCount; Index++) {
+            switch (Ring->AddressTable[Index].si_family) {
+            case AF_INET: {
+                IPV4_ADDRESS    Address;
+
+                RtlCopyMemory(Address.Byte,
+                              &Ring->AddressTable[Index].Ipv4.sin_addr.s_addr,
+                              IPV4_ADDRESS_LENGTH);
+
+                DEBUG(Printf,
+                      Transmitter->DebugInterface,
+                      Transmitter->DebugCallback,
+                      "AddressTable[%u]: %u.%u.%u.%u\n",
+                      Index,
+                      Address.Byte[0],
+                      Address.Byte[1],
+                      Address.Byte[2],
+                      Address.Byte[3]);
+                break;
+            }
+            case AF_INET6: {
+                IPV6_ADDRESS    Address;
+
+                RtlCopyMemory(Address.Byte,
+                              &Ring->AddressTable[Index].Ipv6.sin6_addr.s6_addr,
+                              IPV6_ADDRESS_LENGTH);
+
+                DEBUG(Printf,
+                      Transmitter->DebugInterface,
+                      Transmitter->DebugCallback,
+                      "AddressTable[%u]: %04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x\n",
+                      Index,
+                      NTOHS(Address.Word[0]),
+                      NTOHS(Address.Word[1]),
+                      NTOHS(Address.Word[2]),
+                      NTOHS(Address.Word[3]),
+                      NTOHS(Address.Word[4]),
+                      NTOHS(Address.Word[5]),
+                      NTOHS(Address.Word[6]),
+                      NTOHS(Address.Word[7]));
+                break;
+            }
+            }
+        }
+    }
+}
+
+static BOOLEAN
+TransmitterPullup(
+    IN      PVOID                   Argument,
+    IN      PUCHAR                  DestinationVa,
+    IN OUT  PXENVIF_PACKET_PAYLOAD  Payload,
+    IN      ULONG                   Length
+    )
+{
+    PMDL                            Mdl;
+    ULONG                           Offset;
+
+    UNREFERENCED_PARAMETER(Argument);
+
+    Mdl = Payload->Mdl;
+    Offset = Payload->Offset;
+
+    if (Payload->Length < Length)
+        goto fail1;
+
+    Payload->Length -= Length;
+
+    while (Length != 0) {
+        PUCHAR  MdlMappedSystemVa;
+        ULONG   MdlByteCount;
+        ULONG   CopyLength;
+
+        ASSERT(Mdl != NULL);
+
+        MdlMappedSystemVa = MmGetSystemAddressForMdlSafe(Mdl, NormalPagePriority);
+        ASSERT(MdlMappedSystemVa != NULL);
+
+        MdlMappedSystemVa += Offset;
+
+        MdlByteCount = Mdl->ByteCount - Offset;
+
+        CopyLength = __min(MdlByteCount, Length);
+
+        RtlCopyMemory(DestinationVa, MdlMappedSystemVa, CopyLength);
+        DestinationVa += CopyLength;
+
+        Offset += CopyLength;
+        Length -= CopyLength;
+
+        MdlByteCount -= CopyLength;
+        if (MdlByteCount == 0) {
+            Mdl = Mdl->Next;
+            Offset = 0;
+        }
+    }
+
+    Payload->Mdl = Mdl;
+    Payload->Offset = Offset;
+
+    return TRUE;
+
+fail1:
+    Error("fail1\n");
+
+    return FALSE;
+}
+
+#define INCREMENT_PACKET_REFERENCE(_Packet)                         \
+        do {                                                        \
+            PULONG_PTR Reference = (PULONG_PTR)&(_Packet)->Next;    \
+                                                                    \
+            (*Reference)++;                                         \
+        } while (FALSE)
+
+#define DECREMENT_PACKET_REFERENCE(_Packet)                         \
+        do {                                                        \
+            PULONG_PTR Reference = (PULONG_PTR)&(_Packet)->Next;    \
+                                                                    \
+            ASSERT(*Reference != 0);                                \
+            --(*Reference);                                         \
+        } while (FALSE)
+
+#define PACKET_REFERENCE(_Packet)                                   \
+        (*(PULONG_PTR)&(_Packet)->Next)
+
+static FORCEINLINE NTSTATUS
+__RingCopyPayload(
+    IN  PTRANSMITTER_RING       Ring
+    )
+{
+    PXENVIF_TRANSMITTER         Transmitter;
+    PXENVIF_FRONTEND            Frontend;
+    PTRANSMITTER_STATE          State;
+    PXENVIF_TRANSMITTER_PACKET  Packet;
+    XENVIF_PACKET_PAYLOAD       Payload;
+    PTRANSMITTER_TAG            Tag;
+    PTRANSMITTER_BUFFER         Buffer;
+    NTSTATUS                    status;
+
+    Transmitter = Ring->Transmitter;
+    Frontend = Transmitter->Frontend;
+
+    State = &Ring->State;
+    Packet = State->Packet;
+
+    Payload = State->Payload;
+
+    ASSERT3U(PACKET_REFERENCE(Packet), ==, 1);
+
+    while (Payload.Length != 0) {
+        PMDL        Mdl;
+        ULONG       Length;
+        PUCHAR      MdlMappedSystemVa;
+        PFN_NUMBER  Pfn;
+
+        Buffer = __TransmitterGetBuffer(Ring);
+
+        status = STATUS_NO_MEMORY;
+        if (Buffer == NULL)
+            goto fail1;
+
+        Buffer->Context = Packet;
+        INCREMENT_PACKET_REFERENCE(Packet);
+
+        Mdl = Buffer->Mdl;
+
+        Length = __min(Payload.Length, PAGE_SIZE);
+
+        MdlMappedSystemVa = MmGetSystemAddressForMdlSafe(Mdl, NormalPagePriority);
+        TransmitterPullup(NULL, MdlMappedSystemVa, &Payload, Length);
+
+        Mdl->ByteCount = Length;
+
+        Tag = __TransmitterGetTag(Ring);
+
+        Tag->Type = TAG_BUFFER;
+        Tag->Context = Buffer;
+        Buffer->Reference++;
+
+        Pfn = MmGetMdlPfnArray(Mdl)[0];
+
+        GNTTAB(PermitForeignAccess,
+               Transmitter->GnttabInterface,
+               Tag->Reference,
+               FrontendGetBackendDomain(Frontend),
+               GNTTAB_ENTRY_FULL_PAGE,
+               Pfn,
+               TRUE);
+
+        Tag->Offset = 0;
+        Tag->Length = Mdl->ByteCount;
+
+        ASSERT(IsZeroMemory(&Tag->ListEntry, sizeof (LIST_ENTRY)));
+        InsertTailList(&State->List, &Tag->ListEntry);
+        State->Count++;
+
+        ASSERT3U(State->Count, <=, MAX_SKB_FRAGS + 1);
+    }
+
+    Ring->PacketsCopied++;
+    return STATUS_SUCCESS;
+
+fail1:
+    Error("fail1 (%08x)\n", status);
+
+    while (PACKET_REFERENCE(Packet) != 1) {
+        PLIST_ENTRY         ListEntry;
+
+        ASSERT(State->Count != 0);
+        --State->Count;
+
+        ListEntry = RemoveTailList(&State->List);
+        ASSERT3P(ListEntry, !=, &State->List);
+
+        RtlZeroMemory(ListEntry, sizeof (LIST_ENTRY));
+
+        Tag = CONTAINING_RECORD(ListEntry, TRANSMITTER_TAG, ListEntry);
+
+        Tag->Length = 0;
+        Tag->Offset = 0;
+
+        GNTTAB(RevokeForeignAccess,
+               Transmitter->GnttabInterface,
+               Tag->Reference);
+
+        ASSERT3U(Tag->Type, ==, TAG_BUFFER);
+        Buffer = Tag->Context;
+        Tag->Context = NULL;
+        Tag->Type = TAG_TYPE_INVALID;
+
+        ASSERT(Buffer->Reference != 0);
+        --Buffer->Reference;
+
+        __TransmitterPutTag(Ring, Tag);
+
+        ASSERT3P(Buffer->Context, ==, Packet);
+        Buffer->Context = NULL;        
+
+        DECREMENT_PACKET_REFERENCE(Packet);
+
+        __TransmitterPutBuffer(Ring, Buffer);
+    }
+
+    return status;
+}
+
+static FORCEINLINE NTSTATUS
+__RingGrantPayload(
+    IN  PTRANSMITTER_RING       Ring
+    )
+{
+    PXENVIF_TRANSMITTER         Transmitter;
+    PXENVIF_FRONTEND            Frontend;
+    PTRANSMITTER_STATE          State;
+    PXENVIF_TRANSMITTER_PACKET  Packet;
+    PXENVIF_PACKET_PAYLOAD      Payload;
+    PMDL                        Mdl;
+    ULONG                       Offset;
+    ULONG                       Length;
+    NTSTATUS                    status;
+
+    Transmitter = Ring->Transmitter;
+    Frontend = Transmitter->Frontend;
+
+    State = &Ring->State;
+    Packet = State->Packet;
+    Payload = &State->Payload;
+
+    ASSERT3U(PACKET_REFERENCE(Packet), ==, 1);
+
+    Mdl = Payload->Mdl;
+    Offset = Payload->Offset;
+    Length = Payload->Length;
+
+    while (Length != 0) {
+        ULONG   MdlOffset;
+        ULONG   MdlByteCount;
+        ULONG   MdlLength;
+
+        MdlOffset = Mdl->ByteOffset + Offset;
+        MdlByteCount = Mdl->ByteCount - Offset;
+
+        MdlLength = __min(MdlByteCount, Length);
+
+        while (MdlLength != 0) {
+            PTRANSMITTER_TAG    Tag;
+            PFN_NUMBER          Pfn;
+            ULONG               PageOffset;
+            ULONG               PageLength;
+
+            Tag = __TransmitterGetTag(Ring);
+
+            Tag->Type = TAG_PACKET;
+            Tag->Context = Packet;
+            INCREMENT_PACKET_REFERENCE(Packet);
+
+            Pfn = MmGetMdlPfnArray(Mdl)[MdlOffset / PAGE_SIZE];
+            PageOffset = MdlOffset & (PAGE_SIZE - 1);
+            PageLength = __min(MdlLength, PAGE_SIZE - PageOffset);
+
+            GNTTAB(PermitForeignAccess,
+                   Transmitter->GnttabInterface,
+                   Tag->Reference,
+                   FrontendGetBackendDomain(Frontend),
+                   GNTTAB_ENTRY_FULL_PAGE,
+                   Pfn,
+                   TRUE);
+
+            Tag->Offset = PageOffset;
+            Tag->Length = PageLength;
+
+            ASSERT(IsZeroMemory(&Tag->ListEntry, sizeof (LIST_ENTRY)));
+            InsertTailList(&State->List, &Tag->ListEntry);
+            State->Count++;
+
+            // Bounce the packet if it is too highly fragmented
+            status = STATUS_BUFFER_OVERFLOW;
+            if (State->Count > MAX_SKB_FRAGS + 1)
+                goto fail1;
+
+            MdlOffset += PageLength;
+
+            ASSERT3U(MdlLength, >=, PageLength);
+            MdlLength -= PageLength;
+
+            ASSERT3U(Length, >=, PageLength);
+            Length -= PageLength;
+        }
+
+        Mdl = Mdl->Next;
+        Offset = 0;
+    }
+
+    Ring->PacketsGranted++;
+    return STATUS_SUCCESS;
+
+fail1:
+    while (PACKET_REFERENCE(Packet) != 1) {
+        PLIST_ENTRY         ListEntry;
+        PTRANSMITTER_TAG    Tag;
+
+        ASSERT(State->Count != 0);
+        --State->Count;
+
+        ListEntry = RemoveTailList(&State->List);
+        ASSERT3P(ListEntry, !=, &State->List);
+
+        RtlZeroMemory(ListEntry, sizeof (LIST_ENTRY));
+
+        Tag = CONTAINING_RECORD(ListEntry, TRANSMITTER_TAG, ListEntry);
+
+        Tag->Length = 0;
+        Tag->Offset = 0;
+
+        GNTTAB(RevokeForeignAccess,
+               Transmitter->GnttabInterface,
+               Tag->Reference);
+
+        Tag->Context = NULL;
+        Tag->Type = TAG_TYPE_INVALID;
+
+        DECREMENT_PACKET_REFERENCE(Packet);
+
+        __TransmitterPutTag(Ring, Tag);
+    }
+
+    return status;
+}
+
+static FORCEINLINE NTSTATUS
+__RingPrepareHeader(
+    IN  PTRANSMITTER_RING       Ring
+    )
+{
+    PXENVIF_TRANSMITTER         Transmitter;
+    PXENVIF_FRONTEND            Frontend;
+    PXENVIF_MAC                 Mac;
+    PTRANSMITTER_STATE          State;
+    PXENVIF_TRANSMITTER_PACKET  Packet;
+    PXENVIF_PACKET_PAYLOAD      Payload;
+    PXENVIF_PACKET_INFO         Info;
+    PTRANSMITTER_TAG            Tag;
+    PTRANSMITTER_BUFFER         Buffer;
+    PMDL                        Mdl;
+    PUCHAR                      StartVa;
+    PFN_NUMBER                  Pfn;
+    PETHERNET_HEADER            EthernetHeader;
+    NTSTATUS                    status;
+
+    Transmitter = Ring->Transmitter;
+    Frontend = Transmitter->Frontend;
+    Mac = FrontendGetMac(Frontend);
+
+    State = &Ring->State;
+    Packet = State->Packet;
+    Payload = &State->Payload;
+    Info = &State->Info;
+
+    ASSERT3U(PACKET_REFERENCE(Packet), ==, 0);
+
+    Buffer = __TransmitterGetBuffer(Ring);
+
+    status = STATUS_NO_MEMORY;
+    if (Buffer == NULL)
+        goto fail1;
+
+    Buffer->Context = Packet;
+    INCREMENT_PACKET_REFERENCE(Packet);
+
+    Mdl = Buffer->Mdl;
+
+    StartVa = MmGetSystemAddressForMdlSafe(Mdl, NormalPagePriority);
+    ASSERT(StartVa != NULL);
+
+    status = ParsePacket(StartVa, TransmitterPullup, NULL, &Ring->HeaderStatistics, Payload, Info);
+    if (!NT_SUCCESS(status))
+        goto fail2;
+
+    State->StartVa = StartVa;
+
+    Mdl->ByteCount = Info->Length;
+
+    Tag = __TransmitterGetTag(Ring);
+
+    Tag->Context = Buffer;
+    Tag->Type = TAG_BUFFER;
+    Buffer->Reference++;
+
+    Pfn = MmGetMdlPfnArray(Mdl)[0];
+
+    GNTTAB(PermitForeignAccess,
+           Transmitter->GnttabInterface,
+           Tag->Reference,
+           FrontendGetBackendDomain(Frontend),
+           GNTTAB_ENTRY_FULL_PAGE,
+           Pfn,
+           TRUE);
+
+    Tag->Offset = 0;
+    Tag->Length = Mdl->ByteCount + Payload->Length;
+
+    ASSERT(IsZeroMemory(&Tag->ListEntry, sizeof (LIST_ENTRY)));
+    InsertTailList(&State->List, &Tag->ListEntry);
+    State->Count++;
+
+    ASSERT(Info->EthernetHeader.Length != 0);
+    EthernetHeader = (PETHERNET_HEADER)(StartVa + Info->EthernetHeader.Offset);        
+
+    if (State->Send.OffloadOptions.OffloadTagManipulation) {
+        ULONG   Offset;
+
+        Ring->OffloadStatistics.TagManipulation++;
+
+        ASSERT(!ETHERNET_HEADER_IS_TAGGED(EthernetHeader));
+
+        Offset = FIELD_OFFSET(ETHERNET_TAGGED_HEADER, Tag);
+
+        RtlMoveMemory((PUCHAR)EthernetHeader + Offset + sizeof (ETHERNET_TAG),
+                      (PUCHAR)EthernetHeader + Offset,
+                      Mdl->ByteCount - Offset);
+
+        // Insert the tag
+        EthernetHeader->Tagged.Tag.ProtocolID = HTONS(ETHERTYPE_TPID);
+        EthernetHeader->Tagged.Tag.ControlInformation = HTONS(State->Send.TagControlInformation);
+        ASSERT(ETHERNET_HEADER_IS_TAGGED(EthernetHeader));
+
+        Ring->HeaderStatistics.Tagged++;
+
+        Mdl->ByteCount += sizeof (ETHERNET_TAG);
+        Tag->Length += sizeof (ETHERNET_TAG);
+
+        // Fix up the packet information
+        Info->EthernetHeader.Length += sizeof (ETHERNET_TAG);
+        Info->Length += sizeof (ETHERNET_TAG);
+
+        if (Info->IpHeader.Length != 0)
+            Info->IpHeader.Offset += sizeof (ETHERNET_TAG);
+
+        if (Info->IpOptions.Length != 0)
+            Info->IpOptions.Offset += sizeof (ETHERNET_TAG);
+
+        if (Info->UdpHeader.Length != 0)
+            Info->UdpHeader.Offset += sizeof (ETHERNET_TAG);
+
+        if (Info->TcpHeader.Length != 0)
+            Info->TcpHeader.Offset += sizeof (ETHERNET_TAG);
+
+        if (Info->TcpOptions.Length != 0)
+            Info->TcpOptions.Offset += sizeof (ETHERNET_TAG);
+    }
+
+    if (State->Send.OffloadOptions.OffloadIpVersion4LargePacket) {
+        PIP_HEADER  IpHeader;
+        PTCP_HEADER TcpHeader;
+        ULONG       Length;
+
+        Ring->OffloadStatistics.IpVersion4LargePacket++;
+
+        ASSERT(Info->IpHeader.Length != 0);
+        IpHeader = (PIP_HEADER)(StartVa + Info->IpHeader.Offset);
+
+        ASSERT(Info->TcpHeader.Length != 0);
+        TcpHeader = (PTCP_HEADER)(StartVa + Info->TcpHeader.Offset);
+
+        // Fix up the IP packet length
+        Length = Info->IpHeader.Length +
+                 Info->IpOptions.Length + 
+                 Info->TcpHeader.Length + 
+                 Info->TcpOptions.Length + 
+                 Payload->Length;
+
+        ASSERT3U((USHORT)Length, ==, Length);
+
+        ASSERT3U(IpHeader->Version, ==, 4);
+
+        IpHeader->Version4.PacketLength = HTONS((USHORT)Length);
+
+        // IP checksum calulcation must be offloaded for large packets
+        State->Send.OffloadOptions.OffloadIpVersion4HeaderChecksum = 1;
+
+        // TCP checksum calulcation must be offloaded for large packets
+        TcpHeader->Checksum = ChecksumPseudoHeader(StartVa, Info);
+        State->Send.OffloadOptions.OffloadIpVersion4TcpChecksum = 1;
+
+        // If the MSS is such that the payload would constitute only a single fragment then
+        // we no longer need trate the packet as a large packet.
+        ASSERT3U(State->Send.MaximumSegmentSize, <=, Payload->Length);
+        if (State->Send.MaximumSegmentSize == Payload->Length)
+            State->Send.OffloadOptions.OffloadIpVersion4LargePacket = 0;
+    }
+    
+    if (State->Send.OffloadOptions.OffloadIpVersion6LargePacket) {
+        PIP_HEADER  IpHeader;
+        PTCP_HEADER TcpHeader;
+        ULONG       Length;
+
+        Ring->OffloadStatistics.IpVersion6LargePacket++;
+
+        ASSERT(Info->IpHeader.Length != 0);
+        IpHeader = (PIP_HEADER)(StartVa + Info->IpHeader.Offset);
+
+        ASSERT(Info->TcpHeader.Length != 0);
+        TcpHeader = (PTCP_HEADER)(StartVa + Info->TcpHeader.Offset);
+
+        // Fix up the IP payload length
+        Length = Info->IpOptions.Length + 
+                 Info->TcpHeader.Length + 
+                 Info->TcpOptions.Length + 
+                 Payload->Length;
+
+        ASSERT3U((USHORT)Length, ==, Length);
+
+        ASSERT3U(IpHeader->Version, ==, 6);
+
+        IpHeader->Version6.PayloadLength = HTONS((USHORT)Length);
+
+        // TCP checksum calulcation must be offloaded for large packets
+        TcpHeader->Checksum = ChecksumPseudoHeader(StartVa, Info);
+        State->Send.OffloadOptions.OffloadIpVersion6TcpChecksum = 1;
+
+        // If the MSS is such that the payload would constitute only a single fragment then
+        // we no longer need trate the packet as a large packet.
+        ASSERT3U(State->Send.MaximumSegmentSize, <=, Payload->Length);
+        if (State->Send.MaximumSegmentSize == Payload->Length)
+            State->Send.OffloadOptions.OffloadIpVersion6LargePacket = 0;
+    }
+    
+    // Non-GSO packets must not exceed MTU
+    if (!State->Send.OffloadOptions.OffloadIpVersion4LargePacket &&
+        !State->Send.OffloadOptions.OffloadIpVersion6LargePacket &&
+        Tag->Length > MacGetMaximumFrameSize(Mac)) {
+        status = STATUS_INVALID_PARAMETER;
+        goto fail3;
+    }
+
+    if (State->Send.OffloadOptions.OffloadIpVersion4HeaderChecksum) {
+        PIP_HEADER  IpHeader;
+
+        ASSERT(Info->IpHeader.Length != 0);
+        IpHeader = (PIP_HEADER)(StartVa + Info->IpHeader.Offset);
+
+        ASSERT3U(IpHeader->Version, ==, 4);
+        IpHeader->Version4.Checksum = ChecksumIpVersion4Header(StartVa, Info);
+
+        Ring->OffloadStatistics.IpVersion4HeaderChecksum++;
+    }
+#ifdef  VERIFY_CHECKSUMS
+    else if (Info->IpHeader.Length != 0) {
+        PIP_HEADER  IpHeader;
+
+        ASSERT(Info->IpHeader.Length != 0);
+        IpHeader = (PIP_HEADER)(StartVa + Info->IpHeader.Offset);
+
+        if (IpHeader->Version == 4) {
+            USHORT  Embedded;
+            USHORT  Calculated;
+
+            Embedded = IpHeader->Version4.Checksum;
+            Calculated = ChecksumIpVersion4Header(StartVa, Info);
+
+            if (Embedded != Calculated)
+                Warning("Bad Ip Version 4 Header Checksum (expected %04x, found %04x)\n",
+                        Calculated,
+                        Embedded);
+        }
+    }
+#endif  // VERIFY_CHECKSUMS
+
+    if (State->Send.OffloadOptions.OffloadIpVersion4TcpChecksum) {
+        Ring->OffloadStatistics.IpVersion4TcpChecksum++;
+    }
+#ifdef  VERIFY_CHECKSUMS
+    else if (Info->TcpHeader.Length != 0) {
+        PIP_HEADER  IpHeader;
+
+        ASSERT(Info->IpHeader.Length != 0);
+        IpHeader = (PIP_HEADER)(StartVa + Info->IpHeader.Offset);
+
+        if (IpHeader->Version == 4) {
+            PTCP_HEADER TcpHeader;
+            USHORT      Embedded;
+            USHORT      Calculated;
+
+            TcpHeader = (PTCP_HEADER)(StartVa + Info->TcpHeader.Offset);
+
+            Embedded = TcpHeader->Checksum;
+            Calculated = ChecksumPseudoHeader(StartVa, Info);
+
+            if (Embedded != Calculated) {
+                Calculated = ChecksumTcpPacket(StartVa, Info, Calculated, Payload);
+
+                if (Embedded != Calculated)
+                    Warning("Bad Ip Version 4 TCP Checksum (expected %04x, found %04x)\n",
+                            Calculated,
+                            Embedded);
+            } else {
+                Warning("Ip Version 4 TCP Checksum only covers pseudo-header\n");
+            }
+        }
+    }
+#endif  // VERIFY_CHECKSUMS
+
+    if (State->Send.OffloadOptions.OffloadIpVersion6TcpChecksum) {
+        Ring->OffloadStatistics.IpVersion6TcpChecksum++;
+    }
+#ifdef  VERIFY_CHECKSUMS
+    else if (Info->TcpHeader.Length != 0) {
+        PIP_HEADER  IpHeader;
+
+        ASSERT(Info->IpHeader.Length != 0);
+        IpHeader = (PIP_HEADER)(StartVa + Info->IpHeader.Offset);
+
+        if (IpHeader->Version == 6) {
+            PTCP_HEADER TcpHeader;
+            USHORT      Embedded;
+            USHORT      Calculated;
+
+            TcpHeader = (PTCP_HEADER)(StartVa + Info->TcpHeader.Offset);
+
+            Embedded = TcpHeader->Checksum;
+            Calculated = ChecksumPseudoHeader(StartVa, Info);
+
+            if (Embedded != Calculated) {
+                Calculated = ChecksumTcpPacket(StartVa, Info, Calculated, Payload);
+
+                if (Embedded != Calculated)
+                    Warning("Bad Ip Version 6 TCP Checksum (expected %04x, found %04x)\n",
+                            Calculated,
+                            Embedded);
+            } else {
+                Warning("Ip Version 6 TCP Checksum only covers pseudo-header\n");
+            }
+        }
+    }
+#endif  // VERIFY_CHECKSUMS
+
+    if (State->Send.OffloadOptions.OffloadIpVersion4UdpChecksum) {
+        Ring->OffloadStatistics.IpVersion4UdpChecksum++;
+    }
+#ifdef  VERIFY_CHECKSUMS
+    else if (Info->UdpHeader.Length != 0) {
+        PIP_HEADER  IpHeader;
+
+        ASSERT(Info->IpHeader.Length != 0);
+        IpHeader = (PIP_HEADER)(StartVa + Info->IpHeader.Offset);
+
+        if (IpHeader->Version == 4) {
+            PUDP_HEADER UdpHeader;
+            USHORT      Embedded;
+            USHORT      Calculated;
+
+            UdpHeader = (PUDP_HEADER)(StartVa + Info->UdpHeader.Offset);
+
+            Embedded = UdpHeader->Checksum;
+            Calculated = ChecksumPseudoHeader(StartVa, Info);
+
+            if (Embedded != Calculated) {
+                Calculated = ChecksumUdpPacket(StartVa, Info, Calculated, Payload);
+
+                if (Embedded != Calculated)
+                    Warning("Bad Ip Version 4 UDP Checksum (expected %04x, found %04x)\n",
+                            Calculated,
+                            Embedded);
+            } else {
+                Warning("Ip Version 4 UDP Checksum only covers pseudo-header\n");
+            }
+        }
+    }
+#endif  // VERIFY_CHECKSUMS
+
+    if (State->Send.OffloadOptions.OffloadIpVersion6UdpChecksum) {
+        Ring->OffloadStatistics.IpVersion6UdpChecksum++;
+    }
+#ifdef  VERIFY_CHECKSUMS
+    else if (Info->UdpHeader.Length != 0) {
+        PIP_HEADER  IpHeader;
+
+        ASSERT(Info->IpHeader.Length != 0);
+        IpHeader = (PIP_HEADER)(StartVa + Info->IpHeader.Offset);
+
+        if (IpHeader->Version == 6) {
+            PUDP_HEADER UdpHeader;
+            USHORT      Embedded;
+            USHORT      Calculated;
+
+            UdpHeader = (PUDP_HEADER)(StartVa + Info->UdpHeader.Offset);
+
+            Embedded = UdpHeader->Checksum;
+            Calculated = ChecksumPseudoHeader(StartVa, Info);
+
+            if (Embedded != Calculated) {
+                Calculated = ChecksumUdpPacket(StartVa, Info, Calculated, Payload);
+
+                if (Embedded != Calculated)
+                    Warning("Bad Ip Version 6 UDP Checksum (expected %04x, found %04x)\n",
+                            Calculated,
+                            Embedded);
+            } else {
+                Warning("Ip Version 6 UDP Checksum only covers pseudo-header\n");
+            }
+        }
+    }
+#endif  // VERIFY_CHECKSUMS
+
+    return STATUS_SUCCESS;
+
+fail3:
+    ASSERT(State->Count != 0);
+    --State->Count;
+
+    RemoveEntryList(&Tag->ListEntry);
+    RtlZeroMemory(&Tag->ListEntry, sizeof (LIST_ENTRY));
+
+    Tag->Length = 0;
+    Tag->Offset = 0;
+
+    GNTTAB(RevokeForeignAccess,
+           Transmitter->GnttabInterface,
+           Tag->Reference);
+
+    Tag->Context = NULL;
+    Tag->Type = TAG_TYPE_INVALID;
+
+    ASSERT(Buffer->Reference != 0);
+    --Buffer->Reference;
+
+    __TransmitterPutTag(Ring, Tag);
+
+    Mdl->ByteCount = 0;
+
+fail2:
+    DECREMENT_PACKET_REFERENCE(Packet);
+    Buffer->Context = NULL;
+
+    __TransmitterPutBuffer(Ring, Buffer);
+
+fail1:
+    ASSERT3U(PACKET_REFERENCE(Packet), ==, 0);
+
+    return status;
+}
+
+static FORCEINLINE VOID
+__RingUnprepareTags(
+    IN  PTRANSMITTER_RING   Ring
+    )
+{
+    PXENVIF_TRANSMITTER     Transmitter;
+    PXENVIF_FRONTEND        Frontend;
+    PTRANSMITTER_STATE      State;
+
+    Transmitter = Ring->Transmitter;
+    Frontend = Transmitter->Frontend;
+
+    State = &Ring->State;
+
+    while (State->Count != 0) {
+        PLIST_ENTRY                 ListEntry;
+        PTRANSMITTER_TAG            Tag;
+        PXENVIF_TRANSMITTER_PACKET  Packet;
+
+        --State->Count;
+
+        ListEntry = RemoveTailList(&State->List);
+        ASSERT3P(ListEntry, !=, &State->List);
+
+        RtlZeroMemory(ListEntry, sizeof (LIST_ENTRY));
+
+        Tag = CONTAINING_RECORD(ListEntry, TRANSMITTER_TAG, ListEntry);
+
+        Tag->Length = 0;
+        Tag->Offset = 0;
+
+        GNTTAB(RevokeForeignAccess,
+               Transmitter->GnttabInterface,
+               Tag->Reference);
+
+        switch (Tag->Type) {
+        case TAG_BUFFER: {
+            PTRANSMITTER_BUFFER Buffer;
+
+            Buffer = Tag->Context;
+            Tag->Context = NULL;
+            Tag->Type = TAG_TYPE_INVALID;
+
+            Packet = Buffer->Context;
+            Buffer->Context = NULL;
+
+            ASSERT(Buffer->Reference != 0);
+            if (--Buffer->Reference == 0)
+                __TransmitterPutBuffer(Ring, Buffer);
+
+            break;
+        }
+        case TAG_PACKET:
+            Packet = Tag->Context;
+            Tag->Context = NULL;
+            Tag->Type = TAG_TYPE_INVALID;
+
+            break;
+
+        default:
+            Packet = NULL;
+            ASSERT(FALSE);
+        }
+
+        __TransmitterPutTag(Ring, Tag);
+
+        if (Packet != NULL)
+            DECREMENT_PACKET_REFERENCE(Packet);
+    }
+}
+
+static FORCEINLINE NTSTATUS
+__RingPreparePacket(
+    IN  PTRANSMITTER_RING           Ring,
+    IN  PXENVIF_TRANSMITTER_PACKET  Packet
+    )
+{
+#define METADATA_EXISTS(_Ring, _Packet, _Type)                                          \
+        ((_Ring)->Transmitter->Metadata. _Type ## Offset != 0)
+
+#define METADATA(_Ring, _Packet, _Type)                                                 \
+        ((METADATA_EXISTS(_Ring, _Packet, _Type)) ?                                     \
+         (PVOID)((PUCHAR)(_Packet) + (_Ring)->Transmitter->Metadata. _Type ## Offset) : \
+         NULL)
+
+    PTRANSMITTER_STATE              State;
+    PXENVIF_PACKET_PAYLOAD          Payload;
+    PXENVIF_PACKET_INFO             Info;
+    NTSTATUS                        status;
+
+    ASSERT(IsZeroMemory(&Ring->State, sizeof (TRANSMITTER_STATE)));
+    ASSERT3P(Packet->Next, ==, NULL);
+
+    State = &Ring->State;
+
+    State->Packet = Packet;
+
+    State->Send = Packet->Send;
+    RtlZeroMemory(&Packet->Send, sizeof (XENVIF_SEND_INFO));
+
+    Payload = &State->Payload;
+
+    ASSERT(METADATA_EXISTS(Ring, Packet, Mdl));
+    Payload->Mdl = *(PMDL *)METADATA(Ring, Packet, Mdl);
+
+    if (METADATA_EXISTS(Ring, Packet, Offset))
+        Payload->Offset = *(PULONG)METADATA(Ring, Packet, Offset);
+    else
+        Payload->Offset = 0;
+
+    ASSERT(METADATA_EXISTS(Ring, Packet, Length));
+    Payload->Length = *(PULONG)METADATA(Ring, Packet, Length);
+
+    InitializeListHead(&State->List);
+    ASSERT3U(State->Count, ==, 0);
+
+    status = __RingPrepareHeader(Ring);
+    if (!NT_SUCCESS(status))
+        goto fail1;
+
+    ASSERT3U(State->Count, ==, PACKET_REFERENCE(Packet));
+
+    Info = &State->Info;
+
+    // Is the packet too short?
+    if (Info->Length + Payload->Length < ETHERNET_MIN) {
+        ULONG   Trailer;
+        BOOLEAN SingleFragment;
+
+        Trailer = ETHERNET_MIN - Payload->Length - Info->Length;
+        SingleFragment = (Payload->Length == 0) ? TRUE : FALSE;
+
+        status = __RingCopyPayload(Ring);
+
+        if (NT_SUCCESS(status)) {
+            PLIST_ENTRY         ListEntry;
+            PTRANSMITTER_TAG    Tag;
+            PTRANSMITTER_BUFFER Buffer;
+            PMDL                Mdl;
+            PUCHAR              MdlMappedSystemVa;
+
+            // Add padding to the tail buffer
+            ListEntry = State->List.Blink;
+            Tag = CONTAINING_RECORD(ListEntry, TRANSMITTER_TAG, ListEntry);
+
+            ASSERT3U(Tag->Type, ==, TAG_BUFFER);
+            Buffer = Tag->Context;
+
+            Mdl = Buffer->Mdl;
+
+            ASSERT3U(Mdl->ByteCount, <=, PAGE_SIZE - Trailer);
+
+            MdlMappedSystemVa = MmGetSystemAddressForMdlSafe(Mdl, NormalPagePriority);
+            ASSERT(MdlMappedSystemVa != NULL);
+
+            MdlMappedSystemVa += Mdl->ByteCount;
+
+            RtlZeroMemory(MdlMappedSystemVa, Trailer);
+            Mdl->ByteCount += Trailer;
+
+            if (!SingleFragment) {
+                ASSERT3P(State->List.Flink, !=, ListEntry);
+                Tag->Length += Trailer;
+            }
+
+            // Adjust length of header tag
+            ListEntry = State->List.Flink;
+            Tag = CONTAINING_RECORD(ListEntry, TRANSMITTER_TAG, ListEntry);
+
+            Tag->Length += Trailer;
+            ASSERT3U(Tag->Length, ==, ETHERNET_MIN);
+        }
+    } else {
+        status = __RingGrantPayload(Ring);
+        if (!NT_SUCCESS(status) && status == STATUS_BUFFER_OVERFLOW) {
+            ASSERT3U(State->Count, ==, PACKET_REFERENCE(Packet));
+
+            status = __RingCopyPayload(Ring);
+        }
+    }
+
+    if (!NT_SUCCESS(status))
+        goto fail2;
+
+    ASSERT3U(State->Count, ==, PACKET_REFERENCE(Packet));
+
+    Ring->PacketsPrepared++;
+    return STATUS_SUCCESS;
+
+fail2:
+    __RingUnprepareTags(Ring);
+
+fail1:
+    State->StartVa = NULL;
+    RtlZeroMemory(&State->Info, sizeof (XENVIF_PACKET_INFO));
+
+    ASSERT(IsListEmpty(&State->List));
+    RtlZeroMemory(&State->List, sizeof (LIST_ENTRY));
+
+    RtlZeroMemory(&State->Payload, sizeof (XENVIF_PACKET_PAYLOAD));
+
+    Packet->Send = State->Send;
+    RtlZeroMemory(&State->Send, sizeof (XENVIF_SEND_INFO));
+
+    State->Packet = NULL;
+
+    ASSERT(IsZeroMemory(&Ring->State, sizeof (TRANSMITTER_STATE)));
+
+    return status;
+
+#undef  METADATA
+#undef  METADATA_EXISTS
+}
+
+static FORCEINLINE PXENVIF_TRANSMITTER_PACKET
+__RingUnpreparePacket(
+    IN  PTRANSMITTER_RING       Ring
+    )
+{
+    PTRANSMITTER_STATE          State;
+    PXENVIF_TRANSMITTER_PACKET  Packet;
+
+    State = &Ring->State;
+    Packet = State->Packet;
+
+    // This has the side effect of freeing up resources associated with a pending
+    // gratuitous ARP, which is why the call is not conditional on Packet being
+    // non-NULL
+    __RingUnprepareTags(Ring);
+    RtlZeroMemory(&State->Info, sizeof (XENVIF_PACKET_INFO));
+
+    if (Packet == NULL)
+        goto done;
+
+    Ring->PacketsUnprepared++;
+
+    ASSERT(IsListEmpty(&State->List));
+    RtlZeroMemory(&State->List, sizeof (LIST_ENTRY));
+
+    RtlZeroMemory(&State->Payload, sizeof (XENVIF_PACKET_PAYLOAD));
+
+    Packet->Send = State->Send;
+    RtlZeroMemory(&State->Send, sizeof (XENVIF_SEND_INFO));
+
+    State->Packet = NULL;
+
+    ASSERT(IsZeroMemory(&Ring->State, sizeof (TRANSMITTER_STATE)));
+
+done:
+    return Packet;
+}
+
+static FORCEINLINE NTSTATUS
+__RingPrepareGratuitousArp(
+    IN  PTRANSMITTER_RING       Ring,
+    IN  PIPV4_ADDRESS           Address
+    )
+{
+    PXENVIF_TRANSMITTER         Transmitter;
+    PXENVIF_FRONTEND            Frontend;
+    PXENVIF_MAC                 Mac;
+    PTRANSMITTER_STATE          State;
+    PTRANSMITTER_TAG            Tag;
+    PTRANSMITTER_BUFFER         Buffer;
+    PMDL                        Mdl;
+    PUCHAR                      MdlMappedSystemVa;
+    PETHERNET_UNTAGGED_HEADER   EthernetHeader;
+    PARP_HEADER                 ArpHeader;
+    PETHERNET_ADDRESS           SenderHardwareAddress;
+    PIPV4_ADDRESS               SenderProtocolAddress;
+    PETHERNET_ADDRESS           TargetHardwareAddress;
+    PIPV4_ADDRESS               TargetProtocolAddress;
+    PFN_NUMBER                  Pfn;
+    NTSTATUS                    status;
+
+    ASSERT(IsZeroMemory(&Ring->State, sizeof (TRANSMITTER_STATE)));
+
+    Info("%u.%u.%u.%u\n",
+         Address->Byte[0],
+         Address->Byte[1],
+         Address->Byte[2],
+         Address->Byte[3]);
+
+    Transmitter = Ring->Transmitter;
+    Frontend = Transmitter->Frontend;
+    Mac = FrontendGetMac(Frontend);
+
+    TargetProtocolAddress = SenderProtocolAddress = Address;
+    SenderHardwareAddress = MacGetCurrentAddress(Mac);
+    TargetHardwareAddress = MacGetBroadcastAddress(Mac);
+
+    State = &Ring->State;
+
+    Buffer = __TransmitterGetBuffer(Ring);
+
+    status = STATUS_NO_MEMORY;
+    if (Buffer == NULL)
+        goto fail1;
+
+    Mdl = Buffer->Mdl;
+
+    MdlMappedSystemVa = MmGetSystemAddressForMdlSafe(Mdl, NormalPagePriority);
+    ASSERT(MdlMappedSystemVa != NULL);
+
+    EthernetHeader = (PETHERNET_UNTAGGED_HEADER)MdlMappedSystemVa;
+
+    RtlCopyMemory(EthernetHeader->DestinationAddress.Byte, MacGetBroadcastAddress(Mac), ETHERNET_ADDRESS_LENGTH);
+    RtlCopyMemory(EthernetHeader->SourceAddress.Byte, MacGetCurrentAddress(Mac), ETHERNET_ADDRESS_LENGTH);
+    EthernetHeader->TypeOrLength = HTONS(ETHERTYPE_ARP);
+
+    MdlMappedSystemVa += sizeof (ETHERNET_UNTAGGED_HEADER);
+
+    ArpHeader = (PARP_HEADER)MdlMappedSystemVa;
+
+    ArpHeader->HardwareType = HTONS(HARDWARE_ETHER);
+    ArpHeader->ProtocolType = HTONS(PROTOCOL_IPV4);
+    ArpHeader->HardwareAddressLength = ETHERNET_ADDRESS_LENGTH;
+    ArpHeader->ProtocolAddressLength = IPV4_ADDRESS_LENGTH;
+    ArpHeader->Operation = HTONS(ARP_REQUEST);
+
+    MdlMappedSystemVa += sizeof (ARP_HEADER);
+
+    RtlCopyMemory(MdlMappedSystemVa, SenderHardwareAddress->Byte, ETHERNET_ADDRESS_LENGTH);
+    MdlMappedSystemVa += ETHERNET_ADDRESS_LENGTH;
+
+    RtlCopyMemory(MdlMappedSystemVa, SenderProtocolAddress->Byte, IPV4_ADDRESS_LENGTH);
+    MdlMappedSystemVa += IPV4_ADDRESS_LENGTH;
+
+    RtlCopyMemory(MdlMappedSystemVa, TargetHardwareAddress->Byte, ETHERNET_ADDRESS_LENGTH);
+    MdlMappedSystemVa += ETHERNET_ADDRESS_LENGTH;
+
+    RtlCopyMemory(MdlMappedSystemVa, TargetProtocolAddress->Byte, IPV4_ADDRESS_LENGTH);
+    MdlMappedSystemVa += IPV4_ADDRESS_LENGTH;
+
+    Mdl->ByteCount = (ULONG)(MdlMappedSystemVa - (PUCHAR)MmGetSystemAddressForMdlSafe(Mdl, NormalPagePriority));
+
+    Tag = __TransmitterGetTag(Ring);
+
+    Tag->Context = Buffer;
+    Tag->Type = TAG_BUFFER;
+    Buffer->Reference++;
+
+    Pfn = MmGetMdlPfnArray(Mdl)[0];
+
+    GNTTAB(PermitForeignAccess,
+           Transmitter->GnttabInterface,
+           Tag->Reference,
+           FrontendGetBackendDomain(Frontend),
+           GNTTAB_ENTRY_FULL_PAGE,
+           Pfn,
+           TRUE);
+
+    Tag->Offset = 0;
+    Tag->Length = Mdl->ByteCount;
+
+    InitializeListHead(&State->List);
+
+    ASSERT(IsZeroMemory(&Tag->ListEntry, sizeof (LIST_ENTRY)));
+    InsertTailList(&State->List, &Tag->ListEntry);
+    State->Count++;
+
+    return STATUS_SUCCESS;
+
+fail1:
+    Error("fail1 (%08x)\n", status);
+
+    ASSERT(IsZeroMemory(&Ring->State, sizeof (TRANSMITTER_STATE)));
+
+    return status;
+}
+
+static FORCEINLINE NTSTATUS
+__RingPrepareNeighbourAdvertisement(
+    IN  PTRANSMITTER_RING       Ring,
+    IN  PIPV6_ADDRESS           Address
+    )
+{
+    PXENVIF_TRANSMITTER         Transmitter;
+    PXENVIF_FRONTEND            Frontend;
+    PXENVIF_MAC                 Mac;
+    PTRANSMITTER_STATE          State;
+    PTRANSMITTER_TAG            Tag;
+    PTRANSMITTER_BUFFER         Buffer;
+    PMDL                        Mdl;
+    PUCHAR                      MdlMappedSystemVa;
+    PETHERNET_UNTAGGED_HEADER   EthernetHeader;
+    PIPV6_HEADER                IpHeader;
+    PICMPV6_HEADER              IcmpHeader;
+    PIPV6_ADDRESS               TargetProtocolAddress;
+    PETHERNET_ADDRESS           SenderHardwareAddress;
+    USHORT                      PayloadLength;
+    ULONG                       Accumulator;
+    PFN_NUMBER                  Pfn;
+    NTSTATUS                    status;
+
+    ASSERT(IsZeroMemory(&Ring->State, sizeof (TRANSMITTER_STATE)));
+
+    Info("%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x\n",
+         HTONS(Address->Word[0]),
+         HTONS(Address->Word[1]),
+         HTONS(Address->Word[2]),
+         HTONS(Address->Word[3]),
+         HTONS(Address->Word[4]),
+         HTONS(Address->Word[5]),
+         HTONS(Address->Word[6]),
+         HTONS(Address->Word[7]));
+
+    Transmitter = Ring->Transmitter;
+    Frontend = Transmitter->Frontend;
+    Mac = FrontendGetMac(Frontend);
+
+    TargetProtocolAddress = Address;
+    SenderHardwareAddress = MacGetCurrentAddress(Mac);
+
+    State = &Ring->State;
+
+    Buffer = __TransmitterGetBuffer(Ring);
+
+    status = STATUS_NO_MEMORY;
+    if (Buffer == NULL)
+        goto fail1;
+
+    Mdl = Buffer->Mdl;
+
+    MdlMappedSystemVa = MmGetSystemAddressForMdlSafe(Buffer->Mdl, NormalPagePriority);
+    ASSERT(MdlMappedSystemVa != NULL);
+
+    EthernetHeader = (PETHERNET_UNTAGGED_HEADER)MdlMappedSystemVa;
+
+    RtlCopyMemory(EthernetHeader->DestinationAddress.Byte, MacGetBroadcastAddress(Mac), ETHERNET_ADDRESS_LENGTH);
+    RtlCopyMemory(EthernetHeader->SourceAddress.Byte, MacGetCurrentAddress(Mac), ETHERNET_ADDRESS_LENGTH);
+    EthernetHeader->TypeOrLength = HTONS(ETHERTYPE_IPV6);
+
+    MdlMappedSystemVa += sizeof (ETHERNET_UNTAGGED_HEADER);
+
+    IpHeader = (PIPV6_HEADER)MdlMappedSystemVa;
+    RtlZeroMemory(IpHeader, sizeof (IPV6_HEADER));
+
+    IpHeader->Version = 6;
+    IpHeader->NextHeader = IPPROTO_ICMPV6;
+    IpHeader->HopLimit = 255;
+
+    RtlCopyMemory(IpHeader->SourceAddress.Byte, Address, IPV6_ADDRESS_LENGTH);
+
+    // Destination is all-nodes multicast address
+    IpHeader->DestinationAddress.Byte[0] = 0xFF;
+    IpHeader->DestinationAddress.Byte[1] = 0x02;
+    IpHeader->DestinationAddress.Byte[15] = 0x02;
+
+    PayloadLength = 0;
+    MdlMappedSystemVa += sizeof (IPV6_HEADER);
+
+    IcmpHeader = (PICMPV6_HEADER)MdlMappedSystemVa;
+
+    IcmpHeader->Type = ICMPV6_TYPE_NA;
+    IcmpHeader->Code = 0;
+    IcmpHeader->Data = HTONL(0x02); // Override flag
+
+    PayloadLength += sizeof (ICMPV6_HEADER);
+    MdlMappedSystemVa += sizeof (ICMPV6_HEADER);
+
+    RtlCopyMemory(MdlMappedSystemVa, TargetProtocolAddress->Byte, IPV6_ADDRESS_LENGTH);
+
+    PayloadLength += IPV6_ADDRESS_LENGTH;
+    MdlMappedSystemVa += IPV6_ADDRESS_LENGTH;
+
+    RtlCopyMemory(MdlMappedSystemVa, SenderHardwareAddress->Byte, ETHERNET_ADDRESS_LENGTH);
+
+    PayloadLength += ETHERNET_ADDRESS_LENGTH;
+    MdlMappedSystemVa += ETHERNET_ADDRESS_LENGTH;
+
+    Mdl->ByteCount = (ULONG)(MdlMappedSystemVa - (PUCHAR)MmGetSystemAddressForMdlSafe(Mdl, NormalPagePriority));
+
+    // Fix up IP payload length and ICMPv6 checksum
+    IpHeader->PayloadLength = HTONS(PayloadLength);
+
+    Accumulator = ChecksumIpVersion6PseudoHeader(&IpHeader->SourceAddress,
+                                                 &IpHeader->DestinationAddress,
+                                                 PayloadLength,
+                                                 IPPROTO_ICMPV6);
+    AccumulateChecksum(&Accumulator, IcmpHeader, PayloadLength);
+
+    IcmpHeader->Checksum = FoldChecksum(Accumulator, TRUE);
+
+    Tag = __TransmitterGetTag(Ring);
+
+    Tag->Context = Buffer;
+    Tag->Type = TAG_BUFFER;
+    Buffer->Reference++;
+
+    Pfn = MmGetMdlPfnArray(Mdl)[0];
+
+    GNTTAB(PermitForeignAccess,
+           Transmitter->GnttabInterface,
+           Tag->Reference,
+           FrontendGetBackendDomain(Frontend),
+           GNTTAB_ENTRY_FULL_PAGE,
+           Pfn,
+           TRUE);
+
+    Tag->Offset = 0;
+    Tag->Length = Mdl->ByteCount;
+
+    InitializeListHead(&State->List);
+
+    ASSERT(IsZeroMemory(&Tag->ListEntry, sizeof (LIST_ENTRY)));
+    InsertTailList(&State->List, &Tag->ListEntry);
+    State->Count++;
+
+    return STATUS_SUCCESS;
+
+fail1:
+    Error("fail1 (%08x)\n", status);
+
+    ASSERT(IsZeroMemory(&Ring->State, sizeof (TRANSMITTER_STATE)));
+
+    return status;
+}
+
+static FORCEINLINE NTSTATUS
+__RingPostTags(
+    IN  PTRANSMITTER_RING       Ring
+    )
+{
+#define RING_SLOTS_AVAILABLE(_Front, _req_prod, _rsp_cons)   \
+        (RING_SIZE(_Front) - ((_req_prod) - (_rsp_cons)))
+
+    PTRANSMITTER_STATE          State;
+    PXENVIF_TRANSMITTER_PACKET  Packet;
+    PXENVIF_PACKET_PAYLOAD      Payload;
+    RING_IDX                    req_prod;
+    RING_IDX                    rsp_cons;
+    ULONG                       Extra;
+    ULONG                       PacketLength;
+    BOOLEAN                     FirstRequest;
+    netif_tx_request_t          *req;
+    NTSTATUS                    status;
+
+    State = &Ring->State;
+    Packet = State->Packet;
+    Payload = &State->Payload;
+
+    ASSERT(!IsListEmpty(&State->List));
+    ASSERT(State->Count != 0);
+    ASSERT3U(State->Count, <=, MAX_SKB_FRAGS + 1);
+    ASSERT(IMPLY(Packet != NULL, State->Count == PACKET_REFERENCE(Packet)));
+
+    req_prod = Ring->Front.req_prod_pvt;
+    rsp_cons = Ring->Front.rsp_cons;
+
+    Extra = (State->Send.OffloadOptions.OffloadIpVersion4LargePacket ||
+             State->Send.OffloadOptions.OffloadIpVersion6LargePacket) ? 1 : 0;
+
+    ASSERT3U(State->Count + Extra, <=, RING_SIZE(&Ring->Front));
+
+    status = STATUS_ALLOTTED_SPACE_EXCEEDED;
+    if (State->Count + Extra > RING_SLOTS_AVAILABLE(&Ring->Front, req_prod, rsp_cons))
+        goto fail1;
+
+    req = NULL;
+
+    FirstRequest = TRUE;
+    PacketLength = 0;
+    while (State->Count != 0) {
+        PLIST_ENTRY         ListEntry;
+        PTRANSMITTER_TAG    Tag;
+        uint16_t            id;
+
+        --State->Count;
+
+        ListEntry = RemoveHeadList(&State->List);
+        ASSERT3P(ListEntry, !=, &State->List);
+
+        RtlZeroMemory(ListEntry, sizeof (LIST_ENTRY));
+
+        Tag = CONTAINING_RECORD(ListEntry, TRANSMITTER_TAG, ListEntry);
+
+        req = RING_GET_REQUEST(&Ring->Front, req_prod);
+        req_prod++;
+        Ring->RequestsPosted++;
+
+        id = (USHORT)(Tag - &Ring->Tag[0]);
+
+        req->id = id | REQ_ID_INTEGRITY_CHECK;
+        req->gref = Tag->Reference;
+        req->offset = (USHORT)Tag->Offset;
+        req->size = (USHORT)Tag->Length;
+        req->flags = NETTXF_more_data;
+
+        if (FirstRequest) {
+            FirstRequest = FALSE;
+
+            if (State->Send.OffloadOptions.OffloadIpVersion4TcpChecksum ||
+                State->Send.OffloadOptions.OffloadIpVersion4UdpChecksum ||
+                State->Send.OffloadOptions.OffloadIpVersion6TcpChecksum ||
+                State->Send.OffloadOptions.OffloadIpVersion6UdpChecksum)
+                req->flags |= NETTXF_csum_blank | NETTXF_data_validated;
+
+            if (State->Send.OffloadOptions.OffloadIpVersion4LargePacket ||
+                State->Send.OffloadOptions.OffloadIpVersion6LargePacket) {
+                uint8_t                 type;
+                uint16_t                size;
+                struct netif_extra_info *extra;
+
+                ASSERT(Extra != 0);
+
+                ASSERT(!(State->Send.OffloadOptions.OffloadIpVersion4LargePacket &&
+                         State->Send.OffloadOptions.OffloadIpVersion6LargePacket));
+                type = (State->Send.OffloadOptions.OffloadIpVersion4LargePacket) ?
+                       XEN_NETIF_GSO_TYPE_TCPV4 :
+                       XEN_NETIF_GSO_TYPE_TCPV6;
+
+                ASSERT(State->Send.MaximumSegmentSize != 0);
+                size = State->Send.MaximumSegmentSize;
+
+                ASSERT(req->flags & (NETTXF_csum_blank | NETTXF_data_validated));
+                req->flags |= NETTXF_extra_info;
+
+                extra = (struct netif_extra_info *)RING_GET_REQUEST(&Ring->Front, req_prod);
+                req_prod++;
+                Ring->RequestsPosted++;
+
+                extra->type = XEN_NETIF_EXTRA_TYPE_GSO;
+                extra->flags = 0;
+
+                extra->u.gso.size = size;
+                extra->u.gso.type = type;
+                extra->u.gso.pad = 0;
+                extra->u.gso.features = 0;
+            }
+
+            // The first tag length is the length of the entire packet
+            PacketLength = Tag->Length;
+        }
+
+        // Store a copy of the request in case we need to fake a response ourselves
+        ASSERT3U(id, <, MAXIMUM_TAG_COUNT);
+        ASSERT(IsZeroMemory(&Ring->Pending[id], sizeof (netif_tx_request_t)));
+        Ring->Pending[id] = *req;
+    }
+    ASSERT(!FirstRequest);
+    ASSERT(PacketLength != 0);
+
+    ASSERT(req != NULL);
+    req->flags &= ~NETTXF_more_data;
+
+    Ring->Front.req_prod_pvt = req_prod;
+
+    ASSERT3U(State->Count, ==, 0);
+    RtlZeroMemory(&State->List, sizeof (LIST_ENTRY));
+
+    // Set the initial completion information
+    if (Packet != NULL) {
+        PUCHAR              StartVa;
+        PXENVIF_PACKET_INFO Info;
+        PETHERNET_HEADER    Header;
+
+        StartVa = State->StartVa;
+        Info = &State->Info;
+
+        ASSERT(IsZeroMemory(&Packet->Completion, sizeof (XENVIF_COMPLETION_INFO)));
+
+        ASSERT(Info->EthernetHeader.Length != 0);
+        Header = (PETHERNET_HEADER)(StartVa + Info->EthernetHeader.Offset);
+
+        Packet->Completion.Type = GET_ETHERNET_ADDRESS_TYPE(&Header->Untagged.DestinationAddress);
+        Packet->Completion.Status = PACKET_PENDING;
+        Packet->Completion.PacketLength = (USHORT)PacketLength;
+        Packet->Completion.PayloadLength = (USHORT)Payload->Length;
+
+        State->StartVa = NULL;
+        RtlZeroMemory(&State->Info, sizeof (XENVIF_PACKET_INFO));
+        RtlZeroMemory(&State->Payload, sizeof (XENVIF_PACKET_PAYLOAD));
+        RtlZeroMemory(&State->Send, sizeof (XENVIF_SEND_INFO));
+        State->Packet = NULL;
+
+        Ring->PacketsSent++;
+    }
+
+    ASSERT(IsZeroMemory(&Ring->State, sizeof (TRANSMITTER_STATE)));
+
+    return STATUS_SUCCESS;
+
+fail1:
+    return status;
+
+#undef  RING_SLOTS_AVAILABLE
+}
+
+static FORCEINLINE VOID
+__RingFakeResponses(
+    IN  PTRANSMITTER_RING   Ring
+    )
+{
+    RING_IDX                rsp_prod;
+    USHORT                  id;
+    ULONG                   Count;
+
+    // This is only called when the backend went away. We need
+    // to mimic the behavior of the backend and turn requests into
+    // appropriate responses.
+
+    KeMemoryBarrier();
+
+    rsp_prod = Ring->Shared->rsp_prod;
+
+    KeMemoryBarrier();
+
+    Count = 0;
+    for (id = 0; id < MAXIMUM_TAG_COUNT; id++) {
+        netif_tx_request_t  *req;
+        netif_tx_response_t *rsp;
+
+        req = &Ring->Pending[id];
+        if (req->id == 0)
+            continue;
+
+        ASSERT3U((req->id & REQ_ID_INTEGRITY_CHECK), ==, REQ_ID_INTEGRITY_CHECK);
+        ASSERT3U((req->id & ~REQ_ID_INTEGRITY_CHECK), ==, id);
+
+        rsp = RING_GET_RESPONSE(&Ring->Front, rsp_prod);
+        rsp_prod++;
+        Count++;
+
+        rsp->id = req->id;
+        rsp->status = NETIF_RSP_DROPPED;
+
+        if (req->flags & NETTXF_extra_info) {
+            rsp = RING_GET_RESPONSE(&Ring->Front, rsp_prod);
+            rsp_prod++;
+            Count++;
+
+            rsp->status = NETIF_RSP_NULL;
+        }
+    }
+
+    KeMemoryBarrier();
+
+    Ring->Shared->rsp_prod = rsp_prod;
+
+    KeMemoryBarrier();
+
+    ASSERT3U(Ring->Shared->rsp_prod, ==, Ring->Front.req_prod_pvt);
+
+    if (Count != 0)
+        Info("Faked %lu responses\n", Count);
+}
+
+static FORCEINLINE VOID
+__RingReleaseTag(
+    IN  PTRANSMITTER_RING   Ring,
+    IN  PTRANSMITTER_TAG    Tag
+    )
+{
+    PXENVIF_TRANSMITTER     Transmitter;
+
+    Transmitter = Ring->Transmitter;
+
+    Tag->Length = 0;
+    Tag->Offset = 0;
+
+    GNTTAB(RevokeForeignAccess,
+           Transmitter->GnttabInterface,
+           Tag->Reference);
+
+    __TransmitterPutTag(Ring, Tag);
+}
+
+static FORCEINLINE VOID
+__RingCompletePacket(
+    IN  PTRANSMITTER_RING           Ring,
+    IN  PXENVIF_TRANSMITTER_PACKET  Packet
+    )
+{
+    ASSERT(Packet->Completion.Status != PACKET_PENDING);
+
+    if (Packet->Completion.Status != PACKET_OK) {
+        Ring->PacketStatistics.Drop++;
+
+        if (Packet->Completion.Status == PACKET_ERROR)
+            Ring->PacketStatistics.BackendError++;
+    } else {
+        ULONG   Length;
+
+        Length = (ULONG)Packet->Completion.PacketLength;
+
+        switch (Packet->Completion.Type) {
+        case ETHERNET_ADDRESS_UNICAST:
+            Ring->PacketStatistics.Unicast++;
+            Ring->PacketStatistics.UnicastBytes += Length;
+            break;
+            
+        case ETHERNET_ADDRESS_MULTICAST:
+            Ring->PacketStatistics.Multicast++;
+            Ring->PacketStatistics.MulticastBytes += Length;
+            break;
+
+        case ETHERNET_ADDRESS_BROADCAST:
+            Ring->PacketStatistics.Broadcast++;
+            Ring->PacketStatistics.BroadcastBytes += Length;
+            break;
+
+        default:
+            ASSERT(FALSE);
+            break;
+        }
+    }
+
+    *Ring->Completed.TailPacket = Packet;
+    ASSERT3P(Packet->Next, ==, NULL);
+    Ring->Completed.TailPacket = &Packet->Next;
+
+    Ring->PacketsCompleted++;
+}
+
+#define TRANSMITTER_BATCH(_Ring)   (RING_SIZE(&(_Ring)->Front) / 4)
+
+static DECLSPEC_NOINLINE VOID
+RingPoll(
+    IN  PTRANSMITTER_RING   Ring
+    )
+{
+    for (;;) {
+        RING_IDX    rsp_prod;
+        RING_IDX    rsp_cons;
+        ULONG       Delta;
+
+        KeMemoryBarrier();
+
+        rsp_prod = Ring->Shared->rsp_prod;
+        rsp_cons = Ring->Front.rsp_cons;
+
+        KeMemoryBarrier();
+
+        if (rsp_cons == rsp_prod)
+            break;
+
+        while (rsp_cons != rsp_prod) {
+            netif_tx_response_t         *rsp;
+            uint16_t                    id;
+            netif_tx_request_t          *req;
+            PTRANSMITTER_TAG            Tag;
+            PXENVIF_TRANSMITTER_PACKET  Packet;
+
+            rsp = RING_GET_RESPONSE(&Ring->Front, rsp_cons);
+            rsp_cons++;
+            Ring->ResponsesProcessed++;
+
+            Ring->Stopped = FALSE;
+
+            if (rsp->status == NETIF_RSP_NULL)
+                continue;
+
+            ASSERT3U((rsp->id & REQ_ID_INTEGRITY_CHECK), ==, REQ_ID_INTEGRITY_CHECK);
+            id = rsp->id & ~REQ_ID_INTEGRITY_CHECK;
+
+            ASSERT3U(id, <, MAXIMUM_TAG_COUNT);
+            req = &Ring->Pending[id];
+
+            ASSERT3U(req->id, ==, rsp->id);
+            RtlZeroMemory(req, sizeof (netif_tx_request_t));
+
+            Tag = &Ring->Tag[id];
+
+            switch (Tag->Type) {
+            case TAG_BUFFER: {
+                PTRANSMITTER_BUFFER  Buffer;
+
+                Buffer = Tag->Context;
+                Tag->Context = NULL;
+                Tag->Type = TAG_TYPE_INVALID;
+
+                Packet = Buffer->Context;
+                Buffer->Context = NULL;
+
+                ASSERT(Buffer->Reference != 0);
+                if (--Buffer->Reference == 0)
+                    __TransmitterPutBuffer(Ring, Buffer);
+
+                break;
+            }
+            case TAG_PACKET:
+                Packet = Tag->Context;
+                Tag->Context = NULL;
+                Tag->Type = TAG_TYPE_INVALID;
+
+                break;
+
+            default:
+                Packet = NULL;
+                ASSERT(FALSE);
+            }
+
+            __RingReleaseTag(Ring, Tag);
+            Tag = NULL;
+
+            if (Packet == NULL) {
+                RtlZeroMemory(rsp, sizeof (netif_tx_response_t));
+                continue;
+            }
+
+            DECREMENT_PACKET_REFERENCE(Packet);
+
+            if (rsp->status != NETIF_RSP_OKAY &&
+                Packet->Completion.Status == PACKET_PENDING) {
+                switch (rsp->status) {
+                case NETIF_RSP_DROPPED:
+                    Packet->Completion.Status = PACKET_DROPPED;
+                    break;
+
+                case NETIF_RSP_ERROR:
+                    Packet->Completion.Status = PACKET_ERROR;
+                    break;
+
+                default:
+                    ASSERT(FALSE);
+                    break;
+                }
+            }
+
+            RtlZeroMemory(rsp, sizeof (netif_tx_response_t));
+
+            if (PACKET_REFERENCE(Packet) != 0)
+                continue;
+
+            if (Packet->Completion.Status == PACKET_PENDING)
+                Packet->Completion.Status = PACKET_OK;
+
+            __RingCompletePacket(Ring, Packet);
+        }
+
+        KeMemoryBarrier();
+
+        Ring->Front.rsp_cons = rsp_cons;
+
+        Delta = Ring->Front.req_prod_pvt - rsp_cons;
+        Delta = __min(Delta, TRANSMITTER_BATCH(Ring));
+        Delta = __max(Delta, 1);
+
+        Ring->Shared->rsp_event = rsp_cons + Delta;
+    }
+}
+
+static FORCEINLINE VOID
+__RingPushRequests(
+    IN  PTRANSMITTER_RING   Ring
+    )
+{
+    BOOLEAN                 Notify;
+
+    if (Ring->RequestsPosted == Ring->RequestsPushed)
+        return;
+
+#pragma warning (push)
+#pragma warning (disable:4244)
+
+    // Make the requests visible to the backend
+    RING_PUSH_REQUESTS_AND_CHECK_NOTIFY(&Ring->Front, Notify);
+
+#pragma warning (pop)
+
+    if (Notify) {
+        PXENVIF_TRANSMITTER Transmitter;
+        PXENVIF_FRONTEND    Frontend;
+
+        Transmitter = Ring->Transmitter;
+        Frontend = Transmitter->Frontend;
+
+        NotifierSend(FrontendGetNotifier(Frontend));
+    }
+
+    Ring->RequestsPushed = Ring->RequestsPosted;
+}
+
+#define TRANSMITTER_ADVERTISEMENT_COUNT 3
+
+#define LOCK_BIT    ((ULONG_PTR)1)
+
+static FORCEINLINE ULONG
+__RingReversePacketList(
+    IN  PXENVIF_TRANSMITTER_PACKET  *Packet
+    )
+{
+    PXENVIF_TRANSMITTER_PACKET  HeadPacket;
+    ULONG                       Count;
+
+    HeadPacket = NULL;
+    Count = 0;
+
+    while (*Packet != NULL) {
+        PXENVIF_TRANSMITTER_PACKET  Next;
+
+        ASSERT(((ULONG_PTR)*Packet & LOCK_BIT) == 0);
+
+        Next = (*Packet)->Next;
+
+        (*Packet)->Next = HeadPacket;
+        HeadPacket = *Packet;
+
+        *Packet = Next;
+        Count++;
+    }
+
+    *Packet = HeadPacket;
+
+    return Count;
+}
+
+static DECLSPEC_NOINLINE VOID
+RingSwizzle(
+    IN  PTRANSMITTER_RING       Ring
+    )
+{
+    ULONG_PTR                   Old;
+    ULONG_PTR                   New;
+    PXENVIF_TRANSMITTER_PACKET  HeadPacket;
+    PXENVIF_TRANSMITTER_PACKET  *TailPacket;
+    ULONG                       Count;
+
+    ASSERT3P(Ring->LockThread, ==, KeGetCurrentThread());
+
+    New = LOCK_BIT;    
+    Old = (ULONG_PTR)InterlockedExchangePointer(&Ring->Lock, (PVOID)New);
+
+    ASSERT(Old & LOCK_BIT);
+    HeadPacket = (PVOID)(Old & ~LOCK_BIT);
+
+    if (HeadPacket == NULL)
+        return;
+
+    // Packets are held in the atomic packet list in reverse order
+    // so that the most recent is always head of the list. This is
+    // necessary to allow addition to the list to be done atomically.
+
+    TailPacket = &HeadPacket->Next;
+    Count = __RingReversePacketList(&HeadPacket);
+    ASSERT3P(*TailPacket, ==, NULL);
+
+    *(Ring->Queued.TailPacket) = HeadPacket;
+    Ring->Queued.TailPacket = TailPacket;
+    Ring->PacketsQueued += Count;
+}
+
+static DECLSPEC_NOINLINE VOID
+RingSchedule(
+    IN  PTRANSMITTER_RING   Ring
+    )
+{
+    PTRANSMITTER_STATE      State;
+
+    if(!Ring->Enabled || Ring->Stopped)
+        return;
+
+    State = &Ring->State;
+
+    for (;;) {
+        PXENVIF_TRANSMITTER_PACKET  Packet;
+        NTSTATUS                    status;
+
+        if (State->Count != 0) {
+            status = __RingPostTags(Ring);
+            if (!NT_SUCCESS(status)) {
+                Ring->Stopped = TRUE;
+                break;
+            }
+        }
+
+        if (Ring->RequestsPosted - Ring->RequestsPushed >=
+            RING_SIZE(&Ring->Front) / 4)
+            __RingPushRequests(Ring);
+
+        ASSERT3U(State->Count, ==, 0);
+
+        if (Ring->AddressIndex != 0) {
+            ULONG   Index = (--Ring->AddressIndex) % Ring->AddressCount;
+
+            switch (Ring->AddressTable[Index].si_family) {
+            case AF_INET: {
+                IPV4_ADDRESS    Address;
+
+                RtlCopyMemory(Address.Byte,
+                              &Ring->AddressTable[Index].Ipv4.sin_addr.s_addr,
+                              IPV4_ADDRESS_LENGTH);
+
+                (VOID) __RingPrepareGratuitousArp(Ring, &Address);
+
+                break;
+            }
+            case AF_INET6: {
+                IPV6_ADDRESS    Address;
+
+                RtlCopyMemory(Address.Byte,
+                              &Ring->AddressTable[Index].Ipv6.sin6_addr.s6_addr,
+                              IPV6_ADDRESS_LENGTH);
+
+                (VOID) __RingPrepareNeighbourAdvertisement(Ring, &Address);
+
+                break;
+            }
+            default:
+                ASSERT(FALSE);
+            }
+
+            continue;
+        }
+
+        Packet = Ring->Queued.HeadPacket;
+
+        if (Packet == NULL)
+            break;
+
+        if (Packet->Next == NULL) {
+            Ring->Queued.HeadPacket = NULL;
+            Ring->Queued.TailPacket = &Ring->Queued.HeadPacket;
+        } else {
+            Ring->Queued.HeadPacket = Packet->Next;
+            Packet->Next = NULL;
+        }
+
+        status = __RingPreparePacket(Ring, Packet);
+        if (!NT_SUCCESS(status)) {
+            ASSERT(status != STATUS_BUFFER_OVERFLOW);
+
+            // Fake that we prapared and sent this packet
+            Ring->PacketsPrepared++;
+            Ring->PacketsSent++;
+            Ring->PacketsFaked++;
+
+            Packet->Completion.Status = PACKET_DROPPED;
+
+            Ring->PacketStatistics.FrontendError++;
+
+            __RingCompletePacket(Ring, Packet);
+        }
+
+        ASSERT3U(Ring->PacketsPrepared, ==, Ring->PacketsCopied + Ring->PacketsGranted + Ring->PacketsFaked);
+    }
+
+    ASSERT(IMPLY(Ring->Queued.HeadPacket == NULL, Ring->Queued.TailPacket == &Ring->Queued.HeadPacket));
+
+    __RingPushRequests(Ring);
+}
+
+static FORCEINLINE BOOLEAN
+__drv_requiresIRQL(DISPATCH_LEVEL)
+__RingTryAcquireLock(
+    IN  PTRANSMITTER_RING   Ring
+    )
+{
+    ULONG_PTR               Old;
+    ULONG_PTR               New;
+    BOOLEAN                 Acquired;
+
+    ASSERT3U(KeGetCurrentIrql(), ==, DISPATCH_LEVEL);
+
+    Old = (ULONG_PTR)Ring->Lock & ~LOCK_BIT;
+    New = Old | LOCK_BIT;
+
+    Acquired = ((ULONG_PTR)InterlockedCompareExchangePointer(&Ring->Lock, (PVOID)New, (PVOID)Old) == Old) ? TRUE : FALSE;
+    if (Acquired) {
+        ASSERT3P(Ring->LockThread, ==, NULL);
+        Ring->LockThread = KeGetCurrentThread();
+    }
+
+    return Acquired;
+}
+
+static FORCEINLINE VOID
+__drv_requiresIRQL(DISPATCH_LEVEL)
+__RingAcquireLock(
+    IN  PTRANSMITTER_RING   Ring
+    )
+{
+    ASSERT3U(KeGetCurrentIrql(), ==, DISPATCH_LEVEL);
+
+    for (;;) {
+        if (__RingTryAcquireLock(Ring))
+            break;
+    }
+}
+
+static DECLSPEC_NOINLINE VOID
+RingAcquireLock(
+    IN  PTRANSMITTER_RING   Ring
+    )
+{
+    __RingAcquireLock(Ring);
+}
+
+static FORCEINLINE BOOLEAN
+__drv_requiresIRQL(DISPATCH_LEVEL)
+__RingTryReleaseLock(
+    IN  PTRANSMITTER_RING   Ring
+    )
+{
+    ULONG_PTR               Old;
+    ULONG_PTR               New;
+    BOOLEAN                 Released;
+
+    ASSERT3U(KeGetCurrentIrql(), ==, DISPATCH_LEVEL);
+    ASSERT3P(KeGetCurrentThread(), ==, Ring->LockThread);
+
+    Old = LOCK_BIT;
+    New = 0;
+
+    Ring->LockThread = NULL;
+
+    Released = ((ULONG_PTR)InterlockedCompareExchangePointer(&Ring->Lock, (PVOID)New, (PVOID)Old) == Old) ? TRUE : FALSE;
+    if (!Released)
+        Ring->LockThread = KeGetCurrentThread();
+
+    return Released;
+}
+
+static FORCEINLINE VOID
+__drv_requiresIRQL(DISPATCH_LEVEL)
+__RingReleaseLock(
+    IN  PTRANSMITTER_RING       Ring
+    )
+{
+    PXENVIF_TRANSMITTER_PACKET  HeadPacket;
+    PXENVIF_TRANSMITTER_PACKET  *TailPacket;
+
+    HeadPacket = NULL;
+    TailPacket = &HeadPacket;
+
+    ASSERT3U(KeGetCurrentIrql(), ==, DISPATCH_LEVEL);
+
+    // As lock holder it is our responsibility to drain the atomic
+    // packet list into the transmit queue before we actually drop the
+    // lock. This may, of course, take a few attempts as another
+    // thread could be simuntaneously adding to the list.
+
+    do {
+        RingSwizzle(Ring);
+        RingSchedule(Ring);
+
+        *TailPacket = Ring->Completed.HeadPacket;
+        TailPacket = Ring->Completed.TailPacket;
+
+        Ring->Completed.HeadPacket = NULL;
+        Ring->Completed.TailPacket = &Ring->Completed.HeadPacket;
+    } while (!__RingTryReleaseLock(Ring));
+
+    if (HeadPacket != NULL) {
+        PXENVIF_TRANSMITTER Transmitter;
+
+        Transmitter = Ring->Transmitter;
+
+        VifCompletePackets(Transmitter->VifInterface, HeadPacket);
+    }
+}
+
+static DECLSPEC_NOINLINE VOID
+RingReleaseLock(
+    IN  PTRANSMITTER_RING   Ring
+    )
+{
+    __RingReleaseLock(Ring);
+}
+
+#define TIME_US(_us)        ((_us) * 10)
+#define TIME_MS(_ms)        (TIME_US((_ms) * 1000))
+#define TIME_RELATIVE(_t)   (-(_t))
+
+#define RING_PERIOD         30000
+
+static NTSTATUS
+RingWatchdog(
+    IN  PXENVIF_THREAD  Self,
+    IN  PVOID           Context
+    )
+{
+    PTRANSMITTER_RING   Ring = Context;
+    LARGE_INTEGER       Timeout;
+    ULONG               PacketsQueued;
+
+    Trace("====>\n");
+
+    Timeout.QuadPart = TIME_RELATIVE(TIME_MS(RING_PERIOD));
+    PacketsQueued = 0;
+
+    for (;;) { 
+        PKEVENT Event;
+        KIRQL   Irql;
+
+        Event = ThreadGetEvent(Self);
+
+        (VOID) KeWaitForSingleObject(Event,
+                                     Executive,
+                                     KernelMode,
+                                     FALSE,
+                                     &Timeout);
+        KeClearEvent(Event);
+
+        if (ThreadIsAlerted(Self))
+            break;
+
+        KeRaiseIrql(DISPATCH_LEVEL, &Irql);
+        __RingAcquireLock(Ring);
+
+        if (Ring->Enabled &&
+            Ring->PacketsQueued == PacketsQueued &&
+            Ring->PacketsCompleted != PacketsQueued) {
+            PXENVIF_TRANSMITTER Transmitter;
+            PXENVIF_FRONTEND    Frontend;
+
+            Transmitter = Ring->Transmitter;
+            Frontend = Transmitter->Frontend;
+
+            Info("PATH: %s\n", FrontendGetPath(Frontend));
+
+            Info("FRONT: req_prod_pvt = %u rsp_cons = %u nr_ents = %u sring = %p\n",
+                 Ring->Front.req_prod_pvt,
+                 Ring->Front.rsp_cons,
+                 Ring->Front.nr_ents,
+                 Ring->Front.sring);
+
+            Info("SHARED: req_prod = %u req_event = %u rsp_prod = %u rsp_event = %u\n",
+                 Ring->Shared->req_prod,
+                 Ring->Shared->req_event,
+                 Ring->Shared->rsp_prod,
+                 Ring->Shared->rsp_event);
+
+            Info("RequestsPosted = %u RequestsPushed = %u ResponsesProcessed = %u\n",
+                 Ring->RequestsPosted,
+                 Ring->RequestsPushed,
+                 Ring->ResponsesProcessed);
+
+            // Try to move things along
+            NotifierSend(FrontendGetNotifier(Frontend));
+            NotifierTrigger(FrontendGetNotifier(Frontend));
+        }
+
+        PacketsQueued = Ring->PacketsQueued;
+
+        __RingReleaseLock(Ring);
+        KeLowerIrql(Irql);
+    }
+
+    Trace("<====\n");
+
+    return STATUS_SUCCESS;
+}
+
+static FORCEINLINE VOID
+__RingDumpAddressTable(
+    IN  PTRANSMITTER_RING   Ring
+    )
+{
+    PXENVIF_TRANSMITTER     Transmitter;
+    PXENVIF_FRONTEND        Frontend;
+    ULONG                   Index;
+    ULONG                   IpVersion4Count;
+    ULONG                   IpVersion6Count;
+
+    __RingAcquireLock(Ring);
+    if (!Ring->Connected)
+        goto done;
+
+    Transmitter = Ring->Transmitter;
+    Frontend = Transmitter->Frontend;
+
+    STORE(Remove,
+          Transmitter->StoreInterface,
+          NULL,
+          FrontendGetPrefix(Frontend),
+          "ip");
+
+    STORE(Remove,
+          Transmitter->StoreInterface,
+          NULL,
+          FrontendGetPrefix(Frontend),
+          "ipv4");
+
+    STORE(Remove,
+          Transmitter->StoreInterface,
+          NULL,
+          FrontendGetPrefix(Frontend),
+          "ipv6");
+
+    IpVersion4Count = 0;
+    IpVersion6Count = 0;
+
+    for (Index = 0; Index < Ring->AddressCount; Index++) {
+        switch (Ring->AddressTable[Index].si_family) {
+        case AF_INET: {
+            IPV4_ADDRESS    Address;
+            CHAR            Node[sizeof ("ipv4/XX/addr")];
+
+            RtlCopyMemory(Address.Byte,
+                          &Ring->AddressTable[Index].Ipv4.sin_addr.s_addr,
+                          IPV4_ADDRESS_LENGTH);
+
+            (VOID) RtlStringCbPrintfA(Node,
+                                      sizeof (Node),
+                                      "ipv4/%u/addr",
+                                      IpVersion4Count);
+
+            STORE(Printf,
+                  Transmitter->StoreInterface,
+                  NULL,
+                  FrontendGetPrefix(Frontend),
+                  Node,
+                  "%u.%u.%u.%u",
+                  Address.Byte[0],
+                  Address.Byte[1],
+                  Address.Byte[2],
+                  Address.Byte[3]);
+
+            if (IpVersion4Count == 0)
+                STORE(Printf,
+                      Transmitter->StoreInterface,
+                      NULL,
+                      FrontendGetPrefix(Frontend),
+                      "ip",
+                      "%u.%u.%u.%u",
+                      Address.Byte[0],
+                      Address.Byte[1],
+                      Address.Byte[2],
+                      Address.Byte[3]);
+
+            IpVersion4Count++;
+            break;
+        }
+        case AF_INET6: {
+            IPV6_ADDRESS    Address;
+            CHAR            Node[sizeof ("ipv6/XX/addr")];
+
+            RtlCopyMemory(Address.Byte,
+                          &Ring->AddressTable[Index].Ipv6.sin6_addr.s6_addr,
+                          IPV6_ADDRESS_LENGTH);
+
+            RtlStringCbPrintfA(Node,
+                               sizeof (Node),
+                               "ipv6/%u/addr",
+                               IpVersion6Count);
+
+            STORE(Printf,
+                  Transmitter->StoreInterface,
+                  NULL,
+                  FrontendGetPrefix(Frontend),
+                  Node,
+                  "%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x",
+                  NTOHS(Address.Word[0]),
+                  NTOHS(Address.Word[1]),
+                  NTOHS(Address.Word[2]),
+                  NTOHS(Address.Word[3]),
+                  NTOHS(Address.Word[4]),
+                  NTOHS(Address.Word[5]),
+                  NTOHS(Address.Word[6]),
+                  NTOHS(Address.Word[7]));
+
+            IpVersion6Count++;
+            break;
+        }
+        default:
+            break;
+        }
+    }
+
+done:
+    __RingReleaseLock(Ring);
+}
+
+static FORCEINLINE VOID
+__RingUpdateAddressTable(
+    IN  PTRANSMITTER_RING   Ring,
+    IN  PSOCKADDR_INET      Table,
+    IN  ULONG               Count
+    )
+{
+    NTSTATUS                status;
+
+    __RingAcquireLock(Ring);
+
+    if (Ring->AddressCount != 0) {
+        Ring->AddressCount = 0;
+
+        ASSERT(Ring->AddressTable != NULL);
+        __TransmitterFree(Ring->AddressTable);
+        Ring->AddressTable = NULL;
+    }
+
+    if (Count == 0)
+        goto done;
+
+    Ring->AddressTable = __TransmitterAllocate(sizeof (SOCKADDR_INET) * Count);
+
+    status = STATUS_NO_MEMORY;
+    if (Ring->AddressTable == NULL)
+        goto fail1;
+
+    RtlCopyMemory(Ring->AddressTable, Table, sizeof (SOCKADDR_INET) * Count);
+    Ring->AddressCount = Count;
+
+    // Re-advertize if we were part way through
+    if (Ring->AddressIndex != 0)
+        Ring->AddressIndex = Ring->AddressCount;
+
+done:
+    __RingReleaseLock(Ring);
+
+    __RingDumpAddressTable(Ring);
+
+    return;
+
+fail1:
+    Error("fail1 (%08x)\n", status);
+
+    __RingReleaseLock(Ring);
+}
+
+static FORCEINLINE VOID
+__RingAdvertizeAddresses(
+    IN  PTRANSMITTER_RING   Ring
+    )
+{
+    __RingAcquireLock(Ring);
+    Ring->AddressIndex = Ring->AddressCount;
+    __RingReleaseLock(Ring);
+}
+
+static FORCEINLINE NTSTATUS
+__RingInitialize(
+    IN  PXENVIF_TRANSMITTER Transmitter,
+    OUT PTRANSMITTER_RING   *Ring
+    )
+{
+    NTSTATUS                status;
+
+    *Ring = __TransmitterAllocate(sizeof (TRANSMITTER_RING));
+
+    status = STATUS_NO_MEMORY;
+    if (*Ring == NULL)
+        goto fail1;
+
+    status = PoolInitialize("TransmitterBuffer",
+                            sizeof (TRANSMITTER_BUFFER),
+                            TransmitterBufferCtor,
+                            TransmitterBufferDtor,
+                            RingAcquireLock,
+                            RingReleaseLock,
+                            *Ring,
+                            &(*Ring)->BufferPool);
+    if (!NT_SUCCESS(status))
+        goto fail2;
+
+    (*Ring)->Queued.TailPacket = &(*Ring)->Queued.HeadPacket;
+    (*Ring)->Completed.TailPacket = &(*Ring)->Completed.HeadPacket;
+
+    (*Ring)->Transmitter = Transmitter;
+
+    status = ThreadCreate(RingWatchdog,
+                          *Ring,
+                          &(*Ring)->Thread);
+    if (!NT_SUCCESS(status))
+        goto fail3;
+
+    return STATUS_SUCCESS;
+
+fail3:
+    Error("fail3\n");
+
+    (*Ring)->Transmitter = NULL;
+
+    (*Ring)->Queued.TailPacket = NULL;
+    (*Ring)->Completed.TailPacket = NULL;
+
+    PoolTeardown((*Ring)->BufferPool);
+    (*Ring)->BufferPool = NULL;
+
+fail2:
+    Error("fail2\n");
+
+    ASSERT(IsZeroMemory(*Ring, sizeof (TRANSMITTER_RING)));
+    __TransmitterFree(*Ring);
+    *Ring = NULL;
+
+fail1:
+    Error("fail1 (%08x)\n", status);
+
+    return status;
+}
+
+static FORCEINLINE NTSTATUS
+__RingConnect(
+    IN  PTRANSMITTER_RING   Ring
+    )
+{
+    PXENVIF_TRANSMITTER     Transmitter;
+    PXENVIF_FRONTEND        Frontend;
+    ULONG                   Index;
+    PFN_NUMBER              Pfn;
+    NTSTATUS                status;
+
+    ASSERT(!Ring->Connected);
+
+    Transmitter = Ring->Transmitter;
+    Frontend = Transmitter->Frontend;
+
+    Ring->Mdl = __AllocatePage();
+
+    status = STATUS_NO_MEMORY;
+    if (Ring->Mdl == NULL)
+       goto fail1;
+
+    Ring->Shared = MmGetSystemAddressForMdlSafe(Ring->Mdl, NormalPagePriority);
+    ASSERT(Ring->Shared != NULL);
+
+    SHARED_RING_INIT(Ring->Shared);
+    FRONT_RING_INIT(&Ring->Front, Ring->Shared, PAGE_SIZE);
+    ASSERT3P(Ring->Front.sring, ==, Ring->Shared);
+
+    Transmitter->GnttabInterface = FrontendGetGnttabInterface(Frontend);
+
+    GNTTAB(Acquire, Transmitter->GnttabInterface);
+
+    status = GNTTAB(Get,
+                    Transmitter->GnttabInterface,
+                    &Ring->Reference);
+    if (!NT_SUCCESS(status))
+        goto fail2;
+
+    Ring->HeadFreeTag = TAG_INDEX_INVALID;
+    for (Index = 0; Index < MAXIMUM_TAG_COUNT; Index++) {
+        PTRANSMITTER_TAG Tag = &Ring->Tag[Index];
+
+        status = GNTTAB(Get,
+                        Transmitter->GnttabInterface,
+                        &Tag->Reference);
+        if (!NT_SUCCESS(status))
+            goto fail3;
+
+        Tag->Next = Ring->HeadFreeTag;
+        Ring->HeadFreeTag = Index;
+    }
+
+    Pfn = MmGetMdlPfnArray(Ring->Mdl)[0];
+
+    GNTTAB(PermitForeignAccess,
+           Transmitter->GnttabInterface,
+           Ring->Reference,
+           FrontendGetBackendDomain(Frontend),
+           GNTTAB_ENTRY_FULL_PAGE,
+           Pfn,
+           FALSE);
+
+    Ring->Connected = TRUE;
+
+    return STATUS_SUCCESS;
+
+fail3:
+    Error("fail3\n");
+
+    while (Ring->HeadFreeTag != TAG_INDEX_INVALID) {
+        PTRANSMITTER_TAG    Tag = &Ring->Tag[Ring->HeadFreeTag];
+
+        Ring->HeadFreeTag = Tag->Next;
+        Tag->Next = 0;
+
+        GNTTAB(Put,
+               Transmitter->GnttabInterface,
+               Tag->Reference);
+        Tag->Reference = 0;
+    }
+    Ring->HeadFreeTag = 0;
+
+    GNTTAB(Put,
+           Transmitter->GnttabInterface,
+           Ring->Reference);
+    Ring->Reference = 0;
+
+fail2:
+    Error("fail2\n");
+
+    GNTTAB(Release, Transmitter->GnttabInterface);
+    Transmitter->GnttabInterface = NULL;
+
+    RtlZeroMemory(&Ring->Front, sizeof (netif_tx_front_ring_t));
+    RtlZeroMemory(Ring->Shared, PAGE_SIZE);
+
+    Ring->Shared = NULL;
+    __FreePage(Ring->Mdl);
+    Ring->Mdl = NULL;
+
+fail1:
+    Error("fail1 (%08x)\n", status);
+
+    return status;
+}
+
+static FORCEINLINE NTSTATUS
+__RingStoreWrite(
+    IN  PTRANSMITTER_RING           Ring,
+    IN  PXENBUS_STORE_TRANSACTION   Transaction
+    )
+{
+    PXENVIF_TRANSMITTER             Transmitter;
+    PXENVIF_FRONTEND                Frontend;
+    NTSTATUS                        status;
+
+    Transmitter = Ring->Transmitter;
+    Frontend = Transmitter->Frontend;
+
+    status = STORE(Printf,
+                   Transmitter->StoreInterface,
+                   Transaction,
+                   FrontendGetPath(Frontend),
+                   "tx-ring-ref",
+                   "%u",
+                   Ring->Reference);
+
+    if (!NT_SUCCESS(status))
+        goto fail1;
+
+    return STATUS_SUCCESS;
+
+fail1:
+    Error("fail1 (%08x)\n", status);
+
+    return status;
+}
+
+static FORCEINLINE NTSTATUS
+__RingEnable(
+    IN  PTRANSMITTER_RING   Ring
+    )
+{
+    __RingAcquireLock(Ring);
+
+    ASSERT(!Ring->Enabled);
+    Ring->Enabled = TRUE;
+
+    __RingReleaseLock(Ring);
+
+    return STATUS_SUCCESS;
+}
+
+static FORCEINLINE VOID
+__RingDisable(
+    IN  PTRANSMITTER_RING       Ring
+    )
+{    
+    PXENVIF_TRANSMITTER         Transmitter;
+    PXENVIF_FRONTEND            Frontend;
+    PXENVIF_TRANSMITTER_PACKET  Packet;
+    PCHAR                       Buffer;
+    XenbusState                 State;
+    ULONG                       Attempt;
+    NTSTATUS                    status;
+
+    Transmitter = Ring->Transmitter;
+    Frontend = Transmitter->Frontend;
+
+    __RingAcquireLock(Ring);
+
+    ASSERT(Ring->Enabled);
+    Ring->Enabled = FALSE;
+
+    // Release any tags associated with a pending packet
+    Packet = __RingUnpreparePacket(Ring);
+
+    // Put any packet back on the head of the queue
+    if (Packet != NULL) {
+        ASSERT3P(Packet->Next, ==, NULL);
+
+        Packet->Next = Ring->Queued.HeadPacket;
+
+        if (Ring->Queued.TailPacket == &Ring->Queued.HeadPacket)
+            Ring->Queued.TailPacket = &Packet->Next;
+
+        Ring->Queued.HeadPacket = Packet;
+    }
+
+    Ring->AddressIndex = 0;
+
+    status = STORE(Read,
+                   Transmitter->StoreInterface,
+                   NULL,
+                   FrontendGetBackendPath(Frontend),
+                   "state",
+                   &Buffer);
+    if (!NT_SUCCESS(status)) {
+        State = XenbusStateUnknown;
+    } else {
+        State = (XenbusState)strtol(Buffer, NULL, 10);
+
+        STORE(Free,
+              Transmitter->StoreInterface,
+              Buffer);
+    }
+
+    Attempt = 0;
+    ASSERT3U(Ring->RequestsPushed, ==, Ring->RequestsPosted);
+    while (Ring->ResponsesProcessed != Ring->RequestsPushed) {
+        Attempt++;
+        ASSERT(Attempt < 100);
+
+        RingPoll(Ring);
+
+        if (State != XenbusStateConnected)
+            __RingFakeResponses(Ring);
+
+        KeStallExecutionProcessor(10000);   // 10 ms
+    }
+
+    __RingReleaseLock(Ring);
+}
+
+static FORCEINLINE VOID
+__RingDisconnect(
+    IN  PTRANSMITTER_RING   Ring
+    )
+{
+    PXENVIF_TRANSMITTER     Transmitter;
+    PXENVIF_FRONTEND        Frontend;
+    ULONG                   Count;
+
+    ASSERT(Ring->Connected);
+    Ring->Connected = FALSE;
+
+    Transmitter = Ring->Transmitter;
+    Frontend = Transmitter->Frontend;
+
+    ASSERT3U(Ring->ResponsesProcessed, ==, Ring->RequestsPushed);
+    ASSERT3U(Ring->RequestsPushed, ==, Ring->RequestsPosted);
+
+    Ring->ResponsesProcessed = 0;
+    Ring->RequestsPushed = 0;
+    Ring->RequestsPosted = 0;
+
+    GNTTAB(RevokeForeignAccess,
+           Transmitter->GnttabInterface,
+           Ring->Reference);
+
+    Count = 0;
+    while (Ring->HeadFreeTag != TAG_INDEX_INVALID) {
+        ULONG               Index = Ring->HeadFreeTag;
+        PTRANSMITTER_TAG    Tag = &Ring->Tag[Index];
+
+        Ring->HeadFreeTag = Tag->Next;
+        Tag->Next = 0;
+
+        GNTTAB(Put,
+               Transmitter->GnttabInterface,
+               Tag->Reference);
+        Tag->Reference = 0;
+
+        Count++;
+    }
+    ASSERT3U(Count, ==, MAXIMUM_TAG_COUNT);
+
+    Ring->HeadFreeTag = 0;
+
+    GNTTAB(Put,
+           Transmitter->GnttabInterface,
+           Ring->Reference);
+    Ring->Reference = 0;
+
+    GNTTAB(Release, Transmitter->GnttabInterface);
+    Transmitter->GnttabInterface = NULL;
+
+    RtlZeroMemory(&Ring->Front, sizeof (netif_tx_front_ring_t));
+    RtlZeroMemory(Ring->Shared, PAGE_SIZE);
+
+    Ring->Shared = NULL;
+    __FreePage(Ring->Mdl);
+    Ring->Mdl = NULL;
+}
+
+static FORCEINLINE VOID
+__RingTeardown(
+    IN  PTRANSMITTER_RING   Ring
+    )
+{
+    PXENVIF_TRANSMITTER     Transmitter;
+    PXENVIF_FRONTEND        Frontend;
+
+    Transmitter = Ring->Transmitter;
+    Frontend = Transmitter->Frontend;
+
+    ASSERT3U(Ring->PacketsCompleted, ==, Ring->PacketsSent);
+    ASSERT3U(Ring->PacketsSent, ==, Ring->PacketsPrepared - Ring->PacketsUnprepared);
+    ASSERT3U(Ring->PacketsPrepared, ==, Ring->PacketsCopied + Ring->PacketsGranted + Ring->PacketsFaked);
+    ASSERT3U(Ring->PacketsQueued, ==, Ring->PacketsPrepared - Ring->PacketsUnprepared);
+
+    Ring->PacketsCompleted = 0;
+    Ring->PacketsSent = 0;
+    Ring->PacketsCopied = 0;
+    Ring->PacketsGranted = 0;
+    Ring->PacketsFaked = 0;
+    Ring->PacketsUnprepared = 0;
+    Ring->PacketsPrepared = 0;
+    Ring->PacketsQueued = 0;
+
+    RtlZeroMemory(&Ring->HeaderStatistics, sizeof (XENVIF_HEADER_STATISTICS));
+    RtlZeroMemory(&Ring->OffloadStatistics, sizeof (TRANSMITTER_OFFLOAD_STATISTICS));
+    RtlZeroMemory(&Ring->PacketStatistics, sizeof (XENVIF_TRANSMITTER_PACKET_STATISTICS));
+
+    if (Ring->AddressCount != 0) {
+        ASSERT(Ring->AddressTable != NULL);
+        __TransmitterFree(Ring->AddressTable);
+    }
+
+    Ring->AddressTable = NULL;
+    Ring->AddressCount = 0;
+
+    ThreadAlert(Ring->Thread);
+    ThreadJoin(Ring->Thread);
+    Ring->Thread = NULL;
+
+    Ring->Transmitter = NULL;
+
+    ASSERT3P(Ring->Queued.TailPacket, ==, &Ring->Queued.HeadPacket);
+    Ring->Queued.TailPacket = NULL;
+
+    ASSERT3P(Ring->Completed.TailPacket, ==, &Ring->Completed.HeadPacket);
+    Ring->Completed.TailPacket = NULL;
+
+    PoolTeardown(Ring->BufferPool);
+    Ring->BufferPool = NULL;
+
+    ASSERT(IsZeroMemory(Ring, sizeof (TRANSMITTER_RING)));
+    __TransmitterFree(Ring);
+}
+
+static FORCEINLINE VOID
+__RingQueuePackets(
+    IN  PTRANSMITTER_RING           Ring,
+    IN  PXENVIF_TRANSMITTER_PACKET  HeadPacket
+    )
+{
+    PXENVIF_TRANSMITTER_PACKET      *TailPacket;
+    ULONG_PTR                       Old;
+    ULONG_PTR                       LockBit;
+    ULONG_PTR                       New;
+
+    TailPacket = &HeadPacket->Next;
+    (VOID) __RingReversePacketList(&HeadPacket);
+    ASSERT3P(*TailPacket, ==, NULL);
+
+    do {
+        Old = (ULONG_PTR)Ring->Lock;
+        LockBit = Old & LOCK_BIT;
+
+        *TailPacket = (PVOID)(Old & ~LOCK_BIT);
+        New = (ULONG_PTR)HeadPacket;
+        ASSERT((New & LOCK_BIT) == 0);
+        New |= LockBit;
+    } while ((ULONG_PTR)InterlockedCompareExchangePointer(&Ring->Lock, (PVOID)New, (PVOID)Old) != Old);
+
+    // __RingReleaseLock() drains the atomic packet list into the transmit queue therefore,
+    // after adding to the list we need to attempt to grab and release the lock. If we can't
+    // grab it then that's ok because whichever thread is holding it will have to call
+    // __RingReleaseLock() and will therefore drain the atomic packet list.
+
+    if (!__RingTryAcquireLock(Ring))
+        return;
+
+    __RingReleaseLock(Ring);
+}
+
+static FORCEINLINE VOID
+__RingAbortPackets(
+    IN  PTRANSMITTER_RING       Ring
+    )
+{
+    PXENVIF_TRANSMITTER_PACKET  Packet;
+
+    __RingAcquireLock(Ring);
+
+    RingSwizzle(Ring);
+
+    Packet = Ring->Queued.HeadPacket;
+
+    Ring->Queued.HeadPacket = NULL;
+    Ring->Queued.TailPacket = &Ring->Queued.HeadPacket;
+
+    while (Packet != NULL) {
+        PXENVIF_TRANSMITTER_PACKET  Next;
+        
+        Next = Packet->Next;
+        Packet->Next = NULL;
+
+        // Fake that we prapared and sent this packet
+        Ring->PacketsPrepared++;
+        Ring->PacketsSent++;
+        Ring->PacketsFaked++;
+
+        Packet->Completion.Status = PACKET_DROPPED;
+
+        __RingCompletePacket(Ring, Packet);
+
+        Packet = Next;
+    }
+
+    ASSERT3U(Ring->PacketsSent, ==, Ring->PacketsPrepared - Ring->PacketsUnprepared);
+    ASSERT3U(Ring->PacketsPrepared, ==, Ring->PacketsCopied + Ring->PacketsGranted + Ring->PacketsFaked);
+    ASSERT3U(Ring->PacketsQueued, ==, Ring->PacketsPrepared - Ring->PacketsUnprepared);
+
+    ASSERT3P((ULONG_PTR)Ring->Lock, ==, LOCK_BIT);
+    __RingReleaseLock(Ring);
+}
+
+static FORCEINLINE VOID
+__RingNotify(
+    IN  PTRANSMITTER_RING   Ring
+    )
+{
+    __RingAcquireLock(Ring);
+
+    RingPoll(Ring);
+
+    __RingReleaseLock(Ring);
+}
+
+static FORCEINLINE VOID
+__RingAddPacketStatistics(
+    IN      PTRANSMITTER_RING                       Ring,
+    IN OUT  PXENVIF_TRANSMITTER_PACKET_STATISTICS   Statistics
+    )
+{
+    // Don't bother locking
+
+    Statistics->Drop += Ring->PacketStatistics.Drop;
+    Statistics->BackendError += Ring->PacketStatistics.BackendError;
+    Statistics->FrontendError += Ring->PacketStatistics.FrontendError;
+    Statistics->Unicast += Ring->PacketStatistics.Unicast;
+    Statistics->UnicastBytes += Ring->PacketStatistics.UnicastBytes;
+    Statistics->Multicast += Ring->PacketStatistics.Multicast;
+    Statistics->MulticastBytes += Ring->PacketStatistics.MulticastBytes;
+    Statistics->Broadcast += Ring->PacketStatistics.Broadcast;
+    Statistics->BroadcastBytes += Ring->PacketStatistics.BroadcastBytes;
+}
+
+static FORCEINLINE ULONG
+__RingGetSize(
+    IN  PTRANSMITTER_RING   Ring
+    )
+{
+    UNREFERENCED_PARAMETER(Ring);
+
+    return TRANSMITTER_RING_SIZE;
+}
+
+static VOID
+TransmitterDebugCallback(
+    IN  PVOID           Argument,
+    IN  BOOLEAN         Crashing
+    )
+{
+    PXENVIF_TRANSMITTER Transmitter = Argument;
+    PLIST_ENTRY         ListEntry;
+
+    UNREFERENCED_PARAMETER(Crashing);
+
+    for (ListEntry = Transmitter->List.Flink;
+         ListEntry != &Transmitter->List;
+         ListEntry = ListEntry->Flink) {
+        PTRANSMITTER_RING    Ring;
+
+        Ring = CONTAINING_RECORD(ListEntry, TRANSMITTER_RING, ListEntry);
+
+        __RingDebugCallback(Ring);
+    }    
+
+    DEBUG(Printf,
+          Transmitter->DebugInterface,
+          Transmitter->DebugCallback,
+          "METADATA: Offset @ %ld Length @ %ld Mdl @ %ld\n",
+          Transmitter->Metadata.OffsetOffset,
+          Transmitter->Metadata.LengthOffset,
+          Transmitter->Metadata.MdlOffset);
+}
+
+NTSTATUS
+TransmitterInitialize(
+    IN  PXENVIF_FRONTEND    Frontend,
+    IN  ULONG               Count,
+    OUT PXENVIF_TRANSMITTER *Transmitter
+    )
+{
+    ULONG                   Done;
+    NTSTATUS                status;
+
+    *Transmitter = __TransmitterAllocate(sizeof (XENVIF_TRANSMITTER));
+
+    status = STATUS_NO_MEMORY;
+    if (*Transmitter == NULL)
+        goto fail1;
+
+    InitializeListHead(&(*Transmitter)->List);
+
+    Done = 0;
+    while (Done < Count) {
+        PTRANSMITTER_RING   Ring;
+
+        status = __RingInitialize(*Transmitter, &Ring);
+        if (!NT_SUCCESS(status))
+            goto fail2;
+
+        InsertTailList(&(*Transmitter)->List, &Ring->ListEntry);
+        Done++;
+    }
+
+    (*Transmitter)->Frontend = Frontend;
+
+    return STATUS_SUCCESS;
+
+fail2:
+    Error("fail2\n");    
+
+    while (!IsListEmpty(&(*Transmitter)->List)) {
+        PLIST_ENTRY         ListEntry;
+        PTRANSMITTER_RING   Ring;
+
+        ListEntry = RemoveTailList(&(*Transmitter)->List);
+        ASSERT3P(ListEntry, !=, &(*Transmitter)->List);
+
+        RtlZeroMemory(ListEntry, sizeof (LIST_ENTRY));
+
+        Ring = CONTAINING_RECORD(ListEntry, TRANSMITTER_RING, ListEntry);
+
+        __RingTeardown(Ring);
+        --Done;
+    }
+    ASSERT3U(Done, ==, 0);
+
+    RtlZeroMemory(&(*Transmitter)->List, sizeof (LIST_ENTRY));
+    
+    ASSERT(IsZeroMemory(*Transmitter, sizeof (XENVIF_TRANSMITTER)));
+    __TransmitterFree(*Transmitter);
+
+fail1:
+    Error("fail1 (%08x)\n", status);
+
+    return status;
+}
+
+NTSTATUS
+TransmitterConnect(
+    IN  PXENVIF_TRANSMITTER Transmitter
+    )
+{
+    PXENVIF_FRONTEND        Frontend;
+    PLIST_ENTRY             ListEntry;
+    PTRANSMITTER_RING       Ring;
+    NTSTATUS                status;
+
+    Frontend = Transmitter->Frontend;
+
+    Transmitter->StoreInterface = FrontendGetStoreInterface(Frontend);
+
+    STORE(Acquire, Transmitter->StoreInterface);
+
+    for (ListEntry = Transmitter->List.Flink;
+         ListEntry != &Transmitter->List;
+         ListEntry = ListEntry->Flink) {
+
+        Ring = CONTAINING_RECORD(ListEntry, TRANSMITTER_RING, ListEntry);
+
+        status = __RingConnect(Ring);
+        if (!NT_SUCCESS(status))
+            goto fail1;
+    }    
+
+    Transmitter->DebugInterface = FrontendGetDebugInterface(Frontend);
+
+    DEBUG(Acquire, Transmitter->DebugInterface);
+
+    status = DEBUG(Register,
+                   Transmitter->DebugInterface,
+                   __MODULE__ "|TRANSMITTER",
+                   TransmitterDebugCallback,
+                   Transmitter,
+                   &Transmitter->DebugCallback);
+    if (!NT_SUCCESS(status))
+        goto fail2;
+
+    ListEntry = Transmitter->List.Flink;
+    Ring = CONTAINING_RECORD(ListEntry, TRANSMITTER_RING, ListEntry);
+
+    __RingDumpAddressTable(Ring);
+
+    Transmitter->VifInterface = FrontendGetVifInterface(Frontend);
+
+    return STATUS_SUCCESS;
+
+fail2:
+    Error("fail2\n");
+
+    DEBUG(Release, Transmitter->DebugInterface);
+    Transmitter->DebugInterface = NULL;
+
+    ListEntry = &Transmitter->List;
+
+fail1:
+    Error("fail1 (%08x)\n", status);
+
+    ListEntry = ListEntry->Blink;
+
+    while (ListEntry != &Transmitter->List) {
+        PLIST_ENTRY         Prev = ListEntry->Blink;
+
+        Ring = CONTAINING_RECORD(ListEntry, TRANSMITTER_RING, ListEntry);
+
+        __RingDisconnect(Ring);
+
+        ListEntry = Prev;
+    }
+
+    STORE(Release, Transmitter->StoreInterface);
+    Transmitter->StoreInterface = NULL;
+
+    return status;
+}
+
+NTSTATUS
+TransmitterStoreWrite(
+    IN  PXENVIF_TRANSMITTER         Transmitter,
+    IN  PXENBUS_STORE_TRANSACTION   Transaction
+    )
+{
+    PLIST_ENTRY                     ListEntry;
+    NTSTATUS                        status;
+
+    for (ListEntry = Transmitter->List.Flink;
+         ListEntry != &Transmitter->List;
+         ListEntry = ListEntry->Flink) {
+        PTRANSMITTER_RING   Ring;
+
+        Ring = CONTAINING_RECORD(ListEntry, TRANSMITTER_RING, ListEntry);
+
+        status = __RingStoreWrite(Ring, Transaction);
+        if (!NT_SUCCESS(status))
+            goto fail1;
+    }    
+
+    return STATUS_SUCCESS;
+
+fail1:
+    Error("fail1 (%08x)\n", status);
+
+    return status;
+}
+
+NTSTATUS
+TransmitterEnable(
+    IN  PXENVIF_TRANSMITTER Transmitter
+    )
+{
+    PLIST_ENTRY             ListEntry;
+
+    for (ListEntry = Transmitter->List.Flink;
+         ListEntry != &Transmitter->List;
+         ListEntry = ListEntry->Flink) {
+        PTRANSMITTER_RING   Ring;
+
+        Ring = CONTAINING_RECORD(ListEntry, TRANSMITTER_RING, ListEntry);
+
+        __RingEnable(Ring);
+    }    
+
+    return STATUS_SUCCESS;
+}
+
+VOID
+TransmitterDisable(
+    IN  PXENVIF_TRANSMITTER Transmitter
+    )
+{
+    PLIST_ENTRY             ListEntry;
+
+    for (ListEntry = Transmitter->List.Blink;
+         ListEntry != &Transmitter->List;
+         ListEntry = ListEntry->Blink) {
+        PTRANSMITTER_RING   Ring;
+
+        Ring = CONTAINING_RECORD(ListEntry, TRANSMITTER_RING, ListEntry);
+
+        __RingDisable(Ring);
+    }
+}
+
+VOID
+TransmitterDisconnect(
+    IN  PXENVIF_TRANSMITTER Transmitter
+    )
+{
+    PXENVIF_FRONTEND        Frontend;
+    PLIST_ENTRY             ListEntry;
+
+    Frontend = Transmitter->Frontend;
+
+    Transmitter->VifInterface = NULL;
+
+    DEBUG(Deregister,
+          Transmitter->DebugInterface,
+          Transmitter->DebugCallback);
+    Transmitter->DebugCallback = NULL;
+
+    DEBUG(Release, Transmitter->DebugInterface);
+    Transmitter->DebugInterface = NULL;
+
+    for (ListEntry = Transmitter->List.Blink;
+         ListEntry != &Transmitter->List;
+         ListEntry = ListEntry->Blink) {
+        PTRANSMITTER_RING    Ring;
+
+        Ring = CONTAINING_RECORD(ListEntry, TRANSMITTER_RING, ListEntry);
+
+        __RingDisconnect(Ring);
+    }
+
+    STORE(Remove,
+          Transmitter->StoreInterface,
+          NULL,
+          FrontendGetPrefix(Frontend),
+          "ip");
+
+    STORE(Remove,
+          Transmitter->StoreInterface,
+          NULL,
+          FrontendGetPrefix(Frontend),
+          "ipv4");
+
+    STORE(Remove,
+          Transmitter->StoreInterface,
+          NULL,
+          FrontendGetPrefix(Frontend),
+          "ipv6");
+
+    STORE(Release, Transmitter->StoreInterface);
+    Transmitter->StoreInterface = NULL;
+}
+
+VOID
+TransmitterTeardown(
+    IN  PXENVIF_TRANSMITTER Transmitter
+    )
+{
+    RtlZeroMemory(&Transmitter->Metadata, sizeof (XENVIF_TRANSMITTER_PACKET_METADATA));
+
+    Transmitter->Frontend = NULL;
+
+    while (!IsListEmpty(&Transmitter->List)) {
+        PLIST_ENTRY         ListEntry;
+        PTRANSMITTER_RING   Ring;
+
+        ListEntry = RemoveHeadList(&Transmitter->List);
+        ASSERT3P(ListEntry, !=, &Transmitter->List);
+        RtlZeroMemory(ListEntry, sizeof (LIST_ENTRY));
+
+        Ring = CONTAINING_RECORD(ListEntry, TRANSMITTER_RING, ListEntry);
+
+        __RingTeardown(Ring);
+    }
+
+    RtlZeroMemory(&Transmitter->List, sizeof (LIST_ENTRY));
+
+    ASSERT(IsZeroMemory(Transmitter, sizeof (XENVIF_TRANSMITTER)));
+    __TransmitterFree(Transmitter);
+}
+
+VOID
+TransmitterUpdateAddressTable(
+    IN  PXENVIF_TRANSMITTER Transmitter,
+    IN  SOCKADDR_INET       Table[],
+    IN  ULONG               Count
+    )
+{
+    KIRQL                   Irql;
+    PLIST_ENTRY             ListEntry;
+    PTRANSMITTER_RING       Ring;
+
+    // Make sure we don't suspend
+    KeRaiseIrql(DISPATCH_LEVEL, &Irql);
+
+    // Use the first ring for address advertisment
+    ListEntry = Transmitter->List.Flink;
+    Ring = CONTAINING_RECORD(ListEntry, TRANSMITTER_RING, ListEntry);
+
+    __RingUpdateAddressTable(Ring, Table, Count);
+
+    KeLowerIrql(Irql);
+}
+
+VOID
+TransmitterAdvertizeAddresses(
+    IN  PXENVIF_TRANSMITTER Transmitter
+    )
+{
+    PLIST_ENTRY             ListEntry;
+    PTRANSMITTER_RING       Ring;
+
+    // Use the first ring for address advertisment
+    ListEntry = Transmitter->List.Flink;
+    Ring = CONTAINING_RECORD(ListEntry, TRANSMITTER_RING, ListEntry);
+
+    __RingAdvertizeAddresses(Ring);
+}
+
+VOID
+TransmitterSetPacketMetadata(
+    IN  PXENVIF_TRANSMITTER                 Transmitter,
+    IN  XENVIF_TRANSMITTER_PACKET_METADATA  Metadata
+    )
+{
+    Transmitter->Metadata = Metadata;
+}
+
+VOID
+TransmitterQueuePackets(
+    IN  PXENVIF_TRANSMITTER         Transmitter,
+    IN  PXENVIF_TRANSMITTER_PACKET  HeadPacket
+    )
+{
+    PLIST_ENTRY                     ListEntry;
+    PTRANSMITTER_RING               Ring;
+
+    // We need to hash for a ring eventually. Since there is only a
+    // single ring for now, we just use that.
+    ListEntry = Transmitter->List.Flink;
+    Ring = CONTAINING_RECORD(ListEntry, TRANSMITTER_RING, ListEntry);
+
+    __RingQueuePackets(Ring, HeadPacket);
+}
+
+VOID
+TransmitterAbortPackets(
+    IN  PXENVIF_TRANSMITTER Transmitter
+    )
+{
+    PLIST_ENTRY             ListEntry;
+    KIRQL                   Irql;
+
+    KeRaiseIrql(DISPATCH_LEVEL, &Irql);
+
+    for (ListEntry = Transmitter->List.Flink;
+         ListEntry != &Transmitter->List;
+         ListEntry = ListEntry->Flink) {
+        PTRANSMITTER_RING   Ring;
+
+        Ring = CONTAINING_RECORD(ListEntry, TRANSMITTER_RING, ListEntry);
+
+        __RingAbortPackets(Ring);
+    }    
+
+    KeLowerIrql(Irql);
+}
+
+VOID
+TransmitterGetPacketStatistics(
+    IN  PXENVIF_TRANSMITTER                     Transmitter,
+    OUT PXENVIF_TRANSMITTER_PACKET_STATISTICS   Statistics
+    )
+{
+    PLIST_ENTRY                                 ListEntry;
+
+    RtlZeroMemory(Statistics, sizeof (XENVIF_TRANSMITTER_PACKET_STATISTICS));
+
+    for (ListEntry = Transmitter->List.Flink;
+         ListEntry != &Transmitter->List;
+         ListEntry = ListEntry->Flink) {
+        PTRANSMITTER_RING   Ring;
+
+        Ring = CONTAINING_RECORD(ListEntry, TRANSMITTER_RING, ListEntry);
+
+        __RingAddPacketStatistics(Ring, Statistics);
+    }    
+}
+
+ULONG
+TransmitterGetRingSize(
+    IN  PXENVIF_TRANSMITTER Transitter
+    )
+{
+    PLIST_ENTRY             ListEntry;
+    PTRANSMITTER_RING       Ring;
+
+    // Use the first ring
+    ListEntry = Transitter->List.Flink;
+    Ring = CONTAINING_RECORD(ListEntry, TRANSMITTER_RING, ListEntry);
+
+    return __RingGetSize(Ring);
+}
+
+VOID
+TransmitterNotify(
+    IN  PXENVIF_TRANSMITTER Transmitter
+    )
+{
+    PLIST_ENTRY             ListEntry;
+
+    for (ListEntry = Transmitter->List.Flink;
+         ListEntry != &Transmitter->List;
+         ListEntry = ListEntry->Flink) {
+        PTRANSMITTER_RING   Ring;
+
+        Ring = CONTAINING_RECORD(ListEntry, TRANSMITTER_RING, ListEntry);
+
+        __RingNotify(Ring);
+    }    
+}
+
+VOID
+TransmitterGetOffloadOptions(
+    IN  PXENVIF_TRANSMITTER     Transmitter,
+    OUT PXENVIF_OFFLOAD_OPTIONS Options
+    )
+{
+    PXENVIF_FRONTEND            Frontend;
+    PCHAR                       Buffer;
+    NTSTATUS                    status;
+
+    Frontend = Transmitter->Frontend;
+
+    Options->Value = 0;
+
+    Options->OffloadTagManipulation = 1;
+
+    status = STORE(Read,
+                   Transmitter->StoreInterface,
+                   NULL,
+                   FrontendGetBackendPath(Frontend),
+                   "feature-gso-tcpv4",
+                   &Buffer);
+    if (!NT_SUCCESS(status)) {
+        Options->OffloadIpVersion4LargePacket = 0;
+    } else {
+        Options->OffloadIpVersion4LargePacket = (USHORT)strtol(Buffer, NULL, 2);
+
+        STORE(Free,
+              Transmitter->StoreInterface,
+              Buffer);
+    }
+
+    status = STORE(Read,
+                   Transmitter->StoreInterface,
+                   NULL,
+                   FrontendGetBackendPath(Frontend),
+                   "feature-gso-tcpv6",
+                   &Buffer);
+    if (!NT_SUCCESS(status)) {
+        Options->OffloadIpVersion6LargePacket = 0;
+    } else {
+        Options->OffloadIpVersion6LargePacket = (USHORT)strtol(Buffer, NULL, 2);
+
+        STORE(Free,
+              Transmitter->StoreInterface,
+              Buffer);
+    }
+
+    Options->OffloadIpVersion4HeaderChecksum = 1;
+
+    status = STORE(Read,
+                   Transmitter->StoreInterface,
+                   NULL,
+                   FrontendGetBackendPath(Frontend),
+                   "feature-no-csum-offload",
+                   &Buffer);
+    if (!NT_SUCCESS(status)) {
+        Options->OffloadIpVersion4TcpChecksum = 1;
+        Options->OffloadIpVersion4UdpChecksum = 1;
+    } else {
+        BOOLEAN Flag;
+
+        Flag = (BOOLEAN)strtol(Buffer, NULL, 2);
+
+        Options->OffloadIpVersion4TcpChecksum = (Flag) ? 0 : 1;
+        Options->OffloadIpVersion4UdpChecksum = (Flag) ? 0 : 1;
+
+        STORE(Free,
+              Transmitter->StoreInterface,
+              Buffer);
+    }
+
+    status = STORE(Read,
+                   Transmitter->StoreInterface,
+                   NULL,
+                   FrontendGetBackendPath(Frontend),
+                   "feature-ipv6-csum-offload",
+                   &Buffer);
+    if (!NT_SUCCESS(status)) {
+        Options->OffloadIpVersion6TcpChecksum = 0;
+        Options->OffloadIpVersion6UdpChecksum = 0;
+    } else {
+        BOOLEAN Flag;
+
+        Flag = (BOOLEAN)strtol(Buffer, NULL, 2);
+
+        Options->OffloadIpVersion6TcpChecksum = (Flag) ? 1 : 0;
+        Options->OffloadIpVersion6UdpChecksum = (Flag) ? 1 : 0;
+
+        STORE(Free,
+              Transmitter->StoreInterface,
+              Buffer);
+    }
+}
+
+#define MAXIMUM_TX_REQ_SIZE         ((1 << (RTL_FIELD_SIZE(netif_tx_request_t, size) * 8)) - 1)
+
+#define MAXIMUM_TCPV4_PAYLOAD_SIZE    (MAXIMUM_TX_REQ_SIZE -          \
+                                       sizeof (ETHERNET_HEADER) -     \
+                                       MAXIMUM_IPV4_HEADER_LENGTH -   \
+                                       MAXIMUM_TCP_HEADER_LENGTH)
+
+#define MAXIMUM_TCPV6_PAYLOAD_SIZE    (MAXIMUM_TX_REQ_SIZE -          \
+                                       sizeof (ETHERNET_HEADER) -     \
+                                       MAXIMUM_IPV6_HEADER_LENGTH -   \
+                                       MAXIMUM_TCP_HEADER_LENGTH)
+
+ULONG
+TransmitterGetLargePacketSize(
+    IN  PXENVIF_TRANSMITTER     Transmitter,
+    IN  UCHAR                   Version
+    )
+{
+    PXENVIF_FRONTEND            Frontend;
+    PCHAR                       Buffer;
+    ULONG                       OffloadIpLargePacket;
+    NTSTATUS                    status;
+
+    Frontend = Transmitter->Frontend;
+
+    if (Version == 4) {
+        status = STORE(Read,
+                       Transmitter->StoreInterface,
+                       NULL,
+                       FrontendGetBackendPath(Frontend),
+                       "feature-gso-tcpv4",
+                       &Buffer);
+    } else if (Version == 6) {
+        status = STORE(Read,
+                       Transmitter->StoreInterface,
+                       NULL,
+                       FrontendGetBackendPath(Frontend),
+                       "feature-gso-tcpv6",
+                       &Buffer);
+    } else {
+        Buffer = NULL;
+        status = STATUS_UNSUCCESSFUL;
+    }
+
+    if (!NT_SUCCESS(status)) {
+        OffloadIpLargePacket = 0;
+    } else {
+        OffloadIpLargePacket = (ULONG)strtol(Buffer, NULL, 2);
+
+        STORE(Free,
+              Transmitter->StoreInterface,
+              Buffer);
+    }
+
+    // The OffloadParity certification test requires that we have a single LSO size for IPv4 and IPv6 packets
+    return (OffloadIpLargePacket) ? __min(MAXIMUM_TCPV4_PAYLOAD_SIZE, MAXIMUM_TCPV6_PAYLOAD_SIZE) : 0;
+}
diff --git a/src/xenvif/transmitter.h b/src/xenvif/transmitter.h
new file mode 100644 (file)
index 0000000..b2a5c9e
--- /dev/null
@@ -0,0 +1,138 @@
+/* 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 _XENVIF_TRANSMITTER_H
+#define _XENVIF_TRANSMITTER_H
+
+#include <ntddk.h>
+#include <netioapi.h>
+#include <vif_interface.h>
+
+#include "frontend.h"
+
+typedef struct _XENVIF_TRANSMITTER XENVIF_TRANSMITTER, *PXENVIF_TRANSMITTER;
+
+extern NTSTATUS
+TransmitterInitialize(
+    IN  PXENVIF_FRONTEND    Frontend,
+    IN  ULONG               Count,
+    OUT PXENVIF_TRANSMITTER *Transmitter
+    );
+
+extern NTSTATUS
+TransmitterConnect(
+    IN  PXENVIF_TRANSMITTER Transmitter
+    );
+
+extern NTSTATUS
+TransmitterStoreWrite(
+    IN  PXENVIF_TRANSMITTER         Transmitter,
+    IN  PXENBUS_STORE_TRANSACTION   Transaction
+    );
+
+extern NTSTATUS
+TransmitterEnable(
+    IN  PXENVIF_TRANSMITTER Transmitter
+    );
+
+extern VOID
+TransmitterDisable(
+    IN  PXENVIF_TRANSMITTER Transmitter
+    );
+
+extern VOID
+TransmitterDisconnect(
+    IN  PXENVIF_TRANSMITTER Transmitter
+    );
+
+extern VOID
+TransmitterTeardown(
+    IN  PXENVIF_TRANSMITTER Transmitter
+    );
+
+extern VOID
+TransmitterNotify(
+    IN  PXENVIF_TRANSMITTER Transmitter
+    );
+
+extern VOID
+TransmitterAbortPackets(
+    IN  PXENVIF_TRANSMITTER Transmitter
+    );
+
+extern VOID
+TransmitterGetPacketStatistics(
+    IN  PXENVIF_TRANSMITTER                     Transmitter,
+    OUT PXENVIF_TRANSMITTER_PACKET_STATISTICS   Statistics
+    );
+
+extern ULONG
+TransmitterGetRingSize(
+    IN  PXENVIF_TRANSMITTER Transmitter
+    );
+
+extern VOID
+TransmitterUpdateAddressTable(
+    IN  PXENVIF_TRANSMITTER Transmitter,
+    IN  SOCKADDR_INET       Table[],
+    IN  ULONG               Count
+    );
+
+extern VOID
+TransmitterAdvertizeAddresses(
+    IN  PXENVIF_TRANSMITTER Transmitter
+    );
+
+extern VOID
+TransmitterSetPacketMetadata(
+    IN  PXENVIF_TRANSMITTER                 Transmitter,
+    IN  XENVIF_TRANSMITTER_PACKET_METADATA  Metadata
+    );
+
+extern VOID
+TransmitterQueuePackets(
+    IN  PXENVIF_TRANSMITTER         Transmitter,
+    IN  PXENVIF_TRANSMITTER_PACKET  HeadPacket
+    );
+
+extern VOID
+TransmitterGetOffloadOptions(
+    IN  PXENVIF_TRANSMITTER     Transmitter,
+    OUT PXENVIF_OFFLOAD_OPTIONS Options
+    );
+
+extern ULONG
+TransmitterGetLargePacketSize(
+    IN  PXENVIF_TRANSMITTER     Transmitter,
+    IN  UCHAR                   Version
+    );
+
+#endif  // _XENVIF_TRANSMITTER_H
diff --git a/src/xenvif/types.h b/src/xenvif/types.h
new file mode 100644 (file)
index 0000000..3c1ec40
--- /dev/null
@@ -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 _XENVIF_TYPES_H
+#define _XENVIF_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  // _XENVIF_TYPES_H
diff --git a/src/xenvif/vif.c b/src/xenvif/vif.c
new file mode 100644 (file)
index 0000000..4d098a8
--- /dev/null
@@ -0,0 +1,787 @@
+/* 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 <ntstrsafe.h>
+#include <stdarg.h>
+#include <xen.h>
+#include <util.h>
+
+#include "pdo.h"
+#include "vif.h"
+#include "mrsw.h"
+#include "thread.h"
+#include "log.h"
+#include "assert.h"
+
+#define VIF_POOL   ' FIV'
+
+typedef struct _VIF_CALLBACK {
+    VOID    (*Function)(PVOID, XENVIF_CALLBACK_TYPE, ...);
+    PVOID   Argument;
+} VIF_CALLBACK, *PVIF_CALLBACK;
+
+struct _XENVIF_VIF_CONTEXT {
+    LONG                        References;
+    XENVIF_MRSW_LOCK            Lock;
+    PXENVIF_PDO                 Pdo;
+    BOOLEAN                     Enabled;
+    PXENVIF_THREAD              MonitorThread;
+    KEVENT                      MonitorEvent;
+    VIF_CALLBACK                Callback;
+
+    PXENBUS_SUSPEND_INTERFACE   SuspendInterface;
+    PXENBUS_SUSPEND_CALLBACK    SuspendCallbackLate;
+};
+
+static FORCEINLINE PVOID
+__VifAllocate(
+    IN  ULONG   Length
+    )
+{
+    return __AllocateNonPagedPoolWithTag(Length, VIF_POOL);
+}
+
+static FORCEINLINE VOID
+__VifFree(
+    IN  PVOID   Buffer
+    )
+{
+    __FreePoolWithTag(Buffer, VIF_POOL);
+}
+
+VOID
+VifCompletePackets(
+    IN  PXENVIF_VIF_INTERFACE       Interface,
+    IN  PXENVIF_TRANSMITTER_PACKET  HeadPacket
+    )
+{
+    PXENVIF_VIF_CONTEXT             Context = Interface->Context;
+    PVIF_CALLBACK                   Callback = &Context->Callback;
+
+    ASSERT(Callback != NULL);
+
+    Callback->Function(Callback->Argument,
+                       XENVIF_CALLBACK_COMPLETE_PACKETS,
+                       HeadPacket);
+}
+
+VOID
+VifReceivePackets(
+    IN  PXENVIF_VIF_INTERFACE   Interface,
+    IN  PLIST_ENTRY             List
+    )
+{
+    PXENVIF_VIF_CONTEXT         Context = Interface->Context;
+    PVIF_CALLBACK               Callback = &Context->Callback;
+
+    ASSERT(Callback != NULL);
+
+    Callback->Function(Callback->Argument,
+                       XENVIF_CALLBACK_RECEIVE_PACKETS,
+                       List);
+}
+
+enum {
+    THREAD_EVENT = 0,
+    MAC_EVENT,
+    EVENT_COUNT
+};
+
+C_ASSERT(EVENT_COUNT <= THREAD_WAIT_OBJECTS);
+
+#define STATUS_MASK ((1 << 6) - 1)
+
+static NTSTATUS
+VifMonitor(
+    IN  PXENVIF_THREAD  Self,
+    IN  PVOID           _Context
+    )
+{
+    PXENVIF_VIF_CONTEXT Context = _Context;
+    PXENVIF_FRONTEND    Frontend = PdoGetFrontend(Context->Pdo);
+    PKEVENT             Event[EVENT_COUNT];
+
+    Trace("====>\n");
+
+    Event[THREAD_EVENT] = ThreadGetEvent(Self);
+    Event[MAC_EVENT] = MacGetEvent(FrontendGetMac(Frontend));
+
+    for (;;) {
+        NTSTATUS    status;
+
+        Trace("waiting...\n");
+
+        status = KeWaitForMultipleObjects(EVENT_COUNT,
+                                          Event,
+                                          WaitAny,
+                                          Executive,
+                                          KernelMode,
+                                          FALSE,
+                                          NULL,
+                                          NULL);
+
+        Trace("awake\n");
+
+        if (status >= STATUS_WAIT_0 &&
+            status < STATUS_WAIT_0 + EVENT_COUNT) {
+            switch (status & STATUS_MASK) {
+            case MAC_EVENT: {
+                KeClearEvent(Event[MAC_EVENT]);
+
+                Trace("MAC_EVENT\n");
+
+                if (Context->Enabled) {
+                    PVIF_CALLBACK   Callback = &Context->Callback;
+
+                    Callback->Function(Callback->Argument,
+                                       XENVIF_CALLBACK_MEDIA_STATE_CHANGE);
+                }
+
+                break;
+            }
+            case THREAD_EVENT:
+                KeClearEvent(Event[THREAD_EVENT]);
+
+                Trace("THREAD_EVENT\n");
+
+                if (ThreadIsAlerted(Self))
+                    goto done;
+
+                KeSetEvent(&Context->MonitorEvent, IO_NO_INCREMENT, FALSE);
+                break;
+
+            default:
+                ASSERT(FALSE);
+                break;
+            }
+        }
+    }
+
+done:
+    KeSetEvent(&Context->MonitorEvent, IO_NO_INCREMENT, FALSE);
+
+    Trace("<====\n");
+
+    return STATUS_SUCCESS;
+}
+
+static DECLSPEC_NOINLINE VOID
+VifSuspendCallbackLate(
+    IN  PVOID           Argument
+    )
+{
+    PXENVIF_VIF_CONTEXT Context = Argument;
+    PXENVIF_FRONTEND    Frontend = PdoGetFrontend(Context->Pdo);
+    NTSTATUS            status;
+
+    status = FrontendSetState(Frontend, FRONTEND_ENABLED);
+    ASSERT(NT_SUCCESS(status));
+
+    TransmitterAdvertizeAddresses(FrontendGetTransmitter(Frontend));
+}
+
+static NTSTATUS
+VifEnable(
+    IN  PXENVIF_VIF_CONTEXT Context,
+    IN  VOID                (*Function)(PVOID, XENVIF_CALLBACK_TYPE, ...),
+    IN  PVOID               Argument
+    )
+{
+    PXENVIF_FRONTEND        Frontend = PdoGetFrontend(Context->Pdo);
+    KIRQL                   Irql;
+    PVIF_CALLBACK           Callback;
+    PKEVENT                 Event;
+    NTSTATUS                status;
+
+    Trace("====>\n");
+
+    AcquireMrswLockExclusive(&Context->Lock, &Irql);
+
+    if (Context->Enabled)
+        goto done;
+
+    Callback = &Context->Callback;
+    Callback->Function = Function;
+    Callback->Argument = Argument;
+
+    status = FrontendSetState(Frontend, FRONTEND_ENABLED);
+    if (!NT_SUCCESS(status))
+        goto fail1;
+
+    Context->SuspendInterface = FrontendGetSuspendInterface(Frontend);
+
+    SUSPEND(Acquire, Context->SuspendInterface);
+
+    status = SUSPEND(Register,
+                     Context->SuspendInterface,
+                     SUSPEND_CALLBACK_LATE,
+                     VifSuspendCallbackLate,
+                     Context,
+                     &Context->SuspendCallbackLate);
+    if (!NT_SUCCESS(status))
+        goto fail2;
+
+    Context->Enabled = TRUE;
+
+    Event = MacGetEvent(FrontendGetMac(Frontend));
+    KeSetEvent(Event, IO_NO_INCREMENT, FALSE);
+
+done:
+    ReleaseMrswLockExclusive(&Context->Lock, Irql, FALSE);
+
+    Trace("<====\n");
+
+    return STATUS_SUCCESS;
+
+fail2:
+    Error("fail2\n");
+
+    SUSPEND(Release, Context->SuspendInterface);
+    Context->SuspendInterface = NULL;
+
+fail1:
+    Error("fail1 (%08x)\n", status);
+
+    RtlZeroMemory(&Context->Callback, sizeof (VIF_CALLBACK));
+
+    ReleaseMrswLockExclusive(&Context->Lock, Irql, FALSE);
+
+    return status;
+}
+
+static VOID
+VifDisable(
+    IN  PXENVIF_VIF_CONTEXT Context
+    )
+{
+    PXENVIF_FRONTEND        Frontend = PdoGetFrontend(Context->Pdo);
+    KIRQL                   Irql;
+    PKEVENT                 Event;
+
+    Trace("====>\n");
+
+    AcquireMrswLockExclusive(&Context->Lock, &Irql);
+
+    if (!Context->Enabled) {
+        ReleaseMrswLockExclusive(&Context->Lock, Irql, FALSE);
+        goto done;
+    }
+
+    Context->Enabled = FALSE;
+
+    Event = MacGetEvent(FrontendGetMac(Frontend));
+    KeSetEvent(Event, IO_NO_INCREMENT, FALSE);
+
+    SUSPEND(Deregister,
+            Context->SuspendInterface,
+            Context->SuspendCallbackLate);
+    Context->SuspendCallbackLate = NULL;
+
+    SUSPEND(Release, Context->SuspendInterface);
+    Context->SuspendInterface = NULL;
+
+    (VOID) FrontendSetState(Frontend, FRONTEND_CONNECTED);
+
+    ReleaseMrswLockExclusive(&Context->Lock, Irql, TRUE);
+
+    ReceiverWaitForPackets(FrontendGetReceiver(Frontend));
+    TransmitterAbortPackets(FrontendGetTransmitter(Frontend));
+
+    KeClearEvent(&Context->MonitorEvent);
+    ThreadWake(Context->MonitorThread);
+
+    Trace("waiting for monitor thread\n");
+
+    (VOID) KeWaitForSingleObject(&Context->MonitorEvent,
+                                 Executive,
+                                 KernelMode,
+                                 FALSE,
+                                 NULL);
+
+    RtlZeroMemory(&Context->Callback, sizeof (VIF_CALLBACK));
+
+    ReleaseMrswLockShared(&Context->Lock);
+
+done:
+    Trace("<====\n");
+}
+
+static VOID
+VifQueryPacketStatistics(
+    IN  PXENVIF_VIF_CONTEXT         Context,
+    OUT PXENVIF_PACKET_STATISTICS   Statistics
+    )
+{
+    PXENVIF_FRONTEND                Frontend = PdoGetFrontend(Context->Pdo);
+
+    AcquireMrswLockShared(&Context->Lock);
+
+    ReceiverGetPacketStatistics(FrontendGetReceiver(Frontend),
+                                &Statistics->Receiver);
+    TransmitterGetPacketStatistics(FrontendGetTransmitter(Frontend),
+                                   &Statistics->Transmitter);
+
+    ReleaseMrswLockShared(&Context->Lock);
+}
+
+static VOID
+VifUpdatePacketMetadata(
+    IN  PXENVIF_VIF_CONTEXT                 Context,
+    IN  PXENVIF_TRANSMITTER_PACKET_METADATA Metadata
+    )
+{
+    PXENVIF_FRONTEND                        Frontend = PdoGetFrontend(Context->Pdo);
+
+    AcquireMrswLockShared(&Context->Lock);
+
+    TransmitterSetPacketMetadata(FrontendGetTransmitter(Frontend),
+                                 *Metadata);
+    
+    ReleaseMrswLockShared(&Context->Lock);
+}
+
+static VOID
+VifReturnPacket(
+    IN  PXENVIF_VIF_CONTEXT     Context,
+    IN  PXENVIF_RECEIVER_PACKET Packet
+    )
+{
+    PXENVIF_FRONTEND            Frontend = PdoGetFrontend(Context->Pdo);
+
+    AcquireMrswLockShared(&Context->Lock);
+
+    ReceiverReturnPacket(FrontendGetReceiver(Frontend),
+                         Packet);
+
+    ReleaseMrswLockShared(&Context->Lock);
+}
+
+static NTSTATUS
+VifQueuePackets(
+    IN  PXENVIF_VIF_CONTEXT         Context,
+    IN  PXENVIF_TRANSMITTER_PACKET  HeadPacket
+    )
+{
+    PXENVIF_FRONTEND                Frontend = PdoGetFrontend(Context->Pdo);
+    NTSTATUS                        status;
+
+    AcquireMrswLockShared(&Context->Lock);
+
+    status = STATUS_UNSUCCESSFUL;
+    if (Context->Enabled == FALSE) {
+        Trace("NOT ENABLED\n");
+
+        goto fail1;
+    }
+
+    TransmitterQueuePackets(FrontendGetTransmitter(Frontend),
+                            HeadPacket);            
+
+    ReleaseMrswLockShared(&Context->Lock);
+
+    return STATUS_SUCCESS;
+
+fail1:
+    ReleaseMrswLockShared(&Context->Lock);
+
+    return status;
+}
+
+static VOID
+VifQueryOffloadOptions(
+    IN  PXENVIF_VIF_CONTEXT     Context,
+    OUT PXENVIF_OFFLOAD_OPTIONS Options
+    )
+{
+    PXENVIF_FRONTEND            Frontend = PdoGetFrontend(Context->Pdo);
+
+    AcquireMrswLockShared(&Context->Lock);
+
+    TransmitterGetOffloadOptions(FrontendGetTransmitter(Frontend),
+                                 Options);
+
+    ReleaseMrswLockShared(&Context->Lock);
+}
+
+static NTSTATUS
+VifUpdateOffloadOptions(
+    IN  PXENVIF_VIF_CONTEXT     Context,
+    IN  XENVIF_OFFLOAD_OPTIONS  Options
+    )
+{
+    PXENVIF_FRONTEND            Frontend = PdoGetFrontend(Context->Pdo);
+    NTSTATUS                    status;
+
+    AcquireMrswLockShared(&Context->Lock);
+
+    status = ReceiverSetOffloadOptions(FrontendGetReceiver(Frontend),
+                                       Options);
+
+    ReleaseMrswLockShared(&Context->Lock);
+
+    return status;
+}
+
+static VOID
+VifQueryLargePacketSize(
+    IN  PXENVIF_VIF_CONTEXT Context,
+    IN  UCHAR               Version,
+    OUT PULONG              Size
+    )
+{
+    PXENVIF_FRONTEND        Frontend = PdoGetFrontend(Context->Pdo);
+
+    AcquireMrswLockShared(&Context->Lock);
+
+    *Size = TransmitterGetLargePacketSize(FrontendGetTransmitter(Frontend), Version);
+
+    ReleaseMrswLockShared(&Context->Lock);
+}
+
+static VOID
+VifQueryMediaState(
+    IN  PXENVIF_VIF_CONTEXT         Context,
+    OUT PNET_IF_MEDIA_CONNECT_STATE MediaConnectState OPTIONAL,
+    OUT PULONG64                    LinkSpeed OPTIONAL,
+    OUT PNET_IF_MEDIA_DUPLEX_STATE  MediaDuplexState OPTIONAL
+    )
+{
+    PXENVIF_FRONTEND                Frontend = PdoGetFrontend(Context->Pdo);
+
+    AcquireMrswLockShared(&Context->Lock);
+
+    if (Context->Enabled && MacGetLinkState(FrontendGetMac(Frontend))) {
+        if (MediaConnectState != NULL)
+            *MediaConnectState = MediaConnectStateConnected;
+
+        if (LinkSpeed != NULL)
+            *LinkSpeed = (ULONGLONG)MacGetLinkSpeed(FrontendGetMac(Frontend)) * 1000000000ull;
+
+        if (MediaDuplexState != NULL)
+            *MediaDuplexState = MediaDuplexStateFull;
+    } else {
+        if (MediaConnectState != NULL)
+            *MediaConnectState = MediaConnectStateDisconnected;
+
+        if (LinkSpeed != NULL)
+            *LinkSpeed = 0;
+
+        if (MediaDuplexState != NULL)
+            *MediaDuplexState = MediaDuplexStateUnknown;
+    }
+
+    ReleaseMrswLockShared(&Context->Lock);
+}
+
+static VOID
+VifQueryMaximumFrameSize(
+    IN  PXENVIF_VIF_CONTEXT Context,
+    OUT PULONG              Size
+    )
+{
+    PXENVIF_FRONTEND        Frontend = PdoGetFrontend(Context->Pdo);
+
+    AcquireMrswLockShared(&Context->Lock);
+
+    *Size = MacGetMaximumFrameSize(FrontendGetMac(Frontend));
+
+    ReleaseMrswLockShared(&Context->Lock);
+}
+
+static VOID
+VifQueryPermanentAddress(
+    IN  PXENVIF_VIF_CONTEXT Context,
+    OUT PETHERNET_ADDRESS   Address
+    )
+{
+    PXENVIF_FRONTEND        Frontend = PdoGetFrontend(Context->Pdo);
+
+    AcquireMrswLockShared(&Context->Lock);
+
+    *Address = *MacGetPermanentAddress(FrontendGetMac(Frontend));
+
+    ReleaseMrswLockShared(&Context->Lock);
+}
+
+static VOID
+VifQueryCurrentAddress(
+    IN  PXENVIF_VIF_CONTEXT Context,
+    OUT PETHERNET_ADDRESS   Address
+    )
+{
+    PXENVIF_FRONTEND        Frontend = PdoGetFrontend(Context->Pdo);
+
+    AcquireMrswLockShared(&Context->Lock);
+
+    *Address = *MacGetCurrentAddress(FrontendGetMac(Frontend));
+
+    ReleaseMrswLockShared(&Context->Lock);
+}
+
+static NTSTATUS
+VifUpdateCurrentAddress(
+    IN  PXENVIF_VIF_CONTEXT Context,
+    OUT PETHERNET_ADDRESS   Address
+    )
+{
+    PXENVIF_FRONTEND        Frontend = PdoGetFrontend(Context->Pdo);
+    NTSTATUS                status;
+
+    AcquireMrswLockShared(&Context->Lock);
+
+    status = MacSetCurrentAddress(FrontendGetMac(Frontend), Address);
+
+    ReleaseMrswLockShared(&Context->Lock);
+
+    return status;
+}
+
+static NTSTATUS
+VifQueryMulticastAddresses(
+    IN  PXENVIF_VIF_CONTEXT Context,
+    OUT ETHERNET_ADDRESS    Address[] OPTIONAL,
+    OUT PULONG              Count
+    )
+{
+    PXENVIF_FRONTEND        Frontend = PdoGetFrontend(Context->Pdo);
+    ULONG                   Size;
+    NTSTATUS                status;
+
+    AcquireMrswLockShared(&Context->Lock);
+
+    Size = sizeof (ETHERNET_ADDRESS) * *Count;
+
+    (VOID) MacGetMulticastAddresses(FrontendGetMac(Frontend),
+                                    Count);
+
+    status = STATUS_BUFFER_TOO_SMALL;
+    if (Size < sizeof (ETHERNET_ADDRESS) * *Count)
+        goto fail1;
+
+    RtlCopyMemory(Address,
+                  MacGetMulticastAddresses(FrontendGetMac(Frontend), Count),
+                  Size);
+
+    ReleaseMrswLockShared(&Context->Lock);
+
+    return STATUS_SUCCESS;
+
+fail1:
+    ReleaseMrswLockShared(&Context->Lock);
+
+    return status;
+}
+
+static NTSTATUS
+VifUpdateMulticastAddresses(
+    IN  PXENVIF_VIF_CONTEXT Context,
+    IN  ETHERNET_ADDRESS    Address[],
+    IN  ULONG               Count
+    )
+{
+    PXENVIF_FRONTEND        Frontend = PdoGetFrontend(Context->Pdo);
+    NTSTATUS                status;
+
+    AcquireMrswLockShared(&Context->Lock);
+
+    status = MacSetMulticastAddresses(FrontendGetMac(Frontend),
+                                      Address,
+                                      Count);
+
+    ReleaseMrswLockShared(&Context->Lock);
+
+    return status;
+}
+
+static VOID
+VifQueryFilterLevel(
+    IN  PXENVIF_VIF_CONTEXT         Context,
+    IN  ETHERNET_ADDRESS_TYPE       Type,
+    OUT PXENVIF_MAC_FILTER_LEVEL    Level
+    )
+{
+    PXENVIF_FRONTEND                Frontend = PdoGetFrontend(Context->Pdo);
+
+    AcquireMrswLockShared(&Context->Lock);
+
+    *Level = MacGetFilterLevel(FrontendGetMac(Frontend),
+                               Type);
+
+    ReleaseMrswLockShared(&Context->Lock);
+}
+
+static NTSTATUS
+VifUpdateFilterLevel(
+    IN  PXENVIF_VIF_CONTEXT     Context,
+    IN  ETHERNET_ADDRESS_TYPE   Type,
+    IN  XENVIF_MAC_FILTER_LEVEL Level
+    )
+{
+    PXENVIF_FRONTEND            Frontend = PdoGetFrontend(Context->Pdo);
+    NTSTATUS                    status;
+
+    AcquireMrswLockShared(&Context->Lock);
+
+    status = MacSetFilterLevel(FrontendGetMac(Frontend),
+                               Type,
+                               Level);
+
+    ReleaseMrswLockShared(&Context->Lock);
+
+    return status;
+}
+
+static VOID
+VifQueryReceiverRingSize(
+    IN  PXENVIF_VIF_CONTEXT Context,
+    OUT PULONG              Size
+    )
+{
+    PXENVIF_FRONTEND        Frontend = PdoGetFrontend(Context->Pdo);
+
+    AcquireMrswLockShared(&Context->Lock);
+
+    *Size = ReceiverGetRingSize(FrontendGetReceiver(Frontend));
+
+    ReleaseMrswLockShared(&Context->Lock);
+}
+
+static VOID
+VifQueryTransmitterRingSize(
+    IN  PXENVIF_VIF_CONTEXT Context,
+    OUT PULONG              Size
+    )
+{
+    PXENVIF_FRONTEND        Frontend = PdoGetFrontend(Context->Pdo);
+
+    AcquireMrswLockShared(&Context->Lock);
+
+    *Size = TransmitterGetRingSize(FrontendGetTransmitter(Frontend));
+
+    ReleaseMrswLockShared(&Context->Lock);
+}
+
+static VOID
+VifAcquire(
+    IN  PXENVIF_VIF_CONTEXT Context
+    )
+{
+    InterlockedIncrement(&Context->References);
+}
+
+static VOID
+VifRelease(
+    IN  PXENVIF_VIF_CONTEXT Context
+    )
+{
+    ASSERT(Context->References != 0);
+    InterlockedDecrement(&Context->References);
+}
+
+#define VIF_OPERATION(_Type, _Name, _Arguments) \
+        Vif ## _Name,
+
+static XENVIF_VIF_OPERATIONS  Operations = {
+    DEFINE_VIF_OPERATIONS
+};
+
+#undef VIF_OPERATION
+
+NTSTATUS
+VifInitialize(
+    IN  PXENVIF_PDO             Pdo,
+    OUT PXENVIF_VIF_INTERFACE   Interface
+    )
+{
+    PXENVIF_VIF_CONTEXT         Context;
+    NTSTATUS                    status;
+
+    Context = __VifAllocate(sizeof (XENVIF_VIF_CONTEXT));
+
+    status = STATUS_NO_MEMORY;
+    if (Context == NULL)
+        goto fail1;
+
+    InitializeMrswLock(&Context->Lock);
+
+    Context->Pdo = Pdo;
+
+    KeInitializeEvent(&Context->MonitorEvent, NotificationEvent, FALSE);
+
+    status = ThreadCreate(VifMonitor, Context, &Context->MonitorThread);
+    if (!NT_SUCCESS(status))
+        goto fail2;
+
+    Interface->Context = Context;
+    Interface->Operations = &Operations;
+
+    return STATUS_SUCCESS;
+
+fail2:
+    Error("fail2\n");
+
+    RtlZeroMemory(&Context->MonitorEvent, sizeof (KEVENT));
+
+    Context->Pdo = NULL;
+    RtlZeroMemory(&Context->Lock, sizeof (XENVIF_MRSW_LOCK));
+
+    ASSERT(IsZeroMemory(Context, sizeof (XENVIF_VIF_CONTEXT)));
+    __VifFree(Context);
+
+fail1:
+    Error("fail1 (%08x)\n", status);
+
+    return status;
+}
+
+VOID
+VifTeardown(
+    IN OUT  PXENVIF_VIF_INTERFACE   Interface
+    )
+{
+    PXENVIF_VIF_CONTEXT             Context = Interface->Context;
+
+    ThreadAlert(Context->MonitorThread);
+    ThreadJoin(Context->MonitorThread);
+    Context->MonitorThread = NULL;
+
+    RtlZeroMemory(&Context->MonitorEvent, sizeof (KEVENT));
+
+    Context->Pdo = NULL;
+    RtlZeroMemory(&Context->Lock, sizeof (XENVIF_MRSW_LOCK));
+
+    ASSERT(IsZeroMemory(Context, sizeof (XENVIF_VIF_CONTEXT)));
+    __VifFree(Context);
+
+    RtlZeroMemory(Interface, sizeof (XENVIF_VIF_INTERFACE));
+}
+
diff --git a/src/xenvif/vif.h b/src/xenvif/vif.h
new file mode 100644 (file)
index 0000000..0f4c285
--- /dev/null
@@ -0,0 +1,72 @@
+/* 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 _XENVIF_VIF_H
+#define _XENVIF_VIF_H
+
+#include <ntddk.h>
+#include <vif_interface.h>
+
+#include "fdo.h"
+
+struct _XENVIF_VIF_INTERFACE {
+    PXENVIF_VIF_OPERATIONS  Operations;
+    PXENVIF_VIF_CONTEXT     Context;
+};
+
+C_ASSERT(FIELD_OFFSET(XENVIF_VIF_INTERFACE, Operations) == (ULONG_PTR)VIF_OPERATIONS(NULL));
+C_ASSERT(FIELD_OFFSET(XENVIF_VIF_INTERFACE, Context) == (ULONG_PTR)VIF_CONTEXT(NULL));
+
+extern NTSTATUS
+VifInitialize(
+    IN  PXENVIF_PDO                 Pdo,
+    OUT PXENVIF_VIF_INTERFACE       Interface
+    );
+
+extern VOID
+VifTeardown(
+    IN OUT  PXENVIF_VIF_INTERFACE   Interface
+    );
+
+extern VOID
+VifCompletePackets(
+    IN  PXENVIF_VIF_INTERFACE       Interface,
+    IN  PXENVIF_TRANSMITTER_PACKET  HeadPacket
+    );
+
+extern VOID
+VifReceivePackets(
+    IN  PXENVIF_VIF_INTERFACE   Interface,
+    IN  PLIST_ENTRY             List
+    );
+
+#endif  // _XENVIF_VIF_H
+
diff --git a/src/xenvif/xenvif.rc b/src/xenvif/xenvif.rc
new file mode 100644 (file)
index 0000000..6410e56
--- /dev/null
@@ -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
+
+#define        VER_COMPANYNAME_STR         "Citrix Systems Inc."
+#define VER_LEGALCOPYRIGHT_STR      "Copyright " YEAR_STR VER_COMPANYNAME_STR
+
+#include <version.h>
+
+#define VER_PRODUCTNAME_STR         "Citrix PV Tools for Virtual Machines"
+#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        "XENVIF.SYS"
+#define VER_FILEDESCRIPTION_STR     "Citrix VIF Class Driver"
+
+#define VER_FILETYPE                VFT_DRV
+#define VER_FILESUBTYPE             VFT2_DRV_SYSTEM
+
+#include <common.ver>