]> xenbits.xensource.com Git - pvdrivers/win/xenvkbd.git/commitdiff
Initial commit
authorOwen Smith <owen.smith@citrix.com>
Fri, 2 Jun 2017 13:36:26 +0000 (14:36 +0100)
committerPaul Durrant <paul.durrant@citrix.com>
Fri, 2 Jun 2017 13:36:26 +0000 (14:36 +0100)
Signed-off-by: Owen Smith <owen.smith@citrix.com>
87 files changed:
.gitignore [new file with mode: 0644]
BUILD.md [new file with mode: 0644]
INSTALL.md [new file with mode: 0644]
INTERFACES.md [new file with mode: 0644]
LICENSE [new file with mode: 0644]
MAINTAINERS [new file with mode: 0644]
README.md [new file with mode: 0644]
build.py [new file with mode: 0644]
clean.py [new file with mode: 0644]
get_xen_headers.py [new file with mode: 0644]
include/cache_interface.h [new file with mode: 0644]
include/debug_interface.h [new file with mode: 0644]
include/evtchn_interface.h [new file with mode: 0644]
include/gnttab_interface.h [new file with mode: 0644]
include/hid_interface.h [new file with mode: 0644]
include/range_set_interface.h [new file with mode: 0644]
include/revision.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/unplug_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/public/arch-x86/xen-x86_32.h [new file with mode: 0644]
include/xen/public/arch-x86/xen-x86_64.h [new file with mode: 0644]
include/xen/public/arch-x86/xen.h [new file with mode: 0644]
include/xen/public/errno.h [new file with mode: 0644]
include/xen/public/grant_table.h [new file with mode: 0644]
include/xen/public/io/kbdif.h [new file with mode: 0644]
include/xen/public/io/ring.h [new file with mode: 0644]
include/xen/public/io/xenbus.h [new file with mode: 0644]
include/xen/public/trace.h [new file with mode: 0644]
include/xen/public/xen-compat.h [new file with mode: 0644]
include/xen/public/xen.h [new file with mode: 0644]
include/xen/xen/errno.h [new file with mode: 0644]
kdfiles.py [new file with mode: 0644]
msbuild.bat [new file with mode: 0644]
src/coinst/coinst.c [new file with mode: 0644]
src/coinst/xenvkbd_coinst.def [new file with mode: 0644]
src/xenvkbd.inf [new file with mode: 0644]
src/xenvkbd.pfx [new file with mode: 0644]
src/xenvkbd/assert.h [new file with mode: 0644]
src/xenvkbd/bus.c [new file with mode: 0644]
src/xenvkbd/bus.h [new file with mode: 0644]
src/xenvkbd/dbg_print.h [new file with mode: 0644]
src/xenvkbd/driver.c [new file with mode: 0644]
src/xenvkbd/driver.h [new file with mode: 0644]
src/xenvkbd/fdo.c [new file with mode: 0644]
src/xenvkbd/fdo.h [new file with mode: 0644]
src/xenvkbd/frontend.c [new file with mode: 0644]
src/xenvkbd/frontend.h [new file with mode: 0644]
src/xenvkbd/hid.c [new file with mode: 0644]
src/xenvkbd/hid.h [new file with mode: 0644]
src/xenvkbd/mrsw.h [new file with mode: 0644]
src/xenvkbd/mutex.h [new file with mode: 0644]
src/xenvkbd/names.h [new file with mode: 0644]
src/xenvkbd/pdo.c [new file with mode: 0644]
src/xenvkbd/pdo.h [new file with mode: 0644]
src/xenvkbd/registry.c [new file with mode: 0644]
src/xenvkbd/registry.h [new file with mode: 0644]
src/xenvkbd/ring.c [new file with mode: 0644]
src/xenvkbd/ring.h [new file with mode: 0644]
src/xenvkbd/thread.c [new file with mode: 0644]
src/xenvkbd/thread.h [new file with mode: 0644]
src/xenvkbd/types.h [new file with mode: 0644]
src/xenvkbd/util.h [new file with mode: 0644]
src/xenvkbd/vkbd.h [new file with mode: 0644]
src/xenvkbd/xenvkbd.rc [new file with mode: 0644]
vs2013/configs.props [new file with mode: 0644]
vs2013/package/package.vcxproj [new file with mode: 0644]
vs2013/package/package.vcxproj.user [new file with mode: 0644]
vs2013/targets.props [new file with mode: 0644]
vs2013/xenvkbd.sln [new file with mode: 0644]
vs2013/xenvkbd/xenvkbd.vcxproj [new file with mode: 0644]
vs2013/xenvkbd/xenvkbd.vcxproj.user [new file with mode: 0644]
vs2013/xenvkbd_coinst/xenvkbd_coinst.vcxproj [new file with mode: 0644]
vs2013/xenvkbd_coinst/xenvkbd_coinst.vcxproj.user [new file with mode: 0644]
vs2015/configs.props [new file with mode: 0644]
vs2015/package/package.vcxproj [new file with mode: 0644]
vs2015/package/package.vcxproj.user [new file with mode: 0644]
vs2015/targets.props [new file with mode: 0644]
vs2015/xenvkbd.sln [new file with mode: 0644]
vs2015/xenvkbd/xenvkbd.vcxproj [new file with mode: 0644]
vs2015/xenvkbd/xenvkbd.vcxproj.user [new file with mode: 0644]
vs2015/xenvkbd_coinst/xenvkbd_coinst.vcxproj [new file with mode: 0644]
vs2015/xenvkbd_coinst/xenvkbd_coinst.vcxproj.user [new file with mode: 0644]

diff --git a/.gitignore b/.gitignore
new file mode 100644 (file)
index 0000000..baee20d
--- /dev/null
@@ -0,0 +1,7 @@
+# Cscope/Tags
+/cscope.files
+/cscope.out
+/tags
+
+# Output
+/xenvkbd
diff --git a/BUILD.md b/BUILD.md
new file mode 100644 (file)
index 0000000..0c68b20
--- /dev/null
+++ b/BUILD.md
@@ -0,0 +1,59 @@
+Building the XenVkbd Package
+============================
+
+First you'll need a device driver build environment for Windows 8, Windows
+8.1, or Windows 10.
+For Windows 8 this means:
+
+*   Visual Studio 2012 (Professional or Ultimate)
+*   Windows Driver Kit 8
+
+For Windows 8.1 this means:
+
+*   Visual Studio 2013 (Any SKU, including Express)
+*   Windows Driver Kit 8.1
+
+For Windows 10 this means:
+
+*   Visual Studio 2015 (Any SKU, including Express or Community)
+*   Windows Driver Kit 10
+
+(See http://msdn.microsoft.com/en-us/windows/hardware/hh852365.aspx). You
+may find it useful to install VirtualCloneDrive from http://www.slysoft.com
+as Visual Studio is generally supplied in ISO form.
+
+Install Visual Studio first (you only need install MFC for C++) and then
+the WDK. Set an environment variable called VS to the base of the Visual
+Studio Installation (e.g. C:\Program Files\Microsoft Visual Studio 12.0) and
+a variable called KIT to the base of the WDK
+(e.g. C:\Program Files\Windows Kits\8.1). Also set an environment variable
+called SYMBOL\_SERVER to point at a location where driver symbols can be
+stored. This can be local directory e.g. C:\Symbols.
+
+NOTE: If you are using WDK 10 then you will need to acquire the DIFx
+      re-distributable package from one of the other WDKs, so that the
+      driver build can copy dpinst.exe into the output.
+      Set the environment variable DPINST_REDIST to the base dpinst
+      directory (i.e. the directory under which the x86 and x64 sub-
+      directories containing dpinst.exe can be found).
+
+Next you'll need a 3.x version of python (which you can get from
+http://www.python.org). Make sure python.exe is somewhere on your default
+path.
+
+Now fire up a Command Prompt and navigate to the base of your git repository.
+At the prompt type:
+
+    build.py checked
+
+This will create a debug build of the driver. To create a non-debug build
+type:
+
+    build.py free
+
+Note that Static Driver Verifier is run by default as part of the build
+process. This can be very time consuming. If you don't want to run the
+verifier then you can add the 'nosdv' keyword to the end of your command
+e.g.:
+
+    build.py free nosdv
diff --git a/INSTALL.md b/INSTALL.md
new file mode 100644 (file)
index 0000000..d23b88d
--- /dev/null
@@ -0,0 +1,26 @@
+Installing XenVkbd
+==================
+
+It's important to note that the build scripts generate a driver which is
+*test signed*. This means that when the driver is installed on a 64-bit
+version of Windows you must enabled testsigning mode otherwise your system
+will fail signature verification checked on the next reboot.
+If you wish to install the test certificate on the target system then copy
+xenvkbd.pfx (which you'll find in he proj subdirectory) onto your system and
+use certmgr to install it. (It is not password protected).
+
+xenvkbd.sys binds to one of three devices which may be created by XenBus:
+
+1. XENBUS\\VEN_XSC000&DEV_VKBD&REV_00000001
+2. XENBUS\\VEN_XS0001&DEV_VKBD&REV_00000001
+3. XENBUS\\VEN_XS0002&DEV_VKBD&REV_00000001
+
+The particular device present in your VM will be determined by the binding
+of the XenBus driver. The DeviceID of the PCI device to which it is bound is
+echoed in the VEN_ substring of the devices it creates. Hence only one of the
+above three variants will be present.
+
+To install the driver on your target system, copy the contents of the xenvkbd
+subdirectory onto the system, then navigate into the copy, to either the x86
+or x64 subdirectory (whichever is appropriate), and execute the copy of
+dpinst.exe you find there with Administrator privilege.
diff --git a/INTERFACES.md b/INTERFACES.md
new file mode 100644 (file)
index 0000000..9d296d5
--- /dev/null
@@ -0,0 +1,33 @@
+Interface Versions and PDO Revisions\r
+====================================\r
+\r
+It is important that introduction of a new API, introduction of a new\r
+version of an existing API or retirement of an old version of an API is\r
+managed carefully to avoid incompatibilities between clients and\r
+providers. The general API versioning policy is described below:\r
+\r
+Each distinct set of API versions exported by a bus driver maps to a PDO\r
+revision. The DeviceID of each PDO created will specify the latest\r
+revision supported and all others will be contained within the\r
+HardwareIDs and CompatibleIDs. When a new version of an API is added,\r
+a new PDO revision must be added. When a version of an API is removed\r
+then ALL revisions that API version maps to must be removed. The mapping\r
+of interface versions to PDO revisions is specified in the header file\r
+include/revision.h in the bus driver source repository.\r
+\r
+Whe introducing a new version of an interface in a bus driver it is good\r
+practice to continue to support the previous version so it is not\r
+necessary to simultaneously introduce a new PDO revision and retire a\r
+previous one that child drivers may still be binding to.\r
+Child drivers should, of course, always be built to use the latest\r
+interface versions (which can be copied from the include directory in the\r
+source repository of the bus driver providing them) but it may take\r
+some time to make the necessary changes and deploy new builds of child\r
+drivers and so some overlap is desirable.\r
+\r
+To try to avoid installation of a version of a bus driver that is\r
+incompatible with child drivers installed on a system. There is a check\r
+in the pre-install phase in the co-intaller which compares the\r
+MatchingDeviceId values for each child driver against the table in\r
+include/revision.h in the bus driver source to make sure that the\r
+matching revision number is present.\r
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/MAINTAINERS b/MAINTAINERS
new file mode 100644 (file)
index 0000000..88cc97a
--- /dev/null
@@ -0,0 +1,51 @@
+List of maintainers and how to submit changes
+=============================================
+
+If you wish to submit code, we recommend first reaching out to the maintainers,
+who will advise you on the precise procedure they wish to use.
+
+We also request you follow these basic guidelines:
+
+1.  Make sure you test your changes on both a 32- and 64-bit version of Windows.
+    (The more versions of Windows you can test on the better).
+
+2.  Make sure your changes do not introduce any new prefast warnings.
+
+3.  Make a patch available to the relevant maintainer in the list. Use 'diff -u'
+    to make the patch easy to merge. Be prepared to get your changes sent back
+    with seemingly silly requests about formatting and variable names. These
+    aren't as silly as they seem. One job the maintainers do is to keep things
+    looking the same.
+
+    NOTE that all source should have Unix line endings.
+
+    PLEASE see http://wiki.xen.org/wiki/Submitting_Xen_Patches for hints on how
+    to submit a patch in a suitable form. Whilst the PV driver source
+    repositories are distinct from the Xen Project hypervisor source, we will
+    follow the same general patch submission and review process.
+
+    PLEASE try to include any credit lines you want added with the  patch. It
+    avoids people being missed off by mistake and makes it easier to know who
+    wants adding and who doesn't.
+
+    PLEASE document known bugs. If it doesn't work for everything or does
+    something very odd once a month document it.
+
+    PLEASE remember that submissions must be made under the terms of the
+    "Developer's Certificate of Origin" (DCO) and should include a
+    Signed-off-by: line.
+
+4.  Make sure you have the right to submit any changes you make. If you do 
+    changes at work you may find your employer owns the patches instead of 
+    you.
+
+
+Maintainers List
+----------------
+
+* Paul Durrant <paul.durrant@citrix.com>
+
+* Ben Chalmers <ben.chalmers@citrix.com>
+
+* Owen Smith <owen.smith@citrix.com>
+
diff --git a/README.md b/README.md
new file mode 100644 (file)
index 0000000..04ec1ba
--- /dev/null
+++ b/README.md
@@ -0,0 +1,56 @@
+XenVkbd - The Xen Paravitual Keyboard/Mouse Driver for Windows
+==============================================================
+
+The XenVkbd package consists of a single device driver:
+
+*    xenvkbd.sys is a bus driver which attaches to a virtual device created
+     by XenBus and creates a child device for each VKBD for XenHid to attach
+     to.
+     It is also a protocol driver for the kbdif wire protocol (see
+     include\\xen\\io\\kbdif.h).
+
+Quick Start Guide
+=================
+
+Building the driver
+-------------------
+
+See BUILD.md
+
+Installing the driver
+---------------------
+
+See INSTALL.md
+
+Driver Interfaces
+=================
+
+See INTERFACES.md
+
+Miscellaneous
+=============
+
+For convenience the source repository includes some other scripts:
+
+kdfiles.py
+----------
+
+This generates two files called kdfiles32.txt and kdfiles64.txt which can
+be used as map files for the .kdfiles WinDBG command.
+
+sdv.py
+------
+
+This runs Static Driver Verifier on the source.
+
+clean.py
+--------
+
+This removes any files not checked into the repository and not covered by
+the .gitignore file.
+
+get_xen_headers.py
+------------------
+
+This will import any necessary headers from a given tag of that Xen
+repository at git://xenbits.xen.org/xen.git.
diff --git a/build.py b/build.py
new file mode 100644 (file)
index 0000000..35c991b
--- /dev/null
+++ b/build.py
@@ -0,0 +1,412 @@
+#!python -u
+
+import os, sys
+import datetime
+import re
+import glob
+import tarfile
+import subprocess
+import shutil
+import time
+
+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 VENDOR_NAME_STR\t\t"' + os.environ['VENDOR_NAME'] + '"\n')
+    file.write('#define VENDOR_PREFIX_STR\t"' + os.environ['VENDOR_PREFIX'] + '"\n')
+
+    if 'VENDOR_DEVICE_ID' in os.environ.keys():
+        file.write('#define VENDOR_DEVICE_ID_STR\t"' + os.environ['VENDOR_DEVICE_ID'] + '"\n')
+
+    file.write('#define PRODUCT_NAME_STR\t"' + os.environ['PRODUCT_NAME'] + '"\n')
+    file.write('\n')
+
+    file.write('#define MAJOR_VERSION\t\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\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\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\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\t\t' + str(now.year) + '\n')
+    file.write('#define YEAR_STR\t\t"' + str(now.year) + '"\n')
+    file.write('\n')
+
+    file.write('#define MONTH\t\t\t' + str(now.month) + '\n')
+    file.write('#define MONTH_STR\t\t"' + str(now.month) + '"\n')
+    file.write('\n')
+
+    file.write('#define DAY\t\t\t' + str(now.day) + '\n')
+    file.write('#define DAY_STR\t\t\t"' + str(now.day) + '"\n')
+    file.write('\n')
+
+    file.close()
+
+
+def copy_inf(vs, name):
+    src = open('src\\%s.inf' % name, 'r')
+    dst = open('%s\\%s.inf' % (vs, 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)
+        line = re.sub('@VENDOR_NAME@', os.environ['VENDOR_NAME'], line)
+        line = re.sub('@VENDOR_PREFIX@', os.environ['VENDOR_PREFIX'], line)
+        line = re.sub('@PRODUCT_NAME@', os.environ['PRODUCT_NAME'], line)
+
+        if re.search('@VENDOR_DEVICE_ID@', line):
+            if 'VENDOR_DEVICE_ID' not in os.environ.keys():
+                continue
+            line = re.sub('@VENDOR_DEVICE_ID@', os.environ['VENDOR_DEVICE_ID'], 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(release, debug):
+    configuration = release
+
+    if debug:
+        configuration += ' Debug'
+    else:
+        configuration += ' Release'
+
+    return configuration
+
+
+def get_target_path(release, arch, debug, vs):
+    configuration = get_configuration(release, debug)
+    name = ''.join(configuration.split(' '))
+    target = { 'x86': os.sep.join([name, 'Win32']), 'x64': os.sep.join([name, 'x64']) }
+    target_path = os.sep.join([vs, target[arch]])
+
+    return target_path
+
+
+def shell(command, dir):
+    print(dir)
+    print(command)
+    sys.stdout.flush()
+    
+    sub = subprocess.Popen(' '.join(command), cwd=dir,
+                           stdout=subprocess.PIPE,
+                           stderr=subprocess.STDOUT)
+
+    for line in sub.stdout:
+        print(line.decode(sys.getdefaultencoding()).rstrip())
+
+    sub.wait()
+
+    return sub.returncode
+
+
+class msbuild_failure(Exception):
+    def __init__(self, value):
+        self.value = value
+    def __str__(self):
+        return repr(self.value)
+
+def msbuild(platform, configuration, target, file, args, dir):
+    os.environ['PLATFORM'] = platform
+    os.environ['CONFIGURATION'] = configuration
+    os.environ['TARGET'] = target
+    os.environ['FILE'] = file
+    os.environ['EXTRA'] = args
+
+    bin = os.path.join(os.getcwd(), 'msbuild.bat')
+
+    status = shell([bin], dir)
+
+    if (status != 0):
+        raise msbuild_failure(configuration)
+
+
+def build_sln(name, release, arch, debug, vs):
+    configuration = get_configuration(release, debug)
+
+    if arch == 'x86':
+        platform = 'Win32'
+    elif arch == 'x64':
+        platform = 'x64'
+
+    cwd = os.getcwd()
+
+    msbuild(platform, configuration, 'Build', name + '.sln', '', vs)
+
+
+def remove_timestamps(path):
+    try:
+        os.unlink(path + '.orig')
+    except OSError:
+        pass
+
+    os.rename(path, path + '.orig')
+
+    src = open(path + '.orig', 'r')
+    dst = open(path, 'w')
+
+    for line in src:
+        if line.find('TimeStamp') == -1:
+            dst.write(line)
+
+    dst.close()
+    src.close()
+
+def run_sdv(name, dir, vs):
+    release = { 'vs2012':'Windows 8',
+                'vs2013':'Windows 8',
+                'vs2015':'Windows 10' }
+
+    configuration = get_configuration(release[vs], False)
+    platform = 'x64'
+
+    msbuild(platform, configuration, 'Build', name + '.vcxproj',
+            '', os.path.join(vs, name))
+
+    msbuild(platform, configuration, 'sdv', name + '.vcxproj',
+            '/p:Inputs="/clean"', os.path.join(vs, name))
+
+    msbuild(platform, configuration, 'sdv', name + '.vcxproj',
+            '/p:Inputs="/check:default.sdv"', os.path.join(vs, name))
+
+    path = [vs, name, 'sdv', 'SDV.DVL.xml']
+    remove_timestamps(os.path.join(*path))
+
+    msbuild(platform, configuration, 'dvl', name + '.vcxproj',
+            '', os.path.join(vs, name))
+
+    path = [vs, name, name + '.DVL.XML']
+    shutil.copy(os.path.join(*path), dir)
+
+    path = [vs, name, 'refine.sdv']
+    if os.path.isfile(os.path.join(*path)):
+        msbuild(platform, configuration, 'sdv', name + '.vcxproj',
+                '/p:Inputs=/refine', os.path.join(vs, name))
+
+
+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(command, None)
+
+
+def symstore_add(name, release, arch, debug, vs):
+    target_path = get_target_path(release, arch, debug, vs)
+
+    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']])
+
+    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(command, target_path)
+
+
+def manifest():
+    cmd = ['git', 'ls-tree', '-r', '--name-only', 'HEAD']
+
+    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:
+            tar.add(name)
+        except:
+            pass
+    tar.close()
+
+
+def getVsVersion():
+    vsenv ={} 
+    vars = subprocess.check_output([os.environ['VS']+'\\VC\\vcvarsall.bat', 
+                                        '&&', 'set'], 
+                                    shell=True)
+    for var in vars.splitlines():
+        k, _, v = map(str.strip, var.strip().decode('utf-8').partition('='))
+        if k.startswith('?'):
+            continue
+        vsenv[k] = v
+
+    mapping = { '11.0':'vs2012',
+                '12.0':'vs2013',
+                '14.0':'vs2015' }
+
+    return mapping[vsenv['VisualStudioVersion']]
+
+
+if __name__ == '__main__':
+    debug = { 'checked': True, 'free': False }
+    sdv = { 'nosdv': False, None: True }
+    driver = 'xenvkbd'
+    vs = getVsVersion()
+
+    if 'VENDOR_NAME' not in os.environ.keys():
+        os.environ['VENDOR_NAME'] = 'Xen Project'
+
+    if 'VENDOR_PREFIX' not in os.environ.keys():
+        os.environ['VENDOR_PREFIX'] = 'XP'
+
+    if 'PRODUCT_NAME' not in os.environ.keys():
+        os.environ['PRODUCT_NAME'] = 'Xen'
+
+    os.environ['MAJOR_VERSION'] = '9'
+    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()
+
+    if 'GIT_REVISION' in os.environ.keys():
+        revision = open('revision', 'w')
+        print(os.environ['GIT_REVISION'], file=revision)
+        revision.close()
+
+    print("VENDOR_NAME\t\t'%s'" % os.environ['VENDOR_NAME'])
+    print("VENDOR_PREFIX\t\t'%s'" % os.environ['VENDOR_PREFIX'])
+    print("PRODUCT_NAME\t\t'%s'" % os.environ['PRODUCT_NAME'])
+    print("MAJOR_VERSION\t\t%s" % os.environ['MAJOR_VERSION'])
+    print("MINOR_VERSION\t\t%s" % os.environ['MINOR_VERSION'])
+    print("MICRO_VERSION\t\t%s" % os.environ['MICRO_VERSION'])
+    print("BUILD_NUMBER\t\t%s" % os.environ['BUILD_NUMBER'])
+    print()
+
+    make_header()
+    copy_inf(vs, driver)
+
+    symstore_del(driver, 30)
+
+    release = { 'vs2012':'Windows Vista',
+                'vs2013':'Windows 7',
+                'vs2015':'Windows 8' }
+
+    build_sln(driver, release[vs], 'x86', debug[sys.argv[1]], vs)
+    build_sln(driver, release[vs], 'x64', debug[sys.argv[1]], vs)
+
+    symstore_add(driver, release[vs], 'x86', debug[sys.argv[1]], vs)
+    symstore_add(driver, release[vs], 'x64', debug[sys.argv[1]], vs)
+
+    if len(sys.argv) <= 2 or sdv[sys.argv[2]]:
+        run_sdv('xenvkbd', driver, vs)
+
+    archive(driver + '\\source.tgz', manifest().splitlines(), tgz=True)
+    archive(driver + '.tar', [driver,'revision'])
+
diff --git a/clean.py b/clean.py
new file mode 100644 (file)
index 0000000..88d6ed6
--- /dev/null
+++ b/clean.py
@@ -0,0 +1,21 @@
+#!/usr/bin/env python
+
+import os, sys, shutil
+
+if __name__ == '__main__':
+    file = os.popen('git status -u --porcelain')
+
+    for line in file:
+        item = line.split(' ')
+        if item[0] == '??':
+            path = ' '.join(item[1:]).rstrip()
+            print(path)
+            try:
+                if os.path.isfile(path):
+                    os.remove(path)
+                if os.path.isdir(path):
+                    shutil.rmtree(path)
+            except OSError:
+                None
+                
+    file.close()
diff --git a/get_xen_headers.py b/get_xen_headers.py
new file mode 100644 (file)
index 0000000..f197d80
--- /dev/null
@@ -0,0 +1,81 @@
+#!python -u
+
+import os, sys
+import shutil
+import subprocess
+import re
+
+def shell(command, dir = '.'):
+    print("in '%s' execute '%s'" % (dir, ' '.join(command)))
+    sys.stdout.flush()
+
+    sub = subprocess.Popen(' '.join(command), cwd=dir,
+                           stdout=subprocess.PIPE,
+                           stderr=subprocess.STDOUT)
+
+    for line in sub.stdout:
+        print(line.decode(sys.getdefaultencoding()).rstrip())
+
+    sub.wait()
+
+    return sub.returncode
+
+def get_repo(url, working):
+    shell(['git', 'clone', '--no-checkout', url, working])
+
+def get_branch(tag, working):
+    shell(['git', 'checkout', '-b', 'tmp', tag], working)
+
+def put_branch(working):
+    shell(['git', 'checkout', 'master'], working)
+    shell(['git', 'branch', '-d', 'tmp'], working)
+
+def copy_file(working, dir, name):
+    try:
+        os.makedirs('include\\xen\\%s' % dir)
+    except OSError:
+        None
+
+    src = open('%s\\xen\\include\\%s\\%s' % (working, dir, name), 'r')
+    dst = open('include\\xen\\%s\\%s' % (dir, name), 'w', newline='\n')
+
+    print(name)
+
+    for line in src:
+        line = re.sub(' unsigned long', ' ULONG_PTR', line)
+        line = re.sub('\(unsigned long', '(ULONG_PTR', line)
+        line = re.sub(' long', ' LONG_PTR', line)
+        line = re.sub('\(long', '(LONG_PTR', line)
+        dst.write(line)
+
+    dst.close()
+    src.close()
+
+if __name__ == '__main__':
+    tag = sys.argv[1]
+    working = sys.argv[2]
+
+    get_repo('git://xenbits.xen.org/xen.git', working)
+    get_branch(tag, working)
+
+    shell(['git', 'rm', '-r', '-f', 'xen'], 'include')
+
+    copy_file(working, 'public', 'xen.h')
+    copy_file(working, 'public', 'xen-compat.h')
+    copy_file(working, 'public', 'trace.h')
+    copy_file(working, 'public', 'grant_table.h')
+    copy_file(working, 'public', 'errno.h')
+
+    copy_file(working, 'xen', 'errno.h')
+
+    copy_file(working, 'public\\arch-x86', 'xen.h')
+    copy_file(working, 'public\\arch-x86', 'xen-x86_32.h')
+    copy_file(working, 'public\\arch-x86', 'xen-x86_64.h')
+
+    copy_file(working, 'public\\io', 'ring.h')
+    copy_file(working, 'public\\io', 'kbdif.h')
+    copy_file(working, 'public\\io', 'xenbus.h')
+
+    put_branch(working)
+
+    shell(['git', 'add', 'xen'], 'include')
diff --git a/include/cache_interface.h b/include/cache_interface.h
new file mode 100644 (file)
index 0000000..dae3ac6
--- /dev/null
@@ -0,0 +1,233 @@
+/* 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.
+ */
+
+/*! \file cache_interface.h
+    \brief XENBUS CACHE Interface
+
+    This interface provides access to XENBUS's object cache
+    implementation.
+*/
+
+#ifndef _XENBUS_CACHE_INTERFACE_H
+#define _XENBUS_CACHE_INTERFACE_H
+
+#ifndef _WINDLL
+
+/*! \typedef XENBUS_CACHE
+    \brief Cache handle
+*/
+typedef struct _XENBUS_CACHE    XENBUS_CACHE, *PXENBUS_CACHE;
+
+/*! \typedef XENBUS_CACHE_ACQUIRE
+    \brief Acquire a reference to the CACHE interface
+
+    \param Interface The interface header
+*/  
+typedef NTSTATUS
+(*XENBUS_CACHE_ACQUIRE)(
+    IN  PINTERFACE  Interface
+    );
+
+/*! \typedef XENBUS_CACHE_RELEASE
+    \brief Release a reference to the CACHE interface
+
+    \param Interface The interface header
+*/  
+typedef VOID
+(*XENBUS_CACHE_RELEASE)(
+    IN  PINTERFACE  Interface
+    );
+
+/*! \typedef XENBUS_CACHE_CTOR
+    \brief Object creator callback
+
+    \param Argument Context \a Argument supplied to \a XENBUS_CACHE_CREATE
+    \param Object Newly allocated object
+
+    This callback is invoked just after a new object is allocated and may
+    be used to initialize any object data prior to its insertion into the
+    cache.
+*/
+typedef NTSTATUS
+(*XENBUS_CACHE_CTOR)(
+    IN  PVOID   Argument,
+    IN  PVOID   Object
+    );
+
+/*! \typedef XENBUS_CACHE_DTOR
+    \brief Object destructor callback
+
+    \param Argument Context \a Argument supplied to \a XENBUS_CACHE_CREATE
+    \param Object Object about to be freed
+
+    This callback is invoked just after an object is removed from the
+    cache and before it is freed and may be used to tear down any object data.
+*/
+typedef VOID
+(*XENBUS_CACHE_DTOR)(
+    IN  PVOID   Argument,
+    IN  PVOID   Object
+    );
+
+/*! \typedef XENBUS_CACHE_ACQUIRE_LOCK
+    \brief Cache lock callback
+
+    \param Argument Context \a Argument supplied to \a XENBUS_CACHE_CREATE
+
+    This callback is invoked if the cache implementation requires mutual
+    exclusion.
+*/
+typedef VOID
+(*XENBUS_CACHE_ACQUIRE_LOCK)(
+    IN  PVOID   Argument
+    );
+
+/*! \typedef XENBUS_CACHE_RELEASE_LOCK
+    \brief Cache unlock callback
+
+    \param Argument Context \a Argument supplied to \a XENBUS_CACHE_CREATE
+
+    This callback is invoked to release the mutual exclusion lock acquired
+    by a previous invocation of \a XENBUS_CACHE_ACQUIRE_LOCK.
+*/
+typedef VOID
+(*XENBUS_CACHE_RELEASE_LOCK)(
+    IN  PVOID   Argument
+    );
+
+/*! \typedef XENBUS_CACHE_CREATE
+    \brief Create a cache of objects of the given \a Size
+
+    \param Interface The interface header
+    \param Name A name for the cache which will be used in debug output
+    \param Size The size of each object in bytes
+    \param Reservation The target minimum population of the cache
+    \param Ctor A callback which is invoked when a new object created
+    \param Dtor A callback which is invoked when an object is destroyed
+    \param AcquireLock A callback invoked to acquire a spinlock
+    \param ReleaseLock A callback invoked to release the spinlock
+    \param Argument An optional context argument passed to the callbacks
+    \param Cache A pointer to a cache handle to be initialized
+
+    If a non-zero \a Reservation is specified then this method will fail
+    unless that number of objects can be immediately created.
+*/  
+typedef NTSTATUS
+(*XENBUS_CACHE_CREATE)(
+    IN  PINTERFACE                  Interface,
+    IN  const CHAR                  *Name,
+    IN  ULONG                       Size,
+    IN  ULONG                       Reservation,
+    IN  XENBUS_CACHE_CTOR           Ctor,
+    IN  XENBUS_CACHE_DTOR           Dtor,
+    IN  XENBUS_CACHE_ACQUIRE_LOCK   AcquireLock,
+    IN  XENBUS_CACHE_RELEASE_LOCK   ReleaseLock,
+    IN  PVOID                       Argument OPTIONAL,
+    OUT PXENBUS_CACHE               *Cache
+    );
+
+/*! \typedef XENBUS_CACHE_GET
+    \brief Get an object from a \a Cache
+
+    \param Interface The interface header
+    \param Cache The cache handle
+    \param Locked If mutually exclusive access to the cache is already
+    guaranteed then set this to TRUE
+*/
+typedef PVOID
+(*XENBUS_CACHE_GET)(
+    IN  PINTERFACE      Interface,
+    IN  PXENBUS_CACHE   Cache,
+    IN  BOOLEAN         Locked
+    );
+
+/*! \typedef XENBUS_CACHE_PUT
+    \brief Return an object to a \a Cache
+
+    \param Interface The interface header
+    \param Cache The cache handle
+    \param Locked If mutually exclusive access to the cache is already
+    guaranteed then set this to TRUE
+*/
+typedef VOID
+(*XENBUS_CACHE_PUT)(
+    IN  PINTERFACE      Interface,
+    IN  PXENBUS_CACHE   Cache,
+    IN  PVOID           Object,
+    IN  BOOLEAN         Locked
+    );
+
+/*! \typedef XENBUS_CACHE_DESTROY
+    \brief Destroy a \a Cache
+
+    \param Interface The interface header
+    \param Cache The cache handle
+
+    All objects must have been returned to the cache prior to destruction
+*/
+typedef VOID
+(*XENBUS_CACHE_DESTROY)(
+    IN  PINTERFACE      Interface,
+    IN  PXENBUS_CACHE   Cache
+    );
+
+// {A98DFD78-416A-4949-92A5-E084F2F4B44E}
+DEFINE_GUID(GUID_XENBUS_CACHE_INTERFACE, 
+0xa98dfd78, 0x416a, 0x4949, 0x92, 0xa5, 0xe0, 0x84, 0xf2, 0xf4, 0xb4, 0x4e);
+
+/*! \struct _XENBUS_CACHE_INTERFACE_V1
+    \brief CACHE interface version 1
+    \ingroup interfaces
+*/
+struct _XENBUS_CACHE_INTERFACE_V1 {
+    INTERFACE               Interface;
+    XENBUS_CACHE_ACQUIRE    CacheAcquire;
+    XENBUS_CACHE_RELEASE    CacheRelease;
+    XENBUS_CACHE_CREATE     CacheCreate;
+    XENBUS_CACHE_GET        CacheGet;
+    XENBUS_CACHE_PUT        CachePut;
+    XENBUS_CACHE_DESTROY    CacheDestroy;
+};
+
+typedef struct _XENBUS_CACHE_INTERFACE_V1 XENBUS_CACHE_INTERFACE, *PXENBUS_CACHE_INTERFACE;
+
+/*! \def XENBUS_CACHE
+    \brief Macro at assist in method invocation
+*/
+#define XENBUS_CACHE(_Method, _Interface, ...)    \
+    (_Interface)->Cache ## _Method((PINTERFACE)(_Interface), __VA_ARGS__)
+
+#endif  // _WINDLL
+
+#define XENBUS_CACHE_INTERFACE_VERSION_MIN  1
+#define XENBUS_CACHE_INTERFACE_VERSION_MAX  1
+
+#endif  // _XENBUS_CACHE_INTERFACE_H
diff --git a/include/debug_interface.h b/include/debug_interface.h
new file mode 100644 (file)
index 0000000..cc71db0
--- /dev/null
@@ -0,0 +1,175 @@
+/* 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.
+ */
+
+/*! \file debug_interface.h
+    \brief XENBUS DEBUG Interface
+
+    This interface provides to register and invoke debug callbacks
+*/
+
+#ifndef _XENBUS_DEBUG_INTERFACE_H
+#define _XENBUS_DEBUG_INTERFACE_H
+
+#ifndef _WINDLL
+
+/*! \typedef XENBUS_DEBUG_CALLBACK
+    \brief Debug callback handle
+*/
+typedef struct _XENBUS_DEBUG_CALLBACK   XENBUS_DEBUG_CALLBACK, *PXENBUS_DEBUG_CALLBACK;
+
+/*! \typedef XENBUS_DEBUG_ACQUIRE
+    \brief Acquire a reference to the DEBUG interface
+
+    \param Interface The interface header
+*/  
+typedef NTSTATUS
+(*XENBUS_DEBUG_ACQUIRE)(
+    IN  PINTERFACE  Interface
+    );
+
+/*! \typedef XENBUS_DEBUG_RELEASE
+    \brief Release a reference to the DEBUG interface
+
+    \param Interface The interface header
+*/  
+typedef VOID
+(*XENBUS_DEBUG_RELEASE)(
+    IN  PINTERFACE  Interface
+    );
+
+/*! \typedef XENBUS_DEBUG_FUNCTION
+    \brief Debug callback function
+
+    \param Argument Context \a Argument supplied to \a XENBUS_DEBUG_REGISTER
+    \param Crashing This is set to TRUE if the function is invoked as
+    part of pre-crash logging
+
+    Debug callback functions are always invoked with IRQL == HIGH_LEVEL
+*/  
+typedef VOID
+(*XENBUS_DEBUG_FUNCTION)(
+    IN  PVOID   Argument,
+    IN  BOOLEAN Crashing
+    );
+
+/*! \typedef XENBUS_DEBUG_REGISTER
+    \brief Register a debug callback function
+
+    \param Interface The interface header
+    \param Prefix A prefix applied to each line logged by \a XENBUS_DEBUG_PRINTF
+    \param Function The callback function
+    \param Argument An optional context argument passed to the callback
+    \param Callback A pointer to a callback handle to be initialized
+*/  
+typedef NTSTATUS
+(*XENBUS_DEBUG_REGISTER)(
+    IN  PINTERFACE              Interface,
+    IN  PCHAR                   Prefix,
+    IN  XENBUS_DEBUG_FUNCTION   Function,
+    IN  PVOID                   Argument OPTIONAL,
+    OUT PXENBUS_DEBUG_CALLBACK  *Callback
+    );
+
+/*! \typedef XENBUS_DEBUG_PRINTF
+    \brief Print a debug message in to the log
+
+    \param Interface The interface header
+    \param Format A format specifier
+    \param ... Additional parameters required by \a Format
+
+    This method must only be invoked from the context of a debug
+    callback
+*/  
+typedef VOID
+(*XENBUS_DEBUG_PRINTF)(
+    IN  PINTERFACE              Interface,
+    IN  const CHAR              *Format,
+    ...
+    );
+
+/*! \typedef XENBUS_DEBUG_DEREGISTER
+    \brief Deregister a debug callback function
+
+    \param Interface The interface header
+    \param Callback The callback handle
+*/
+typedef VOID
+(*XENBUS_DEBUG_DEREGISTER)(
+    IN  PINTERFACE              Interface,
+    IN  PXENBUS_DEBUG_CALLBACK  Callback
+    );
+
+/*! \typedef XENBUS_DEBUG_TRIGGER
+    \brief Invoke debug callback functions
+
+    \param Interface The interface header
+    \param Callback Optional argument to restrict invocation to a singe
+    debug callback (NULL invokes all debug callbacks)
+*/
+typedef VOID
+(*XENBUS_DEBUG_TRIGGER)(
+    IN  PINTERFACE              Interface,
+    IN  PXENBUS_DEBUG_CALLBACK  Callback OPTIONAL
+    );
+
+// {0DF600AE-6B20-4227-BF94-03DA9A26A114}
+DEFINE_GUID(GUID_XENBUS_DEBUG_INTERFACE, 
+0xdf600ae, 0x6b20, 0x4227, 0xbf, 0x94, 0x3, 0xda, 0x9a, 0x26, 0xa1, 0x14);
+
+/*! \struct _XENBUS_DEBUG_INTERFACE_V1
+    \brief DEBUG interface version 1
+    \ingroup interfaces
+*/
+struct _XENBUS_DEBUG_INTERFACE_V1 {
+    INTERFACE               Interface;
+    XENBUS_DEBUG_ACQUIRE    DebugAcquire;
+    XENBUS_DEBUG_RELEASE    DebugRelease;
+    XENBUS_DEBUG_REGISTER   DebugRegister;
+    XENBUS_DEBUG_PRINTF     DebugPrintf;
+    XENBUS_DEBUG_TRIGGER    DebugTrigger;
+    XENBUS_DEBUG_DEREGISTER DebugDeregister;
+};
+
+typedef struct _XENBUS_DEBUG_INTERFACE_V1 XENBUS_DEBUG_INTERFACE, *PXENBUS_DEBUG_INTERFACE;
+
+/*! \def XENBUS_DEBUG
+    \brief Macro at assist in method invocation
+*/
+#define XENBUS_DEBUG(_Method, _Interface, ...)    \
+    (_Interface)->Debug ## _Method((PINTERFACE)(_Interface), __VA_ARGS__)
+
+#endif  // _WINDLL
+
+#define XENBUS_DEBUG_INTERFACE_VERSION_MIN  1
+#define XENBUS_DEBUG_INTERFACE_VERSION_MAX  1
+
+#endif  // _XENBUS_DEBUG_INTERFACE_H
+
diff --git a/include/evtchn_interface.h b/include/evtchn_interface.h
new file mode 100644 (file)
index 0000000..a9952d6
--- /dev/null
@@ -0,0 +1,325 @@
+/* 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.
+ */
+
+/*! \file evtchn_interface.h
+    \brief XENBUS EVTCHN Interface
+
+    This interface provides access to hypervisor event channels
+*/
+
+#ifndef _XENBUS_EVTCHN_INTERFACE_H
+#define _XENBUS_EVTCHN_INTERFACE_H
+
+#ifndef _WINDLL
+
+/*! \enum _XENBUS_EVTCHN_TYPE
+    \brief Event channel type to be opened
+*/
+typedef enum _XENBUS_EVTCHN_TYPE {
+    XENBUS_EVTCHN_TYPE_INVALID = 0,
+    XENBUS_EVTCHN_TYPE_FIXED,           /*!< Fixed */
+    XENBUS_EVTCHN_TYPE_UNBOUND,         /*!< Unbound */
+    XENBUS_EVTCHN_TYPE_INTER_DOMAIN,    /*!< Interdomain */
+    XENBUS_EVTCHN_TYPE_VIRQ             /*!< VIRQ */
+} XENBUS_EVTCHN_TYPE, *PXENBUS_EVTCHN_TYPE;
+
+/*! \typedef XENBUS_EVTCHN_CHANNEL
+    \brief Event channel handle
+*/  
+typedef struct _XENBUS_EVTCHN_CHANNEL XENBUS_EVTCHN_CHANNEL, *PXENBUS_EVTCHN_CHANNEL;
+
+/*! \typedef XENBUS_EVTCHN_ACQUIRE
+    \brief Acquire a reference to the EVTCHN interface
+
+    \param Interface The interface header
+*/  
+typedef NTSTATUS
+(*XENBUS_EVTCHN_ACQUIRE)(
+    IN  PINTERFACE  Interface
+    );
+
+/*! \typedef XENBUS_EVTCHN_RELEASE
+    \brief Release a reference to the EVTCHN interface
+
+    \param Interface The interface header
+*/  
+typedef VOID
+(*XENBUS_EVTCHN_RELEASE)(
+    IN  PINTERFACE  Interface
+    );
+
+/*! \typedef XENBUS_EVTCHN_OPEN
+    \brief Open an event channel
+
+    \param Interface The interface header
+    \param Type The type of event channel to open
+    \param Function The callback function
+    \param Argument An optional context argument passed to the callback
+    \param ... Additional parameters required by \a Type
+
+    \b Fixed:
+    \param LocalPort The local port number of the (already bound) channel
+    \param Mask Set to TRUE if the channel should be automatically masked before invoking the callback
+
+    \b Unbound:
+    \param RemoteDomain The domid of the remote domain which will bind the channel
+    \param Mask Set to TRUE if the channel should be automatically masked before invoking the callback
+
+    \b Interdomain:
+    \param RemoteDomain The domid of the remote domain which has already bound the channel
+    \param RemotePort The port number bound to the channel in the remote domain
+    \param Mask Set to TRUE if the channel should be automatically masked before invoking the callback
+
+    \b VIRQ:
+    \param Index The index number of the VIRQ
+
+    \return Event channel handle
+*/  
+typedef PXENBUS_EVTCHN_CHANNEL
+(*XENBUS_EVTCHN_OPEN)(
+    IN  PINTERFACE          Interface,
+    IN  XENBUS_EVTCHN_TYPE  Type,
+    IN  PKSERVICE_ROUTINE   Function,
+    IN  PVOID               Argument OPTIONAL,
+    ...
+    );
+
+typedef NTSTATUS
+(*XENBUS_EVTCHN_BIND_V2)(
+    IN  PINTERFACE              Interface,
+    IN  PXENBUS_EVTCHN_CHANNEL  Channel,
+    IN  ULONG                   Cpu
+    );
+
+/*! \typedef XENBUS_EVTCHN_BIND
+    \brief Bind an event channel to a specific CPU
+
+    \param Interface The interface header
+    \param Channel The channel handle
+    \param Group The group number of the CPU that should handle events
+    \param Number The relative number of the CPU that should handle events
+*/
+typedef NTSTATUS
+(*XENBUS_EVTCHN_BIND)(
+    IN  PINTERFACE              Interface,
+    IN  PXENBUS_EVTCHN_CHANNEL  Channel,
+    IN  USHORT                  Group,
+    IN  UCHAR                   Number
+    );
+
+typedef BOOLEAN
+(*XENBUS_EVTCHN_UNMASK_V1)(
+    IN  PINTERFACE              Interface,
+    IN  PXENBUS_EVTCHN_CHANNEL  Channel,
+    IN  BOOLEAN                 InCallback
+    );
+
+/*! \typedef XENBUS_EVTCHN_UNMASK
+    \brief Unmask an event channel
+
+    \param Interface The interface header
+    \param Channel The channel handle
+    \param InCallback Set to TRUE if this method is invoked in context of the channel callback
+*/
+typedef VOID
+(*XENBUS_EVTCHN_UNMASK)(
+    IN  PINTERFACE              Interface,
+    IN  PXENBUS_EVTCHN_CHANNEL  Channel,
+    IN  BOOLEAN                 InCallback
+    );
+
+/*! \typedef XENBUS_EVTCHN_SEND
+    \brief Send an event to the remote end of the channel
+
+    \param Interface The interface header
+    \param Channel The channel handle
+*/  
+typedef VOID
+(*XENBUS_EVTCHN_SEND)(
+    IN  PINTERFACE              Interface,
+    IN  PXENBUS_EVTCHN_CHANNEL  Channel
+    );
+
+/*! \typedef XENBUS_EVTCHN_TRIGGER
+    \brief Send an event to the local end of the channel
+
+    \param Interface The interface header
+    \param Channel The channel handle
+*/  
+typedef VOID
+(*XENBUS_EVTCHN_TRIGGER)(
+    IN  PINTERFACE              Interface,
+    IN  PXENBUS_EVTCHN_CHANNEL  Channel
+    );
+
+/*! \typedef XENBUS_EVTCHN_WAIT
+    \brief Wait for an event to the local end of the channel
+
+    \param Interface The interface header
+    \param Channel The channel handle
+    \param Timeout An optional timeout value (similar to KeWaitForSingleObject(), but non-zero values are allowed at DISPATCH_LEVEL).
+*/
+typedef NTSTATUS
+(*XENBUS_EVTCHN_WAIT)(
+    IN  PINTERFACE              Interface,
+    IN  PXENBUS_EVTCHN_CHANNEL  Channel,
+    IN  PLARGE_INTEGER          Timeout OPTIONAL
+    );
+
+/*! \typedef XENBUS_EVTCHN_GET_PORT
+    \brief Get the local port number bound to the channel
+
+    \param Interface The interface header
+    \param Channel The channel handle
+    \return The port number
+*/  
+typedef ULONG
+(*XENBUS_EVTCHN_GET_PORT)(
+    IN  PINTERFACE              Interface,
+    IN  PXENBUS_EVTCHN_CHANNEL  Channel
+    );
+
+/*! \typedef XENBUS_EVTCHN_CLOSE
+    \brief Close an event channel
+
+    \param Interface The interface header
+    \param Channel The channel handle
+*/  
+typedef VOID
+(*XENBUS_EVTCHN_CLOSE)(
+    IN  PINTERFACE              Interface,
+    IN  PXENBUS_EVTCHN_CHANNEL  Channel
+    );
+
+// {BE2440AC-1098-4150-AF4D-452FADCEF923}
+DEFINE_GUID(GUID_XENBUS_EVTCHN_INTERFACE, 
+0xbe2440ac, 0x1098, 0x4150, 0xaf, 0x4d, 0x45, 0x2f, 0xad, 0xce, 0xf9, 0x23);
+
+/*! \struct _XENBUS_EVTCHN_INTERFACE_V1
+    \brief EVTCHN interface version 1
+    \ingroup interfaces
+*/
+struct _XENBUS_EVTCHN_INTERFACE_V1 {
+    INTERFACE               Interface;
+    XENBUS_EVTCHN_ACQUIRE   EvtchnAcquire;
+    XENBUS_EVTCHN_RELEASE   EvtchnRelease;
+    XENBUS_EVTCHN_OPEN      EvtchnOpen;
+    XENBUS_EVTCHN_UNMASK_V1 EvtchnUnmaskVersion1;
+    XENBUS_EVTCHN_SEND      EvtchnSend;
+    XENBUS_EVTCHN_TRIGGER   EvtchnTrigger;
+    XENBUS_EVTCHN_GET_PORT  EvtchnGetPort;
+    XENBUS_EVTCHN_CLOSE     EvtchnClose;
+};
+
+/*! \struct _XENBUS_EVTCHN_INTERFACE_V2
+    \brief EVTCHN interface version 2
+    \ingroup interfaces
+*/
+struct _XENBUS_EVTCHN_INTERFACE_V2 {
+    INTERFACE               Interface;
+    XENBUS_EVTCHN_ACQUIRE   EvtchnAcquire;
+    XENBUS_EVTCHN_RELEASE   EvtchnRelease;
+    XENBUS_EVTCHN_OPEN      EvtchnOpen;
+    XENBUS_EVTCHN_BIND_V2   EvtchnBindVersion2;
+    XENBUS_EVTCHN_UNMASK_V1 EvtchnUnmaskVersion1;
+    XENBUS_EVTCHN_SEND      EvtchnSend;
+    XENBUS_EVTCHN_TRIGGER   EvtchnTrigger;
+    XENBUS_EVTCHN_GET_PORT  EvtchnGetPort;
+    XENBUS_EVTCHN_CLOSE     EvtchnClose;
+};
+
+/*! \struct _XENBUS_EVTCHN_INTERFACE_V3
+    \brief EVTCHN interface version 3
+    \ingroup interfaces
+*/
+struct _XENBUS_EVTCHN_INTERFACE_V3 {
+    INTERFACE               Interface;
+    XENBUS_EVTCHN_ACQUIRE   EvtchnAcquire;
+    XENBUS_EVTCHN_RELEASE   EvtchnRelease;
+    XENBUS_EVTCHN_OPEN      EvtchnOpen;
+    XENBUS_EVTCHN_BIND_V2   EvtchnBindVersion2;
+    XENBUS_EVTCHN_UNMASK    EvtchnUnmask;
+    XENBUS_EVTCHN_SEND      EvtchnSend;
+    XENBUS_EVTCHN_TRIGGER   EvtchnTrigger;
+    XENBUS_EVTCHN_GET_PORT  EvtchnGetPort;
+    XENBUS_EVTCHN_CLOSE     EvtchnClose;
+};
+
+/*! \struct _XENBUS_EVTCHN_INTERFACE_V4
+    \brief EVTCHN interface version 4
+    \ingroup interfaces
+*/
+struct _XENBUS_EVTCHN_INTERFACE_V4 {
+    INTERFACE               Interface;
+    XENBUS_EVTCHN_ACQUIRE   EvtchnAcquire;
+    XENBUS_EVTCHN_RELEASE   EvtchnRelease;
+    XENBUS_EVTCHN_OPEN      EvtchnOpen;
+    XENBUS_EVTCHN_BIND      EvtchnBind;
+    XENBUS_EVTCHN_UNMASK    EvtchnUnmask;
+    XENBUS_EVTCHN_SEND      EvtchnSend;
+    XENBUS_EVTCHN_TRIGGER   EvtchnTrigger;
+    XENBUS_EVTCHN_GET_PORT  EvtchnGetPort;
+    XENBUS_EVTCHN_CLOSE     EvtchnClose;
+};
+
+/*! \struct _XENBUS_EVTCHN_INTERFACE_V5
+    \brief EVTCHN interface version 5
+    \ingroup interfaces
+*/
+struct _XENBUS_EVTCHN_INTERFACE_V5 {
+    INTERFACE               Interface;
+    XENBUS_EVTCHN_ACQUIRE   EvtchnAcquire;
+    XENBUS_EVTCHN_RELEASE   EvtchnRelease;
+    XENBUS_EVTCHN_OPEN      EvtchnOpen;
+    XENBUS_EVTCHN_BIND      EvtchnBind;
+    XENBUS_EVTCHN_UNMASK    EvtchnUnmask;
+    XENBUS_EVTCHN_SEND      EvtchnSend;
+    XENBUS_EVTCHN_TRIGGER   EvtchnTrigger;
+    XENBUS_EVTCHN_WAIT      EvtchnWait;
+    XENBUS_EVTCHN_GET_PORT  EvtchnGetPort;
+    XENBUS_EVTCHN_CLOSE     EvtchnClose;
+};
+
+typedef struct _XENBUS_EVTCHN_INTERFACE_V5 XENBUS_EVTCHN_INTERFACE, *PXENBUS_EVTCHN_INTERFACE;
+
+/*! \def XENBUS_EVTCHN
+    \brief Macro at assist in method invocation
+*/
+#define XENBUS_EVTCHN(_Method, _Interface, ...)    \
+    (_Interface)->Evtchn ## _Method((PINTERFACE)(_Interface), __VA_ARGS__)
+
+#endif  // _WINDLL
+
+#define XENBUS_EVTCHN_INTERFACE_VERSION_MIN 1
+#define XENBUS_EVTCHN_INTERFACE_VERSION_MAX 5
+
+#endif  // _XENBUS_EVTCHN_INTERFACE_H
+
diff --git a/include/gnttab_interface.h b/include/gnttab_interface.h
new file mode 100644 (file)
index 0000000..d29440a
--- /dev/null
@@ -0,0 +1,199 @@
+/* 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.
+ */
+
+/*! \file gnttab_interface.h
+    \brief XENBUS GNTTAB Interface
+
+    This interface provides access to the hypervisor grant table
+*/
+
+#ifndef _XENBUS_GNTTAB_INTERFACE_H
+#define _XENBUS_GNTTAB_INTERFACE_H
+
+#include <cache_interface.h>
+
+#ifndef _WINDLL
+
+/*! \typedef XENBUS_GNTTAB_ENTRY
+    \brief Grant table entry handle
+*/
+typedef struct _XENBUS_GNTTAB_ENTRY XENBUS_GNTTAB_ENTRY, *PXENBUS_GNTTAB_ENTRY;
+
+/*! \typedef XENBUS_GNTTAB_CACHE
+    \brief Grant table cache handle
+*/
+typedef struct _XENBUS_GNTTAB_CACHE XENBUS_GNTTAB_CACHE, *PXENBUS_GNTTAB_CACHE;
+
+/*! \typedef XENBUS_GNTTAB_ACQUIRE
+    \brief Acquire a reference to the GNTTAB interface
+
+    \param Interface The interface header
+*/  
+typedef NTSTATUS
+(*XENBUS_GNTTAB_ACQUIRE)(
+    IN  PINTERFACE  Interface
+    );
+
+/*! \typedef XENBUS_GNTTAB_RELEASE
+    \brief Release a reference to the GNTTAB interface
+
+    \param Interface The interface header
+*/  
+typedef VOID
+(*XENBUS_GNTTAB_RELEASE)(
+    IN  PINTERFACE  Interface
+    );
+
+/*! \typedef XENBUS_GNTTAB_CREATE_CACHE
+    \brief Create a cache of grant table entries
+
+    \param Interface The interface header
+    \param Name A name for the cache which will be used in debug output
+    \param Reservation The target minimum population of the cache
+    \param AcquireLock A callback invoked to acquire a spinlock
+    \param ReleaseLock A callback invoked to release the spinlock
+    \param Argument An optional context argument passed to the callbacks
+    \param Cache A pointer to a grant table cache handle to be initialized
+*/  
+typedef NTSTATUS
+(*XENBUS_GNTTAB_CREATE_CACHE)(
+    IN  PINTERFACE                  Interface,
+    IN  const CHAR                  *Name,
+    IN  ULONG                       Reservation,
+    IN  XENBUS_CACHE_ACQUIRE_LOCK   AcquireLock,
+    IN  XENBUS_CACHE_RELEASE_LOCK   ReleaseLock,
+    IN  PVOID                       Argument OPTIONAL,
+    OUT PXENBUS_GNTTAB_CACHE        *Cache
+    );
+
+/*! \typedef XENBUS_GNTTAB_PERMIT_FOREIGN_ACCESS
+    \brief Get a table entry from the \a Cache permitting access to a given \a Pfn
+
+    \param Interface The interface header
+    \param Cache The grant table cache handle
+    \param Locked If mutually exclusive access to the cache is already
+    guaranteed then set this to TRUE
+    \param Domain The domid of the domain being granted access
+    \param Pfn The frame number of the page that we are granting access to
+    \param ReadOnly Set to TRUE if the foreign domain is only being granted
+    read access
+    \param Entry A pointer to a grant table entry handle to be initialized
+*/
+typedef NTSTATUS
+(*XENBUS_GNTTAB_PERMIT_FOREIGN_ACCESS)(
+    IN  PINTERFACE                  Interface,
+    IN  PXENBUS_GNTTAB_CACHE        Cache,
+    IN  BOOLEAN                     Locked,
+    IN  USHORT                      Domain,
+    IN  PFN_NUMBER                  Pfn,
+    IN  BOOLEAN                     ReadOnly,
+    OUT PXENBUS_GNTTAB_ENTRY        *Entry
+    );
+
+/*! \typedef XENBUS_GNTTAB_REVOKE_FOREIGN_ACCESS
+    \brief Revoke foreign access and return the \a Entry to the \a Cache
+
+    \param Interface The interface header
+    \param Cache The grant table cache handle
+    \param Locked If mutually exclusive access to the cache is already
+    guaranteed then set this to TRUE
+    \param Entry The grant table entry handle
+*/
+typedef NTSTATUS
+(*XENBUS_GNTTAB_REVOKE_FOREIGN_ACCESS)(
+    IN  PINTERFACE                  Interface,
+    IN  PXENBUS_GNTTAB_CACHE        Cache,
+    IN  BOOLEAN                     Locked,
+    IN  PXENBUS_GNTTAB_ENTRY        Entry
+    );
+
+/*! \typedef XENBUS_GNTTAB_GET_REFERENCE
+    \brief Get the reference number of the entry
+
+    \param Interface The interface header
+    \param Entry The grant table entry handle
+    \return The reference number
+*/  
+typedef ULONG
+(*XENBUS_GNTTAB_GET_REFERENCE)(
+    IN  PINTERFACE                  Interface,
+    IN  PXENBUS_GNTTAB_ENTRY        Entry
+    );
+
+/*! \typedef XENBUS_GNTTAB_DESTROY_CACHE
+    \brief Destroy a cache of grant table entries
+
+    \param Interface The interface header
+    \param Cache The grant table cache handle
+
+    All grant table entries must have been revoked prior to destruction
+    of the cache 
+*/  
+typedef VOID
+(*XENBUS_GNTTAB_DESTROY_CACHE)(
+    IN  PINTERFACE              Interface,
+    IN  PXENBUS_GNTTAB_CACHE    Cache
+    );
+
+// {763679C5-E5C2-4A6D-8B88-6BB02EC42D8E}
+DEFINE_GUID(GUID_XENBUS_GNTTAB_INTERFACE, 
+0x763679c5, 0xe5c2, 0x4a6d, 0x8b, 0x88, 0x6b, 0xb0, 0x2e, 0xc4, 0x2d, 0x8e);
+
+/*! \struct _XENBUS_GNTTAB_INTERFACE_V1
+    \brief GNTTAB interface version 1
+    \ingroup interfaces
+*/
+struct _XENBUS_GNTTAB_INTERFACE_V1 {
+    INTERFACE                           Interface;
+    XENBUS_GNTTAB_ACQUIRE               GnttabAcquire;
+    XENBUS_GNTTAB_RELEASE               GnttabRelease;
+    XENBUS_GNTTAB_CREATE_CACHE          GnttabCreateCache;
+    XENBUS_GNTTAB_PERMIT_FOREIGN_ACCESS GnttabPermitForeignAccess;
+    XENBUS_GNTTAB_REVOKE_FOREIGN_ACCESS GnttabRevokeForeignAccess;
+    XENBUS_GNTTAB_GET_REFERENCE         GnttabGetReference;
+    XENBUS_GNTTAB_DESTROY_CACHE         GnttabDestroyCache;
+};
+
+typedef struct _XENBUS_GNTTAB_INTERFACE_V1 XENBUS_GNTTAB_INTERFACE, *PXENBUS_GNTTAB_INTERFACE;
+
+/*! \def XENBUS_GNTTAB
+    \brief Macro at assist in method invocation
+*/
+#define XENBUS_GNTTAB(_Method, _Interface, ...)    \
+    (_Interface)->Gnttab ## _Method((PINTERFACE)(_Interface), __VA_ARGS__)
+
+#endif  // _WINDLL
+
+#define XENBUS_GNTTAB_INTERFACE_VERSION_MIN 1
+#define XENBUS_GNTTAB_INTERFACE_VERSION_MAX 1
+
+#endif  // _XENBUS_GNTTAB_INTERFACE_H
+
diff --git a/include/hid_interface.h b/include/hid_interface.h
new file mode 100644 (file)
index 0000000..08be5e0
--- /dev/null
@@ -0,0 +1,323 @@
+/* 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.
+ */
+
+/*! \file hid_interface.h
+    \brief XENHID HID Interface
+
+    This interface provides access to the PV network frontend
+*/
+
+#ifndef _XENHID_HID_INTERFACE_H
+#define _XENHID_HID_INTERFACE_H
+
+#ifndef _WINDLL
+
+/*! \typedef XENHID_HID_ACQUIRE
+    \brief Acquire a reference to the HID interface
+
+    \param Interface The interface header
+*/  
+typedef NTSTATUS
+(*XENHID_HID_ACQUIRE)(
+    IN  PINTERFACE  Interface
+    );
+
+/*! \typedef XENHID_HID_RELEASE
+    \brief Release a reference to the HID interface
+
+    \param Interface The interface header
+*/  
+typedef VOID
+(*XENHID_HID_RELEASE)(
+    IN  PINTERFACE  Interface
+    );
+
+/*! \typedef XENHID_HID_CALLBACK
+    \brief Provider to subscriber callback function
+
+    \param Argument An optional context argument passed to the callback
+    \param Buffer A HID report buffer to complete
+    \param Length The length of the \a Buffer
+*/
+typedef BOOLEAN
+(*XENHID_HID_CALLBACK)(
+    IN  PVOID       Argument OPTIONAL,
+    IN  PVOID       Buffer,
+    IN  ULONG       Length
+    );
+
+/*! \typedef XENHID_HID_ENABLE
+    \brief Enable the HID interface
+
+    All packets queued for transmit will be rejected and no packets will
+    be queued for receive until this method completes. 
+
+    \param Interface The interface header
+    \param Callback The subscriber's callback function
+    \param Argument An optional context argument passed to the callback
+*/
+typedef NTSTATUS
+(*XENHID_HID_ENABLE)(
+    IN  PINTERFACE          Interface,
+    IN  XENHID_HID_CALLBACK Callback,
+    IN  PVOID               Argument OPTIONAL
+    );
+
+/*! \typedef XENHID_HID_DISABLE
+    \brief Disable the HID interface
+
+    This method will not complete until any packets queued for receive
+    have been returned. Any packets queued for transmit may be aborted.
+
+    \param Interface The interface header
+*/
+typedef VOID
+(*XENHID_HID_DISABLE)(
+    IN  PINTERFACE  Interface
+    );
+
+/*! \typedef XENHID_HID_GET_DEVICE_ATTRIBUTES
+    \brief Get the HID Device Attributes structure
+
+*/
+typedef NTSTATUS
+(*XENHID_HID_GET_DEVICE_ATTRIBUTES)(
+    IN  PINTERFACE      Interface,
+    IN  PVOID           Buffer,
+    IN  ULONG           Length,
+    OUT PULONG          Returned
+    );
+
+/*! \typedef XENHID_HID_GET_DEVICE_DESCRIPTOR
+    \brief Get the HID Device Descriptor structure
+
+    \param Interface The interface header
+    \param Buffer The buffer to fill
+    \param Length The length of the buffer
+    \param Returned The number of bytes returned
+*/
+typedef NTSTATUS
+(*XENHID_HID_GET_DEVICE_DESCRIPTOR)(
+    IN  PINTERFACE      Interface,
+    IN  PVOID           Buffer,
+    IN  ULONG           Length,
+    OUT PULONG          Returned
+    );
+
+/*! \typedef XENHID_HID_GET_REPORT_DESCRIPTOR
+    \brief Get the HID Report Descriptor structure
+
+    \param Interface The interface header
+    \param Buffer The buffer to fill
+    \param Length The length of the buffer
+    \param Returned The number of bytes returned
+*/
+typedef NTSTATUS
+(*XENHID_HID_GET_REPORT_DESCRIPTOR)(
+    IN  PINTERFACE      Interface,
+    IN  PVOID           Buffer,
+    IN  ULONG           Length,
+    OUT PULONG          Returned
+    );
+
+/*! \typedef XENHID_HID_GET_STRING
+    \brief Get the HID Device Descriptor structure
+
+    \param Interface The interface header
+    \param Identifier The string identifier
+    \param Buffer The buffer to fill
+    \param Length The length of the buffer
+    \param Returned The number of bytes returned
+*/
+typedef NTSTATUS
+(*XENHID_HID_GET_STRING)(
+    IN  PINTERFACE      Interface,
+    IN  ULONG           Identifier,
+    IN  PVOID           Buffer,
+    IN  ULONG           Length,
+    OUT PULONG          Returned
+    );
+
+/*! \typedef XENHID_HID_GET_INDEXED_STRING
+    \brief Set the HID Device Descriptor structure
+
+    \param Interface The interface header
+    \param Index The index of the string
+    \param Buffer The buffer to fill
+    \param Length The length of the buffer
+    \param Returned The number of bytes returned
+*/
+typedef NTSTATUS
+(*XENHID_HID_GET_INDEXED_STRING)(
+    IN  PINTERFACE      Interface,
+    IN  ULONG           Index,
+    IN  PVOID           Buffer,
+    IN  ULONG           Length,
+    OUT PULONG          Returned
+    );
+
+/*! \typedef XENHID_HID_GET_FEATURE
+    \brief Get the feature
+
+    \param Interface The interface header
+    \param ReportId The report id to set
+    \param Buffer The report buffer
+    \param Length The length of the buffer
+    \param Returned The number of bytes returned
+*/
+typedef NTSTATUS
+(*XENHID_HID_GET_FEATURE)(
+    IN  PINTERFACE      Interface,
+    IN  ULONG           ReportId,
+    IN  PVOID           Buffer,
+    IN  ULONG           Length,
+    OUT PULONG          Returned
+    );
+
+/*! \typedef XENHID_HID_SET_FEATURE
+    \brief Set the feature report
+
+    \param Interface The interface header
+    \param ReportId The report id to set
+    \param Buffer The report buffer
+    \param Length The length of the buffer
+*/
+typedef NTSTATUS
+(*XENHID_HID_SET_FEATURE)(
+    IN  PINTERFACE      Interface,
+    IN  ULONG           ReportId,
+    IN  PVOID           Buffer,
+    IN  ULONG           Length
+    );
+
+/*! \typedef XENHID_HID_GET_INPUT_REPORT
+    \brief Get the input report
+
+    \param Interface The interface header
+    \param ReportId The report id to set
+    \param Buffer The report buffer
+    \param Length The length of the buffer
+    \param Returned The number of bytes returned
+*/
+typedef NTSTATUS
+(*XENHID_HID_GET_INPUT_REPORT)(
+    IN  PINTERFACE      Interface,
+    IN  ULONG           ReportId,
+    IN  PVOID           Buffer,
+    IN  ULONG           Length,
+    OUT PULONG          Returned
+    );
+
+/*! \typedef XENHID_HID_SET_OUTPUT_REPORT
+    \brief Set the output report
+
+    \param Interface The interface header
+    \param ReportId The report id to set
+    \param Buffer The write report buffer
+    \param Length The length of the buffer
+*/
+typedef NTSTATUS
+(*XENHID_HID_SET_OUTPUT_REPORT)(
+    IN  PINTERFACE      Interface,
+    IN  ULONG           ReportId,
+    IN  PVOID           Buffer,
+    IN  ULONG           Length
+    );
+
+/*! \typedef XENHID_HID_READ_REPORT
+    \brief Checks to see if any pending read reports
+           need completing. A single read report will be 
+           completed by calling the callback
+
+    \param Interface The interface header
+*/
+typedef VOID
+(*XENHID_HID_READ_REPORT)(
+    IN  PINTERFACE      Interface
+    );
+
+/*! \typedef XENHID_HID_WRITE_REPORT
+    \brief Set the output report
+
+    \param Interface The interface header
+    \param ReportId The report id to set
+    \param Buffer The write report buffer
+    \param Length The length of the buffer
+*/
+typedef NTSTATUS
+(*XENHID_HID_WRITE_REPORT)(
+    IN  PINTERFACE      Interface,
+    IN  ULONG           ReportId,
+    IN  PVOID           Buffer,
+    IN  ULONG           Length
+    );
+
+// {D215E1B5-8C38-420A-AEA6-02520DF3A621}\r
+DEFINE_GUID(GUID_XENHID_HID_INTERFACE,\r
+0xd215e1b5, 0x8c38, 0x420a, 0xae, 0xa6, 0x2, 0x52, 0xd, 0xf3, 0xa6, 0x21);\r
+
+/*! \struct _XENHID_HID_INTERFACE_V1
+    \brief HID interface version 1
+    \ingroup interfaces
+*/
+struct _XENHID_HID_INTERFACE_V1 {
+    INTERFACE                                       Interface;
+    XENHID_HID_ACQUIRE                              Acquire;
+    XENHID_HID_RELEASE                              Release;
+    XENHID_HID_ENABLE                               Enable;
+    XENHID_HID_DISABLE                              Disable;
+    XENHID_HID_GET_DEVICE_ATTRIBUTES                GetDeviceAttributes;
+    XENHID_HID_GET_DEVICE_DESCRIPTOR                GetDeviceDescriptor;
+    XENHID_HID_GET_REPORT_DESCRIPTOR                GetReportDescriptor;
+    XENHID_HID_GET_STRING                           GetString;
+    XENHID_HID_GET_INDEXED_STRING                   GetIndexedString;
+    XENHID_HID_GET_FEATURE                          GetFeature;
+    XENHID_HID_SET_FEATURE                          SetFeature;
+    XENHID_HID_GET_INPUT_REPORT                     GetInputReport;
+    XENHID_HID_SET_OUTPUT_REPORT                    SetOutputReport;
+    XENHID_HID_READ_REPORT                          ReadReport;
+    XENHID_HID_WRITE_REPORT                         WriteReport;
+};
+
+typedef struct _XENHID_HID_INTERFACE_V1 XENHID_HID_INTERFACE, *PXENHID_HID_INTERFACE;
+
+/*! \def XENHID_HID
+    \brief Macro at assist in method invocation
+*/
+#define XENHID_HID(_Method, _Interface, ...)    \
+    (_Interface)-> ## _Method((PINTERFACE)(_Interface), __VA_ARGS__)
+
+#endif  // _WINDLL
+
+#define XENHID_HID_INTERFACE_VERSION_MIN    1
+#define XENHID_HID_INTERFACE_VERSION_MAX    1
+
+#endif  // _XENHID_INTERFACE_H
diff --git a/include/range_set_interface.h b/include/range_set_interface.h
new file mode 100644 (file)
index 0000000..5ab416d
--- /dev/null
@@ -0,0 +1,179 @@
+/* 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.
+ */
+
+/*! \file range_set_interface.h
+    \brief XENBUS RANGE_SET Interface
+
+    This interface provides access to XENBUS's range-set
+    implementation.
+*/
+
+#ifndef _XENBUS_RANGE_SET_INTERFACE_H
+#define _XENBUS_RANGE_SET_INTERFACE_H
+
+#ifndef _WINDLL
+
+/*! \typedef XENBUS_RANGE_SET
+    \brief Range-set handle
+*/
+typedef struct _XENBUS_RANGE_SET    XENBUS_RANGE_SET, *PXENBUS_RANGE_SET;
+
+/*! \typedef XENBUS_RANGE_SET_ACQUIRE
+    \brief Acquire a reference to the RANGE_SET interface
+
+    \param Interface The interface header
+*/  
+typedef NTSTATUS
+(*XENBUS_RANGE_SET_ACQUIRE)(
+    IN  PINTERFACE  Interface
+    );
+
+/*! \typedef XENBUS_RANGE_SET_RELEASE
+    \brief Release a reference to the RANGE_SET interface
+
+    \param Interface The interface header
+*/  
+typedef VOID
+(*XENBUS_RANGE_SET_RELEASE)(
+    IN  PINTERFACE  Interface
+    );
+
+/*! \typedef XENBUS_RANGE_SET_CREATE
+    \brief Create a new empty range-set
+
+    \param Interface The interface header
+    \param Name A name for the ramge-set which will be used in debug output
+    \param RangeSet A pointer to a range-set handle to be initialized
+*/  
+typedef NTSTATUS
+(*XENBUS_RANGE_SET_CREATE)(
+    IN  PINTERFACE          Interface,
+    IN  const CHAR          *Name,
+    OUT PXENBUS_RANGE_SET   *RangeSet
+    );
+
+/*! \typedef XENBUS_RANGE_SET_PUT
+    \brief Put a range into a range-set
+
+    \param Interface The interface header
+    \param RangeSet The range-set handle
+    \param Start The base of the range
+    \param Count The number of items of the range
+*/  
+typedef NTSTATUS
+(*XENBUS_RANGE_SET_PUT)(
+    IN  PINTERFACE          Interface,
+    IN  PXENBUS_RANGE_SET   RangeSet,
+    IN  LONGLONG            Start,
+    IN  ULONGLONG           Count
+    );
+
+/*! \typedef XENBUS_RANGE_SET_POP
+    \brief Pop a range out of a range-set
+
+    \param Interface The interface header
+    \param RangeSet The range-set handle
+    \param Count The number of items required
+    \param Start A pointer to a value which will be set to the base of
+    a suitable range
+*/  
+typedef NTSTATUS
+(*XENBUS_RANGE_SET_POP)(
+    IN  PINTERFACE          Interface,
+    IN  PXENBUS_RANGE_SET   RangeSet,
+    IN  ULONGLONG           Count,
+    OUT PLONGLONG           Start
+    );
+
+/*! \typedef XENBUS_RANGE_SET_GET
+    \brief Get a specific range out of a range-set
+
+    \param Interface The interface header
+    \param RangeSet The range-set handle
+    \param Start The base of the range
+    \param Count The number of items in the range
+*/  
+typedef NTSTATUS
+(*XENBUS_RANGE_SET_GET)(
+    IN  PINTERFACE          Interface,
+    IN  PXENBUS_RANGE_SET   RangeSet,
+    IN  LONGLONG            Start,
+    IN  ULONGLONG           Count
+    );
+
+/*! \typedef XENBUS_RANGE_SET_DESTROY
+    \brief Destroy a range-set
+
+    \param Interface The interface header
+    \param RangeSet The range-set handle
+
+    The range-set must be empty when it is destroyed
+*/  
+typedef VOID
+(*XENBUS_RANGE_SET_DESTROY)(
+    IN  PINTERFACE          Interface,
+    IN  PXENBUS_RANGE_SET   RangeSet
+    );
+
+// {EE7E78A2-6847-48C5-B123-BB012F0EABF4}
+DEFINE_GUID(GUID_XENBUS_RANGE_SET_INTERFACE, 
+0xee7e78a2, 0x6847, 0x48c5, 0xb1, 0x23, 0xbb, 0x1, 0x2f, 0xe, 0xab, 0xf4);
+
+/*! \struct _XENBUS_RANGE_SET_INTERFACE_V1
+    \brief RANGE_SET interface version 1
+    \ingroup interfaces
+*/
+struct _XENBUS_RANGE_SET_INTERFACE_V1 {
+    INTERFACE                   Interface;
+    XENBUS_RANGE_SET_ACQUIRE    RangeSetAcquire;
+    XENBUS_RANGE_SET_RELEASE    RangeSetRelease;
+    XENBUS_RANGE_SET_CREATE     RangeSetCreate;
+    XENBUS_RANGE_SET_PUT        RangeSetPut;
+    XENBUS_RANGE_SET_POP        RangeSetPop;
+    XENBUS_RANGE_SET_GET        RangeSetGet;
+    XENBUS_RANGE_SET_DESTROY    RangeSetDestroy;
+};
+
+typedef struct _XENBUS_RANGE_SET_INTERFACE_V1 XENBUS_RANGE_SET_INTERFACE, *PXENBUS_RANGE_SET_INTERFACE;
+
+/*! \def XENBUS_RANGE_SET
+    \brief Macro at assist in method invocation
+*/
+#define XENBUS_RANGE_SET(_Method, _Interface, ...)    \
+    (_Interface)->RangeSet ## _Method((PINTERFACE)(_Interface), __VA_ARGS__)
+
+#endif  // _WINDLL
+
+#define XENBUS_RANGE_SET_INTERFACE_VERSION_MIN 1
+#define XENBUS_RANGE_SET_INTERFACE_VERSION_MAX 1
+
+#endif  // _XENBUS_RANGE_SET_INTERFACE_H
+
diff --git a/include/revision.h b/include/revision.h
new file mode 100644 (file)
index 0000000..b30aee7
--- /dev/null
@@ -0,0 +1,45 @@
+/* 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 _REVISION_H
+#define _REVISION_H
+
+// Key:
+// H  - XENHID_HID_INTERFACE
+
+//                    REVISION   H
+#define DEFINE_REVISION_TABLE           \
+    DEFINE_REVISION(0x0800000B,  1),    \
+    DEFINE_REVISION(0x0800000C,  1),    \
+    DEFINE_REVISION(0x0800000D,  1),    \
+    DEFINE_REVISION(0x09000000,  1)
+
+#endif  // _REVISION_H
diff --git a/include/store_interface.h b/include/store_interface.h
new file mode 100644 (file)
index 0000000..52f1a1d
--- /dev/null
@@ -0,0 +1,348 @@
+/* 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.
+ */
+
+/*! \file store_interface.h
+    \brief XENBUS STORE Interface
+
+    This interface provides access to XenStore
+*/
+
+#ifndef _XENBUS_STORE_INTERFACE_H
+#define _XENBUS_STORE_INTERFACE_H
+
+#ifndef _WINDLL
+
+/*! \typedef XENBUS_STORE_TRANSACTION
+    \brief XenStore transaction handle
+*/
+typedef struct _XENBUS_STORE_TRANSACTION    XENBUS_STORE_TRANSACTION, *PXENBUS_STORE_TRANSACTION;
+
+/*! \typedef XENBUS_STORE_WATCH
+    \brief XenStore watch handle
+*/
+typedef struct _XENBUS_STORE_WATCH          XENBUS_STORE_WATCH, *PXENBUS_STORE_WATCH;
+
+/*! \typedef XENBUS_STORE_PERMISSION_MASK
+    \brief Bitmask of XenStore key permissions
+*/
+typedef enum _XENBUS_STORE_PERMISSION_MASK {
+    XENBUS_STORE_PERM_NONE = 0,
+    XENBUS_STORE_PERM_READ = 1,
+    XENBUS_STORE_PERM_WRITE = 2,
+} XENBUS_STORE_PERMISSION_MASK;
+
+/*! \typedef XENBUS_STORE_PERMISSION
+    \brief XenStore key permissions entry for a single domain
+*/
+typedef struct _XENBUS_STORE_PERMISSION {
+    USHORT                          Domain;
+    XENBUS_STORE_PERMISSION_MASK    Mask;
+} XENBUS_STORE_PERMISSION, *PXENBUS_STORE_PERMISSION;
+
+/*! \typedef XENBUS_STORE_ACQUIRE
+    \brief Acquire a reference to the STORE interface
+
+    \param Interface The interface header
+*/  
+typedef NTSTATUS
+(*XENBUS_STORE_ACQUIRE)(
+    IN  PINTERFACE  Interface
+    );
+
+/*! \typedef XENBUS_STORE_RELEASE
+    \brief Release a reference to the STORE interface
+
+    \param Interface The interface header
+*/  
+typedef VOID
+(*XENBUS_STORE_RELEASE)(
+    IN  PINTERFACE  Interface
+    );
+
+/*! \typedef XENBUS_STORE_FREE
+    \brief Free a memory buffer allocated by the STORE interface
+
+    \param Interface The interface header
+    \param Buffer Pointer to the memory buffer
+*/  
+typedef VOID
+(*XENBUS_STORE_FREE)(
+    IN  PINTERFACE  Interface,
+    IN  PCHAR       Buffer
+    );
+
+/*! \typedef XENBUS_STORE_READ
+    \brief Read a value from XenStore
+
+    \param Interface The interface header
+    \param Transaction The transaction handle (NULL if this read is not
+    part of a transaction)
+    \param Prefix An optional prefix for the \a Node
+    \param Node The concatenation of the \a Prefix and this value specifies
+    the XenStore key to read
+    \param A pointer to a pointer that will be initialized with a memory
+    buffer containing the value read
+
+    The \a Buffer should be freed using \a XENBUS_STORE_FREE
+*/  
+typedef NTSTATUS
+(*XENBUS_STORE_READ)(
+    IN  PINTERFACE                  Interface,
+    IN  PXENBUS_STORE_TRANSACTION   Transaction OPTIONAL,
+    IN  PCHAR                       Prefix OPTIONAL,
+    IN  PCHAR                       Node,
+    OUT PCHAR                       *Buffer
+    );
+
+/*! \typedef XENBUS_STORE_PRINTF
+    \brief Write a value to XenStore
+
+    \param Interface The interface header
+    \param Transaction The transaction handle (NULL if this write is not
+    part of a transaction)
+    \param Prefix An optional prefix for the \a Node
+    \param Node The concatenation of the \a Prefix and this value specifies
+    the XenStore key to write
+    \param Format A format specifier
+    \param ... Additional parameters required by \a Format
+
+    If the \a Node does not exist then it is created
+*/  
+typedef NTSTATUS
+(*XENBUS_STORE_PRINTF)(
+    IN  PINTERFACE                  Interface,
+    IN  PXENBUS_STORE_TRANSACTION   Transaction OPTIONAL,
+    IN  PCHAR                       Prefix OPTIONAL,
+    IN  PCHAR                       Node,
+    IN  const CHAR                  *Format,
+    ...
+    );
+
+/*! \typedef XENBUS_STORE_REMOVE
+    \brief Remove a key from XenStore
+
+    \param Interface The interface header
+    \param Transaction The transaction handle (NULL if this removal is not
+    part of a transaction)
+    \param Prefix An optional prefix for the \a Node
+    \param Node The concatenation of the \a Prefix and this value specifies
+    the XenStore key to remove
+*/  
+typedef NTSTATUS
+(*XENBUS_STORE_REMOVE)(
+    IN  PINTERFACE                  Interface,
+    IN  PXENBUS_STORE_TRANSACTION   Transaction OPTIONAL,
+    IN  PCHAR                       Prefix OPTIONAL,
+    IN  PCHAR                       Node
+    );
+
+/*! \typedef XENBUS_STORE_DIRECTORY
+    \brief Enumerate all immediate child keys of a XenStore key
+
+    \param Interface The interface header
+    \param Transaction The transaction handle (NULL if this removal is not
+    part of a transaction)
+    \param Prefix An optional prefix for the \a Node
+    \param Node The concatenation of the \a Prefix and this value specifies
+    the XenStore key to enumerate
+    \param A pointer to a pointer that will be initialized with a memory
+    buffer containing a NUL separated list of key names
+
+    The \a Buffer should be freed using \a XENBUS_STORE_FREE
+*/  
+typedef NTSTATUS
+(*XENBUS_STORE_DIRECTORY)(
+    IN  PINTERFACE                  Interface,
+    IN  PXENBUS_STORE_TRANSACTION   Transaction OPTIONAL,
+    IN  PCHAR                       Prefix OPTIONAL,
+    IN  PCHAR                       Node,
+    OUT PCHAR                       *Buffer
+    );
+
+/*! \typedef XENBUS_STORE_TRANSACTION_START
+    \brief Start a XenStore transaction
+
+    \param Interface The interface header
+    \param Transaction Pointer to a transaction handle to be initialized
+*/  
+typedef NTSTATUS
+(*XENBUS_STORE_TRANSACTION_START)(
+    IN  PINTERFACE                  Interface,
+    OUT PXENBUS_STORE_TRANSACTION   *Transaction
+    );
+
+/*! \typedef XENBUS_STORE_TRANSACTION_END
+    \brief End a XenStore transaction
+
+    \param Interface The interface header
+    \param Transaction The transaction handle
+    \param Commit Set to TRUE if actions performed within the transaction should
+    be made visible, or FALSE if they should not be
+
+    If \a Commit is TRUE and the transaction to found to clash then
+    STATUS_RETRY will be returned
+*/  
+typedef NTSTATUS
+(*XENBUS_STORE_TRANSACTION_END)(
+    IN  PINTERFACE                  Interface,
+    IN  PXENBUS_STORE_TRANSACTION   Transaction,
+    IN  BOOLEAN                     Commit
+    );
+
+/*! \typedef XENBUS_STORE_WATCH_ADD
+    \brief Add a XenStore watch
+
+    \param Interface The interface header
+    \param Prefix An optional prefix for the \a Node
+    \param Node The concatenation of the \a Prefix and this value specifies
+    the XenStore key to watch
+    \param Event A pointer to an event object to be signalled when the
+    watch fires
+    \param Watch A pointer to a watch handle to be initialized
+*/  
+typedef NTSTATUS
+(*XENBUS_STORE_WATCH_ADD)(
+    IN  PINTERFACE          Interface,
+    IN  PCHAR               Prefix OPTIONAL,
+    IN  PCHAR               Node,
+    IN  PKEVENT             Event,
+    OUT PXENBUS_STORE_WATCH *Watch
+    );
+
+/*! \typedef XENBUS_STORE_WATCH_REMOVE
+    \brief Remove a XenStore watch
+
+    \param Interface The interface header
+    \param Watch The watch handle
+*/  
+typedef NTSTATUS
+(*XENBUS_STORE_WATCH_REMOVE)(
+    IN  PINTERFACE          Interface,
+    IN  PXENBUS_STORE_WATCH Watch
+    );
+
+/*! \typedef XENBUS_STORE_POLL
+    \brief Poll for XenStore activity
+
+    \param Interface The interface header
+
+    If it is necessary to spin at DISPATCH_LEVEL waiting for XenStore
+    activity then this will block the normal STORE interface DPC so this
+    method must be regularly invoked during the spin loop to check for
+    XenStore activity
+*/  
+typedef VOID
+(*XENBUS_STORE_POLL)(
+    IN  PINTERFACE  Interface
+    );
+
+/*! \typedef XENBUS_STORE_PERMISSIONS_SET
+    \brief Set permissions for a XenStore key
+
+    \param Interface The interface header
+    \param Transaction The transaction handle (NULL if this is not
+    part of a transaction)
+    \param Prefix An optional prefix for the \a Node
+    \param Node The concatenation of the \a Prefix and this value specifies
+    the XenStore key to set permissions of
+    \param Permissions An array of permissions to set
+    \param NumberPermissions Number of elements in the \a Permissions array
+*/
+typedef NTSTATUS
+(*XENBUS_STORE_PERMISSIONS_SET)(
+    IN  PINTERFACE                  Interface,
+    IN  PXENBUS_STORE_TRANSACTION   Transaction OPTIONAL,
+    IN  PCHAR                       Prefix OPTIONAL,
+    IN  PCHAR                       Node,
+    IN  PXENBUS_STORE_PERMISSION    Permissions,
+    IN  ULONG                       NumberPermissions
+    );
+
+// {86824C3B-D34E-4753-B281-2F1E3AD214D7}
+DEFINE_GUID(GUID_XENBUS_STORE_INTERFACE, 
+0x86824c3b, 0xd34e, 0x4753, 0xb2, 0x81, 0x2f, 0x1e, 0x3a, 0xd2, 0x14, 0xd7);
+
+/*! \struct _XENBUS_STORE_INTERFACE_V1
+    \brief STORE interface version 1
+    \ingroup interfaces
+*/
+struct _XENBUS_STORE_INTERFACE_V1 {
+    INTERFACE                       Interface;
+    XENBUS_STORE_ACQUIRE            StoreAcquire;
+    XENBUS_STORE_RELEASE            StoreRelease;
+    XENBUS_STORE_FREE               StoreFree;
+    XENBUS_STORE_READ               StoreRead;
+    XENBUS_STORE_PRINTF             StorePrintf;
+    XENBUS_STORE_REMOVE             StoreRemove;
+    XENBUS_STORE_DIRECTORY          StoreDirectory;
+    XENBUS_STORE_TRANSACTION_START  StoreTransactionStart;
+    XENBUS_STORE_TRANSACTION_END    StoreTransactionEnd;
+    XENBUS_STORE_WATCH_ADD          StoreWatchAdd;
+    XENBUS_STORE_WATCH_REMOVE       StoreWatchRemove;
+    XENBUS_STORE_POLL               StorePoll;
+};
+
+/*! \struct _XENBUS_STORE_INTERFACE_V2
+    \brief STORE interface version 2
+    \ingroup interfaces
+*/
+struct _XENBUS_STORE_INTERFACE_V2 {
+    INTERFACE                       Interface;
+    XENBUS_STORE_ACQUIRE            StoreAcquire;
+    XENBUS_STORE_RELEASE            StoreRelease;
+    XENBUS_STORE_FREE               StoreFree;
+    XENBUS_STORE_READ               StoreRead;
+    XENBUS_STORE_PRINTF             StorePrintf;
+    XENBUS_STORE_PERMISSIONS_SET    StorePermissionsSet;
+    XENBUS_STORE_REMOVE             StoreRemove;
+    XENBUS_STORE_DIRECTORY          StoreDirectory;
+    XENBUS_STORE_TRANSACTION_START  StoreTransactionStart;
+    XENBUS_STORE_TRANSACTION_END    StoreTransactionEnd;
+    XENBUS_STORE_WATCH_ADD          StoreWatchAdd;
+    XENBUS_STORE_WATCH_REMOVE       StoreWatchRemove;
+    XENBUS_STORE_POLL               StorePoll;
+};
+
+typedef struct _XENBUS_STORE_INTERFACE_V2 XENBUS_STORE_INTERFACE, *PXENBUS_STORE_INTERFACE;
+
+/*! \def XENBUS_STORE
+    \brief Macro at assist in method invocation
+*/
+#define XENBUS_STORE(_Method, _Interface, ...)    \
+    (_Interface)->Store ## _Method((PINTERFACE)(_Interface), __VA_ARGS__)
+
+#endif  // _WINDLL
+
+#define XENBUS_STORE_INTERFACE_VERSION_MIN  1
+#define XENBUS_STORE_INTERFACE_VERSION_MAX  2
+
+#endif  // _XENBUS_STORE_INTERFACE_H
+
diff --git a/include/suspend_interface.h b/include/suspend_interface.h
new file mode 100644 (file)
index 0000000..cbe11ab
--- /dev/null
@@ -0,0 +1,177 @@
+/* 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.
+ */
+
+/*! \file suspend_interface.h
+    \brief XENBUS SUSPEND Interface
+
+    This interface provides primitives to handle VM suspend/resume
+*/
+
+#ifndef _XENBUS_SUSPEND_INTERFACE_H
+#define _XENBUS_SUSPEND_INTERFACE_H
+
+#ifndef _WINDLL
+
+/*! \enum _XENBUS_SUSPEND_CALLBACK_TYPE
+    \brief Suspend callback type to be registered
+*/
+typedef enum _XENBUS_SUSPEND_CALLBACK_TYPE {
+    SUSPEND_CALLBACK_TYPE_INVALID = 0,
+    SUSPEND_CALLBACK_EARLY,             /*!< Early */
+    SUSPEND_CALLBACK_LATE               /*!< Late */
+} XENBUS_SUSPEND_CALLBACK_TYPE, *PXENBUS_SUSPEND_CALLBACK_TYPE;
+
+/*! \typedef XENBUS_SUSPEND_CALLBACK
+    \brief Suspend callback handle
+*/  
+typedef struct _XENBUS_SUSPEND_CALLBACK   XENBUS_SUSPEND_CALLBACK, *PXENBUS_SUSPEND_CALLBACK;
+
+/*! \typedef XENBUS_SUSPEND_ACQUIRE
+    \brief Acquire a reference to the SUSPEND interface
+
+    \param Interface The interface header
+*/  
+typedef NTSTATUS
+(*XENBUS_SUSPEND_ACQUIRE)(
+    IN  PINTERFACE  Interface
+    );
+
+/*! \typedef XENBUS_SUSPEND_RELEASE
+    \brief Release a reference to the SUSPEND interface
+
+    \param Interface The interface header
+*/  
+typedef VOID
+(*XENBUS_SUSPEND_RELEASE)(
+    IN  PINTERFACE  Interface
+    );
+
+/*! \typedef XENBUS_SUSPEND_FUNCTION
+    \brief Suspend callback function
+
+    \param Argument Context \a Argument supplied to \a XENBUS_SUSPEND_REGISTER
+
+    Suspend callback functions are always invoked on one vCPU with all other
+    vCPUs corralled at the same IRQL as the callback. \a Early callback
+    functions are always invoked with IRQL == HIGH_LEVEL and \a Late callback
+    functions are always invoked with IRQL == DISPATCH_LEVEL
+*/  
+typedef VOID
+(*XENBUS_SUSPEND_FUNCTION)(
+    IN  PVOID   Argument
+    );
+
+/*! \typedef XENBUS_SUSPEND_REGISTER
+    \brief Register a suspend callback function
+
+    \param Interface The interface header
+    \param Type The type of callback function to register
+    \param Function The callback function
+    \param Argument An optional context argument passed to the callback
+    \param Callback A pointer to a callback handle to be initialized
+*/  
+typedef NTSTATUS
+(*XENBUS_SUSPEND_REGISTER)(
+    IN  PINTERFACE                      Interface,
+    IN  XENBUS_SUSPEND_CALLBACK_TYPE    Type,
+    IN  XENBUS_SUSPEND_FUNCTION         Function,
+    IN  PVOID                           Argument OPTIONAL,
+    OUT PXENBUS_SUSPEND_CALLBACK        *Callback
+    );
+
+/*! \typedef XENBUS_SUSPEND_DEREGISTER
+    \brief Deregister a suspend callback function
+
+    \param Interface The interface header
+    \param Callback The callback handle
+*/
+typedef VOID
+(*XENBUS_SUSPEND_DEREGISTER)(
+    IN  PINTERFACE                  Interface,
+    IN  PXENBUS_SUSPEND_CALLBACK    Callback
+    );
+
+/*! \typedef XENBUS_SUSPEND_TRIGGER
+    \brief Trigger a VM suspend
+
+    \param Interface The interface header
+
+    This method must always be invoked with IRQL == PASSIVE_LEVEL
+*/
+typedef NTSTATUS
+(*XENBUS_SUSPEND_TRIGGER)(
+    IN  PINTERFACE  Interface
+    );
+
+/*! \typedef XENBUS_SUSPEND_GET_COUNT
+    \brief Get the number of VM suspends that have occurred since boot
+
+    \param Interface The interface header
+    \return The number of VM suspends
+*/
+typedef ULONG
+(*XENBUS_SUSPEND_GET_COUNT)(
+    IN  PINTERFACE  Interface
+    );
+
+// {0554F2AF-B510-4C71-AC03-1C503E394238}
+DEFINE_GUID(GUID_XENBUS_SUSPEND_INTERFACE,
+0x554f2af, 0xb510, 0x4c71, 0xac, 0x3, 0x1c, 0x50, 0x3e, 0x39, 0x42, 0x38);
+
+/*! \struct _XENBUS_SUSPEND_INTERFACE_V1
+    \brief SUSPEND interface version 1
+    \ingroup interfaces
+*/
+struct _XENBUS_SUSPEND_INTERFACE_V1 {
+    INTERFACE                   Interface;
+    XENBUS_SUSPEND_ACQUIRE      Acquire;
+    XENBUS_SUSPEND_RELEASE      Release;
+    XENBUS_SUSPEND_REGISTER     Register;
+    XENBUS_SUSPEND_DEREGISTER   Deregister;
+    XENBUS_SUSPEND_TRIGGER      Trigger;
+    XENBUS_SUSPEND_GET_COUNT    GetCount;
+};
+
+typedef struct _XENBUS_SUSPEND_INTERFACE_V1 XENBUS_SUSPEND_INTERFACE, *PXENBUS_SUSPEND_INTERFACE;
+
+/*! \def XENBUS_SUSPEND
+    \brief Macro at assist in method invocation
+*/
+#define XENBUS_SUSPEND(_Method, _Interface, ...)    \
+    (_Interface)-> ## _Method((PINTERFACE)(_Interface), __VA_ARGS__)
+
+#endif  // _WINDLL
+
+#define XENBUS_SUSPEND_INTERFACE_VERSION_MIN    1
+#define XENBUS_SUSPEND_INTERFACE_VERSION_MAX    1
+
+#endif  // _XENBUS_SUSPEND_INTERFACE_H
+
diff --git a/include/unplug_interface.h b/include/unplug_interface.h
new file mode 100644 (file)
index 0000000..6f45975
--- /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.
+ */
+
+/*! \file unplug_interface.h
+    \brief XENBUS UNPLUG Interface
+
+    This interface provides a method to request emulated device unplug
+*/
+
+#ifndef _XENBUS_UNPLUG_INTERFACE_H
+#define _XENBUS_UNPLUG_INTERFACE_H
+
+#ifndef _WINDLL
+
+/*! \typedef XENBUS_UNPLUG_ACQUIRE
+    \brief Acquire a reference to the UNPLUG interface
+
+    \param Interface The interface header
+*/  
+typedef NTSTATUS
+(*XENBUS_UNPLUG_ACQUIRE)(
+    IN  PINTERFACE  Interface
+    );
+
+/*! \typedef XENBUS_UNPLUG_RELEASE
+    \brief Release a reference to the UNPLUG interface
+
+    \param Interface The interface header
+*/  
+typedef VOID
+(*XENBUS_UNPLUG_RELEASE)(
+    IN  PINTERFACE  Interface
+    );
+
+/*! \enum _XENBUS_UNPLUG_DEVICE_TYPE
+    \brief Type of device to be unplugged
+*/
+typedef enum _XENBUS_UNPLUG_DEVICE_TYPE {
+    XENBUS_UNPLUG_DEVICE_TYPE_INVALID = 0,
+    XENBUS_UNPLUG_DEVICE_TYPE_NICS,     /*!< NICs */
+    XENBUS_UNPLUG_DEVICE_TYPE_DISKS,    /*!< Disks */
+} XENBUS_UNPLUG_DEVICE_TYPE, *PXENBUS_UNPLUG_DEVICE_TYPE;
+
+/*! \typedef XENBUS_UNPLUG_REQUEST
+    \brief Request unplug of a type of emulated device
+
+    \param Interface The interface header
+    \param Type The type of device
+    \param Make Set to TRUE if the request is being made, FALSE if it is
+           being revoked.
+*/  
+typedef VOID
+(*XENBUS_UNPLUG_REQUEST)(
+    IN  PINTERFACE                  Interface,
+    IN  XENBUS_UNPLUG_DEVICE_TYPE   Type,
+    IN  BOOLEAN                     Make
+    );
+
+// {73db6517-3d06-4937-989f-199b7501e229}
+DEFINE_GUID(GUID_XENBUS_UNPLUG_INTERFACE, 
+0x73db6517, 0x3d06, 0x4937, 0x98, 0x9f, 0x19, 0x9b, 0x75, 0x01, 0xe2, 0x29);
+
+/*! \struct _XENBUS_UNPLUG_INTERFACE_V1
+    \brief UNPLUG interface version 1
+    \ingroup interfaces
+*/
+struct _XENBUS_UNPLUG_INTERFACE_V1 {
+    INTERFACE               Interface;
+    XENBUS_UNPLUG_ACQUIRE   UnplugAcquire;
+    XENBUS_UNPLUG_RELEASE   UnplugRelease;
+    XENBUS_UNPLUG_REQUEST   UnplugRequest;
+};
+
+typedef struct _XENBUS_UNPLUG_INTERFACE_V1 XENBUS_UNPLUG_INTERFACE, *PXENBUS_UNPLUG_INTERFACE;
+
+/*! \def XENBUS_UNPLUG
+    \brief Macro at assist in method invocation
+*/
+#define XENBUS_UNPLUG(_Method, _Interface, ...)    \
+    (_Interface)->Unplug ## _Method((PINTERFACE)(_Interface), __VA_ARGS__)
+
+#endif  // _WINDLL
+
+#define XENBUS_UNPLUG_INTERFACE_VERSION_MIN  1
+#define XENBUS_UNPLUG_INTERFACE_VERSION_MAX  1
+
+#endif  // _XENBUS_UNPLUG_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..8ef8416
--- /dev/null
@@ -0,0 +1,45 @@
+/* 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 <public/io/ring.h>
+#include <public/io/kbdif.h>
+#include <public/io/xenbus.h>
+
+#endif  // _XEN_H
diff --git a/include/xen/public/arch-x86/xen-x86_32.h b/include/xen/public/arch-x86/xen-x86_32.h
new file mode 100644 (file)
index 0000000..d23340b
--- /dev/null
@@ -0,0 +1,169 @@
+/******************************************************************************
+ * 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, %ebp (arguments 1-6)
+ *  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)
+ */
+
+/*
+ * 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      xen_mk_ulong(__HYPERVISOR_VIRT_START_PAE)
+#define MACH2PHYS_VIRT_START_PAE       xen_mk_ulong(__MACH2PHYS_VIRT_START_PAE)
+#define MACH2PHYS_VIRT_END_PAE         xen_mk_ulong(__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   \
+    xen_mk_ulong(__HYPERVISOR_VIRT_START_NONPAE)
+#define MACH2PHYS_VIRT_START_NONPAE    \
+    xen_mk_ulong(__MACH2PHYS_VIRT_START_NONPAE)
+#define MACH2PHYS_VIRT_END_NONPAE      \
+    xen_mk_ulong(__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 xen_mk_ulong(__HYPERVISOR_VIRT_START)
+#endif
+
+#define MACH2PHYS_VIRT_START  xen_mk_ulong(__MACH2PHYS_VIRT_START)
+#define MACH2PHYS_VIRT_END    xen_mk_ulong(__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  int64_aligned_t  int64_t __attribute__((aligned(8)))
+#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-file-style: "BSD"
+ * c-basic-offset: 4
+ * tab-width: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
diff --git a/include/xen/public/arch-x86/xen-x86_64.h b/include/xen/public/arch-x86/xen-x86_64.h
new file mode 100644 (file)
index 0000000..2f53f69
--- /dev/null
@@ -0,0 +1,202 @@
+/******************************************************************************
+ * 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, %r9 (arguments 1-6)
+ *  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)
+ */
+
+/*
+ * 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 xen_mk_ulong(__HYPERVISOR_VIRT_START)
+#define HYPERVISOR_VIRT_END   xen_mk_ulong(__HYPERVISOR_VIRT_END)
+#endif
+
+#define MACH2PHYS_VIRT_START  xen_mk_ulong(__MACH2PHYS_VIRT_START)
+#define MACH2PHYS_VIRT_END    xen_mk_ulong(__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-file-style: "BSD"
+ * c-basic-offset: 4
+ * tab-width: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
diff --git a/include/xen/public/arch-x86/xen.h b/include/xen/public/arch-x86/xen.h
new file mode 100644 (file)
index 0000000..05db77f
--- /dev/null
@@ -0,0 +1,338 @@
+/******************************************************************************
+ * 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
+
+/*
+ * XEN_GUEST_HANDLE represents a guest pointer, when passed as a field
+ * in a struct in memory.
+ * XEN_GUEST_HANDLE_PARAM represent a guest pointer, when passed as an
+ * hypercall argument.
+ * XEN_GUEST_HANDLE_PARAM and XEN_GUEST_HANDLE are the same on X86 but
+ * they might not be on other architectures.
+ */
+#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 XEN_GUEST_HANDLE_PARAM(name)    XEN_GUEST_HANDLE(name)
+#define set_xen_guest_handle_raw(hnd, val)  do { (hnd).p = val; } while (0)
+#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
+
+#define XEN_HAVE_PV_GUEST_ENTRY 1
+
+#define XEN_HAVE_PV_UPCALL_MASK 1
+
+/*
+ * `incontents 200 segdesc Segment Descriptor Tables
+ */
+/*
+ * ` enum neg_errnoval
+ * ` HYPERVISOR_set_gdt(const xen_pfn_t frames[], unsigned int entries);
+ * `
+ */
+/*
+ * 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.
+ *
+ * NB The LDT is set using the MMUEXT_SET_LDT op of HYPERVISOR_mmuext_op
+ */
+#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)
+
+
+/*
+ * ` enum neg_errnoval
+ * ` HYPERVISOR_update_descriptor(u64 pa, u64 desc);
+ * `
+ * ` @pa   The machine physical address of the descriptor to
+ * `       update. Must be either a descriptor page or writable.
+ * ` @desc The descriptor value to update, in the same format as a
+ * `       native descriptor table entry.
+ */
+
+/* Maximum number of virtual CPUs in legacy multi-processor guests. */
+#define XEN_LEGACY_MAX_VCPUS 32
+
+#ifndef __ASSEMBLY__
+
+typedef ULONG_PTR xen_ulong_t;
+#define PRI_xen_ulong "lx"
+
+/*
+ * ` enum neg_errnoval
+ * ` HYPERVISOR_stack_switch(ULONG_PTR ss, ULONG_PTR esp);
+ * `
+ * Sets the stack segment and pointer for the current vcpu.
+ */
+
+/*
+ * ` enum neg_errnoval
+ * ` HYPERVISOR_set_trap_table(const struct trap_info traps[]);
+ * `
+ */
+/*
+ * Send an array of these to HYPERVISOR_set_trap_table().
+ * Terminate the array with a sentinel entry, with traps[].address==0.
+ * 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.
+ *
+ * Also note that when calling DOMCTL_setvcpucontext and VCPU_initialise
+ * for HVM and PVH guests, not all information in this structure is updated:
+ *
+ * - For HVM guests, the structures read include: fpu_ctxt (if
+ * VGCT_I387_VALID is set), flags, user_regs, debugreg[*]
+ *
+ * - PVH guests are the same as HVM guests, but additionally use ctrlreg[3] to
+ * set cr3. All other fields not used should be set to 0.
+ */
+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 {
+    /*
+     * Number of valid entries in the p2m table(s) anchored at
+     * pfn_to_mfn_frame_list_list and/or p2m_vaddr.
+     */
+    ULONG_PTR max_pfn;
+    /*
+     * Frame containing list of mfns containing list of mfns containing p2m.
+     * A value of 0 indicates it has not yet been set up, ~0 indicates it has
+     * been set to invalid e.g. due to the p2m being too large for the 3-level
+     * p2m tree. In this case the linear mapper p2m list anchored at p2m_vaddr
+     * is to be used.
+     */
+    xen_pfn_t     pfn_to_mfn_frame_list_list;
+    ULONG_PTR nmi_reason;
+    /*
+     * Following three fields are valid if p2m_cr3 contains a value different
+     * from 0.
+     * p2m_cr3 is the root of the address space where p2m_vaddr is valid.
+     * p2m_cr3 is in the same format as a cr3 value in the vcpu register state
+     * and holds the folded machine frame number (via xen_pfn_to_cr3) of a
+     * L3 or L4 page table.
+     * p2m_vaddr holds the virtual address of the linear p2m list. All entries
+     * in the range [0...max_pfn[ are accessible via this pointer.
+     * p2m_generation will be incremented by the guest before and after each
+     * change of the mappings of the p2m list. p2m_generation starts at 0 and
+     * a value with the least significant bit set indicates that a mapping
+     * update is in progress. This allows guest external software (e.g. in Dom0)
+     * to verify that read mappings are consistent and whether they have changed
+     * since the last check.
+     * Modifying a p2m element in the linear p2m list is allowed via an atomic
+     * write only.
+     */
+    ULONG_PTR p2m_cr3;         /* cr3 value of the p2m address space */
+    ULONG_PTR p2m_vaddr;       /* virtual address of the p2m list */
+    ULONG_PTR p2m_generation;  /* generation count of p2m mapping */
+#ifdef __i386__
+    /* There's no room for this field in the generic structure. */
+    uint32_t wc_sec_hi;
+#endif
+};
+typedef struct arch_shared_info arch_shared_info_t;
+
+#if defined(__XEN__) || defined(__XEN_TOOLS__)
+/*
+ * struct xen_arch_domainconfig's ABI is covered by
+ * XEN_DOMCTL_INTERFACE_VERSION.
+ */
+struct xen_arch_domainconfig {
+#define _XEN_X86_EMU_LAPIC          0
+#define XEN_X86_EMU_LAPIC           (1U<<_XEN_X86_EMU_LAPIC)
+#define _XEN_X86_EMU_HPET           1
+#define XEN_X86_EMU_HPET            (1U<<_XEN_X86_EMU_HPET)
+#define _XEN_X86_EMU_PM             2
+#define XEN_X86_EMU_PM              (1U<<_XEN_X86_EMU_PM)
+#define _XEN_X86_EMU_RTC            3
+#define XEN_X86_EMU_RTC             (1U<<_XEN_X86_EMU_RTC)
+#define _XEN_X86_EMU_IOAPIC         4
+#define XEN_X86_EMU_IOAPIC          (1U<<_XEN_X86_EMU_IOAPIC)
+#define _XEN_X86_EMU_PIC            5
+#define XEN_X86_EMU_PIC             (1U<<_XEN_X86_EMU_PIC)
+#define _XEN_X86_EMU_VGA            6
+#define XEN_X86_EMU_VGA             (1U<<_XEN_X86_EMU_VGA)
+#define _XEN_X86_EMU_IOMMU          7
+#define XEN_X86_EMU_IOMMU           (1U<<_XEN_X86_EMU_IOMMU)
+#define _XEN_X86_EMU_PIT            8
+#define XEN_X86_EMU_PIT             (1U<<_XEN_X86_EMU_PIT)
+
+#define XEN_X86_EMU_ALL             (XEN_X86_EMU_LAPIC | XEN_X86_EMU_HPET |  \
+                                     XEN_X86_EMU_PM | XEN_X86_EMU_RTC |      \
+                                     XEN_X86_EMU_IOAPIC | XEN_X86_EMU_PIC |  \
+                                     XEN_X86_EMU_VGA | XEN_X86_EMU_IOMMU |   \
+                                     XEN_X86_EMU_PIT)
+    uint32_t emulation_flags;
+};
+#endif
+
+#endif /* !__ASSEMBLY__ */
+
+/*
+ * ` enum neg_errnoval
+ * ` HYPERVISOR_fpu_taskswitch(int set);
+ * `
+ * Sets (if set!=0) or clears (if set==0) CR0.TS.
+ */
+
+/*
+ * ` enum neg_errnoval
+ * ` HYPERVISOR_set_debugreg(int regno, ULONG_PTR value);
+ *
+ * ` ULONG_PTR
+ * ` HYPERVISOR_get_debugreg(int regno);
+ * For 0<=reg<=7, returns the debug register value.
+ * For other values of reg, returns ((ULONG_PTR)-EINVAL).
+ * (Unfortunately, this interface is defective.)
+ */
+
+/*
+ * 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-file-style: "BSD"
+ * c-basic-offset: 4
+ * tab-width: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
diff --git a/include/xen/public/errno.h b/include/xen/public/errno.h
new file mode 100644 (file)
index 0000000..e440237
--- /dev/null
@@ -0,0 +1,124 @@
+/*
+ * There are two expected ways of including this header.
+ *
+ * 1) The "default" case (expected from tools etc).
+ *
+ * Simply #include <public/errno.h>
+ *
+ * In this circumstance, normal header guards apply and the includer shall get
+ * an enumeration in the XEN_xxx namespace, appropriate for C or assembly.
+ *
+ * 2) The special case where the includer provides a XEN_ERRNO() in scope.
+ *
+ * In this case, no inclusion guards apply and the caller is responsible for
+ * their XEN_ERRNO() being appropriate in the included context.  The header
+ * will unilaterally #undef XEN_ERRNO().
+ */
+
+#ifndef XEN_ERRNO
+
+/*
+ * Includer has not provided a custom XEN_ERRNO().  Arrange for normal header
+ * guards, an automatic enum (for C code) and constants in the XEN_xxx
+ * namespace.
+ */
+#ifndef __XEN_PUBLIC_ERRNO_H__
+#define __XEN_PUBLIC_ERRNO_H__
+
+#define XEN_ERRNO_DEFAULT_INCLUDE
+
+#ifndef __ASSEMBLY__
+
+#define XEN_ERRNO(name, value) XEN_##name = value,
+enum xen_errno {
+
+#elif __XEN_INTERFACE_VERSION__ < 0x00040700
+
+#define XEN_ERRNO(name, value) .equ XEN_##name, value
+
+#endif /* __ASSEMBLY__ */
+
+#endif /* __XEN_PUBLIC_ERRNO_H__ */
+#endif /* !XEN_ERRNO */
+
+/* ` enum neg_errnoval {  [ -Efoo for each Efoo in the list below ]  } */
+/* ` enum errnoval { */
+
+#ifdef XEN_ERRNO
+
+/*
+ * Values originating from x86 Linux. Please consider using respective
+ * values when adding new definitions here.
+ *
+ * The set of identifiers to be added here shouldn't extend beyond what
+ * POSIX mandates (see e.g.
+ * http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/errno.h.html)
+ * with the exception that we support some optional (XSR) values
+ * specified there (but no new ones should be added).
+ */
+
+XEN_ERRNO(EPERM,        1)     /* Operation not permitted */
+XEN_ERRNO(ENOENT,       2)     /* No such file or directory */
+XEN_ERRNO(ESRCH,        3)     /* No such process */
+#ifdef __XEN__ /* Internal only, should never be exposed to the guest. */
+XEN_ERRNO(EINTR,        4)     /* Interrupted system call */
+#endif
+XEN_ERRNO(EIO,          5)     /* I/O error */
+XEN_ERRNO(ENXIO,        6)     /* No such device or address */
+XEN_ERRNO(E2BIG,        7)     /* Arg list too LONG_PTR */
+XEN_ERRNO(ENOEXEC,      8)     /* Exec format error */
+XEN_ERRNO(EBADF,        9)     /* Bad file number */
+XEN_ERRNO(ECHILD,      10)     /* No child processes */
+XEN_ERRNO(EAGAIN,      11)     /* Try again */
+XEN_ERRNO(EWOULDBLOCK, 11)     /* Operation would block.  Aliases EAGAIN */
+XEN_ERRNO(ENOMEM,      12)     /* Out of memory */
+XEN_ERRNO(EACCES,      13)     /* Permission denied */
+XEN_ERRNO(EFAULT,      14)     /* Bad address */
+XEN_ERRNO(EBUSY,       16)     /* Device or resource busy */
+XEN_ERRNO(EEXIST,      17)     /* File exists */
+XEN_ERRNO(EXDEV,       18)     /* Cross-device link */
+XEN_ERRNO(ENODEV,      19)     /* No such device */
+XEN_ERRNO(EISDIR,      21)     /* Is a directory */
+XEN_ERRNO(EINVAL,      22)     /* Invalid argument */
+XEN_ERRNO(ENFILE,      23)     /* File table overflow */
+XEN_ERRNO(EMFILE,      24)     /* Too many open files */
+XEN_ERRNO(ENOSPC,      28)     /* No space left on device */
+XEN_ERRNO(EROFS,       30)     /* Read-only file system */
+XEN_ERRNO(EMLINK,      31)     /* Too many links */
+XEN_ERRNO(EDOM,                33)     /* Math argument out of domain of func */
+XEN_ERRNO(ERANGE,      34)     /* Math result not representable */
+XEN_ERRNO(EDEADLK,     35)     /* Resource deadlock would occur */
+XEN_ERRNO(EDEADLOCK,   35)     /* Resource deadlock would occur. Aliases EDEADLK */
+XEN_ERRNO(ENAMETOOLONG,        36)     /* File name too LONG_PTR */
+XEN_ERRNO(ENOLCK,      37)     /* No record locks available */
+XEN_ERRNO(ENOTEMPTY,   39)     /* Directory not empty */
+XEN_ERRNO(ENOSYS,      38)     /* Function not implemented */
+XEN_ERRNO(ENODATA,     61)     /* No data available */
+XEN_ERRNO(ETIME,       62)     /* Timer expired */
+XEN_ERRNO(EBADMSG,     74)     /* Not a data message */
+XEN_ERRNO(EOVERFLOW,   75)     /* Value too large for defined data type */
+XEN_ERRNO(EILSEQ,      84)     /* Illegal byte sequence */
+#ifdef __XEN__ /* Internal only, should never be exposed to the guest. */
+XEN_ERRNO(ERESTART,    85)     /* Interrupted system call should be restarted */
+#endif
+XEN_ERRNO(ENOTSOCK,    88)     /* Socket operation on non-socket */
+XEN_ERRNO(EOPNOTSUPP,  95)     /* Operation not supported on transport endpoint */
+XEN_ERRNO(EADDRINUSE,  98)     /* Address already in use */
+XEN_ERRNO(EADDRNOTAVAIL, 99)   /* Cannot assign requested address */
+XEN_ERRNO(ENOBUFS,     105)    /* No buffer space available */
+XEN_ERRNO(EISCONN,     106)    /* Transport endpoint is already connected */
+XEN_ERRNO(ENOTCONN,    107)    /* Transport endpoint is not connected */
+XEN_ERRNO(ETIMEDOUT,   110)    /* Connection timed out */
+
+#undef XEN_ERRNO
+#endif /* XEN_ERRNO */
+/* ` } */
+
+/* Clean up from a default include.  Close the enum (for C). */
+#ifdef XEN_ERRNO_DEFAULT_INCLUDE
+#undef XEN_ERRNO_DEFAULT_INCLUDE
+#ifndef __ASSEMBLY__
+};
+#endif
+
+#endif /* XEN_ERRNO_DEFAULT_INCLUDE */
diff --git a/include/xen/public/grant_table.h b/include/xen/public/grant_table.h
new file mode 100644 (file)
index 0000000..b68ffdd
--- /dev/null
@@ -0,0 +1,684 @@
+/******************************************************************************
+ * 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"
+
+/*
+ * `incontents 150 gnttab Grant Tables
+ *
+ * Xen's grant tables provide a generic mechanism to memory sharing
+ * between domains. This shared memory interface underpins the split
+ * device drivers for block and network IO.
+ *
+ * Each domain has its own grant table. This is a data structure that
+ * is shared with Xen; it allows the domain to tell Xen what kind of
+ * permissions other domains have on its pages. Entries in the grant
+ * table are identified by grant references. A grant reference is an
+ * integer, which indexes into the grant table. It acts as a
+ * capability which the grantee can use to perform operations on the
+ * granter's memory.
+ *
+ * This capability-based system allows shared-memory communications
+ * between unprivileged domains. A grant reference also encapsulates
+ * the details of a shared page, removing the need for a domain to
+ * know the real machine address of a page it is sharing. This makes
+ * it possible to share memory correctly with domains running in
+ * fully virtualised memory.
+ */
+
+/***********************************
+ * 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 (drivers/xen/grant_table.c, see
+ * http://git.kernel.org/?p=linux/kernel/git/torvalds/linux.git;a=blob;f=drivers/xen/grant-table.c;hb=HEAD
+ *
+ * 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: GFN that @domid is allowed to map and access. [GST]
+     * GTF_accept_transfer: GFN that @domid is allowed to transfer into. [GST]
+     * GTF_transfer_completed: MFN whose ownership transferred by @domid
+     *                         (non-translated guests only). [XEN]
+     */
+    uint32_t frame;
+};
+typedef struct grant_entry_v1 grant_entry_v1_t;
+
+/* The first few grant table entries will be preserved across grant table
+ * version changes and may be pre-populated at domain creation by tools.
+ */
+#define GNTTAB_NR_RESERVED_ENTRIES     8
+#define GNTTAB_RESERVED_CONSOLE        0
+#define GNTTAB_RESERVED_XENSTORE       1
+
+/*
+ * 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
+ */
+
+/* ` enum neg_errnoval
+ * ` HYPERVISOR_grant_table_op(enum grant_table_op cmd,
+ * `                           void *args,
+ * `                           unsigned int count)
+ * `
+ *
+ * @args points to an array of a per-command data structure. The array
+ * has @count members
+ */
+
+/* ` enum grant_table_op { // GNTTABOP_* => struct gnttab_* */
+#define GNTTABOP_map_grant_ref        0
+#define GNTTABOP_unmap_grant_ref      1
+#define GNTTABOP_setup_table          2
+#define GNTTABOP_dump_table           3
+#define GNTTABOP_transfer             4
+#define GNTTABOP_copy                 5
+#define GNTTABOP_query_size           6
+#define GNTTABOP_unmap_and_replace    7
+#if __XEN_INTERFACE_VERSION__ >= 0x0003020a
+#define GNTTABOP_set_version          8
+#define GNTTABOP_get_status_frames    9
+#define GNTTABOP_get_version          10
+#define GNTTABOP_swap_grant_ref              11
+#define GNTTABOP_cache_flush         12
+#endif /* __XEN_INTERFACE_VERSION__ */
+/* ` } */
+
+/*
+ * 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, <status>
+ * 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!
+ */
+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;              /* => enum grant_status */
+    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.
+ */
+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;              /* => enum grant_status */
+};
+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.
+ */
+struct gnttab_setup_table {
+    /* IN parameters. */
+    domid_t  dom;
+    uint32_t nr_frames;
+    /* OUT parameters. */
+    int16_t  status;              /* => enum grant_status */
+#if __XEN_INTERFACE_VERSION__ < 0x00040300
+    XEN_GUEST_HANDLE(ulong) frame_list;
+#else
+    XEN_GUEST_HANDLE(xen_pfn_t) frame_list;
+#endif
+};
+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.
+ */
+struct gnttab_dump_table {
+    /* IN parameters. */
+    domid_t dom;
+    /* OUT parameters. */
+    int16_t status;               /* => enum grant_status */
+};
+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.
+ */
+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)
+
+struct gnttab_copy {
+    /* IN parameters. */
+    struct gnttab_copy_ptr {
+        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;
+};
+typedef struct gnttab_copy  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.
+ */
+struct gnttab_query_size {
+    /* IN parameters. */
+    domid_t  dom;
+    /* OUT parameters. */
+    uint32_t nr_frames;
+    uint32_t max_nr_frames;
+    int16_t  status;              /* => enum grant_status */
+};
+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.
+ */
+struct gnttab_unmap_and_replace {
+    /* IN parameters. */
+    uint64_t host_addr;
+    uint64_t new_addr;
+    grant_handle_t handle;
+    /* OUT parameters. */
+    int16_t  status;              /* => enum grant_status */
+};
+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.
+ */
+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.
+ */
+struct gnttab_get_status_frames {
+    /* IN parameters. */
+    uint32_t nr_frames;
+    domid_t  dom;
+    /* OUT parameters. */
+    int16_t  status;              /* => enum grant_status */
+    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>.
+ */
+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);
+
+/*
+ * GNTTABOP_swap_grant_ref: Swap the contents of two grant entries.
+ */
+struct gnttab_swap_grant_ref {
+    /* IN parameters */
+    grant_ref_t ref_a;
+    grant_ref_t ref_b;
+    /* OUT parameters */
+    int16_t status;             /* => enum grant_status */
+};
+typedef struct gnttab_swap_grant_ref gnttab_swap_grant_ref_t;
+DEFINE_XEN_GUEST_HANDLE(gnttab_swap_grant_ref_t);
+
+/*
+ * Issue one or more cache maintenance operations on a portion of a
+ * page granted to the calling domain by a foreign domain.
+ */
+struct gnttab_cache_flush {
+    union {
+        uint64_t dev_bus_addr;
+        grant_ref_t ref;
+    } a;
+    uint16_t offset; /* offset from start of grant */
+    uint16_t length; /* size within the grant */
+#define GNTTAB_CACHE_CLEAN          (1<<0)
+#define GNTTAB_CACHE_INVAL          (1<<1)
+#define GNTTAB_CACHE_SOURCE_GREF    (1<<31)
+    uint32_t op;
+};
+typedef struct gnttab_cache_flush gnttab_cache_flush_t;
+DEFINE_XEN_GUEST_HANDLE(gnttab_cache_flush_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.
+ */
+/* ` enum grant_status { */
+#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) /* Operation not done; try again.        */
+/* ` } */
+
+#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",              \
+    "operation not done; try again"             \
+}
+
+#endif /* __XEN_PUBLIC_GRANT_TABLE_H__ */
+
+/*
+ * Local variables:
+ * mode: C
+ * c-file-style: "BSD"
+ * c-basic-offset: 4
+ * tab-width: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
diff --git a/include/xen/public/io/kbdif.h b/include/xen/public/io/kbdif.h
new file mode 100644 (file)
index 0000000..313d9d7
--- /dev/null
@@ -0,0 +1,536 @@
+/*
+ * kbdif.h -- Xen virtual keyboard/mouse
+ *
+ * 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 Anthony Liguori &lt;aliguori@us.ibm.com&gt;
+ * Copyright (C) 2006 Red Hat, Inc., Markus Armbruster &lt;armbru@redhat.com&gt;
+ */
+
+#ifndef __XEN_PUBLIC_IO_KBDIF_H__
+#define __XEN_PUBLIC_IO_KBDIF_H__
+
+/*
+ *****************************************************************************
+ *                     Feature and Parameter Negotiation
+ *****************************************************************************
+ *
+ * The two halves of a para-virtual driver utilize nodes within
+ * XenStore to communicate capabilities and to negotiate operating parameters.
+ * This section enumerates these nodes which reside in the respective front and
+ * backend portions of XenStore, following XenBus convention.
+ *
+ * All data in XenStore is stored as strings.  Nodes specifying numeric
+ * values are encoded in decimal. Integer value ranges listed below are
+ * expressed as fixed sized integer types capable of storing the conversion
+ * of a properly formated node string, without loss of information.
+ *
+ *****************************************************************************
+ *                            Backend XenBus Nodes
+ *****************************************************************************
+ *
+ *---------------------------- Features supported ----------------------------
+ *
+ * Capable backend advertises supported features by publishing
+ * corresponding entries in XenStore and puts 1 as the value of the entry.
+ * If a feature is not supported then 0 must be set or feature entry omitted.
+ *
+ * feature-abs-pointer
+ *      Values:         &lt;uint&gt;
+ *
+ *      Backends, which support reporting of absolute coordinates for pointer
+ *      device should set this to 1.
+ *
+ * feature-multi-touch
+ *      Values:         &lt;uint&gt;
+ *
+ *      Backends, which support reporting of multi-touch events
+ *      should set this to 1.
+ *
+ *------------------------- Pointer Device Parameters ------------------------
+ *
+ * width
+ *      Values:         &lt;uint&gt;
+ *
+ *      Maximum X coordinate (width) to be used by the frontend
+ *      while reporting input events, pixels, [0; UINT32_MAX].
+ *
+ * height
+ *      Values:         &lt;uint&gt;
+ *
+ *      Maximum Y coordinate (height) to be used by the frontend
+ *      while reporting input events, pixels, [0; UINT32_MAX].
+ *
+ *****************************************************************************
+ *                            Frontend XenBus Nodes
+ *****************************************************************************
+ *
+ *------------------------------ Feature request -----------------------------
+ *
+ * Capable frontend requests features from backend via setting corresponding
+ * entries to 1 in XenStore. Requests for features not advertised as supported
+ * by the backend have no effect.
+ *
+ * request-abs-pointer
+ *      Values:         &lt;uint&gt;
+ *
+ *      Request backend to report absolute pointer coordinates
+ *      (XENKBD_TYPE_POS) instead of relative ones (XENKBD_TYPE_MOTION).
+ *
+ * request-multi-touch
+ *      Values:         &lt;uint&gt;
+ *
+ *      Request backend to report multi-touch events.
+ *
+ *----------------------- Request Transport Parameters -----------------------
+ *
+ * event-channel
+ *      Values:         &lt;uint&gt;
+ *
+ *      The identifier of the Xen event channel used to signal activity
+ *      in the ring buffer.
+ *
+ * page-gref
+ *      Values:         &lt;uint&gt;
+ *
+ *      The Xen grant reference granting permission for the backend to map
+ *      a sole page in a single page sized event ring buffer.
+ *
+ * page-ref
+ *      Values:         &lt;uint&gt;
+ *
+ *      OBSOLETE, not recommended for use.
+ *      PFN of the shared page.
+ *
+ *----------------------- Multi-touch Device Parameters -----------------------
+ *
+ * multi-touch-num-contacts
+ *      Values:         &lt;uint&gt;
+ *
+ *      Number of simultaneous touches reported.
+ *
+ * multi-touch-width
+ *      Values:         &lt;uint&gt;
+ *
+ *      Width of the touch area to be used by the frontend
+ *      while reporting input events, pixels, [0; UINT32_MAX].
+ *
+ * multi-touch-height
+ *      Values:         &lt;uint&gt;
+ *
+ *      Height of the touch area to be used by the frontend
+ *      while reporting input events, pixels, [0; UINT32_MAX].
+ */
+
+/*
+ * EVENT CODES.
+ */
+
+#define XENKBD_TYPE_MOTION             1
+#define XENKBD_TYPE_RESERVED           2
+#define XENKBD_TYPE_KEY                3
+#define XENKBD_TYPE_POS                4
+#define XENKBD_TYPE_MTOUCH             5
+
+/* Multi-touch event sub-codes */
+
+#define XENKBD_MT_EV_DOWN              0
+#define XENKBD_MT_EV_UP                1
+#define XENKBD_MT_EV_MOTION            2
+#define XENKBD_MT_EV_SYN               3
+#define XENKBD_MT_EV_SHAPE             4
+#define XENKBD_MT_EV_ORIENT            5
+
+/*
+ * CONSTANTS, XENSTORE FIELD AND PATH NAME STRINGS, HELPERS.
+ */
+
+#define XENKBD_DRIVER_NAME             "vkbd"
+
+#define XENKBD_FIELD_FEAT_ABS_POINTER  "feature-abs-pointer"
+#define XENKBD_FIELD_FEAT_MTOUCH       "feature-multi-touch"
+#define XENKBD_FIELD_REQ_ABS_POINTER   "request-abs-pointer"
+#define XENKBD_FIELD_REQ_MTOUCH        "request-multi-touch"
+#define XENKBD_FIELD_RING_GREF         "page-gref"
+#define XENKBD_FIELD_EVT_CHANNEL       "event-channel"
+#define XENKBD_FIELD_WIDTH             "width"
+#define XENKBD_FIELD_HEIGHT            "height"
+#define XENKBD_FIELD_MT_WIDTH          "multi-touch-width"
+#define XENKBD_FIELD_MT_HEIGHT         "multi-touch-height"
+#define XENKBD_FIELD_MT_NUM_CONTACTS   "multi-touch-num-contacts"
+
+/* OBSOLETE, not recommended for use */
+#define XENKBD_FIELD_RING_REF          "page-ref"
+
+/*
+ *****************************************************************************
+ * Description of the protocol between frontend and backend driver.
+ *****************************************************************************
+ *
+ * The two halves of a Para-virtual driver communicate with
+ * each other using a shared page and an event channel.
+ * Shared page contains a ring with event structures.
+ *
+ * All reserved fields in the structures below must be 0.
+ *
+ *****************************************************************************
+ *                           Backend to frontend events
+ *****************************************************************************
+ *
+ * Frontends should ignore unknown in events.
+ * All event packets have the same length (40 octets)
+ * All event packets have common header:
+ *
+ *          0         octet
+ * +-----------------+
+ * |       type      |
+ * +-----------------+
+ * type - uint8_t, event code, XENKBD_TYPE_???
+ *
+ *
+ * Pointer relative movement event
+ *         0                1                 2               3        octet
+ * +----------------+----------------+----------------+----------------+
+ * |  _TYPE_MOTION  |                     reserved                     | 4
+ * +----------------+----------------+----------------+----------------+
+ * |                               rel_x                               | 8
+ * +----------------+----------------+----------------+----------------+
+ * |                               rel_y                               | 12
+ * +----------------+----------------+----------------+----------------+
+ * |                               rel_z                               | 16
+ * +----------------+----------------+----------------+----------------+
+ * |                             reserved                              | 20
+ * +----------------+----------------+----------------+----------------+
+ * |/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/|
+ * +----------------+----------------+----------------+----------------+
+ * |                             reserved                              | 40
+ * +----------------+----------------+----------------+----------------+
+ *
+ * rel_x - int32_t, relative X motion
+ * rel_y - int32_t, relative Y motion
+ * rel_z - int32_t, relative Z motion (wheel)
+ */
+
+struct xenkbd_motion
+{
+    uint8_t type;
+    int32_t rel_x;
+    int32_t rel_y;
+    int32_t rel_z;
+};
+
+/*
+ * Key event (includes pointer buttons)
+ *         0                1                 2               3        octet
+ * +----------------+----------------+----------------+----------------+
+ * |  _TYPE_KEY     |     pressed    |            reserved             | 4
+ * +----------------+----------------+----------------+----------------+
+ * |                              keycode                              | 8
+ * +----------------+----------------+----------------+----------------+
+ * |                             reserved                              | 12
+ * +----------------+----------------+----------------+----------------+
+ * |/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/|
+ * +----------------+----------------+----------------+----------------+
+ * |                             reserved                              | 40
+ * +----------------+----------------+----------------+----------------+
+ *
+ * pressed - uint8_t, 1 if pressed; 0 otherwise
+ * keycode - uint32_t, KEY_* from linux/input.h
+ */
+
+struct xenkbd_key
+{
+    uint8_t type;
+    uint8_t pressed;
+    uint32_t keycode;
+};
+
+/*
+ * Pointer absolute position event
+ *         0                1                 2               3        octet
+ * +----------------+----------------+----------------+----------------+
+ * |  _TYPE_POS     |                     reserved                     | 4
+ * +----------------+----------------+----------------+----------------+
+ * |                               abs_x                               | 8
+ * +----------------+----------------+----------------+----------------+
+ * |                               abs_y                               | 12
+ * +----------------+----------------+----------------+----------------+
+ * |                               rel_z                               | 16
+ * +----------------+----------------+----------------+----------------+
+ * |                             reserved                              | 20
+ * +----------------+----------------+----------------+----------------+
+ * |/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/|
+ * +----------------+----------------+----------------+----------------+
+ * |                             reserved                              | 40
+ * +----------------+----------------+----------------+----------------+
+ *
+ * abs_x - int32_t, absolute X position (in FB pixels)
+ * abs_y - int32_t, absolute Y position (in FB pixels)
+ * rel_z - int32_t, relative Z motion (wheel)
+ */
+
+struct xenkbd_position
+{
+    uint8_t type;
+    int32_t abs_x;
+    int32_t abs_y;
+    int32_t rel_z;
+};
+
+/*
+ * Multi-touch event and its sub-types
+ *
+ * All multi-touch event packets have common header:
+ *
+ *         0                1                 2               3        octet
+ * +----------------+----------------+----------------+----------------+
+ * |  _TYPE_MTOUCH  |   event_type   |   contact_id   |    reserved    | 4
+ * +----------------+----------------+----------------+----------------+
+ * |                             reserved                              | 8
+ * +----------------+----------------+----------------+----------------+
+ *
+ * event_type - unt8_t, multi-touch event sub-type, XENKBD_MT_EV_???
+ * contact_id - unt8_t, ID of the contact
+ *
+ * Touch interactions can consist of one or more contacts.
+ * For each contact, a series of events is generated, starting
+ * with a down event, followed by zero or more motion events,
+ * and ending with an up event. Events relating to the same
+ * contact point can be identified by the ID of the sequence: contact ID.
+ * Contact ID may be reused after XENKBD_MT_EV_UP event and
+ * is in the [0; XENKBD_FIELD_NUM_CONTACTS - 1] range.
+ *
+ * For further information please refer to documentation on Wayland [1],
+ * Linux [2] and Windows [3] multi-touch support.
+ *
+ * [1] https://cgit.freedesktop.org/wayland/wayland/tree/protocol/wayland.xml
+ * [2] https://www.kernel.org/doc/Documentation/input/multi-touch-protocol.txt
+ * [3] https://msdn.microsoft.com/en-us/library/jj151564(v=vs.85).aspx
+ *
+ *
+ * Multi-touch down event - sent when a new touch is made: touch is assigned
+ * a unique contact ID, sent with this and consequent events related
+ * to this touch.
+ *         0                1                 2               3        octet
+ * +----------------+----------------+----------------+----------------+
+ * |  _TYPE_MTOUCH  |   _MT_EV_DOWN  |   contact_id   |    reserved    | 4
+ * +----------------+----------------+----------------+----------------+
+ * |                             reserved                              | 8
+ * +----------------+----------------+----------------+----------------+
+ * |                               abs_x                               | 12
+ * +----------------+----------------+----------------+----------------+
+ * |                               abs_y                               | 16
+ * +----------------+----------------+----------------+----------------+
+ * |                             reserved                              | 20
+ * +----------------+----------------+----------------+----------------+
+ * |/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/|
+ * +----------------+----------------+----------------+----------------+
+ * |                             reserved                              | 40
+ * +----------------+----------------+----------------+----------------+
+ *
+ * abs_x - int32_t, absolute X position, in pixels
+ * abs_y - int32_t, absolute Y position, in pixels
+ *
+ * Multi-touch contact release event
+ *         0                1                 2               3        octet
+ * +----------------+----------------+----------------+----------------+
+ * |  _TYPE_MTOUCH  |  _MT_EV_UP     |   contact_id   |    reserved    | 4
+ * +----------------+----------------+----------------+----------------+
+ * |                             reserved                              | 8
+ * +----------------+----------------+----------------+----------------+
+ * |/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/|
+ * +----------------+----------------+----------------+----------------+
+ * |                             reserved                              | 40
+ * +----------------+----------------+----------------+----------------+
+ *
+ * Multi-touch motion event
+ *         0                1                 2               3        octet
+ * +----------------+----------------+----------------+----------------+
+ * |  _TYPE_MTOUCH  |  _MT_EV_MOTION |   contact_id   |    reserved    | 4
+ * +----------------+----------------+----------------+----------------+
+ * |                             reserved                              | 8
+ * +----------------+----------------+----------------+----------------+
+ * |                               abs_x                               | 12
+ * +----------------+----------------+----------------+----------------+
+ * |                               abs_y                               | 16
+ * +----------------+----------------+----------------+----------------+
+ * |                             reserved                              | 20
+ * +----------------+----------------+----------------+----------------+
+ * |/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/|
+ * +----------------+----------------+----------------+----------------+
+ * |                             reserved                              | 40
+ * +----------------+----------------+----------------+----------------+
+ *
+ * abs_x - int32_t, absolute X position, in pixels,
+ * abs_y - int32_t, absolute Y position, in pixels,
+ *
+ * Multi-touch input synchronization event - shows end of a set of events
+ * which logically belong together.
+ *         0                1                 2               3        octet
+ * +----------------+----------------+----------------+----------------+
+ * |  _TYPE_MTOUCH  |  _MT_EV_SYN    |   contact_id   |    reserved    | 4
+ * +----------------+----------------+----------------+----------------+
+ * |                             reserved                              | 8
+ * +----------------+----------------+----------------+----------------+
+ * |/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/|
+ * +----------------+----------------+----------------+----------------+
+ * |                             reserved                              | 40
+ * +----------------+----------------+----------------+----------------+
+ *
+ * Multi-touch shape event - touch point's shape has changed its shape.
+ * Shape is approximated by an ellipse through the major and minor axis
+ * lengths: major is the longer diameter of the ellipse and minor is the
+ * shorter one. Center of the ellipse is reported via
+ * XENKBD_MT_EV_DOWN/XENKBD_MT_EV_MOTION events.
+ *         0                1                 2               3        octet
+ * +----------------+----------------+----------------+----------------+
+ * |  _TYPE_MTOUCH  |  _MT_EV_SHAPE  |   contact_id   |    reserved    | 4
+ * +----------------+----------------+----------------+----------------+
+ * |                             reserved                              | 8
+ * +----------------+----------------+----------------+----------------+
+ * |                               major                               | 12
+ * +----------------+----------------+----------------+----------------+
+ * |                               minor                               | 16
+ * +----------------+----------------+----------------+----------------+
+ * |                             reserved                              | 20
+ * +----------------+----------------+----------------+----------------+
+ * |/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/|
+ * +----------------+----------------+----------------+----------------+
+ * |                             reserved                              | 40
+ * +----------------+----------------+----------------+----------------+
+ *
+ * major - unt32_t, length of the major axis, pixels
+ * minor - unt32_t, length of the minor axis, pixels
+ *
+ * Multi-touch orientation event - touch point's shape has changed
+ * its orientation: calculated as a clockwise angle between the major axis
+ * of the ellipse and positive Y axis in degrees, [-180; +180].
+ *         0                1                 2               3        octet
+ * +----------------+----------------+----------------+----------------+
+ * |  _TYPE_MTOUCH  |  _MT_EV_ORIENT |   contact_id   |    reserved    | 4
+ * +----------------+----------------+----------------+----------------+
+ * |                             reserved                              | 8
+ * +----------------+----------------+----------------+----------------+
+ * |           orientation           |            reserved             | 12
+ * +----------------+----------------+----------------+----------------+
+ * |                             reserved                              | 16
+ * +----------------+----------------+----------------+----------------+
+ * |/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/|
+ * +----------------+----------------+----------------+----------------+
+ * |                             reserved                              | 40
+ * +----------------+----------------+----------------+----------------+
+ *
+ * orientation - int16_t, clockwise angle of the major axis
+ */
+
+struct xenkbd_mtouch {
+    uint8_t type;            /* XENKBD_TYPE_MTOUCH */
+    uint8_t event_type;      /* XENKBD_MT_EV_??? */
+    uint8_t contact_id;
+    uint8_t reserved[5];     /* reserved for the future use */
+    union {
+        struct {
+            int32_t abs_x;   /* absolute X position, pixels */
+            int32_t abs_y;   /* absolute Y position, pixels */
+        } pos;
+        struct {
+            uint32_t major;  /* length of the major axis, pixels */
+            uint32_t minor;  /* length of the minor axis, pixels */
+        } shape;
+        int16_t orientation; /* clockwise angle of the major axis */
+    } u;
+};
+
+#define XENKBD_IN_EVENT_SIZE 40
+
+union xenkbd_in_event
+{
+    uint8_t type;
+    struct xenkbd_motion motion;
+    struct xenkbd_key key;
+    struct xenkbd_position pos;
+    struct xenkbd_mtouch mtouch;
+    char pad[XENKBD_IN_EVENT_SIZE];
+};
+
+/*
+ *****************************************************************************
+ *                            Frontend to backend events
+ *****************************************************************************
+ *
+ * Out events may be sent only when requested by backend, and receipt
+ * of an unknown out event is an error.
+ * No out events currently defined.
+
+ * All event packets have the same length (40 octets)
+ * All event packets have common header:
+ *          0         octet
+ * +-----------------+
+ * |       type      |
+ * +-----------------+
+ * type - uint8_t, event code
+ */
+
+#define XENKBD_OUT_EVENT_SIZE 40
+
+union xenkbd_out_event
+{
+    uint8_t type;
+    char pad[XENKBD_OUT_EVENT_SIZE];
+};
+
+/*
+ *****************************************************************************
+ *                            Shared page
+ *****************************************************************************
+ */
+
+#define XENKBD_IN_RING_SIZE 2048
+#define XENKBD_IN_RING_LEN (XENKBD_IN_RING_SIZE / XENKBD_IN_EVENT_SIZE)
+#define XENKBD_IN_RING_OFFS 1024
+#define XENKBD_IN_RING(page) \
+    ((union xenkbd_in_event *)((char *)(page) + XENKBD_IN_RING_OFFS))
+#define XENKBD_IN_RING_REF(page, idx) \
+    (XENKBD_IN_RING((page))[(idx) % XENKBD_IN_RING_LEN])
+
+#define XENKBD_OUT_RING_SIZE 1024
+#define XENKBD_OUT_RING_LEN (XENKBD_OUT_RING_SIZE / XENKBD_OUT_EVENT_SIZE)
+#define XENKBD_OUT_RING_OFFS (XENKBD_IN_RING_OFFS + XENKBD_IN_RING_SIZE)
+#define XENKBD_OUT_RING(page) \
+    ((union xenkbd_out_event *)((char *)(page) + XENKBD_OUT_RING_OFFS))
+#define XENKBD_OUT_RING_REF(page, idx) \
+    (XENKBD_OUT_RING((page))[(idx) % XENKBD_OUT_RING_LEN])
+
+struct xenkbd_page
+{
+    uint32_t in_cons, in_prod;
+    uint32_t out_cons, out_prod;
+};
+
+#endif /* __XEN_PUBLIC_IO_KBDIF_H__ */
+
+/*
+ * Local variables:
+ * mode: C
+ * c-file-style: "BSD"
+ * c-basic-offset: 4
+ * tab-width: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
diff --git a/include/xen/public/io/ring.h b/include/xen/public/io/ring.h
new file mode 100644 (file)
index 0000000..1d29cc0
--- /dev/null
@@ -0,0 +1,326 @@
+/******************************************************************************
+ * 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];                                             \
+    } pvt;                                                              \
+    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)->pvt.pvt_pad, 0, sizeof((_s)->pvt.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)
+
+/* 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))
+
+/*
+ * Get a local copy of a request.
+ *
+ * Use this in preference to RING_GET_REQUEST() so all processing is
+ * done on a local copy that cannot be modified by the other end.
+ *
+ * Note that https://gcc.gnu.org/bugzilla/show_bug.cgi?id=58145 may cause this
+ * to be ineffective where _req is a struct which consists of only bitfields.
+ */
+#define RING_COPY_REQUEST(_r, _idx, _req) do {                         \
+       /* Use volatile to force the copy into _req. */                 \
+       *(_req) = *(volatile typeof(_req))RING_GET_REQUEST(_r, _idx);   \
+} while (0)
+
+#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))
+
+/* Ill-behaved frontend determination: Can there be this many requests? */
+#define RING_REQUEST_PROD_OVERFLOW(_r, _prod)                           \
+    (((_prod) - (_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-file-style: "BSD"
+ * c-basic-offset: 4
+ * tab-width: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
diff --git a/include/xen/public/io/xenbus.h b/include/xen/public/io/xenbus.h
new file mode 100644 (file)
index 0000000..927f9db
--- /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-file-style: "BSD"
+ * c-basic-offset: 4
+ * tab-width: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
diff --git a/include/xen/public/trace.h b/include/xen/public/trace.h
new file mode 100644 (file)
index 0000000..95b00e8
--- /dev/null
@@ -0,0 +1,337 @@
+/******************************************************************************
+ * include/public/trace.h
+ *
+ * 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.
+ *
+ * Mark Williamson, (C) 2004 Intel Research Cambridge
+ * Copyright (C) 2005 Bin Ren
+ */
+
+#ifndef __XEN_PUBLIC_TRACE_H__
+#define __XEN_PUBLIC_TRACE_H__
+
+#define TRACE_EXTRA_MAX    7
+#define TRACE_EXTRA_SHIFT 28
+
+/* Trace classes */
+#define TRC_CLS_SHIFT 16
+#define TRC_GEN      0x0001f000    /* General trace            */
+#define TRC_SCHED    0x0002f000    /* Xen Scheduler trace      */
+#define TRC_DOM0OP   0x0004f000    /* Xen DOM0 operation trace */
+#define TRC_HVM      0x0008f000    /* Xen HVM trace            */
+#define TRC_MEM      0x0010f000    /* Xen memory trace         */
+#define TRC_PV       0x0020f000    /* Xen PV traces            */
+#define TRC_SHADOW   0x0040f000    /* Xen shadow tracing       */
+#define TRC_HW       0x0080f000    /* Xen hardware-related traces */
+#define TRC_GUEST    0x0800f000    /* Guest-generated traces   */
+#define TRC_ALL      0x0ffff000
+#define TRC_HD_TO_EVENT(x) ((x)&0x0fffffff)
+#define TRC_HD_CYCLE_FLAG (1UL<<31)
+#define TRC_HD_INCLUDES_CYCLE_COUNT(x) ( !!( (x) & TRC_HD_CYCLE_FLAG ) )
+#define TRC_HD_EXTRA(x)    (((x)>>TRACE_EXTRA_SHIFT)&TRACE_EXTRA_MAX)
+
+/* Trace subclasses */
+#define TRC_SUBCLS_SHIFT 12
+
+/* trace subclasses for SVM */
+#define TRC_HVM_ENTRYEXIT   0x00081000   /* VMENTRY and #VMEXIT       */
+#define TRC_HVM_HANDLER     0x00082000   /* various HVM handlers      */
+#define TRC_HVM_EMUL        0x00084000   /* emulated devices */
+
+#define TRC_SCHED_MIN       0x00021000   /* Just runstate changes */
+#define TRC_SCHED_CLASS     0x00022000   /* Scheduler-specific    */
+#define TRC_SCHED_VERBOSE   0x00028000   /* More inclusive scheduling */
+
+/*
+ * The highest 3 bits of the last 12 bits of TRC_SCHED_CLASS above are
+ * reserved for encoding what scheduler produced the information. The
+ * actual event is encoded in the last 9 bits.
+ *
+ * This means we have 8 scheduling IDs available (which means at most 8
+ * schedulers generating events) and, in each scheduler, up to 512
+ * different events.
+ */
+#define TRC_SCHED_ID_BITS 3
+#define TRC_SCHED_ID_SHIFT (TRC_SUBCLS_SHIFT - TRC_SCHED_ID_BITS)
+#define TRC_SCHED_ID_MASK (((1UL<<TRC_SCHED_ID_BITS) - 1) << TRC_SCHED_ID_SHIFT)
+#define TRC_SCHED_EVT_MASK (~(TRC_SCHED_ID_MASK))
+
+/* Per-scheduler IDs, to identify scheduler specific events */
+#define TRC_SCHED_CSCHED   0
+#define TRC_SCHED_CSCHED2  1
+/* #define XEN_SCHEDULER_SEDF 2 (Removed) */
+#define TRC_SCHED_ARINC653 3
+#define TRC_SCHED_RTDS     4
+
+/* Per-scheduler tracing */
+#define TRC_SCHED_CLASS_EVT(_c, _e) \
+  ( ( TRC_SCHED_CLASS | \
+      ((TRC_SCHED_##_c << TRC_SCHED_ID_SHIFT) & TRC_SCHED_ID_MASK) ) + \
+    (_e & TRC_SCHED_EVT_MASK) )
+
+/* Trace classes for DOM0 operations */
+#define TRC_DOM0_DOMOPS     0x00041000   /* Domains manipulations */
+
+/* Trace classes for Hardware */
+#define TRC_HW_PM           0x00801000   /* Power management traces */
+#define TRC_HW_IRQ          0x00802000   /* Traces relating to the handling of IRQs */
+
+/* Trace events per class */
+#define TRC_LOST_RECORDS        (TRC_GEN + 1)
+#define TRC_TRACE_WRAP_BUFFER  (TRC_GEN + 2)
+#define TRC_TRACE_CPU_CHANGE    (TRC_GEN + 3)
+
+#define TRC_SCHED_RUNSTATE_CHANGE   (TRC_SCHED_MIN + 1)
+#define TRC_SCHED_CONTINUE_RUNNING  (TRC_SCHED_MIN + 2)
+#define TRC_SCHED_DOM_ADD        (TRC_SCHED_VERBOSE +  1)
+#define TRC_SCHED_DOM_REM        (TRC_SCHED_VERBOSE +  2)
+#define TRC_SCHED_SLEEP          (TRC_SCHED_VERBOSE +  3)
+#define TRC_SCHED_WAKE           (TRC_SCHED_VERBOSE +  4)
+#define TRC_SCHED_YIELD          (TRC_SCHED_VERBOSE +  5)
+#define TRC_SCHED_BLOCK          (TRC_SCHED_VERBOSE +  6)
+#define TRC_SCHED_SHUTDOWN       (TRC_SCHED_VERBOSE +  7)
+#define TRC_SCHED_CTL            (TRC_SCHED_VERBOSE +  8)
+#define TRC_SCHED_ADJDOM         (TRC_SCHED_VERBOSE +  9)
+#define TRC_SCHED_SWITCH         (TRC_SCHED_VERBOSE + 10)
+#define TRC_SCHED_S_TIMER_FN     (TRC_SCHED_VERBOSE + 11)
+#define TRC_SCHED_T_TIMER_FN     (TRC_SCHED_VERBOSE + 12)
+#define TRC_SCHED_DOM_TIMER_FN   (TRC_SCHED_VERBOSE + 13)
+#define TRC_SCHED_SWITCH_INFPREV (TRC_SCHED_VERBOSE + 14)
+#define TRC_SCHED_SWITCH_INFNEXT (TRC_SCHED_VERBOSE + 15)
+#define TRC_SCHED_SHUTDOWN_CODE  (TRC_SCHED_VERBOSE + 16)
+
+#define TRC_DOM0_DOM_ADD         (TRC_DOM0_DOMOPS + 1)
+#define TRC_DOM0_DOM_REM         (TRC_DOM0_DOMOPS + 2)
+
+#define TRC_MEM_PAGE_GRANT_MAP      (TRC_MEM + 1)
+#define TRC_MEM_PAGE_GRANT_UNMAP    (TRC_MEM + 2)
+#define TRC_MEM_PAGE_GRANT_TRANSFER (TRC_MEM + 3)
+#define TRC_MEM_SET_P2M_ENTRY       (TRC_MEM + 4)
+#define TRC_MEM_DECREASE_RESERVATION (TRC_MEM + 5)
+#define TRC_MEM_POD_POPULATE        (TRC_MEM + 16)
+#define TRC_MEM_POD_ZERO_RECLAIM    (TRC_MEM + 17)
+#define TRC_MEM_POD_SUPERPAGE_SPLINTER (TRC_MEM + 18)
+
+#define TRC_PV_ENTRY   0x00201000 /* Hypervisor entry points for PV guests. */
+#define TRC_PV_SUBCALL 0x00202000 /* Sub-call in a multicall hypercall */
+
+#define TRC_PV_HYPERCALL             (TRC_PV_ENTRY +  1)
+#define TRC_PV_TRAP                  (TRC_PV_ENTRY +  3)
+#define TRC_PV_PAGE_FAULT            (TRC_PV_ENTRY +  4)
+#define TRC_PV_FORCED_INVALID_OP     (TRC_PV_ENTRY +  5)
+#define TRC_PV_EMULATE_PRIVOP        (TRC_PV_ENTRY +  6)
+#define TRC_PV_EMULATE_4GB           (TRC_PV_ENTRY +  7)
+#define TRC_PV_MATH_STATE_RESTORE    (TRC_PV_ENTRY +  8)
+#define TRC_PV_PAGING_FIXUP          (TRC_PV_ENTRY +  9)
+#define TRC_PV_GDT_LDT_MAPPING_FAULT (TRC_PV_ENTRY + 10)
+#define TRC_PV_PTWR_EMULATION        (TRC_PV_ENTRY + 11)
+#define TRC_PV_PTWR_EMULATION_PAE    (TRC_PV_ENTRY + 12)
+#define TRC_PV_HYPERCALL_V2          (TRC_PV_ENTRY + 13)
+#define TRC_PV_HYPERCALL_SUBCALL     (TRC_PV_SUBCALL + 14)
+
+/*
+ * TRC_PV_HYPERCALL_V2 format
+ *
+ * Only some of the hypercall argument are recorded. Bit fields A0 to
+ * A5 in the first extra word are set if the argument is present and
+ * the arguments themselves are packed sequentially in the following
+ * words.
+ *
+ * The TRC_64_FLAG bit is not set for these events (even if there are
+ * 64-bit arguments in the record).
+ *
+ * Word
+ * 0    bit 31 30|29 28|27 26|25 24|23 22|21 20|19 ... 0
+ *          A5   |A4   |A3   |A2   |A1   |A0   |Hypercall op
+ * 1    First 32 bit (or low word of first 64 bit) arg in record
+ * 2    Second 32 bit (or high word of first 64 bit) arg in record
+ * ...
+ *
+ * A0-A5 bitfield values:
+ *
+ *   00b  Argument not present
+ *   01b  32-bit argument present
+ *   10b  64-bit argument present
+ *   11b  Reserved
+ */
+#define TRC_PV_HYPERCALL_V2_ARG_32(i) (0x1 << (20 + 2*(i)))
+#define TRC_PV_HYPERCALL_V2_ARG_64(i) (0x2 << (20 + 2*(i)))
+#define TRC_PV_HYPERCALL_V2_ARG_MASK  (0xfff00000)
+
+#define TRC_SHADOW_NOT_SHADOW                 (TRC_SHADOW +  1)
+#define TRC_SHADOW_FAST_PROPAGATE             (TRC_SHADOW +  2)
+#define TRC_SHADOW_FAST_MMIO                  (TRC_SHADOW +  3)
+#define TRC_SHADOW_FALSE_FAST_PATH            (TRC_SHADOW +  4)
+#define TRC_SHADOW_MMIO                       (TRC_SHADOW +  5)
+#define TRC_SHADOW_FIXUP                      (TRC_SHADOW +  6)
+#define TRC_SHADOW_DOMF_DYING                 (TRC_SHADOW +  7)
+#define TRC_SHADOW_EMULATE                    (TRC_SHADOW +  8)
+#define TRC_SHADOW_EMULATE_UNSHADOW_USER      (TRC_SHADOW +  9)
+#define TRC_SHADOW_EMULATE_UNSHADOW_EVTINJ    (TRC_SHADOW + 10)
+#define TRC_SHADOW_EMULATE_UNSHADOW_UNHANDLED (TRC_SHADOW + 11)
+#define TRC_SHADOW_WRMAP_BF                   (TRC_SHADOW + 12)
+#define TRC_SHADOW_PREALLOC_UNPIN             (TRC_SHADOW + 13)
+#define TRC_SHADOW_RESYNC_FULL                (TRC_SHADOW + 14)
+#define TRC_SHADOW_RESYNC_ONLY                (TRC_SHADOW + 15)
+
+/* trace events per subclass */
+#define TRC_HVM_NESTEDFLAG      (0x400)
+#define TRC_HVM_VMENTRY         (TRC_HVM_ENTRYEXIT + 0x01)
+#define TRC_HVM_VMEXIT          (TRC_HVM_ENTRYEXIT + 0x02)
+#define TRC_HVM_VMEXIT64        (TRC_HVM_ENTRYEXIT + TRC_64_FLAG + 0x02)
+#define TRC_HVM_PF_XEN          (TRC_HVM_HANDLER + 0x01)
+#define TRC_HVM_PF_XEN64        (TRC_HVM_HANDLER + TRC_64_FLAG + 0x01)
+#define TRC_HVM_PF_INJECT       (TRC_HVM_HANDLER + 0x02)
+#define TRC_HVM_PF_INJECT64     (TRC_HVM_HANDLER + TRC_64_FLAG + 0x02)
+#define TRC_HVM_INJ_EXC         (TRC_HVM_HANDLER + 0x03)
+#define TRC_HVM_INJ_VIRQ        (TRC_HVM_HANDLER + 0x04)
+#define TRC_HVM_REINJ_VIRQ      (TRC_HVM_HANDLER + 0x05)
+#define TRC_HVM_IO_READ         (TRC_HVM_HANDLER + 0x06)
+#define TRC_HVM_IO_WRITE        (TRC_HVM_HANDLER + 0x07)
+#define TRC_HVM_CR_READ         (TRC_HVM_HANDLER + 0x08)
+#define TRC_HVM_CR_READ64       (TRC_HVM_HANDLER + TRC_64_FLAG + 0x08)
+#define TRC_HVM_CR_WRITE        (TRC_HVM_HANDLER + 0x09)
+#define TRC_HVM_CR_WRITE64      (TRC_HVM_HANDLER + TRC_64_FLAG + 0x09)
+#define TRC_HVM_DR_READ         (TRC_HVM_HANDLER + 0x0A)
+#define TRC_HVM_DR_WRITE        (TRC_HVM_HANDLER + 0x0B)
+#define TRC_HVM_MSR_READ        (TRC_HVM_HANDLER + 0x0C)
+#define TRC_HVM_MSR_WRITE       (TRC_HVM_HANDLER + 0x0D)
+#define TRC_HVM_CPUID           (TRC_HVM_HANDLER + 0x0E)
+#define TRC_HVM_INTR            (TRC_HVM_HANDLER + 0x0F)
+#define TRC_HVM_NMI             (TRC_HVM_HANDLER + 0x10)
+#define TRC_HVM_SMI             (TRC_HVM_HANDLER + 0x11)
+#define TRC_HVM_VMMCALL         (TRC_HVM_HANDLER + 0x12)
+#define TRC_HVM_HLT             (TRC_HVM_HANDLER + 0x13)
+#define TRC_HVM_INVLPG          (TRC_HVM_HANDLER + 0x14)
+#define TRC_HVM_INVLPG64        (TRC_HVM_HANDLER + TRC_64_FLAG + 0x14)
+#define TRC_HVM_MCE             (TRC_HVM_HANDLER + 0x15)
+#define TRC_HVM_IOPORT_READ     (TRC_HVM_HANDLER + 0x16)
+#define TRC_HVM_IOMEM_READ      (TRC_HVM_HANDLER + 0x17)
+#define TRC_HVM_CLTS            (TRC_HVM_HANDLER + 0x18)
+#define TRC_HVM_LMSW            (TRC_HVM_HANDLER + 0x19)
+#define TRC_HVM_LMSW64          (TRC_HVM_HANDLER + TRC_64_FLAG + 0x19)
+#define TRC_HVM_RDTSC           (TRC_HVM_HANDLER + 0x1a)
+#define TRC_HVM_INTR_WINDOW     (TRC_HVM_HANDLER + 0x20)
+#define TRC_HVM_NPF             (TRC_HVM_HANDLER + 0x21)
+#define TRC_HVM_REALMODE_EMULATE (TRC_HVM_HANDLER + 0x22)
+#define TRC_HVM_TRAP             (TRC_HVM_HANDLER + 0x23)
+#define TRC_HVM_TRAP_DEBUG       (TRC_HVM_HANDLER + 0x24)
+#define TRC_HVM_VLAPIC           (TRC_HVM_HANDLER + 0x25)
+
+#define TRC_HVM_IOPORT_WRITE    (TRC_HVM_HANDLER + 0x216)
+#define TRC_HVM_IOMEM_WRITE     (TRC_HVM_HANDLER + 0x217)
+
+/* Trace events for emulated devices */
+#define TRC_HVM_EMUL_HPET_START_TIMER  (TRC_HVM_EMUL + 0x1)
+#define TRC_HVM_EMUL_PIT_START_TIMER   (TRC_HVM_EMUL + 0x2)
+#define TRC_HVM_EMUL_RTC_START_TIMER   (TRC_HVM_EMUL + 0x3)
+#define TRC_HVM_EMUL_LAPIC_START_TIMER (TRC_HVM_EMUL + 0x4)
+#define TRC_HVM_EMUL_HPET_STOP_TIMER   (TRC_HVM_EMUL + 0x5)
+#define TRC_HVM_EMUL_PIT_STOP_TIMER    (TRC_HVM_EMUL + 0x6)
+#define TRC_HVM_EMUL_RTC_STOP_TIMER    (TRC_HVM_EMUL + 0x7)
+#define TRC_HVM_EMUL_LAPIC_STOP_TIMER  (TRC_HVM_EMUL + 0x8)
+#define TRC_HVM_EMUL_PIT_TIMER_CB      (TRC_HVM_EMUL + 0x9)
+#define TRC_HVM_EMUL_LAPIC_TIMER_CB    (TRC_HVM_EMUL + 0xA)
+#define TRC_HVM_EMUL_PIC_INT_OUTPUT    (TRC_HVM_EMUL + 0xB)
+#define TRC_HVM_EMUL_PIC_KICK          (TRC_HVM_EMUL + 0xC)
+#define TRC_HVM_EMUL_PIC_INTACK        (TRC_HVM_EMUL + 0xD)
+#define TRC_HVM_EMUL_PIC_POSEDGE       (TRC_HVM_EMUL + 0xE)
+#define TRC_HVM_EMUL_PIC_NEGEDGE       (TRC_HVM_EMUL + 0xF)
+#define TRC_HVM_EMUL_PIC_PEND_IRQ_CALL (TRC_HVM_EMUL + 0x10)
+#define TRC_HVM_EMUL_LAPIC_PIC_INTR    (TRC_HVM_EMUL + 0x11)
+
+/* trace events for per class */
+#define TRC_PM_FREQ_CHANGE      (TRC_HW_PM + 0x01)
+#define TRC_PM_IDLE_ENTRY       (TRC_HW_PM + 0x02)
+#define TRC_PM_IDLE_EXIT        (TRC_HW_PM + 0x03)
+
+/* Trace events for IRQs */
+#define TRC_HW_IRQ_MOVE_CLEANUP_DELAY (TRC_HW_IRQ + 0x1)
+#define TRC_HW_IRQ_MOVE_CLEANUP       (TRC_HW_IRQ + 0x2)
+#define TRC_HW_IRQ_BIND_VECTOR        (TRC_HW_IRQ + 0x3)
+#define TRC_HW_IRQ_CLEAR_VECTOR       (TRC_HW_IRQ + 0x4)
+#define TRC_HW_IRQ_MOVE_FINISH        (TRC_HW_IRQ + 0x5)
+#define TRC_HW_IRQ_ASSIGN_VECTOR      (TRC_HW_IRQ + 0x6)
+#define TRC_HW_IRQ_UNMAPPED_VECTOR    (TRC_HW_IRQ + 0x7)
+#define TRC_HW_IRQ_HANDLED            (TRC_HW_IRQ + 0x8)
+
+/*
+ * Event Flags
+ *
+ * Some events (e.g, TRC_PV_TRAP and TRC_HVM_IOMEM_READ) have multiple
+ * record formats.  These event flags distinguish between the
+ * different formats.
+ */
+#define TRC_64_FLAG 0x100 /* Addresses are 64 bits (instead of 32 bits) */
+
+/* This structure represents a single trace buffer record. */
+struct t_rec {
+    uint32_t event:28;
+    uint32_t extra_u32:3;         /* # entries in trailing extra_u32[] array */
+    uint32_t cycles_included:1;   /* u.cycles or u.no_cycles? */
+    union {
+        struct {
+            uint32_t cycles_lo, cycles_hi; /* cycle counter timestamp */
+            uint32_t extra_u32[7];         /* event data items */
+        } cycles;
+        struct {
+            uint32_t extra_u32[7];         /* event data items */
+        } nocycles;
+    } u;
+};
+
+/*
+ * This structure contains the metadata for a single trace buffer.  The head
+ * field, indexes into an array of struct t_rec's.
+ */
+struct t_buf {
+    /* Assume the data buffer size is X.  X is generally not a power of 2.
+     * CONS and PROD are incremented modulo (2*X):
+     *     0 <= cons < 2*X
+     *     0 <= prod < 2*X
+     * This is done because addition modulo X breaks at 2^32 when X is not a
+     * power of 2:
+     *     (((2^32 - 1) % X) + 1) % X != (2^32) % X
+     */
+    uint32_t cons;   /* Offset of next item to be consumed by control tools. */
+    uint32_t prod;   /* Offset of next item to be produced by Xen.           */
+    /*  Records follow immediately after the meta-data header.    */
+};
+
+/* Structure used to pass MFNs to the trace buffers back to trace consumers.
+ * Offset is an offset into the mapped structure where the mfn list will be held.
+ * MFNs will be at ((ULONG_PTR *)(t_info))+(t_info->cpu_offset[cpu]).
+ */
+struct t_info {
+    uint16_t tbuf_size; /* Size in pages of each trace buffer */
+    uint16_t mfn_offset[];  /* Offset within t_info structure of the page list per cpu */
+    /* MFN lists immediately after the header */
+};
+
+#endif /* __XEN_PUBLIC_TRACE_H__ */
+
+/*
+ * Local variables:
+ * mode: C
+ * c-file-style: "BSD"
+ * c-basic-offset: 4
+ * tab-width: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
diff --git a/include/xen/public/xen-compat.h b/include/xen/public/xen-compat.h
new file mode 100644 (file)
index 0000000..2eea0e2
--- /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__ 0x00040700
+
+#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/public/xen.h b/include/xen/public/xen.h
new file mode 100644 (file)
index 0000000..2057b52
--- /dev/null
@@ -0,0 +1,995 @@
+/******************************************************************************
+ * 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(__arm__) || defined (__aarch64__)
+#include "arch-arm.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);
+#if __XEN_INTERFACE_VERSION__ < 0x00040300
+DEFINE_XEN_GUEST_HANDLE(LONG_PTR);
+__DEFINE_XEN_GUEST_HANDLE(ulong, ULONG_PTR);
+#endif
+DEFINE_XEN_GUEST_HANDLE(void);
+
+DEFINE_XEN_GUEST_HANDLE(uint64_t);
+DEFINE_XEN_GUEST_HANDLE(xen_pfn_t);
+DEFINE_XEN_GUEST_HANDLE(xen_ulong_t);
+
+/* Turn a plain number into a C unsigned (LONG_PTR) constant. */
+#define __xen_mk_uint(x)  x ## U
+#define __xen_mk_ulong(x) x ## UL
+#define xen_mk_uint(x)    __xen_mk_uint(x)
+#define xen_mk_ulong(x)   __xen_mk_ulong(x)
+
+#else
+
+/* In assembly code we cannot use C numeric constant suffixes. */
+#define xen_mk_uint(x)  x
+#define xen_mk_ulong(x) x
+
+#endif
+
+/*
+ * HYPERCALLS
+ */
+
+/* `incontents 100 hcalls List of hypercalls
+ * ` enum hypercall_num { // __HYPERVISOR_* => HYPERVISOR_*()
+ */
+
+#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
+#define __HYPERVISOR_xc_reserved_op       39 /* reserved for XenClient */
+#define __HYPERVISOR_xenpmu_op            40
+
+/* 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.
+ */
+/* ` enum virq { */
+#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           */
+#define VIRQ_XC_RESERVED 11 /* G. Reserved for XenClient                     */
+#define VIRQ_ENOMEM     12 /* G. (DOM0) Low on heap memory       */
+#define VIRQ_XENPMU     13 /* V.  PMC interrupt                              */
+
+/* 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
+
+/*
+ * ` enum neg_errnoval
+ * ` HYPERVISOR_mmu_update(const struct mmu_update reqs[],
+ * `                       unsigned count, unsigned *done_out,
+ * `                       unsigned 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.
+ *
+ * There also certain implicit requirements when using this hypercall. The
+ * pages that make up a pagetable must be mapped read-only in the guest.
+ * This prevents uncontrolled guest updates to the pagetable. Xen strictly
+ * enforces this, and will disallow any pagetable update which will end up
+ * mapping pagetable page RW, and will disallow using any writable page as a
+ * pagetable. In practice it means that when constructing a page table for a
+ * process, thread, etc, we MUST be very dilligient in following these rules:
+ *  1). Start with top-level page (PGD or in Xen language: L4). Fill out
+ *      the entries.
+ *  2). Keep on going, filling out the upper (PUD or L3), and middle (PMD
+ *      or L2).
+ *  3). Start filling out the PTE table (L1) with the PTE entries. Once
+ *     done, make sure to set each of those entries to RO (so writeable bit
+ *     is unset). Once that has been completed, set the PMD (L2) for this
+ *     PTE table as RO.
+ *  4). When completed with all of the PMD (L2) entries, and all of them have
+ *     been set to RO, make sure to set RO the PUD (L3). Do the same
+ *     operation on PGD (L4) pagetable entries that have a PUD (L3) entry.
+ *  5). Now before you can use those pages (so setting the cr3), you MUST also
+ *      pin them so that the hypervisor can verify the entries. This is done
+ *      via the HYPERVISOR_mmuext_op(MMUEXT_PIN_L4_TABLE, guest physical frame
+ *      number of the PGD (L4)). And this point the HYPERVISOR_mmuext_op(
+ *      MMUEXT_NEW_BASEPTR, guest physical frame number of the PGD (L4)) can be
+ *      issued.
+ * For 32-bit guests, the L4 is not used (as there is less pagetables), so
+ * instead use L3.
+ * At this point the pagetables can be modified using the MMU_NORMAL_PT_UPDATE
+ * hypercall. Also if so desired the OS can also try to write to the PTE
+ * and be trapped by the hypervisor (as the PTE entry is RO).
+ *
+ * To deallocate the pages, the operations are the reverse of the steps
+ * mentioned above. The argument is MMUEXT_UNPIN_TABLE for all levels and the
+ * pagetable MUST not be in use (meaning that the cr3 is not set to it).
+ *
+ * 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.
+ *
+ * @val is usually the machine frame number along with some attributes.
+ * The attributes by default follow the architecture defined bits. Meaning that
+ * if this is a X86_64 machine and four page table layout is used, the layout
+ * of val is:
+ *  - 63 if set means No execute (NX)
+ *  - 46-13 the machine frame number
+ *  - 12 available for guest
+ *  - 11 available for guest
+ *  - 10 available for guest
+ *  - 9 available for guest
+ *  - 8 global
+ *  - 7 PAT (PSE is disabled, must use hypercall to make 4MB or 2MB pages)
+ *  - 6 dirty
+ *  - 5 accessed
+ *  - 4 page cached disabled
+ *  - 3 page write through
+ *  - 2 userspace accessible
+ *  - 1 writeable
+ *  - 0 present
+ *
+ *  The one bits that does not fit with the default layout is the PAGE_PSE
+ *  also called PAGE_PAT). The MMUEXT_[UN]MARK_SUPER arguments to the
+ *  HYPERVISOR_mmuext_op serve as mechanism to set a pagetable to be 4MB
+ *  (or 2MB) instead of using the PAGE_PSE bit.
+ *
+ *  The reason that the PAGE_PSE (bit 7) is not being utilized is due to Xen
+ *  using it as the Page Attribute Table (PAT) bit - for details on it please
+ *  refer to Intel SDM 10.12. The PAT allows to set the caching attributes of
+ *  pages instead of using MTRRs.
+ *
+ *  The PAT MSR is as follows (it is a 64-bit value, each entry is 8 bits):
+ *                    PAT4                 PAT0
+ *  +-----+-----+----+----+----+-----+----+----+
+ *  | UC  | UC- | WC | WB | UC | UC- | WC | WB |  <= Linux
+ *  +-----+-----+----+----+----+-----+----+----+
+ *  | UC  | UC- | WT | WB | UC | UC- | WT | WB |  <= BIOS (default when machine boots)
+ *  +-----+-----+----+----+----+-----+----+----+
+ *  | rsv | rsv | WP | WC | UC | UC- | WT | WB |  <= Xen
+ *  +-----+-----+----+----+----+-----+----+----+
+ *
+ *  The lookup of this index table translates to looking up
+ *  Bit 7, Bit 4, and Bit 3 of val entry:
+ *
+ *  PAT/PSE (bit 7) ... PCD (bit 4) .. PWT (bit 3).
+ *
+ *  If all bits are off, then we are using PAT0. If bit 3 turned on,
+ *  then we are using PAT1, if bit 3 and bit 4, then PAT2..
+ *
+ *  As you can see, the Linux PAT1 translates to PAT4 under Xen. Which means
+ *  that if a guest that follows Linux's PAT setup and would like to set Write
+ *  Combined on pages it MUST use PAT4 entry. Meaning that Bit 7 (PAGE_PAT) is
+ *  set. For example, under Linux it only uses PAT0, PAT1, and PAT2 for the
+ *  caching as:
+ *
+ *   WB = none (so PAT0)
+ *   WC = PWT (bit 3 on)
+ *   UC = PWT | PCD (bit 3 and 4 are on).
+ *
+ * To make it work with Xen, it needs to translate the WC bit as so:
+ *
+ *  PWT (so bit 3 on) --> PAT (so bit 7 is on) and clear bit 3
+ *
+ * And to translate back it would:
+ *
+ * PAT (bit 7 on) --> PWT (bit 3 on) and clear bit 7.
+ */
+#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
+ *
+ * ` enum neg_errnoval
+ * ` HYPERVISOR_mmuext_op(mmuext_op_t uops[],
+ * `                      unsigned int count,
+ * `                      unsigned int *pdone,
+ * `                      unsigned int foreigndom)
+ */
+/* 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.
+ */
+/* ` enum mmuext_cmd { */
+#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; /* => enum mmuext_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
+
+/*
+ * ` enum neg_errnoval
+ * ` HYPERVISOR_update_va_mapping(ULONG_PTR va, u64 val,
+ * `                              enum uvm_flags flags)
+ * `
+ * ` enum neg_errnoval
+ * ` HYPERVISOR_update_va_mapping_otherdomain(ULONG_PTR va, u64 val,
+ * `                                          enum uvm_flags flags,
+ * `                                          domid_t domid)
+ * `
+ * ` @va: The virtual address whose mapping we want to change
+ * ` @val: The new page table entry, must contain a machine address
+ * ` @flags: Control TLB flushes
+ */
+/* 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.         */
+/* ` enum uvm_flags { */
+#define UVMF_NONE           (xen_mk_ulong(0)<<0) /* No flushing at all.   */
+#define UVMF_TLB_FLUSH      (xen_mk_ulong(1)<<0) /* Flush entire TLB(s).  */
+#define UVMF_INVLPG         (xen_mk_ulong(2)<<0) /* Flush only one entry. */
+#define UVMF_FLUSHTYPE_MASK (xen_mk_ulong(3)<<0)
+#define UVMF_MULTI          (xen_mk_ulong(0)<<2) /* Flush subset of TLBs. */
+#define UVMF_LOCAL          (xen_mk_ulong(0)<<2) /* Flush local TLB.      */
+#define UVMF_ALL            (xen_mk_ulong(1)<<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
+
+/*
+ * x86 guests: Sane behaviour for virtual iopl
+ *  - virtual iopl updated from do_iret() hypercalls.
+ *  - virtual iopl reported in bounce frames.
+ *  - guest kernels assumed to be level 0 for the purpose of iopl checks.
+ */
+#define VMASST_TYPE_architectural_iopl   4
+
+/*
+ * x86/64 guests: strictly hide M2P from user mode.
+ * This allows the guest to control respective hypervisor behavior:
+ * - when not set, L4 tables get created with the respective slot blank,
+ *   and whenever the L4 table gets used as a kernel one the missing
+ *   mapping gets inserted,
+ * - when set, L4 tables get created with the respective slot initialized
+ *   as before, and whenever the L4 table gets used as a user one the
+ *   mapping gets zapped.
+ */
+#define VMASST_TYPE_m2p_strict           32
+
+#if __XEN_INTERFACE_VERSION__ < 0x00040600
+#define MAX_VMASST_TYPE                  3
+#endif
+
+/* Domain ids >= DOMID_FIRST_RESERVED cannot be used for ordinary domains. */
+#define DOMID_FIRST_RESERVED xen_mk_uint(0x7FF0)
+
+/* DOMID_SELF is used in certain contexts to refer to oneself. */
+#define DOMID_SELF           xen_mk_uint(0x7FF0)
+
+/*
+ * 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             xen_mk_uint(0x7FF1)
+
+/*
+ * 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            xen_mk_uint(0x7FF2)
+
+/*
+ * DOMID_COW is used as the owner of sharable pages */
+#define DOMID_COW            xen_mk_uint(0x7FF3)
+
+/* DOMID_INVALID is used to identify pages with unknown owner. */
+#define DOMID_INVALID        xen_mk_uint(0x7FF4)
+
+/* Idle domain. */
+#define DOMID_IDLE           xen_mk_uint(0x7FFF)
+
+#ifndef __ASSEMBLY__
+
+typedef uint16_t domid_t;
+
+/*
+ * 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);
+
+/*
+ * ` enum neg_errnoval
+ * ` HYPERVISOR_multicall(multicall_entry_t call_list[],
+ * `                      uint32_t nr_calls);
+ *
+ * NB. The fields are logically the natural register size for this
+ * architecture. In cases where xen_ulong_t is larger than this then
+ * any unused bits in the upper portion must be zero.
+ */
+struct multicall_entry {
+    xen_ulong_t op, result;
+    xen_ulong_t args[6];
+};
+typedef struct multicall_entry multicall_entry_t;
+DEFINE_XEN_GUEST_HANDLE(multicall_entry_t);
+
+#if __XEN_INTERFACE_VERSION__ < 0x00040400
+/*
+ * Event channel endpoints per domain (when using the 2-level ABI):
+ *  1024 if a LONG_PTR is 32 bits; 4096 if a LONG_PTR is 64 bits.
+ */
+#define NR_EVENT_CHANNELS EVTCHN_2L_NR_CHANNELS
+#endif
+
+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;
+#if __XEN_INTERFACE_VERSION__ > 0x040600
+    uint8_t  flags;
+    uint8_t  pad1[2];
+#else
+    int8_t   pad1[3];
+#endif
+}; /* 32 bytes */
+typedef struct vcpu_time_info vcpu_time_info_t;
+
+#define XEN_PVCLOCK_TSC_STABLE_BIT     (1 << 0)
+#define XEN_PVCLOCK_GUEST_STOPPED      (1 << 1)
+
+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;
+#ifdef XEN_HAVE_PV_UPCALL_MASK
+    uint8_t evtchn_upcall_mask;
+#else /* XEN_HAVE_PV_UPCALL_MASK */
+    uint8_t pad0;
+#endif /* XEN_HAVE_PV_UPCALL_MASK */
+    xen_ulong_t 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
+
+/*
+ * `incontents 200 startofday_shared Start-of-day shared data structure
+ * 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.
+     */
+    xen_ulong_t evtchn_pending[sizeof(xen_ulong_t) * 8];
+    xen_ulong_t evtchn_mask[sizeof(xen_ulong_t) * 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.  */
+#if !defined(__i386__)
+    uint32_t wc_sec_hi;
+# define xen_wc_sec_hi wc_sec_hi
+#elif !defined(__XEN__) && !defined(__XEN_TOOLS__)
+# define xen_wc_sec_hi arch.wc_sec_hi
+#endif
+
+    struct arch_shared_info arch;
+
+};
+#ifndef __XEN__
+typedef struct shared_info shared_info_t;
+#endif
+
+/*
+ * `incontents 200 startofday 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]
+ *         (may be omitted)
+ *      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)]
+ *         in case of dom0 this page contains the console info, too
+ *      e. unless dom0: xenstore ring page
+ *      f. unless dom0: console ring page
+ *      g. bootstrap page tables         [pt_base and CR3 (x86)]
+ *      h. bootstrap stack               [register ESP (x86)]
+ *  4. Bootstrap elements are packed together, but each is 4kB-aligned.
+ *  5. 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.
+ *  6. All bootstrap elements are mapped read-writable for the guest OS. The
+ *     only exception is the bootstrap page table, which is mapped read-only.
+ *  7. 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.
+ *
+ * Note: Prior to 25833:bb85bbccb1c9. ("x86/32-on-64 adjust Dom0 initial page
+ * table layout") a bug caused the pt_base (3.g above) and cr3 to not point
+ * to the start of the guest page tables (it was offset by two pages).
+ * This only manifested itself on 32-on-64 dom0 kernels and not 32-on-64 domU
+ * or 64-bit kernels of any colour. The page tables for a 32-on-64 dom0 got
+ * allocated in the order: 'first L1','first L2', 'first L3', so the offset
+ * to the page table base is by two pages back. The initial domain if it is
+ * 32-bit and runs under a 64-bit hypervisor should _NOT_ use two of the
+ * pages preceding pt_base and mark them as reserved/unused.
+ */
+#ifdef XEN_HAVE_PV_GUEST_ENTRY
+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.     */
+#define MAX_GUEST_CMDLINE 1024
+    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;
+
+/*
+ * Start of day structure passed to PVH guests in %ebx.
+ *
+ * NOTE: nothing will be loaded at physical address 0, so a 0 value in any
+ * of the address fields should be treated as not present.
+ *
+ *  0 +----------------+
+ *    | magic          | Contains the magic value XEN_HVM_START_MAGIC_VALUE
+ *    |                | ("xEn3" with the 0x80 bit of the "E" set).
+ *  4 +----------------+
+ *    | version        | Version of this structure. Current version is 0. New
+ *    |                | versions are guaranteed to be backwards-compatible.
+ *  8 +----------------+
+ *    | flags          | SIF_xxx flags.
+ * 12 +----------------+
+ *    | nr_modules     | Number of modules passed to the kernel.
+ * 16 +----------------+
+ *    | modlist_paddr  | Physical address of an array of modules
+ *    |                | (layout of the structure below).
+ * 24 +----------------+
+ *    | cmdline_paddr  | Physical address of the command line,
+ *    |                | a zero-terminated ASCII string.
+ * 32 +----------------+
+ *    | rsdp_paddr     | Physical address of the RSDP ACPI data structure.
+ * 40 +----------------+
+ *
+ * The layout of each entry in the module structure is the following:
+ *
+ *  0 +----------------+
+ *    | paddr          | Physical address of the module.
+ *  8 +----------------+
+ *    | size           | Size of the module in bytes.
+ * 16 +----------------+
+ *    | cmdline_paddr  | Physical address of the command line,
+ *    |                | a zero-terminated ASCII string.
+ * 24 +----------------+
+ *    | reserved       |
+ * 32 +----------------+
+ *
+ * The address and sizes are always a 64bit little endian unsigned integer.
+ *
+ * NB: Xen on x86 will always try to place all the data below the 4GiB
+ * boundary.
+ */
+#define XEN_HVM_START_MAGIC_VALUE 0x336ec578
+
+/* 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
+#endif /* XEN_HAVE_PV_GUEST_ENTRY */
+
+/* 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_VIRT_P2M_4TOOLS (1<<4) /* Do Xen tools understand a virt. mapped */
+                                   /* P->M making the 3 level tree obsolete? */
+#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;
+};
+/*
+ * `incontents 200 startofday_dom0_console Dom0_console
+ *
+ * The console structure in start_info.console.dom0
+ *
+ * This structure includes a variety of information required to
+ * have a working VGA/VESA console.
+ */
+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
+#define XEN_VGATYPE_EFI_LFB     0x70
+
+    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];
+
+__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);
+
+#endif /* !__ASSEMBLY__ */
+
+/* Default definitions for macros used by domctl/sysctl. */
+#if defined(__XEN__) || defined(__XEN_TOOLS__)
+
+#ifndef int64_aligned_t
+#define int64_aligned_t int64_t
+#endif
+#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_bitmap {
+    XEN_GUEST_HANDLE_64(uint8) bitmap;
+    uint32_t nr_bits;
+};
+#endif
+
+#endif /* defined(__XEN__) || defined(__XEN_TOOLS__) */
+
+#endif /* __XEN_PUBLIC_XEN_H__ */
+
+/*
+ * Local variables:
+ * mode: C
+ * c-file-style: "BSD"
+ * c-basic-offset: 4
+ * tab-width: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
diff --git a/include/xen/xen/errno.h b/include/xen/xen/errno.h
new file mode 100644 (file)
index 0000000..69b28dd
--- /dev/null
@@ -0,0 +1,18 @@
+#ifndef __XEN_ERRNO_H__
+#define __XEN_ERRNO_H__
+
+#ifndef __ASSEMBLY__
+
+#define XEN_ERRNO(name, value) name = value,
+enum {
+#include <public/errno.h>
+};
+
+#else /* !__ASSEMBLY__ */
+
+#define XEN_ERRNO(name, value) .equ name, value
+#include <public/errno.h>
+
+#endif /* __ASSEMBLY__ */
+
+#endif /*  __XEN_ERRNO_H__ */
diff --git a/kdfiles.py b/kdfiles.py
new file mode 100644 (file)
index 0000000..4a9e231
--- /dev/null
@@ -0,0 +1,26 @@
+#!python -u
+
+import os, sys
+import subprocess
+import glob
+from pprint import pprint
+
+def regenerate_kdfiles(filename, arch, pkg, source):
+       cwd = os.getcwd()
+       file = open(filename, 'w')
+       os.chdir(pkg + '/' + arch)
+       drivers = glob.glob('*.sys')
+       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 = 'xenvkbd'
+       source = os.getcwd()
+       regenerate_kdfiles('kdfiles32.txt', 'x86', pkg, source)
+       regenerate_kdfiles('kdfiles64.txt', 'x64', pkg, source)
diff --git a/msbuild.bat b/msbuild.bat
new file mode 100644 (file)
index 0000000..1b1fbc8
--- /dev/null
@@ -0,0 +1,8 @@
+call "%VS%\VC\vcvarsall.bat" x86
+@echo on
+msbuild.exe /m:1 /p:Configuration="%CONFIGURATION%" /p:Platform="%PLATFORM%" /t:"%TARGET%" %EXTRA% %FILE%
+if errorlevel 1 goto error
+exit 0
+
+:error
+exit 1
diff --git a/src/coinst/coinst.c b/src/coinst/coinst.c
new file mode 100644 (file)
index 0000000..dfa5105
--- /dev/null
@@ -0,0 +1,1361 @@
+/* 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 <assert.h>
+
+#include <version.h>
+#include <revision.h>
+
+__user_code;
+
+#define MAXIMUM_BUFFER_SIZE 1024
+
+#define SERVICES_KEY "SYSTEM\\CurrentControlSet\\Services"
+
+#define SERVICE_KEY(_Driver)    \
+        SERVICES_KEY ## "\\" ## #_Driver
+
+#define UNPLUG_KEY \
+        SERVICE_KEY(XEN) ## "\\Unplug"
+
+#define CONTROL_KEY "SYSTEM\\CurrentControlSet\\Control"
+
+#define CLASS_KEY   \
+        CONTROL_KEY ## "\\Class"
+
+#define ENUM_KEY    "SYSTEM\\CurrentControlSet\\Enum"
+
+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_WARNING;
+
+    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;
+
+    if (!FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
+                       FORMAT_MESSAGE_FROM_SYSTEM |
+                       FORMAT_MESSAGE_IGNORE_INSERTS,
+                       NULL,
+                       Error,
+                       MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+                       (LPTSTR)&Message,
+                       0,
+                       NULL))
+        return NULL;
+
+    for (Index = 0; Message[Index] != '\0'; Index++) {
+        if (Message[Index] == '\r' || Message[Index] == '\n') {
+            Message[Index] = '\0';
+            break;
+        }
+    }
+
+    return Message;
+}
+
+static FORCEINLINE 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
+ClearUnplugRequest(
+    IN  PTCHAR      ClassName
+    )
+{
+    HKEY            UnplugKey;
+    HRESULT         Error;
+
+    Error = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
+                         UNPLUG_KEY,
+                         0,
+                         KEY_ALL_ACCESS,
+                         &UnplugKey);
+    if (Error != ERROR_SUCCESS) {
+        SetLastError(Error);
+        goto fail1;
+    }
+
+    Error = RegDeleteValue(UnplugKey, ClassName);
+    if (Error != ERROR_SUCCESS) {
+        SetLastError(Error);
+        goto fail2;
+    }
+
+    RegCloseKey(UnplugKey);
+
+    return TRUE;
+
+fail2:
+    Log("fail2");
+
+    RegCloseKey(UnplugKey);
+
+fail1:
+    Error = GetLastError();
+
+    {
+        PTCHAR  Message;
+
+        Message = GetErrorMessage(Error);
+        Log("fail1 (%s)", Message);
+        LocalFree(Message);
+    }
+
+    return FALSE;
+}
+
+static BOOLEAN
+AllowUpdate(
+    IN  PTCHAR      DriverName,
+    OUT PBOOLEAN    Allow
+    )
+{
+    TCHAR           ServiceKeyName[MAX_PATH];
+    HKEY            ServiceKey;
+    HRESULT         Result;
+    HRESULT         Error;
+    DWORD           ValueLength;
+    DWORD           Value;
+    DWORD           Type;
+
+    Log("====> (%s)", DriverName);
+
+    Result = StringCbPrintf(ServiceKeyName,
+                            MAX_PATH,
+                            SERVICES_KEY "\\%s",
+                            DriverName);
+    assert(SUCCEEDED(Result));
+
+    Error = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
+                         ServiceKeyName,
+                         0,
+                         KEY_READ,
+                         &ServiceKey);
+    if (Error != ERROR_SUCCESS) {
+        if (Error == ERROR_FILE_NOT_FOUND) {
+            Value = 1;
+            goto done;
+        }
+
+        SetLastError(Error);
+        goto fail1;
+    }
+
+    ValueLength = sizeof (Value);
+
+    Error = RegQueryValueEx(ServiceKey,
+                            "AllowUpdate",
+                            NULL,
+                            &Type,
+                            (LPBYTE)&Value,
+                            &ValueLength);
+    if (Error != ERROR_SUCCESS) {
+        if (Error == ERROR_FILE_NOT_FOUND) {
+            Type = REG_DWORD;
+            Value = 1;
+        } else {
+            SetLastError(Error);
+            goto fail2;
+        }
+    }
+
+    if (Type != REG_DWORD) {
+        SetLastError(ERROR_BAD_FORMAT);
+        goto fail3;
+    }
+
+    RegCloseKey(ServiceKey);
+
+done:
+    if (Value == 0) {
+        Log("DISALLOWED");
+        *Allow = FALSE;
+    }
+
+    Log("<====");
+
+    return TRUE;
+
+fail3:
+    Log("fail3");
+
+fail2:
+    Log("fail2");
+
+    RegCloseKey(ServiceKey);
+
+fail1:
+    Error = GetLastError();
+
+    {
+        PTCHAR  Message;
+        Message = GetErrorMessage(Error);
+        Log("fail1 (%s)", Message);
+        LocalFree(Message);
+    }
+
+    return FALSE;
+}
+
+static BOOLEAN
+AllowInstall(
+    OUT PBOOLEAN    Allow
+    )
+{
+    BOOLEAN         Success;
+    HRESULT         Error;
+
+    Log("====>");
+
+    *Allow = TRUE;
+
+    Success = AllowUpdate("XENVKBD", Allow);
+    if (!Success)
+        goto fail1;
+
+    Log("<====");
+
+    return TRUE;
+
+fail1:
+    Error = GetLastError();
+
+    {
+        PTCHAR  Message;
+        Message = GetErrorMessage(Error);
+        Log("fail1 (%s)", Message);
+        LocalFree(Message);
+    }
+
+    return FALSE;
+}
+
+static BOOLEAN
+OpenEnumKey(
+    OUT PHKEY   EnumKey
+    )
+{
+    HRESULT     Error;
+
+    Error = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
+                         ENUM_KEY,
+                         0,
+                         KEY_READ,
+                         EnumKey);
+    if (Error != ERROR_SUCCESS) {
+        SetLastError(Error);
+        goto fail1;
+    }
+
+    return TRUE;
+
+fail1:
+    Error = GetLastError();
+
+    {
+        PTCHAR  Message;
+        Message = GetErrorMessage(Error);
+        Log("fail1 (%s)", Message);
+        LocalFree(Message);
+    }
+
+    return FALSE;
+}
+
+static BOOLEAN
+OpenBusKey(
+    IN  PTCHAR  BusKeyName,
+    OUT PHKEY   BusKey
+    )
+{
+    BOOLEAN     Success;
+    HKEY        EnumKey;
+    HRESULT     Error;
+
+    Success = OpenEnumKey(&EnumKey);
+    if (!Success)
+        goto fail1;
+
+    Error = RegOpenKeyEx(EnumKey,
+                         BusKeyName,
+                         0,
+                         KEY_READ,
+                         BusKey);
+    if (Error != ERROR_SUCCESS) {
+        SetLastError(Error);
+        goto fail2;
+    }
+
+    RegCloseKey(EnumKey);
+
+    return TRUE;
+
+fail2:
+    Log("fail2");
+
+    RegCloseKey(EnumKey);
+
+fail1:
+    Error = GetLastError();
+
+    {
+        PTCHAR  Message;
+
+        Message = GetErrorMessage(Error);
+        Log("fail1 (%s)", Message);
+        LocalFree(Message);
+    }
+
+    return FALSE;
+}
+
+static BOOLEAN
+OpenDeviceKey(
+    IN  PTCHAR  BusKeyName,
+    IN  PTCHAR  DeviceKeyName,
+    OUT PHKEY   DeviceKey
+    )
+{
+    BOOLEAN     Success;
+    HKEY        BusKey;
+    HRESULT     Error;
+
+    Success = OpenBusKey(BusKeyName, &BusKey);
+    if (!Success)
+        goto fail1;
+
+    Error = RegOpenKeyEx(BusKey,
+                         DeviceKeyName,
+                         0,
+                         KEY_READ,
+                         DeviceKey);
+    if (Error != ERROR_SUCCESS) {
+        SetLastError(Error);
+        goto fail2;
+    }
+
+    RegCloseKey(BusKey);
+
+    return TRUE;
+
+fail2:
+    Log("fail2");
+
+    RegCloseKey(BusKey);
+
+fail1:
+    Error = GetLastError();
+
+    {
+        PTCHAR  Message;
+
+        Message = GetErrorMessage(Error);
+        Log("fail1 (%s)", Message);
+        LocalFree(Message);
+    }
+
+    return FALSE;
+}
+
+static BOOLEAN
+GetDriverKeyName(
+    IN  HKEY    DeviceKey,
+    OUT PTCHAR  *Name
+    )
+{
+    HRESULT     Error;
+    DWORD       SubKeys;
+    DWORD       MaxSubKeyLength;
+    DWORD       SubKeyLength;
+    PTCHAR      SubKeyName;
+    DWORD       Index;
+    HKEY        SubKey;
+    PTCHAR      DriverKeyName;
+
+    Error = RegQueryInfoKey(DeviceKey,
+                            NULL,
+                            NULL,
+                            NULL,
+                            &SubKeys,
+                            &MaxSubKeyLength,
+                            NULL,
+                            NULL,
+                            NULL,
+                            NULL,
+                            NULL,
+                            NULL);
+    if (Error != ERROR_SUCCESS) {
+        SetLastError(Error);
+        goto fail1;
+    }
+
+    SubKeyLength = MaxSubKeyLength + sizeof (TCHAR);
+
+    SubKeyName = malloc(SubKeyLength);
+    if (SubKeyName == NULL)
+        goto fail2;
+
+    SubKey = NULL;
+    DriverKeyName = NULL;
+
+    for (Index = 0; Index < SubKeys; Index++) {
+        DWORD       MaxValueLength;
+        DWORD       DriverKeyNameLength;
+        DWORD       Type;
+
+        SubKeyLength = MaxSubKeyLength + sizeof (TCHAR);
+        memset(SubKeyName, 0, SubKeyLength);
+
+        Error = RegEnumKeyEx(DeviceKey,
+                             Index,
+                             (LPTSTR)SubKeyName,
+                             &SubKeyLength,
+                             NULL,
+                             NULL,
+                             NULL,
+                             NULL);
+        if (Error != ERROR_SUCCESS) {
+            SetLastError(Error);
+            goto fail3;
+        }
+
+        Error = RegOpenKeyEx(DeviceKey,
+                             SubKeyName,
+                             0,
+                             KEY_READ,
+                             &SubKey);
+        if (Error != ERROR_SUCCESS) {
+            SubKey = NULL;
+            continue;
+        }
+
+        Error = RegQueryInfoKey(SubKey,
+                                NULL,
+                                NULL,
+                                NULL,
+                                NULL,
+                                NULL,
+                                NULL,
+                                NULL,
+                                NULL,
+                                &MaxValueLength,
+                                NULL,
+                                NULL);
+        if (Error != ERROR_SUCCESS) {
+            SetLastError(Error);
+            goto fail4;
+        }
+
+        DriverKeyNameLength = MaxValueLength + sizeof (TCHAR);
+
+        DriverKeyName = calloc(1, DriverKeyNameLength);
+        if (DriverKeyName == NULL)
+            goto fail5;
+
+        Error = RegQueryValueEx(SubKey,
+                                "Driver",
+                                NULL,
+                                &Type,
+                                (LPBYTE)DriverKeyName,
+                                &DriverKeyNameLength);
+        if (Error == ERROR_SUCCESS &&
+            Type == REG_SZ)
+            break;
+
+        free(DriverKeyName);
+        DriverKeyName = NULL;
+
+        RegCloseKey(SubKey);
+        SubKey = NULL;
+    }
+
+    Log("%s", (DriverKeyName != NULL) ? DriverKeyName : "none found");
+
+    if (SubKey != NULL)
+        RegCloseKey(SubKey);
+
+    free(SubKeyName);
+
+    *Name = DriverKeyName;
+    return TRUE;
+
+fail5:
+    Log("fail5");
+
+fail4:
+    Log("fail4");
+
+    if (SubKey != NULL)
+        RegCloseKey(SubKey);
+
+fail3:
+    Log("fail3");
+
+    free(SubKeyName);
+
+fail2:
+    Log("fail2");
+
+fail1:
+    Error = GetLastError();
+
+    {
+        PTCHAR  Message;
+
+        Message = GetErrorMessage(Error);
+        Log("fail1 (%s)", Message);
+        LocalFree(Message);
+    }
+
+    return FALSE;
+}
+
+static BOOLEAN
+OpenClassKey(
+    OUT PHKEY   ClassKey
+    )
+{
+    HRESULT     Error;
+
+    Error = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
+                         CLASS_KEY,
+                         0,
+                         KEY_READ,
+                         ClassKey);
+    if (Error != ERROR_SUCCESS) {
+        SetLastError(Error);
+        goto fail1;
+    }
+
+    return TRUE;
+
+fail1:
+    Error = GetLastError();
+
+    {
+        PTCHAR  Message;
+        Message = GetErrorMessage(Error);
+        Log("fail1 (%s)", Message);
+        LocalFree(Message);
+    }
+
+    return FALSE;
+}
+
+static BOOLEAN
+OpenDriverKey(
+    IN  PTCHAR  DriverKeyName,
+    OUT PHKEY   DriverKey
+    )
+{
+    BOOLEAN     Success;
+    HKEY        ClassKey;
+    HRESULT     Error;
+
+    Success = OpenClassKey(&ClassKey);
+    if (!Success)
+        goto fail1;
+
+    Error = RegOpenKeyEx(ClassKey,
+                         DriverKeyName,
+                         0,
+                         KEY_READ,
+                         DriverKey);
+    if (Error != ERROR_SUCCESS) {
+        SetLastError(Error);
+        goto fail2;
+    }
+
+    RegCloseKey(ClassKey);
+
+    return TRUE;
+
+fail2:
+    Log("fail2");
+
+    RegCloseKey(ClassKey);
+
+fail1:
+    Error = GetLastError();
+
+    {
+        PTCHAR  Message;
+
+        Message = GetErrorMessage(Error);
+        Log("fail1 (%s)", Message);
+        LocalFree(Message);
+    }
+
+    return FALSE;
+}
+
+#define DEFINE_REVISION(_N, _H) \
+    (_N)
+
+static DWORD    DeviceRevision[] = {
+    DEFINE_REVISION_TABLE
+};
+
+#undef DEFINE_REVISION
+
+static BOOLEAN
+SupportDeviceID(
+    IN  PTCHAR      DeviceID
+    )
+{
+    unsigned int    Revision;
+    int             Count;
+    DWORD           Index;
+    HRESULT         Error;
+
+    DeviceID = strrchr(DeviceID, '&');
+    assert(DeviceID != NULL);
+    DeviceID++;
+
+    Count = sscanf_s(DeviceID,
+                     "REV_%8x",
+                     &Revision);
+    if (Count != 1) {
+        SetLastError(ERROR_BAD_FORMAT);
+        goto fail1;
+    }
+
+    for (Index = 0; Index < ARRAYSIZE(DeviceRevision); Index++) {
+        if (Revision == DeviceRevision[Index])
+            goto found;
+    }
+
+    SetLastError(ERROR_FILE_NOT_FOUND);
+    goto fail2;
+
+found:
+    Log("%x", Revision);
+
+    return TRUE;
+
+fail2:
+    Log("fail2");
+
+fail1:
+    Error = GetLastError();
+
+    {
+        PTCHAR  Message;
+
+        Message = GetErrorMessage(Error);
+        Log("fail1 (%s)", Message);
+        LocalFree(Message);
+    }
+
+    return FALSE;
+}
+
+static BOOLEAN
+GetMatchingDeviceID(
+    IN  HKEY    DriverKey,
+    OUT PTCHAR  *MatchingDeviceID
+    )
+{
+    HRESULT     Error;
+    DWORD       MaxValueLength;
+    DWORD       MatchingDeviceIDLength;
+    DWORD       Type;
+    DWORD       Index;
+
+    Error = RegQueryInfoKey(DriverKey,
+                            NULL,
+                            NULL,
+                            NULL,
+                            NULL,
+                            NULL,
+                            NULL,
+                            NULL,
+                            NULL,
+                            &MaxValueLength,
+                            NULL,
+                            NULL);
+    if (Error != ERROR_SUCCESS) {
+        SetLastError(Error);
+        goto fail1;
+    }
+
+    MatchingDeviceIDLength = MaxValueLength + sizeof (TCHAR);
+
+    *MatchingDeviceID = calloc(1, MatchingDeviceIDLength);
+    if (*MatchingDeviceID == NULL)
+        goto fail2;
+
+    Error = RegQueryValueEx(DriverKey,
+                            "MatchingDeviceId",
+                            NULL,
+                            &Type,
+                            (LPBYTE)*MatchingDeviceID,
+                            &MatchingDeviceIDLength);
+    if (Error != ERROR_SUCCESS) {
+        SetLastError(Error);
+        goto fail3;
+    }
+
+    if (Type != REG_SZ) {
+        SetLastError(ERROR_BAD_FORMAT);
+        goto fail4;
+    }
+
+    for (Index = 0; Index < strlen(*MatchingDeviceID); Index++)
+        (*MatchingDeviceID)[Index] = (CHAR)toupper((*MatchingDeviceID)[Index]);
+
+    Log("%s", *MatchingDeviceID);
+
+    return TRUE;
+
+fail4:
+    Log("fail4");
+
+fail3:
+    Log("fail3");
+
+    free(*MatchingDeviceID);
+
+fail2:
+    Log("fail2");
+
+fail1:
+    Error = GetLastError();
+
+    {
+        PTCHAR  Message;
+
+        Message = GetErrorMessage(Error);
+        Log("fail1 (%s)", Message);
+        LocalFree(Message);
+    }
+
+    return FALSE;
+}
+
+static BOOLEAN
+SupportChildDrivers(
+    VOID
+    )
+{
+    BOOLEAN     Success;
+    HKEY        XenbusKey;
+    HRESULT     Error;
+    DWORD       SubKeys;
+    DWORD       MaxSubKeyLength;
+    DWORD       SubKeyLength;
+    PTCHAR      SubKeyName;
+    HKEY        DeviceKey;
+    PTCHAR      DriverKeyName;
+    HKEY        DriverKey;
+    PTCHAR      MatchingDeviceID;
+    DWORD       Index;
+
+    Log("====>");
+
+    Success = OpenBusKey("XENVKBD", &XenbusKey);
+    if (!Success) {
+        // If there is no key then this must be a fresh installation
+        if (GetLastError() == ERROR_FILE_NOT_FOUND)
+            goto done;
+
+        goto fail1;
+    }
+
+    Error = RegQueryInfoKey(XenbusKey,
+                            NULL,
+                            NULL,
+                            NULL,
+                            &SubKeys,
+                            &MaxSubKeyLength,
+                            NULL,
+                            NULL,
+                            NULL,
+                            NULL,
+                            NULL,
+                            NULL);
+    if (Error != ERROR_SUCCESS) {
+        SetLastError(Error);
+        goto fail2;
+    }
+
+    SubKeyLength = MaxSubKeyLength + sizeof (TCHAR);
+
+    SubKeyName = malloc(SubKeyLength);
+    if (SubKeyName == NULL)
+        goto fail3;
+
+    for (Index = 0; Index < SubKeys; Index++) {
+        SubKeyLength = MaxSubKeyLength + sizeof (TCHAR);
+        memset(SubKeyName, 0, SubKeyLength);
+
+        Error = RegEnumKeyEx(XenbusKey,
+                             Index,
+                             (LPTSTR)SubKeyName,
+                             &SubKeyLength,
+                             NULL,
+                             NULL,
+                             NULL,
+                             NULL);
+        if (Error != ERROR_SUCCESS) {
+            SetLastError(Error);
+            goto fail4;
+        }
+
+        Success = OpenDeviceKey("XENVKBD", SubKeyName, &DeviceKey);
+        if (!Success)
+            goto fail5;
+
+        Success = GetDriverKeyName(DeviceKey, &DriverKeyName);
+        if (!Success)
+            goto fail6;
+
+        if (DriverKeyName == NULL)
+            goto loop;
+
+        Success = OpenDriverKey(DriverKeyName, &DriverKey);
+        if (!Success)
+            goto loop;
+
+        Success = GetMatchingDeviceID(DriverKey, &MatchingDeviceID);
+        if (!Success)
+            goto fail7;
+
+        Success = SupportDeviceID(MatchingDeviceID);
+        if (!Success)
+            goto fail8;
+
+        free(MatchingDeviceID);
+
+        RegCloseKey(DriverKey);
+
+    loop:
+        if (DriverKeyName != NULL)
+            free(DriverKeyName);
+
+        RegCloseKey(DeviceKey);
+    }
+
+    free(SubKeyName);
+
+    RegCloseKey(XenbusKey);
+
+done:
+    Log("<====");
+
+    return TRUE;
+
+fail8:
+    Log("fail8");
+
+    free(MatchingDeviceID);
+
+fail7:
+    Log("fail7");
+
+    RegCloseKey(DriverKey);
+
+    free(DriverKeyName);
+
+fail6:
+    Log("fail6");
+
+    RegCloseKey(DeviceKey);
+
+fail5:
+    Log("fail5");
+
+fail4:
+    Log("fail4");
+
+    free(SubKeyName);
+
+fail3:
+    Log("fail3");
+
+fail2:
+    Log("fail2");
+
+    RegCloseKey(XenbusKey);
+
+fail1:
+    Error = GetLastError();
+
+    {
+        PTCHAR  Message;
+
+        Message = GetErrorMessage(Error);
+        Log("fail1 (%s)", Message);
+        LocalFree(Message);
+    }
+
+    return FALSE;
+}
+
+static HRESULT
+DifInstallPreProcess(
+    IN  HDEVINFO                    DeviceInfoSet,
+    IN  PSP_DEVINFO_DATA            DeviceInfoData,
+    IN  PCOINSTALLER_CONTEXT_DATA   Context
+    )
+{
+    HRESULT                         Error;
+    BOOLEAN                         Success;
+    BOOLEAN                         Allow;
+
+    UNREFERENCED_PARAMETER(DeviceInfoSet);
+    UNREFERENCED_PARAMETER(DeviceInfoData);
+    UNREFERENCED_PARAMETER(Context);
+
+    Log("====>");
+
+    Success = AllowInstall(&Allow);
+    if (!Success)
+        goto fail1;
+
+    if (!Allow) {
+        SetLastError(ERROR_ACCESS_DENIED);
+        goto fail2;
+    }
+
+    Success = SupportChildDrivers();
+    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 HRESULT
+DifInstallPostProcess(
+    IN  HDEVINFO                    DeviceInfoSet,
+    IN  PSP_DEVINFO_DATA            DeviceInfoData,
+    IN  PCOINSTALLER_CONTEXT_DATA   Context
+    )
+{
+    UNREFERENCED_PARAMETER(DeviceInfoSet);
+    UNREFERENCED_PARAMETER(DeviceInfoData);
+    UNREFERENCED_PARAMETER(Context);
+
+    Log("<===>");
+
+    return NO_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);
+
+    if (!Context->PostProcessing) {
+        Error = DifInstallPreProcess(DeviceInfoSet, DeviceInfoData, Context);
+
+        if (Error == NO_ERROR)
+            Error = ERROR_DI_POSTPROCESSING_REQUIRED; 
+    } else {
+        Error = Context->InstallResult;
+        
+        if (Error == NO_ERROR) {
+            (VOID) DifInstallPostProcess(DeviceInfoSet, DeviceInfoData, Context);
+        } else {
+            PTCHAR  Message;
+
+            Message = GetErrorMessage(Error);
+            Log("NOT RUNNING (DifInstallPreProcess Error: %s)", Message);
+            LocalFree(Message);
+        }
+
+        Error = NO_ERROR; 
+    }
+
+    return Error;
+
+fail1:
+    Error = GetLastError();
+
+    {
+        PTCHAR  Message;
+
+        Message = GetErrorMessage(Error);
+        Log("fail1 (%s)", Message);
+        LocalFree(Message);
+    }
+
+    return Error;
+}
+
+static 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 NO_ERROR; 
+}
+
+static HRESULT
+DifRemovePostProcess(
+    IN  HDEVINFO                    DeviceInfoSet,
+    IN  PSP_DEVINFO_DATA            DeviceInfoData,
+    IN  PCOINSTALLER_CONTEXT_DATA   Context
+    )
+{
+    UNREFERENCED_PARAMETER(DeviceInfoSet);
+    UNREFERENCED_PARAMETER(DeviceInfoData);
+    UNREFERENCED_PARAMETER(Context);
+
+    Log("<===>");
+
+    return NO_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);
+
+    if (!Context->PostProcessing) {
+        Error = DifRemovePreProcess(DeviceInfoSet, DeviceInfoData, Context);
+
+        if (Error == NO_ERROR)
+            Error = ERROR_DI_POSTPROCESSING_REQUIRED; 
+    } else {
+        Error = Context->InstallResult;
+        
+        if (Error == NO_ERROR) {
+            (VOID) DifRemovePostProcess(DeviceInfoSet, DeviceInfoData, Context);
+        } else {
+            PTCHAR  Message;
+
+            Message = GetErrorMessage(Error);
+            Log("NOT RUNNING (DifRemovePreProcess Error: %s)", Message);
+            LocalFree(Message);
+        }
+
+        Error = NO_ERROR; 
+    }
+
+    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);
+
+    if (!Context->PostProcessing) {
+        Log("%s PreProcessing",
+            __FunctionName(Function));
+    } else {
+        Log("%s PostProcessing (%08x)",
+            __FunctionName(Function),
+            Context->InstallResult);
+    }
+
+    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) {
+            Error = NO_ERROR;
+        } else {
+            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/xenvkbd_coinst.def b/src/coinst/xenvkbd_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/xenvkbd.inf b/src/xenvkbd.inf
new file mode 100644 (file)
index 0000000..90b1e3f
--- /dev/null
@@ -0,0 +1,111 @@
+; 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.
+
+[Version] 
+Signature="$Windows NT$" 
+Class=System
+ClassGUID={4d36e97d-e325-11ce-bfc1-08002be10318}
+Provider=%Vendor%
+CatalogFile=xenvkbd.cat
+DriverVer=01/01/1900,0.0.0.0
+DriverPackageDisplayName=%DiskDesc%
+
+[DestinationDirs] 
+DefaultDestDir=12 
+CoInst_CopyFiles=11
+
+[SourceDisksNames]
+0=%DiskDesc%
+
+[SourceDisksFiles]
+xenvkbd.sys=0,,
+xenvkbd_coinst.dll=0,,
+
+[CoInst_CopyFiles]
+xenvkbd_coinst_@MAJOR_VERSION@_@MINOR_VERSION@_@MICRO_VERSION@_@BUILD_NUMBER@.dll,xenvkbd_coinst.dll
+
+[Manufacturer] 
+%Vendor%=Inst,NT$ARCH$
+
+[Inst.NT$ARCH$]
+; DisplayName          Section         DeviceID
+; -----------          -------         --------
+
+%XenVkbdName%          =XenVkbd_Inst,  XENBUS\VEN_@VENDOR_PREFIX@@VENDOR_DEVICE_ID@&DEV_VKBD&REV_09000000
+%XenVkbdName%          =XenVkbd_Inst,  XENBUS\VEN_@VENDOR_PREFIX@0001&DEV_VKBD&REV_09000000
+%XenVkbdName%          =XenVkbd_Inst,  XENBUS\VEN_@VENDOR_PREFIX@0002&DEV_VKBD&REV_09000000
+
+[XenVkbd_Inst] 
+CopyFiles=XenVkbd_Copyfiles
+
+[XenVkbd_Copyfiles]
+xenvkbd.sys
+
+[XenVkbd_Inst.Services] 
+AddService=xenvkbd,0x02,XenVkbd_Service,
+
+[XenVkbd_Service]
+DisplayName=%XenVkbdName%
+ServiceType=%SERVICE_KERNEL_DRIVER% 
+StartType=%SERVICE_DEMAND_START% 
+ErrorControl=%SERVICE_ERROR_NORMAL% 
+ServiceBinary=%12%\xenvkbd.sys 
+AddReg = XenVkbd_Parameters
+
+[XenVkbd_Parameters]
+HKR,"Parameters",,0x00000010
+HKR,"Parameters","RequestKey",0x00000000,%RequestKey%
+
+[XenVkbd_Inst.CoInstallers]
+CopyFiles=CoInst_CopyFiles
+AddReg=CoInst_AddReg
+
+[CoInst_AddReg]
+HKR,,CoInstallers32,0x00010000,"xenvkbd_coinst_@MAJOR_VERSION@_@MINOR_VERSION@_@MICRO_VERSION@_@BUILD_NUMBER@.dll,Entry"
+
+[Strings] 
+
+Vendor="@VENDOR_NAME@"
+DiskDesc="@PRODUCT_NAME@ PV Keyboard/Mouse Package"
+XenVkbdName="@PRODUCT_NAME@ PV Keyboard/Mouse"
+RequestKey="SYSTEM\CurrentControlSet\Services\xenbus_monitor\Request"
+
+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/xenvkbd.pfx b/src/xenvkbd.pfx
new file mode 100644 (file)
index 0000000..b8e384d
Binary files /dev/null and b/src/xenvkbd.pfx differ
diff --git a/src/xenvkbd/assert.h b/src/xenvkbd/assert.h
new file mode 100644 (file)
index 0000000..d28fcfe
--- /dev/null
@@ -0,0 +1,202 @@
+/* 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 _XENVKBD_ASSERT_H
+#define _XENVKBD_ASSERT_H
+
+#include <ntddk.h>
+
+#include "dbg_print.h"
+
+static FORCEINLINE VOID
+__Bug(
+    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");                          \
+            __Bug(ASSERTION_FAILURE,                            \
+                  (ULONG_PTR)_Text,                             \
+                  (ULONG_PTR)_File,                             \
+                  (ULONG_PTR)_Line,                             \
+                  0);                                           \
+        } while (FALSE)
+
+#define BUG_ON(_EXP)                \
+        if (_EXP) BUG(#_EXP)
+
+#undef  ASSERT
+
+#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)
+
+#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)
+
+#else   // DBG
+
+#pragma warning(disable:4100)
+#pragma warning(disable:4189)
+
+#define ASSERT(_EXP)                    \
+        do {                            \
+            __analysis_assume(_EXP);    \
+        } while (FALSE)
+
+#define ASSERT3U(_X, _OP, _Y)           \
+        ASSERT((_X) _OP (_Y))
+
+#define ASSERT3S(_X, _OP, _Y)           \
+        ASSERT((_X) _OP (_Y))
+
+#define ASSERT3P(_X, _OP, _Y)           \
+        ASSERT((_X) _OP (_Y))
+
+#endif  // DBG
+
+#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;
+}
+
+#else   // TEST_MEMORY
+
+static __inline BOOLEAN
+_IsZeroMemory(
+    IN  const PCHAR Caller,
+    IN  const PCHAR Name,
+    IN  PVOID       Buffer,
+    IN  ULONG       Length
+    )
+{
+    UNREFERENCED_PARAMETER(Caller);
+    UNREFERENCED_PARAMETER(Name);
+    UNREFERENCED_PARAMETER(Buffer);
+    UNREFERENCED_PARAMETER(Length);
+
+    return TRUE;
+}
+
+#endif  // TEST_MEMORY
+
+#define IsZeroMemory(_Buffer, _Length) \
+        _IsZeroMemory(__FUNCTION__, #_Buffer, (_Buffer), (_Length))
+
+#define IMPLY(_X, _Y)   (!(_X) || (_Y))
+#define EQUIV(_X, _Y)   (IMPLY((_X), (_Y)) && IMPLY((_Y), (_X)))
+
+#endif  // _XENVKBD_ASSERT_H
diff --git a/src/xenvkbd/bus.c b/src/xenvkbd/bus.c
new file mode 100644 (file)
index 0000000..89e0f24
--- /dev/null
@@ -0,0 +1,220 @@
+/* Copyright (c) Citrix Systems Inc.
+ * All rights reserved.
+ * 
+ * Redistribution and use in source and binary forms, 
+ * with or without modification, are permitted provided 
+ * that the following conditions are met:
+ * 
+ * *   Redistributions of source code must retain the above 
+ *     copyright notice, this list of conditions and the 
+ *     following disclaimer.
+ * *   Redistributions in binary form must reproduce the above 
+ *     copyright notice, this list of conditions and the 
+ *     following disclaimer in the documentation and/or other 
+ *     materials provided with the distribution.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND 
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, 
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR 
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 
+ * SUCH DAMAGE.
+ */
+
+#include <ntddk.h>
+#include <stdarg.h>
+#include <xen.h>
+
+#include "bus.h"
+#include "fdo.h"
+#include "pdo.h"
+#include "dbg_print.h"
+#include "assert.h"
+#include "util.h"
+
+typedef struct _XENVKBD_BUS_CONTEXT {
+    LONG                    References;
+    PXENVKBD_PDO            Pdo;
+    ULONG                   InterceptDmaAdapter;
+} XENVKBD_BUS_CONTEXT, *PXENVKBD_BUS_CONTEXT;
+
+#define BUS_TAG 'SUB'
+
+static FORCEINLINE PVOID
+__BusAllocate(
+    IN  ULONG   Length
+    )
+{
+    return __AllocatePoolWithTag(NonPagedPool, Length, BUS_TAG);
+}
+
+static FORCEINLINE VOID
+__BusFree(
+    IN  PVOID   Buffer
+    )
+{
+    __FreePoolWithTag(Buffer, BUS_TAG);
+}
+
+static VOID
+BusReference(
+    IN  PVOID               _Context
+    )
+{
+    PXENVKBD_BUS_CONTEXT    Context = _Context;
+
+    InterlockedIncrement(&Context->References);
+}
+
+static VOID
+BusDereference(
+    IN  PVOID               _Context
+    )
+{
+    PXENVKBD_BUS_CONTEXT    Context = _Context;
+
+    ASSERT(Context->References != 0);
+    InterlockedDecrement(&Context->References);
+}
+
+static
+__drv_functionClass(TRANSLATE_BUS_ADDRESS)
+BOOLEAN
+BusTranslateAddress(
+    IN      PVOID               _Context,
+    IN      PHYSICAL_ADDRESS    BusAddress,
+    IN      ULONG               Length,
+    IN OUT  PULONG              AddressSpace,
+    OUT     PPHYSICAL_ADDRESS   TranslatedAddress
+    )
+{
+    PXENVKBD_BUS_CONTEXT        Context = _Context;
+
+    return PdoTranslateBusAddress(Context->Pdo,
+                                  BusAddress,
+                                  Length,
+                                  AddressSpace,
+                                  TranslatedAddress);
+}
+
+static
+__drv_functionClass(GET_DMA_ADAPTER)
+PDMA_ADAPTER
+BusGetDmaAdapter(
+    IN  PVOID               _Context,
+    IN  PDEVICE_DESCRIPTION DeviceDescriptor,
+    OUT PULONG              NumberOfMapRegisters
+    )
+{
+    PXENVKBD_BUS_CONTEXT    Context = _Context;
+
+    return PdoGetDmaAdapter(Context->Pdo,
+                            DeviceDescriptor,
+                            NumberOfMapRegisters);
+}
+
+static
+__drv_functionClass(GET_SET_DEVICE_DATA)
+ULONG
+BusSetData(
+    IN  PVOID               _Context,
+    IN  ULONG               DataType,
+    IN  PVOID               Buffer,
+    IN  ULONG               Offset,
+    IN  ULONG               Length
+    )
+{
+    PXENVKBD_BUS_CONTEXT    Context = _Context;
+
+    return PdoSetBusData(Context->Pdo,
+                         DataType,
+                         Buffer,
+                         Offset,
+                         Length);
+}
+
+static
+__drv_functionClass(GET_SET_DEVICE_DATA)
+ULONG
+BusGetData(
+    IN  PVOID               _Context,
+    IN  ULONG               DataType,
+    IN  PVOID               Buffer,
+    IN  ULONG               Offset,
+    IN  ULONG               Length
+    )
+{
+    PXENVKBD_BUS_CONTEXT    Context = _Context;
+
+    return PdoGetBusData(Context->Pdo,
+                         DataType,
+                         Buffer,
+                         Offset,
+                         Length);
+}
+
+NTSTATUS
+BusInitialize(
+    IN  PXENVKBD_PDO            Pdo,
+    OUT PBUS_INTERFACE_STANDARD Interface
+    )
+{
+    PXENVKBD_BUS_CONTEXT        Context;
+    NTSTATUS                    status;
+
+    Trace("====>\n");
+
+    Context = __BusAllocate(sizeof (XENVKBD_BUS_CONTEXT));
+
+    status = STATUS_NO_MEMORY;
+    if (Context == NULL)
+        goto fail1;
+
+    Context->Pdo = Pdo;
+
+    Interface->Size = sizeof (BUS_INTERFACE_STANDARD);
+    Interface->Version = 1;
+    Interface->Context = Context;
+    Interface->InterfaceReference = BusReference;
+    Interface->InterfaceDereference = BusDereference;
+    Interface->TranslateBusAddress = BusTranslateAddress;
+    Interface->GetDmaAdapter = BusGetDmaAdapter;
+    Interface->SetBusData = BusSetData;
+    Interface->GetBusData = BusGetData;
+
+    Trace("<====\n");
+
+    return STATUS_SUCCESS;
+
+fail1:
+    Error("fail1 (%08x)\n", status);
+
+    return status;
+}
+
+VOID
+BusTeardown(
+    IN OUT  PBUS_INTERFACE_STANDARD Interface
+    )
+{
+    PXENVKBD_BUS_CONTEXT            Context = Interface->Context;
+
+    Trace("====>\n");
+
+    Context->Pdo = NULL;
+
+    ASSERT(IsZeroMemory(Context, sizeof (XENVKBD_BUS_CONTEXT)));
+    __BusFree(Context);
+
+    RtlZeroMemory(Interface, sizeof (BUS_INTERFACE_STANDARD));
+
+    Trace("<====\n");
+}
diff --git a/src/xenvkbd/bus.h b/src/xenvkbd/bus.h
new file mode 100644 (file)
index 0000000..a48dff6
--- /dev/null
@@ -0,0 +1,52 @@
+/* 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 _XENVKBD_BUS_H
+#define _XENVKBD_BUS_H
+
+#include <ntddk.h>
+#include <xen.h>
+
+#include "pdo.h"
+
+extern NTSTATUS
+BusInitialize(
+    IN  PXENVKBD_PDO            Pdo,
+    OUT PBUS_INTERFACE_STANDARD Interface
+    );
+
+extern VOID
+BusTeardown(
+    IN OUT  PBUS_INTERFACE_STANDARD Interface
+    );
+
+#endif  // _XENVKBD_BUS_H
+
diff --git a/src/xenvkbd/dbg_print.h b/src/xenvkbd/dbg_print.h
new file mode 100644 (file)
index 0000000..53a4d01
--- /dev/null
@@ -0,0 +1,153 @@
+/* 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 _XENVKBD_DBG_PRINT_H
+#define _XENVKBD_DBG_PRINT_H
+
+#include <ntddk.h>
+#include <stdarg.h>
+
+#ifdef  _SDV_
+#define __MODULE__ ""
+#endif
+
+#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);
+}
+
+#else   // DBG
+
+static __inline VOID
+__Trace(
+    IN  const CHAR  *Prefix,
+    IN  const CHAR  *Format,
+    ...
+    )
+{
+    UNREFERENCED_PARAMETER(Prefix);
+    UNREFERENCED_PARAMETER(Format);
+}
+
+#endif  // DBG
+
+#define Trace(...)  \
+        __Trace(__MODULE__ "|" __FUNCTION__ ": ", __VA_ARGS__)
+
+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  // _XENVKBD_DBG_PRINT_H
diff --git a/src/xenvkbd/driver.c b/src/xenvkbd/driver.c
new file mode 100644 (file)
index 0000000..5e98af0
--- /dev/null
@@ -0,0 +1,402 @@
+/* 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 <procgrp.h>
+#include <ntstrsafe.h>
+#include <version.h>
+
+#include "registry.h"
+#include "fdo.h"
+#include "pdo.h"
+#include "driver.h"
+#include "dbg_print.h"
+#include "assert.h"
+#include "util.h"
+
+typedef struct _XENVKBD_DRIVER {
+    PDRIVER_OBJECT      DriverObject;
+    HANDLE              ParametersKey;
+    BOOLEAN             NeedReboot;
+} XENVKBD_DRIVER, *PXENVKBD_DRIVER;
+
+static XENVKBD_DRIVER    Driver;
+
+extern PULONG   InitSafeBootMode;
+
+static FORCEINLINE BOOLEAN
+__DriverSafeMode(
+    VOID
+    )
+{
+    return (*InitSafeBootMode > 0) ? TRUE : FALSE;
+}
+
+BOOLEAN
+DriverSafeMode(
+    VOID
+    )
+{
+    return __DriverSafeMode();
+}
+
+static FORCEINLINE VOID
+__DriverSetDriverObject(
+    IN  PDRIVER_OBJECT  DriverObject
+    )
+{
+    Driver.DriverObject = DriverObject;
+}
+
+static FORCEINLINE PDRIVER_OBJECT
+__DriverGetDriverObject(
+    VOID
+    )
+{
+    return Driver.DriverObject;
+}
+
+PDRIVER_OBJECT
+DriverGetDriverObject(
+    VOID
+    )
+{
+    return __DriverGetDriverObject();
+}
+
+static FORCEINLINE VOID
+__DriverSetParametersKey(
+    IN  HANDLE  Key
+    )
+{
+    Driver.ParametersKey = Key;
+}
+
+static FORCEINLINE HANDLE
+__DriverGetParametersKey(
+    VOID
+    )
+{
+    return Driver.ParametersKey;
+}
+
+HANDLE
+DriverGetParametersKey(
+    VOID
+    )
+{
+    return __DriverGetParametersKey();
+}
+
+#define MAXNAMELEN  256
+
+static FORCEINLINE VOID
+__DriverRequestReboot(
+    VOID
+    )
+{
+    PANSI_STRING    Ansi;
+    CHAR            RequestKeyName[MAXNAMELEN];
+    HANDLE          RequestKey;
+    HANDLE          SubKey;
+    NTSTATUS        status;
+
+    Info("====>\n");
+
+    ASSERT3U(KeGetCurrentIrql(), ==, PASSIVE_LEVEL);
+
+    status = RegistryQuerySzValue(__DriverGetParametersKey(),
+                                  "RequestKey",
+                                  NULL,
+                                  &Ansi);
+    if (!NT_SUCCESS(status))
+        goto fail1;
+
+    status = RtlStringCbPrintfA(RequestKeyName,
+                                MAXNAMELEN,
+                                "\\Registry\\Machine\\%Z",
+                                &Ansi[0]);
+    ASSERT(NT_SUCCESS(status));
+
+    status = RegistryCreateSubKey(NULL,
+                                  RequestKeyName,
+                                  REG_OPTION_NON_VOLATILE,
+                                  &RequestKey);
+    if (!NT_SUCCESS(status))
+        goto fail2;
+
+    status = RegistryCreateSubKey(RequestKey,
+                                  __MODULE__,
+                                  REG_OPTION_NON_VOLATILE,
+                                  &SubKey);
+    if (!NT_SUCCESS(status))
+        goto fail3;
+
+    status = RegistryUpdateDwordValue(SubKey,
+                                      "Reboot",
+                                      1);
+    if (!NT_SUCCESS(status))
+        goto fail4;
+
+    RegistryCloseKey(SubKey);
+
+    RegistryFreeSzValue(Ansi);
+
+    Info("<====\n");
+
+    return;
+
+fail4:
+    Error("fail4\n");
+
+    RegistryCloseKey(SubKey);
+
+fail3:
+    Error("fail3\n");
+
+    RegistryCloseKey(RequestKey);
+
+fail2:
+    Error("fail2\n");
+
+    RegistryFreeSzValue(Ansi);
+
+fail1:
+    Error("fail1 (%08x)\n", status);
+}
+
+VOID
+DriverRequestReboot(
+    VOID
+    )
+{
+    if (Driver.NeedReboot)
+        return;
+
+    __DriverRequestReboot();
+
+    Driver.NeedReboot = TRUE;
+}
+
+DRIVER_UNLOAD       DriverUnload;
+
+VOID
+DriverUnload(
+    IN  PDRIVER_OBJECT  DriverObject
+    )
+{
+    HANDLE              ParametersKey;
+
+    ASSERT3P(DriverObject, ==, __DriverGetDriverObject());
+
+    Trace("====>\n");
+
+    Driver.NeedReboot = FALSE;
+
+    ParametersKey = __DriverGetParametersKey();
+    __DriverSetParametersKey(NULL);
+
+    RegistryCloseKey(ParametersKey);
+
+    RegistryTeardown();
+
+    Info("XENVKBD %d.%d.%d (%d) (%02d.%02d.%04d)\n",
+         MAJOR_VERSION,
+         MINOR_VERSION,
+         MICRO_VERSION,
+         BUILD_NUMBER,
+         DAY,
+         MONTH,
+         YEAR);
+
+    __DriverSetDriverObject(NULL);
+
+    ASSERT(IsZeroMemory(&Driver, sizeof (XENVKBD_DRIVER)));
+
+    Trace("<====\n");
+}
+
+DRIVER_ADD_DEVICE   AddDevice;
+
+NTSTATUS
+AddDevice(
+    IN  PDRIVER_OBJECT  DriverObject,
+    IN  PDEVICE_OBJECT  DeviceObject
+    )
+{
+    NTSTATUS            status;
+
+    ASSERT3P(DriverObject, ==, __DriverGetDriverObject());
+
+    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
+    )
+{
+    PXENVKBD_DX         Dx;
+    NTSTATUS            status;
+
+    Dx = (PXENVKBD_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: {
+        PXENVKBD_PDO Pdo = Dx->Pdo;
+
+        status = PdoDispatch(Pdo, Irp);
+        break;
+    }
+    case FUNCTION_DEVICE_OBJECT: {
+        PXENVKBD_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              ServiceKey;
+    HANDLE              ParametersKey;
+    ULONG               Index;
+    NTSTATUS            status;
+
+    ASSERT3P(__DriverGetDriverObject(), ==, NULL);
+
+    ExInitializeDriverRuntime(DrvRtPoolNxOptIn);
+    WdmlibProcgrpInitialize();
+
+    Trace("====>\n");
+
+    __DriverSetDriverObject(DriverObject);
+
+    Driver.DriverObject->DriverUnload = DriverUnload;
+
+    Info("XENVKBD %d.%d.%d (%d) (%02d.%02d.%04d)\n",
+         MAJOR_VERSION,
+         MINOR_VERSION,
+         MICRO_VERSION,
+         BUILD_NUMBER,
+         DAY,
+         MONTH,
+         YEAR);
+
+    status = RegistryInitialize(RegistryPath);
+    if (!NT_SUCCESS(status))
+        goto fail1;
+
+    status = RegistryOpenServiceKey(KEY_ALL_ACCESS, &ServiceKey);
+    if (!NT_SUCCESS(status))
+        goto fail2;
+
+    status = RegistryOpenSubKey(ServiceKey, 
+                                "Parameters", 
+                                KEY_READ, 
+                                &ParametersKey);
+    if (!NT_SUCCESS(status))
+        goto fail3;
+
+    __DriverSetParametersKey(ParametersKey);
+
+    RegistryCloseKey(ServiceKey);
+
+    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;
+    }
+
+    Trace("<====\n");
+
+    return STATUS_SUCCESS;
+
+fail3:
+    Error("fail3\n");
+
+    RegistryCloseKey(ServiceKey);
+
+fail2:
+    Error("fail2\n");
+
+    RegistryTeardown();
+
+fail1:
+    Error("fail1 (%08x)\n", status);
+
+    __DriverSetDriverObject(NULL);
+
+    ASSERT(IsZeroMemory(&Driver, sizeof (XENVKBD_DRIVER)));
+
+    return status;
+}
diff --git a/src/xenvkbd/driver.h b/src/xenvkbd/driver.h
new file mode 100644 (file)
index 0000000..3999905
--- /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 _XENVKBD_DRIVER_H
+#define _XENVKBD_DRIVER_H
+
+extern BOOLEAN
+DriverSafeMode(
+    VOID
+    );
+
+extern PDRIVER_OBJECT
+DriverGetDriverObject(
+    VOID
+    );
+
+extern HANDLE
+DriverGetParametersKey(
+    VOID
+    );
+
+extern VOID
+DriverRequestReboot(
+    VOID
+    );
+
+typedef struct _XENVKBD_PDO  XENVKBD_PDO, *PXENVKBD_PDO;
+typedef struct _XENVKBD_FDO  XENVKBD_FDO, *PXENVKBD_FDO;
+
+#include "fdo.h"
+#include "pdo.h"
+
+#define MAX_DEVICE_ID_LEN   200
+#define MAX_GUID_STRING_LEN 39
+
+#pragma warning(push)
+#pragma warning(disable:4201) // nonstandard extension used : nameless struct/union
+
+typedef struct _XENVKBD_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 {
+        PXENVKBD_FDO    Fdo;
+        PXENVKBD_PDO    Pdo;
+    };
+} XENVKBD_DX, *PXENVKBD_DX;
+
+#pragma warning(pop)
+
+#endif  // _XENVKBD_DRIVER_H
diff --git a/src/xenvkbd/fdo.c b/src/xenvkbd/fdo.c
new file mode 100644 (file)
index 0000000..c17eccc
--- /dev/null
@@ -0,0 +1,3307 @@
+/* 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 <evtchn_interface.h>
+#include <debug_interface.h>
+#include <store_interface.h>
+#include <gnttab_interface.h>
+#include <suspend_interface.h>
+#include <unplug_interface.h>
+#include <version.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 "dbg_print.h"
+#include "assert.h"
+#include "util.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 _XENVKBD_FDO {
+    PXENVKBD_DX                 Dx;
+    PDEVICE_OBJECT              LowerDeviceObject;
+    PDEVICE_OBJECT              PhysicalDeviceObject;
+    DEVICE_CAPABILITIES         LowerDeviceCapabilities;
+    PBUS_INTERFACE_STANDARD     LowerBusInterface;
+    ULONG                       Usage[DeviceUsageTypeDumpFile + 1];
+    BOOLEAN                     NotDisableable;
+
+    PXENVKBD_THREAD             SystemPowerThread;
+    PIRP                        SystemPowerIrp;
+    PXENVKBD_THREAD             DevicePowerThread;
+    PIRP                        DevicePowerIrp;
+
+    CHAR                        VendorName[MAXNAMELEN];
+
+    PXENVKBD_THREAD             ScanThread;
+    KEVENT                      ScanEvent;
+    PXENBUS_STORE_WATCH         ScanWatch;
+    MUTEX                       Mutex;
+    ULONG                       References;
+
+    FDO_RESOURCE                Resource[RESOURCE_COUNT];
+
+    XENBUS_DEBUG_INTERFACE      DebugInterface;
+    XENBUS_SUSPEND_INTERFACE    SuspendInterface;
+    XENBUS_EVTCHN_INTERFACE     EvtchnInterface;
+    XENBUS_STORE_INTERFACE      StoreInterface;
+    XENBUS_RANGE_SET_INTERFACE  RangeSetInterface;
+    XENBUS_CACHE_INTERFACE      CacheInterface;
+    XENBUS_GNTTAB_INTERFACE     GnttabInterface;
+    XENBUS_UNPLUG_INTERFACE     UnplugInterface;
+
+    PXENBUS_SUSPEND_CALLBACK    SuspendCallbackLate;
+};
+
+static FORCEINLINE PVOID
+__FdoAllocate(
+    IN  ULONG   Length
+    )
+{
+    return __AllocatePoolWithTag(NonPagedPool, Length, FDO_POOL);
+}
+
+static FORCEINLINE VOID
+__FdoFree(
+    IN  PVOID   Buffer
+    )
+{
+    __FreePoolWithTag(Buffer, FDO_POOL);
+}
+
+static FORCEINLINE VOID
+__FdoSetDevicePnpState(
+    IN  PXENVKBD_FDO        Fdo,
+    IN  DEVICE_PNP_STATE    State
+    )
+{
+    PXENVKBD_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  PXENVKBD_FDO        Fdo,
+    IN  DEVICE_PNP_STATE    State
+    )
+{
+    PXENVKBD_DX             Dx = Fdo->Dx;
+
+    if (Dx->DevicePnpState == State)
+        Dx->DevicePnpState = Dx->PreviousDevicePnpState;
+}
+
+static FORCEINLINE DEVICE_PNP_STATE
+__FdoGetDevicePnpState(
+    IN  PXENVKBD_FDO    Fdo
+    )
+{
+    PXENVKBD_DX         Dx = Fdo->Dx;
+
+    return Dx->DevicePnpState;
+}
+
+static FORCEINLINE DEVICE_PNP_STATE
+__FdoGetPreviousDevicePnpState(
+    IN  PXENVKBD_FDO    Fdo
+    )
+{
+    PXENVKBD_DX         Dx = Fdo->Dx;
+
+    return Dx->PreviousDevicePnpState;
+}
+
+static FORCEINLINE VOID
+__FdoSetDevicePowerState(
+    IN  PXENVKBD_FDO        Fdo,
+    IN  DEVICE_POWER_STATE  State
+    )
+{
+    PXENVKBD_DX             Dx = Fdo->Dx;
+
+    Dx->DevicePowerState = State;
+}
+
+static FORCEINLINE DEVICE_POWER_STATE
+__FdoGetDevicePowerState(
+    IN  PXENVKBD_FDO    Fdo
+    )
+{
+    PXENVKBD_DX         Dx = Fdo->Dx;
+
+    return Dx->DevicePowerState;
+}
+
+static FORCEINLINE VOID
+__FdoSetSystemPowerState(
+    IN  PXENVKBD_FDO        Fdo,
+    IN  SYSTEM_POWER_STATE  State
+    )
+{
+    PXENVKBD_DX             Dx = Fdo->Dx;
+
+    Dx->SystemPowerState = State;
+}
+
+static FORCEINLINE SYSTEM_POWER_STATE
+__FdoGetSystemPowerState(
+    IN  PXENVKBD_FDO    Fdo
+    )
+{
+    PXENVKBD_DX         Dx = Fdo->Dx;
+
+    return Dx->SystemPowerState;
+}
+
+static FORCEINLINE PDEVICE_OBJECT
+__FdoGetPhysicalDeviceObject(
+    IN  PXENVKBD_FDO    Fdo
+    )
+{
+    return Fdo->PhysicalDeviceObject;
+}
+
+PDEVICE_OBJECT
+FdoGetPhysicalDeviceObject(
+    IN  PXENVKBD_FDO    Fdo
+    )
+{
+    return __FdoGetPhysicalDeviceObject(Fdo);
+}
+
+__drv_requiresIRQL(PASSIVE_LEVEL)
+static FORCEINLINE NTSTATUS
+__FdoAcquireLowerBusInterface(
+    IN  PXENVKBD_FDO        Fdo
+    )
+{
+    PBUS_INTERFACE_STANDARD BusInterface;
+    KEVENT                  Event;
+    IO_STATUS_BLOCK         StatusBlock;
+    PIRP                    Irp;
+    PIO_STACK_LOCATION      StackLocation;
+    NTSTATUS                status;
+
+    ASSERT3U(KeGetCurrentIrql(), ==, PASSIVE_LEVEL);
+
+    BusInterface = __FdoAllocate(sizeof (BUS_INTERFACE_STANDARD));
+
+    status = STATUS_NO_MEMORY;
+    if (BusInterface == NULL)
+        goto fail1;
+
+    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 fail2;
+
+    StackLocation = IoGetNextIrpStackLocation(Irp);
+    StackLocation->MinorFunction = IRP_MN_QUERY_INTERFACE;
+
+    StackLocation->Parameters.QueryInterface.InterfaceType = &GUID_BUS_INTERFACE_STANDARD;
+    StackLocation->Parameters.QueryInterface.Size = sizeof (BUS_INTERFACE_STANDARD);
+    StackLocation->Parameters.QueryInterface.Version = 1;
+    StackLocation->Parameters.QueryInterface.Interface = (PINTERFACE)BusInterface;
+
+    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 fail3;
+
+    status = STATUS_INVALID_PARAMETER;
+    if (BusInterface->Version != 1)
+        goto fail4;
+
+    Fdo->LowerBusInterface = BusInterface;
+
+    return STATUS_SUCCESS;
+
+fail4:
+    Error("fail4\n");
+
+fail3:
+    Error("fail3\n");
+
+fail2:
+    Error("fail2\n");
+
+    __FdoFree(BusInterface);
+
+fail1:
+    Error("fail1 (%08x)\n", status);
+
+    return status;
+}
+
+static FORCEINLINE VOID
+__FdoReleaseLowerBusInterface(
+    IN  PXENVKBD_FDO        Fdo
+    )
+{
+    PBUS_INTERFACE_STANDARD BusInterface;
+
+    BusInterface = Fdo->LowerBusInterface;
+
+    if (BusInterface == NULL)
+        return;
+
+    Fdo->LowerBusInterface = NULL;
+
+    BusInterface->InterfaceDereference(BusInterface->Context);
+
+    __FdoFree(BusInterface);
+}
+
+PDMA_ADAPTER
+FdoGetDmaAdapter(
+    IN  PXENVKBD_FDO        Fdo,
+    IN  PDEVICE_DESCRIPTION DeviceDescriptor,
+    OUT PULONG              NumberOfMapRegisters
+    )
+{
+    PBUS_INTERFACE_STANDARD BusInterface;
+
+    BusInterface = Fdo->LowerBusInterface;
+    ASSERT(BusInterface != NULL);
+
+    return BusInterface->GetDmaAdapter(BusInterface->Context,
+                                       DeviceDescriptor,
+                                       NumberOfMapRegisters);
+}
+
+BOOLEAN
+FdoTranslateBusAddress(
+    IN      PXENVKBD_FDO        Fdo,
+    IN      PHYSICAL_ADDRESS    BusAddress,
+    IN      ULONG               Length,
+    IN OUT  PULONG              AddressSpace,
+    OUT     PPHYSICAL_ADDRESS   TranslatedAddress
+    )
+{
+    PBUS_INTERFACE_STANDARD     BusInterface;
+
+    BusInterface = Fdo->LowerBusInterface;
+    ASSERT(BusInterface != NULL);
+
+    return BusInterface->TranslateBusAddress(BusInterface->Context,
+                                             BusAddress,
+                                             Length,
+                                             AddressSpace,
+                                             TranslatedAddress);
+}
+
+ULONG
+FdoSetBusData(
+    IN  PXENVKBD_FDO        Fdo,
+    IN  ULONG               DataType,
+    IN  PVOID               Buffer,
+    IN  ULONG               Offset,
+    IN  ULONG               Length
+    )
+{
+    PBUS_INTERFACE_STANDARD BusInterface;
+
+    BusInterface = Fdo->LowerBusInterface;
+    ASSERT(BusInterface != NULL);
+
+    return BusInterface->SetBusData(BusInterface->Context,
+                                    DataType,
+                                    Buffer,
+                                    Offset,
+                                    Length);
+}
+
+ULONG
+FdoGetBusData(
+    IN  PXENVKBD_FDO        Fdo,
+    IN  ULONG               DataType,
+    IN  PVOID               Buffer,
+    IN  ULONG               Offset,
+    IN  ULONG               Length
+    )
+{
+    PBUS_INTERFACE_STANDARD BusInterface;
+
+    BusInterface = Fdo->LowerBusInterface;
+    ASSERT(BusInterface != NULL);
+
+    return BusInterface->GetBusData(BusInterface->Context,
+                                    DataType,
+                                    Buffer,
+                                    Offset,
+                                    Length);
+}
+
+static FORCEINLINE VOID
+__FdoSetVendorName(
+    IN  PXENVKBD_FDO    Fdo,
+    IN  USHORT          DeviceID
+    )
+{
+    NTSTATUS        status;
+
+    status = RtlStringCbPrintfA(Fdo->VendorName,
+                                MAXNAMELEN,
+                                "%s%04X",
+                                VENDOR_PREFIX_STR,
+                                DeviceID);
+    ASSERT(NT_SUCCESS(status));
+}
+
+static FORCEINLINE PCHAR
+__FdoGetVendorName(
+    IN  PXENVKBD_FDO    Fdo
+    )
+{
+    return Fdo->VendorName;
+}
+
+PCHAR
+FdoGetVendorName(
+    IN  PXENVKBD_FDO    Fdo
+    )
+{
+    return __FdoGetVendorName(Fdo);
+}
+
+static FORCEINLINE VOID
+__FdoSetName(
+    IN  PXENVKBD_FDO    Fdo
+    )
+{
+    PXENVKBD_DX         Dx = Fdo->Dx;
+    NTSTATUS            status;
+
+    status = RtlStringCbPrintfA(Dx->Name,
+                                MAXNAMELEN,
+                                "%s XENVKBD",
+                                __FdoGetVendorName(Fdo));
+    ASSERT(NT_SUCCESS(status));
+}
+
+static FORCEINLINE PCHAR
+__FdoGetName(
+    IN  PXENVKBD_FDO    Fdo
+    )
+{
+    PXENVKBD_DX         Dx = Fdo->Dx;
+
+    return Dx->Name;
+}
+
+PCHAR
+FdoGetName(
+    IN  PXENVKBD_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  PXENVKBD_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  PXENVKBD_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;
+}
+
+NTSTATUS
+FdoAddPhysicalDeviceObject(
+    IN  PXENVKBD_FDO    Fdo,
+    IN  PXENVKBD_PDO    Pdo
+    )
+{
+    PDEVICE_OBJECT      DeviceObject;
+    PXENVKBD_DX         Dx;
+    NTSTATUS            status;
+
+    DeviceObject = PdoGetDeviceObject(Pdo);
+    Dx = (PXENVKBD_DX)DeviceObject->DeviceExtension;
+    ASSERT3U(Dx->Type, ==, PHYSICAL_DEVICE_OBJECT);
+
+    if (__FdoGetDevicePowerState(Fdo) == PowerDeviceD3)
+        goto done;
+
+    status = PdoResume(Pdo);
+    if (!NT_SUCCESS(status))
+        goto fail1;
+
+done:
+    InsertTailList(&Fdo->Dx->ListEntry, &Dx->ListEntry);
+    ASSERT3U(Fdo->References, !=, 0);
+    Fdo->References++;
+
+    return STATUS_SUCCESS;
+
+fail1:
+    Error("fail1 (%08x)\n", status);
+
+    return status;
+}
+
+VOID
+FdoRemovePhysicalDeviceObject(
+    IN  PXENVKBD_FDO    Fdo,
+    IN  PXENVKBD_PDO    Pdo
+    )
+{
+    PDEVICE_OBJECT      DeviceObject;
+    PXENVKBD_DX         Dx;
+
+    DeviceObject = PdoGetDeviceObject(Pdo);
+    Dx = (PXENVKBD_DX)DeviceObject->DeviceExtension;
+    ASSERT3U(Dx->Type, ==, PHYSICAL_DEVICE_OBJECT);
+
+    if (__FdoGetDevicePowerState(Fdo) == PowerDeviceD3)
+        goto done;
+
+    PdoSuspend(Pdo);
+
+done:
+    RemoveEntryList(&Dx->ListEntry);
+    ASSERT3U(Fdo->References, !=, 0);
+    --Fdo->References;
+
+    if (Fdo->ScanThread)
+        ThreadWake(Fdo->ScanThread);
+}
+
+static FORCEINLINE VOID
+__FdoAcquireMutex(
+    IN  PXENVKBD_FDO    Fdo
+    )
+{
+    AcquireMutex(&Fdo->Mutex);
+}
+
+VOID
+FdoAcquireMutex(
+    IN  PXENVKBD_FDO    Fdo
+    )
+{
+    __FdoAcquireMutex(Fdo);
+}
+
+static FORCEINLINE VOID
+__FdoReleaseMutex(
+    IN  PXENVKBD_FDO    Fdo
+    )
+{
+    ReleaseMutex(&Fdo->Mutex);
+}
+
+VOID
+FdoReleaseMutex(
+    IN  PXENVKBD_FDO    Fdo
+    )
+{
+    __FdoReleaseMutex(Fdo);
+
+    if (Fdo->References == 0)
+        FdoDestroy(Fdo);
+}
+
+static FORCEINLINE BOOLEAN
+__FdoEnumerate(
+    IN  PXENVKBD_FDO    Fdo,
+    IN  PANSI_STRING    Devices
+    )
+{
+    BOOLEAN             NeedInvalidate; 
+    HANDLE              ParametersKey;
+    ULONG               Enumerate;
+    PLIST_ENTRY         ListEntry;
+    ULONG               Index;
+    NTSTATUS            status;
+
+    Trace("====>\n");
+
+    NeedInvalidate = FALSE;
+
+    ParametersKey = DriverGetParametersKey();
+
+    status = RegistryQueryDwordValue(ParametersKey,
+                                     "Enumerate",
+                                     &Enumerate);
+    if (!NT_SUCCESS(status))
+        Enumerate = 1;
+
+    if (Enumerate == 0)
+        goto done;
+
+    __FdoAcquireMutex(Fdo);
+
+    ListEntry = Fdo->Dx->ListEntry.Flink;
+    while (ListEntry != &Fdo->Dx->ListEntry) {
+        PLIST_ENTRY     Next = ListEntry->Flink;
+        PXENVKBD_DX     Dx = CONTAINING_RECORD(ListEntry, XENVKBD_DX, ListEntry);
+        PXENVKBD_PDO    Pdo = Dx->Pdo;
+
+        if (PdoGetDevicePnpState(Pdo) != Deleted) {
+            PCHAR           Name;
+            BOOLEAN         Missing;
+
+            Name = PdoGetName(Pdo);
+            Missing = TRUE;
+
+            // If the PDO already exists and its name is in the device list
+            // then we don't want to remove it.
+            for (Index = 0; Devices[Index].Buffer != NULL; Index++) {
+                PANSI_STRING Device = &Devices[Index];
+
+                if (Device->Length == 0)
+                    continue;
+
+                if (strcmp(Name, Device->Buffer) == 0) {
+                    Missing = FALSE;
+                    Device->Length = 0;  // avoid duplication
+                    break;
+                }
+            }
+
+            if (!PdoIsMissing(Pdo)) {
+                if (PdoIsEjectRequested(Pdo)) {
+                    IoRequestDeviceEject(PdoGetDeviceObject(Pdo));
+                } else if (Missing) {
+                    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;
+    }
+
+    // Walk the class list and create PDOs for any new device
+    for (Index = 0; Devices[Index].Buffer != NULL; Index++) {
+        PANSI_STRING Device = &Devices[Index];
+
+        if (Device->Length == 0)
+            continue;
+
+        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] = __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(
+    PXENVKBD_THREAD     Self,
+    PVOID               Context
+    )
+{
+    PXENVKBD_FDO        Fdo = Context;
+    PKEVENT             Event;
+    HANDLE              ParametersKey;
+    NTSTATUS            status;
+
+    Trace("====>\n");
+
+    Event = ThreadGetEvent(Self);
+
+    ParametersKey = DriverGetParametersKey();
+
+    for (;;) {
+        PCHAR           Buffer;
+        PANSI_STRING    Devices;
+        PANSI_STRING    UnsupportedDevices;
+        ULONG           Index;
+        BOOLEAN         NeedInvalidate;
+
+        Trace("waiting...\n");
+
+        (VOID) KeWaitForSingleObject(Event,
+                                     Executive,
+                                     KernelMode,
+                                     FALSE,
+                                     NULL);
+        KeClearEvent(Event);
+
+        if (ThreadIsAlerted(Self))
+            break;
+
+        // It is not safe to use interfaces before this point
+        if (__FdoGetDevicePnpState(Fdo) != Started) {
+            KeSetEvent(&Fdo->ScanEvent, IO_NO_INCREMENT, FALSE);
+            continue;
+        }
+
+        status = XENBUS_STORE(Directory,
+                              &Fdo->StoreInterface,
+                              NULL,
+                              "device",
+                              "vkbd",
+                              &Buffer);
+        if (NT_SUCCESS(status)) {
+            Devices = __FdoMultiSzToUpcaseAnsi(Buffer);
+
+            XENBUS_STORE(Free,
+                         &Fdo->StoreInterface,
+                         Buffer);
+        } else {
+            Devices = NULL;
+        }
+
+        if (Devices == NULL)
+            goto loop;
+
+        if (ParametersKey != NULL) {
+            status = RegistryQuerySzValue(ParametersKey,
+                                          "UnsupportedDevices",
+                                          NULL,
+                                          &UnsupportedDevices);
+            if (!NT_SUCCESS(status))
+                UnsupportedDevices = NULL;
+        } else {
+            UnsupportedDevices = NULL;
+        }
+
+        // NULL out anything in the Devices list that is in the
+        // UnsupportedDevices list    
+        for (Index = 0; Devices[Index].Buffer != NULL; Index++) {
+            PANSI_STRING    Device = &Devices[Index];
+            ULONG           Entry;
+            BOOLEAN         Supported;
+
+            Supported = TRUE;
+
+            for (Entry = 0;
+                 UnsupportedDevices != NULL && UnsupportedDevices[Entry].Buffer != NULL;
+                 Entry++) {
+                if (strncmp(Device->Buffer,
+                            UnsupportedDevices[Entry].Buffer,
+                            Device->Length) == 0) {
+                    Supported = FALSE;
+                    break;
+                }
+            }
+
+            if (!Supported)
+                Device->Length = 0;
+        }
+
+        if (UnsupportedDevices != NULL)
+            RegistryFreeSzValue(UnsupportedDevices);
+
+        NeedInvalidate = __FdoEnumerate(Fdo, Devices);
+
+        __FdoFreeAnsi(Devices);
+
+        if (NeedInvalidate) {
+            NeedInvalidate = FALSE;
+            IoInvalidateDeviceRelations(__FdoGetPhysicalDeviceObject(Fdo), 
+                                        BusRelations);
+        }
+
+loop:
+        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  PXENVKBD_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;
+        }
+    }
+}
+
+static FORCEINLINE BOOLEAN
+__FdoMatchDistribution(
+    IN  PXENVKBD_FDO    Fdo,
+    IN  PCHAR           Buffer
+    )
+{
+    PCHAR               Vendor;
+    PCHAR               Product;
+    PCHAR               Context;
+    const CHAR          *Text;
+    BOOLEAN             Match;
+    ULONG               Index;
+    NTSTATUS            status;
+
+    UNREFERENCED_PARAMETER(Fdo);
+
+    status = STATUS_INVALID_PARAMETER;
+
+    Vendor = __strtok_r(Buffer, " ", &Context);
+    if (Vendor == NULL)
+        goto fail1;
+
+    Product = __strtok_r(NULL, " ", &Context);
+    if (Product == NULL)
+        goto fail2;
+
+    Match = TRUE;
+
+    Text = VENDOR_NAME_STR;
+
+    for (Index = 0; Text[Index] != 0; Index++) {
+        if (!isalnum((UCHAR)Text[Index])) {
+            if (Vendor[Index] != '_') {
+                Match = FALSE;
+                break;
+            }
+        } else {
+            if (Vendor[Index] != Text[Index]) {
+                Match = FALSE;
+                break;
+            }
+        }
+    }
+
+    Text = "XENVKBD";
+
+    if (_stricmp(Product, Text) != 0)
+        Match = FALSE;
+
+    return Match;
+
+fail2:
+    Error("fail2\n");
+
+fail1:
+    Error("fail1 (%08x)\n", status);
+
+    return FALSE;
+}
+
+static VOID
+FdoClearDistribution(
+    IN  PXENVKBD_FDO    Fdo
+    )
+{
+    PCHAR               Buffer;
+    PANSI_STRING        Distributions;
+    ULONG               Index;
+    NTSTATUS            status;
+
+    Trace("====>\n");
+
+    status = XENBUS_STORE(Directory,
+                          &Fdo->StoreInterface,
+                          NULL,
+                          NULL,
+                          "drivers",
+                          &Buffer);
+    if (NT_SUCCESS(status)) {
+        Distributions = __FdoMultiSzToUpcaseAnsi(Buffer);
+
+        XENBUS_STORE(Free,
+                     &Fdo->StoreInterface,
+                     Buffer);
+    } else {
+        Distributions = NULL;
+    }
+
+    if (Distributions == NULL)
+        goto done;
+
+    for (Index = 0; Distributions[Index].Buffer != NULL; Index++) {
+        PANSI_STRING    Distribution = &Distributions[Index];
+
+        status = XENBUS_STORE(Read,
+                              &Fdo->StoreInterface,
+                              NULL,
+                              "drivers",
+                              Distribution->Buffer,
+                              &Buffer);
+        if (!NT_SUCCESS(status))
+            continue;
+
+        if (__FdoMatchDistribution(Fdo, Buffer))
+            (VOID) XENBUS_STORE(Remove,
+                                &Fdo->StoreInterface,
+                                NULL,
+                                "drivers",
+                                Distribution->Buffer);
+
+        XENBUS_STORE(Free,
+                     &Fdo->StoreInterface,
+                     Buffer);
+    }
+
+    __FdoFreeAnsi(Distributions);
+
+done:
+    Trace("<====\n");
+}
+
+#define MAXIMUM_INDEX   255
+
+static NTSTATUS
+FdoSetDistribution(
+    IN  PXENVKBD_FDO    Fdo
+    )
+{
+    ULONG               Index;
+    CHAR                Distribution[MAXNAMELEN];
+    CHAR                Vendor[MAXNAMELEN];
+    const CHAR          *Product;
+    NTSTATUS            status;
+
+    Trace("====>\n");
+
+    Index = 0;
+    while (Index <= MAXIMUM_INDEX) {
+        PCHAR   Buffer;
+
+        status = RtlStringCbPrintfA(Distribution,
+                                    MAXNAMELEN,
+                                    "%u",
+                                    Index);
+        ASSERT(NT_SUCCESS(status));
+
+        status = XENBUS_STORE(Read,
+                              &Fdo->StoreInterface,
+                              NULL,
+                              "drivers",
+                              Distribution,
+                              &Buffer);
+        if (!NT_SUCCESS(status)) {
+            if (status == STATUS_OBJECT_NAME_NOT_FOUND)
+                goto update;
+
+            goto fail1;
+        }
+
+        XENBUS_STORE(Free,
+                     &Fdo->StoreInterface,
+                     Buffer);
+
+        Index++;
+    }
+
+    status = STATUS_UNSUCCESSFUL;
+    goto fail2;
+
+update:
+    status = RtlStringCbPrintfA(Vendor,
+                                MAXNAMELEN,
+                                "%s",
+                                VENDOR_NAME_STR);
+    ASSERT(NT_SUCCESS(status));
+
+    for (Index  = 0; Vendor[Index] != '\0'; Index++)
+        if (!isalnum((UCHAR)Vendor[Index]))
+            Vendor[Index] = '_';
+
+    Product = "XENVKBD";
+
+#if DBG
+#define ATTRIBUTES   "(DEBUG)"
+#else
+#define ATTRIBUTES   ""
+#endif
+
+    (VOID) XENBUS_STORE(Printf,
+                        &Fdo->StoreInterface,
+                        NULL,
+                        "drivers",
+                        Distribution,
+                        "%s %s %u.%u.%u %s",
+                        Vendor,
+                        Product,
+                        MAJOR_VERSION,
+                        MINOR_VERSION,
+                        MICRO_VERSION,
+                        ATTRIBUTES
+                        );
+
+#undef  ATTRIBUTES
+
+    Trace("<====\n");
+    return STATUS_SUCCESS;
+
+fail2:
+    Error("fail2\n");
+
+fail1:
+    Error("fail1 (%08x)\n", status);
+
+    return status;
+}
+
+static FORCEINLINE NTSTATUS
+__FdoD3ToD0(
+    IN  PXENVKBD_FDO    Fdo
+    )
+{
+    NTSTATUS            status;
+
+    Trace("====>\n");
+
+    ASSERT3U(KeGetCurrentIrql(), ==, DISPATCH_LEVEL);
+
+    (VOID) FdoSetDistribution(Fdo);
+
+    status = XENBUS_STORE(WatchAdd,
+                          &Fdo->StoreInterface,
+                          "device",
+                          "vkbd",
+                          ThreadGetEvent(Fdo->ScanThread),
+                          &Fdo->ScanWatch);
+    if (!NT_SUCCESS(status))
+        goto fail1;
+
+    (VOID) XENBUS_STORE(Printf,
+                        &Fdo->StoreInterface,
+                        NULL,
+                        "feature/hotplug",
+                        "vkbd",
+                        "%u",
+                        TRUE);
+
+    Trace("<====\n");
+
+    return STATUS_SUCCESS;
+
+fail1:
+    Error("fail1 (%08x)\n", status);
+
+    return status;
+}
+
+static FORCEINLINE VOID
+__FdoD0ToD3(
+    IN  PXENVKBD_FDO    Fdo
+    )
+{
+    Trace("====>\n");
+
+    ASSERT3U(KeGetCurrentIrql(), ==, DISPATCH_LEVEL);
+
+    (VOID) XENBUS_STORE(Remove,
+                        &Fdo->StoreInterface,
+                        NULL,
+                        "feature/hotplug",
+                        "vkbd");
+
+    (VOID) XENBUS_STORE(WatchRemove,
+                        &Fdo->StoreInterface,
+                        Fdo->ScanWatch);
+    Fdo->ScanWatch = NULL;
+
+    FdoClearDistribution(Fdo);
+
+    Trace("<====\n");
+}
+
+static DECLSPEC_NOINLINE VOID
+FdoSuspendCallbackLate(
+    IN  PVOID       Argument
+    )
+{
+    PXENVKBD_FDO    Fdo = Argument;
+    NTSTATUS        status;
+
+    __FdoD0ToD3(Fdo);
+
+    status = __FdoD3ToD0(Fdo);
+    ASSERT(NT_SUCCESS(status));
+}
+
+// This function must not touch pageable code or data
+static DECLSPEC_NOINLINE NTSTATUS
+FdoD3ToD0(
+    IN  PXENVKBD_FDO    Fdo
+    )
+{
+    POWER_STATE         PowerState;
+    KIRQL               Irql;
+    PLIST_ENTRY         ListEntry;
+    NTSTATUS            status;
+
+    ASSERT3U(KeGetCurrentIrql(), ==, PASSIVE_LEVEL);
+    ASSERT3U(__FdoGetDevicePowerState(Fdo), ==, PowerDeviceD3);
+
+    Trace("====>\n");
+
+    KeRaiseIrql(DISPATCH_LEVEL, &Irql);
+
+    status = XENBUS_SUSPEND(Acquire, &Fdo->SuspendInterface);
+    if (!NT_SUCCESS(status))
+        goto fail1;
+
+    status = XENBUS_STORE(Acquire, &Fdo->StoreInterface);
+    if (!NT_SUCCESS(status))
+        goto fail2;
+
+    status = __FdoD3ToD0(Fdo);
+    if (!NT_SUCCESS(status))
+        goto fail3;
+
+    status = XENBUS_SUSPEND(Register,
+                            &Fdo->SuspendInterface,
+                            SUSPEND_CALLBACK_LATE,
+                            FdoSuspendCallbackLate,
+                            Fdo,
+                            &Fdo->SuspendCallbackLate);
+    if (!NT_SUCCESS(status))
+        goto fail4;
+
+    KeLowerIrql(Irql);
+
+    __FdoSetDevicePowerState(Fdo, PowerDeviceD0);
+
+    PowerState.DeviceState = PowerDeviceD0;
+    PoSetPowerState(Fdo->Dx->DeviceObject,
+                    DevicePowerState,
+                    PowerState);
+
+    __FdoAcquireMutex(Fdo);
+
+    for (ListEntry = Fdo->Dx->ListEntry.Flink;
+         ListEntry != &Fdo->Dx->ListEntry;
+         ListEntry = ListEntry->Flink) {
+        PXENVKBD_DX  Dx = CONTAINING_RECORD(ListEntry, XENVKBD_DX, ListEntry);
+        PXENVKBD_PDO Pdo = Dx->Pdo;
+
+        ASSERT3U(Dx->Type, ==, PHYSICAL_DEVICE_OBJECT);
+
+        status = PdoResume(Pdo);
+        ASSERT(NT_SUCCESS(status));
+    }
+
+    __FdoReleaseMutex(Fdo);
+
+    Trace("<====\n");
+
+    return STATUS_SUCCESS;
+
+fail4:
+    Error("fail4\n");
+
+    __FdoD0ToD3(Fdo);
+
+fail3:
+    Error("fail3\n");
+
+    XENBUS_STORE(Release, &Fdo->StoreInterface);
+
+fail2:
+    Error("fail2\n");
+
+    XENBUS_SUSPEND(Release, &Fdo->SuspendInterface);
+
+    __FdoD0ToD3(Fdo);
+
+fail1:
+    Error("fail1 (%08x)\n", status);
+
+    KeLowerIrql(Irql);
+
+    return status;
+}
+
+// This function must not touch pageable code or data
+static DECLSPEC_NOINLINE VOID
+FdoD0ToD3(
+    IN  PXENVKBD_FDO    Fdo
+    )
+{
+    POWER_STATE         PowerState;
+    PLIST_ENTRY         ListEntry;
+    KIRQL               Irql;
+
+    ASSERT3U(KeGetCurrentIrql(), ==, PASSIVE_LEVEL);
+    ASSERT3U(__FdoGetDevicePowerState(Fdo), ==, PowerDeviceD0);
+
+    Trace("====>\n");
+
+    __FdoAcquireMutex(Fdo);
+
+    for (ListEntry = Fdo->Dx->ListEntry.Flink;
+         ListEntry != &Fdo->Dx->ListEntry;
+         ListEntry = ListEntry->Flink) {
+        PXENVKBD_DX  Dx = CONTAINING_RECORD(ListEntry, XENVKBD_DX, ListEntry);
+        PXENVKBD_PDO Pdo = Dx->Pdo;
+
+        ASSERT3U(Dx->Type, ==, PHYSICAL_DEVICE_OBJECT);
+
+        if (PdoGetDevicePnpState(Pdo) == Deleted ||
+            PdoIsMissing(Pdo))
+            continue;
+
+        PdoSuspend(Pdo);
+    }
+
+    __FdoReleaseMutex(Fdo);
+
+    PowerState.DeviceState = PowerDeviceD3;
+    PoSetPowerState(Fdo->Dx->DeviceObject,
+                    DevicePowerState,
+                    PowerState);
+
+    __FdoSetDevicePowerState(Fdo, PowerDeviceD3);
+
+    KeRaiseIrql(DISPATCH_LEVEL, &Irql);
+
+    XENBUS_SUSPEND(Deregister,
+                   &Fdo->SuspendInterface,
+                   Fdo->SuspendCallbackLate);
+    Fdo->SuspendCallbackLate = NULL;
+
+    __FdoD0ToD3(Fdo);
+
+    XENBUS_STORE(Release, &Fdo->StoreInterface);
+
+    XENBUS_SUSPEND(Release, &Fdo->SuspendInterface);
+
+    KeLowerIrql(Irql);
+
+    Trace("<====\n");
+}
+
+// This function must not touch pageable code or data
+static DECLSPEC_NOINLINE VOID
+FdoS4ToS3(
+    IN  PXENVKBD_FDO    Fdo
+    )
+{
+    ASSERT3U(KeGetCurrentIrql(), ==, PASSIVE_LEVEL);
+    ASSERT3U(__FdoGetSystemPowerState(Fdo), ==, PowerSystemHibernate);
+
+    __FdoSetSystemPowerState(Fdo, PowerSystemSleeping3);
+}
+
+// This function must not touch pageable code or data
+static DECLSPEC_NOINLINE VOID
+FdoS3ToS4(
+    IN  PXENVKBD_FDO    Fdo
+    )
+{
+    ASSERT3U(KeGetCurrentIrql(), ==, PASSIVE_LEVEL);
+    ASSERT3U(__FdoGetSystemPowerState(Fdo), ==, PowerSystemSleeping3);
+
+    __FdoSetSystemPowerState(Fdo, PowerSystemHibernate);
+}
+
+static DECLSPEC_NOINLINE NTSTATUS
+FdoStartDevice(
+    IN  PXENVKBD_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;
+
+    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");
+
+    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  PXENVKBD_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  PXENVKBD_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  PXENVKBD_FDO    Fdo,
+    IN  PIRP            Irp
+    )
+{
+    NTSTATUS            status;
+
+    if (__FdoGetDevicePowerState(Fdo) == PowerDeviceD0)
+        FdoD0ToD3(Fdo);
+
+    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  PXENVKBD_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  PXENVKBD_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  PXENVKBD_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) {
+        PXENVKBD_DX  Dx = CONTAINING_RECORD(ListEntry, XENVKBD_DX, ListEntry);
+        PXENVKBD_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  PXENVKBD_FDO    Fdo,
+    IN  PIRP            Irp
+    )
+{
+    PLIST_ENTRY         ListEntry;
+    NTSTATUS            status;
+
+    ASSERT3U(KeGetCurrentIrql(), ==, PASSIVE_LEVEL);
+
+    if (__FdoGetPreviousDevicePnpState(Fdo) != Started)
+        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;
+        PXENVKBD_DX  Dx = CONTAINING_RECORD(ListEntry, XENVKBD_DX, ListEntry);
+        PXENVKBD_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);
+
+    if (__FdoGetDevicePowerState(Fdo) == PowerDeviceD0)
+        FdoD0ToD3(Fdo);
+
+    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);
+
+    // We must release our reference before the PDO is destroyed
+    __FdoReleaseLowerBusInterface(Fdo);
+
+    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  PXENVKBD_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;
+    }
+
+    KeClearEvent(&Fdo->ScanEvent);
+    ThreadWake(Fdo->ScanThread);
+
+    Trace("waiting for scan thread\n");
+
+    (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 (PDEVICE_OBJECT) * __max(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) {
+        PXENVKBD_DX  Dx = CONTAINING_RECORD(ListEntry, XENVKBD_DX, ListEntry);
+        PXENVKBD_PDO Pdo = Dx->Pdo;
+
+        ASSERT3U(Dx->Type, ==, PHYSICAL_DEVICE_OBJECT);
+
+        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);
+
+    ListEntry = Fdo->Dx->ListEntry.Flink;
+    while (ListEntry != &Fdo->Dx->ListEntry) {
+        PXENVKBD_DX  Dx = CONTAINING_RECORD(ListEntry, XENVKBD_DX, ListEntry);
+        PXENVKBD_PDO Pdo = Dx->Pdo;
+        PLIST_ENTRY Next = ListEntry->Flink;
+
+        ASSERT3U(Dx->Type, ==, PHYSICAL_DEVICE_OBJECT);
+
+        if (PdoGetDevicePnpState(Pdo) == Deleted &&
+            PdoIsMissing(Pdo))
+            PdoDestroy(Pdo);
+
+        ListEntry = Next;
+    }
+
+    __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  PXENVKBD_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];
+    }
+
+    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  PXENVKBD_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 {
+        if (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  PXENVKBD_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  PXENVKBD_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  PXENVKBD_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 -> %s\n",
+         __FdoGetName(Fdo),
+         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  PXENVKBD_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 -> %s\n",
+         __FdoGetName(Fdo),
+         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  PXENVKBD_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  PXENVKBD_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  PXENVKBD_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;
+
+    Info("%s: %s -> %s\n",
+         __FdoGetName(Fdo),
+         PowerSystemStateName(__FdoGetSystemPowerState(Fdo)),
+         PowerSystemStateName(SystemState));
+
+    if (SystemState < PowerSystemHibernate &&
+        __FdoGetSystemPowerState(Fdo) >= PowerSystemHibernate) {
+        __FdoSetSystemPowerState(Fdo, PowerSystemHibernate);
+        FdoS4ToS3(Fdo);
+    }
+
+    __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  PXENVKBD_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 -> %s\n",
+         __FdoGetName(Fdo),
+         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  PXENVKBD_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  PXENVKBD_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  PXENVKBD_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  PXENVKBD_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  PXENVKBD_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  PXENVKBD_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  PXENVKBD_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  PXENVKBD_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  PXENVKBD_THREAD Self,
+    IN  PVOID           Context
+    )
+{
+    PXENVKBD_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  PXENVKBD_THREAD Self,
+    IN  PVOID           Context
+    )
+{
+    PXENVKBD_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  PXENVKBD_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  PXENVKBD_FDO    Fdo,
+    IN  PIRP            Irp
+    )
+{
+    NTSTATUS            status;
+
+    IoSkipCurrentIrpStackLocation(Irp);
+    status = IoCallDriver(Fdo->LowerDeviceObject, Irp);
+
+    return status;
+}
+
+NTSTATUS
+FdoDispatch(
+    IN  PXENVKBD_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;
+}
+
+__drv_requiresIRQL(PASSIVE_LEVEL)
+static NTSTATUS
+FdoQueryInterface(
+    IN  PXENVKBD_FDO    Fdo,
+    IN  const GUID      *Guid,
+    IN  ULONG           Version,
+    OUT PINTERFACE      Interface,
+    IN  ULONG           Size,
+    IN  BOOLEAN         Optional
+    )
+{
+    KEVENT              Event;
+    IO_STATUS_BLOCK     StatusBlock;
+    PIRP                Irp;
+    PIO_STACK_LOCATION  StackLocation;
+    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;
+    StackLocation->Parameters.QueryInterface.Size = (USHORT)Size;
+    StackLocation->Parameters.QueryInterface.Version = (USHORT)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)) {
+        if (status == STATUS_NOT_SUPPORTED && Optional)
+            goto done;
+
+        goto fail2;
+    }
+
+done:
+    return STATUS_SUCCESS;
+
+fail2:
+    Error("fail2\n");
+
+fail1:
+    Error("fail1 (%08x)\n", status);
+
+    return status;
+}
+
+#define FDO_QUERY_INTERFACE(                                                            \
+    _Fdo,                                                                               \
+    _ProviderName,                                                                      \
+    _InterfaceName,                                                                     \
+    _Interface,                                                                         \
+    _Size,                                                                              \
+    _Optional)                                                                          \
+    FdoQueryInterface((_Fdo),                                                           \
+                      &GUID_ ## _ProviderName ## _ ## _InterfaceName ## _INTERFACE,     \
+                      _ProviderName ## _ ## _InterfaceName ## _INTERFACE_VERSION_MAX,   \
+                      (_Interface),                                                     \
+                      (_Size),                                                          \
+                      (_Optional))
+
+#define DEFINE_FDO_GET_INTERFACE(_Interface, _Type)                     \
+VOID                                                                    \
+FdoGet ## _Interface ## Interface(                                      \
+    IN  PXENVKBD_FDO Fdo,                                                \
+    OUT _Type       _Interface ## Interface                             \
+    )                                                                   \
+{                                                                       \
+    * ## _Interface ## Interface = Fdo-> ## _Interface ## Interface;    \
+}
+
+DEFINE_FDO_GET_INTERFACE(Debug, PXENBUS_DEBUG_INTERFACE)
+DEFINE_FDO_GET_INTERFACE(Suspend, PXENBUS_SUSPEND_INTERFACE)
+DEFINE_FDO_GET_INTERFACE(Evtchn, PXENBUS_EVTCHN_INTERFACE)
+DEFINE_FDO_GET_INTERFACE(Store, PXENBUS_STORE_INTERFACE)
+DEFINE_FDO_GET_INTERFACE(RangeSet, PXENBUS_RANGE_SET_INTERFACE)
+DEFINE_FDO_GET_INTERFACE(Cache, PXENBUS_CACHE_INTERFACE)
+DEFINE_FDO_GET_INTERFACE(Gnttab, PXENBUS_GNTTAB_INTERFACE)
+DEFINE_FDO_GET_INTERFACE(Unplug, PXENBUS_UNPLUG_INTERFACE)
+
+NTSTATUS
+FdoCreate(
+    IN  PDEVICE_OBJECT      PhysicalDeviceObject
+    )
+{
+    PDEVICE_OBJECT          FunctionDeviceObject;
+    PXENVKBD_DX             Dx;
+    PXENVKBD_FDO            Fdo;
+    USHORT                  DeviceID;
+    NTSTATUS                status;
+
+#pragma prefast(suppress:28197) // Possibly leaking memory 'FunctionDeviceObject'
+    status = IoCreateDevice(DriverGetDriverObject(),
+                            sizeof (XENVKBD_DX),
+                            NULL,
+                            FILE_DEVICE_BUS_EXTENDER,
+                            FILE_DEVICE_SECURE_OPEN,
+                            FALSE,
+                            &FunctionDeviceObject);
+    if (!NT_SUCCESS(status))
+        goto fail1;
+
+    Dx = (PXENVKBD_DX)FunctionDeviceObject->DeviceExtension;
+    RtlZeroMemory(Dx, sizeof (XENVKBD_DX));
+
+    Dx->Type = FUNCTION_DEVICE_OBJECT;
+    Dx->DeviceObject = FunctionDeviceObject;
+    Dx->DevicePnpState = Added;
+    Dx->SystemPowerState = PowerSystemWorking;
+    Dx->DevicePowerState = PowerDeviceD3;
+
+    Fdo = __FdoAllocate(sizeof (XENVKBD_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 = __FdoAcquireLowerBusInterface(Fdo);
+    if (!NT_SUCCESS(status))
+        goto fail5;
+
+    if (FdoGetBusData(Fdo,
+                      PCI_WHICHSPACE_CONFIG,
+                      &DeviceID,
+                      FIELD_OFFSET(PCI_COMMON_HEADER, DeviceID),
+                      FIELD_SIZE(PCI_COMMON_HEADER, DeviceID)) == 0)
+        goto fail6;
+
+    __FdoSetVendorName(Fdo, DeviceID);
+
+    __FdoSetName(Fdo);
+
+    status = FDO_QUERY_INTERFACE(Fdo,
+                                 XENBUS,
+                                 DEBUG,
+                                 (PINTERFACE)&Fdo->DebugInterface,
+                                 sizeof (Fdo->DebugInterface),
+                                 FALSE);
+    if (!NT_SUCCESS(status))
+        goto fail7;
+
+    status = FDO_QUERY_INTERFACE(Fdo,
+                                 XENBUS,
+                                 SUSPEND,
+                                 (PINTERFACE)&Fdo->SuspendInterface,
+                                 sizeof (Fdo->SuspendInterface),
+                                 FALSE);
+    if (!NT_SUCCESS(status))
+        goto fail8;
+
+    status = FDO_QUERY_INTERFACE(Fdo,
+                                 XENBUS,
+                                 EVTCHN,
+                                 (PINTERFACE)&Fdo->EvtchnInterface,
+                                 sizeof (Fdo->EvtchnInterface),
+                                 FALSE);
+    if (!NT_SUCCESS(status))
+        goto fail9;
+
+    status = FDO_QUERY_INTERFACE(Fdo,
+                                 XENBUS,
+                                 STORE,
+                                 (PINTERFACE)&Fdo->StoreInterface,
+                                 sizeof (Fdo->StoreInterface),
+                                 FALSE);
+    if (!NT_SUCCESS(status))
+        goto fail10;
+
+    status = FDO_QUERY_INTERFACE(Fdo,
+                                 XENBUS,
+                                 RANGE_SET,
+                                 (PINTERFACE)&Fdo->RangeSetInterface,
+                                 sizeof (Fdo->RangeSetInterface),
+                                 FALSE);
+    if (!NT_SUCCESS(status))
+        goto fail11;
+
+    status = FDO_QUERY_INTERFACE(Fdo,
+                                 XENBUS,
+                                 CACHE,
+                                 (PINTERFACE)&Fdo->CacheInterface,
+                                 sizeof (Fdo->CacheInterface),
+                                 FALSE);
+    if (!NT_SUCCESS(status))
+        goto fail12;
+
+    status = FDO_QUERY_INTERFACE(Fdo,
+                                 XENBUS,
+                                 GNTTAB,
+                                 (PINTERFACE)&Fdo->GnttabInterface,
+                                 sizeof (Fdo->GnttabInterface),
+                                 FALSE);
+    if (!NT_SUCCESS(status))
+        goto fail13;
+
+    status = FDO_QUERY_INTERFACE(Fdo,
+                                 XENBUS,
+                                 UNPLUG,
+                                 (PINTERFACE)&Fdo->UnplugInterface,
+                                 sizeof (Fdo->UnplugInterface),
+                                 FALSE);
+    if (!NT_SUCCESS(status))
+        goto fail14;
+
+    Dx->Fdo = Fdo;
+
+    InitializeMutex(&Fdo->Mutex);
+    InitializeListHead(&Dx->ListEntry);
+    Fdo->References = 1;
+
+    Info("%p (%s)\n",
+         FunctionDeviceObject,
+         __FdoGetName(Fdo));
+
+    FunctionDeviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
+    return STATUS_SUCCESS;
+
+fail14:
+    Error("fail14\n");
+
+    RtlZeroMemory(&Fdo->UnplugInterface,
+                  sizeof (XENBUS_UNPLUG_INTERFACE));
+
+fail13:
+    Error("fail13\n");
+
+    RtlZeroMemory(&Fdo->CacheInterface,
+                  sizeof (XENBUS_CACHE_INTERFACE));
+
+fail12:
+    Error("fail12\n");
+
+    RtlZeroMemory(&Fdo->RangeSetInterface,
+                  sizeof (XENBUS_RANGE_SET_INTERFACE));
+
+fail11:
+    Error("fail11\n");
+
+    RtlZeroMemory(&Fdo->StoreInterface,
+                  sizeof (XENBUS_STORE_INTERFACE));
+
+fail10:
+    Error("fail10\n");
+
+    RtlZeroMemory(&Fdo->EvtchnInterface,
+                  sizeof (XENBUS_EVTCHN_INTERFACE));
+
+fail9:
+    Error("fail9\n");
+
+    RtlZeroMemory(&Fdo->SuspendInterface,
+                  sizeof (XENBUS_SUSPEND_INTERFACE));
+
+fail8:
+    Error("fail8\n");
+
+    RtlZeroMemory(&Fdo->DebugInterface,
+                  sizeof (XENBUS_DEBUG_INTERFACE));
+
+fail7:
+    Error("fail7\n");
+
+    RtlZeroMemory(Fdo->VendorName, MAXNAMELEN);
+
+fail6:
+    Error("fail6\n");
+
+    __FdoReleaseLowerBusInterface(Fdo);
+
+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 (XENVKBD_FDO)));
+    __FdoFree(Fdo);
+
+fail2:
+    Error("fail2\n");
+
+    IoDeleteDevice(FunctionDeviceObject);
+
+fail1:
+    Error("fail1 (%08x)\n", status);
+
+    return status;
+}
+
+VOID
+FdoDestroy(
+    IN  PXENVKBD_FDO    Fdo
+    )
+{
+    PXENVKBD_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));
+
+    RtlZeroMemory(&Fdo->Mutex, sizeof (MUTEX));
+
+    Dx->Fdo = NULL;
+
+    RtlZeroMemory(&Fdo->UnplugInterface,
+                  sizeof (XENBUS_UNPLUG_INTERFACE));
+
+    RtlZeroMemory(&Fdo->GnttabInterface,
+                  sizeof (XENBUS_GNTTAB_INTERFACE));
+
+    RtlZeroMemory(&Fdo->CacheInterface,
+                  sizeof (XENBUS_CACHE_INTERFACE));
+
+    RtlZeroMemory(&Fdo->RangeSetInterface,
+                  sizeof (XENBUS_RANGE_SET_INTERFACE));
+
+    RtlZeroMemory(&Fdo->StoreInterface,
+                  sizeof (XENBUS_STORE_INTERFACE));
+
+    RtlZeroMemory(&Fdo->EvtchnInterface,
+                  sizeof (XENBUS_EVTCHN_INTERFACE));
+
+    RtlZeroMemory(&Fdo->SuspendInterface,
+                  sizeof (XENBUS_SUSPEND_INTERFACE));
+
+    RtlZeroMemory(&Fdo->DebugInterface,
+                  sizeof (XENBUS_DEBUG_INTERFACE));
+
+    RtlZeroMemory(Fdo->VendorName, MAXNAMELEN);
+
+    __FdoReleaseLowerBusInterface(Fdo);
+
+    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 (XENVKBD_FDO)));
+    __FdoFree(Fdo);
+
+    IoDeleteDevice(FunctionDeviceObject);
+}
diff --git a/src/xenvkbd/fdo.h b/src/xenvkbd/fdo.h
new file mode 100644 (file)
index 0000000..677a9da
--- /dev/null
@@ -0,0 +1,157 @@
+/* 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 _XENVKBD_FDO_H
+#define _XENVKBD_FDO_H
+
+#include <ntddk.h>
+#include <debug_interface.h>
+#include <suspend_interface.h>
+#include <evtchn_interface.h>
+#include <store_interface.h>
+#include <range_set_interface.h>
+#include <cache_interface.h>
+#include <gnttab_interface.h>
+#include <unplug_interface.h>
+
+#include "driver.h"
+#include "types.h"
+
+extern PCHAR
+FdoGetVendorName(
+    IN  PXENVKBD_FDO    Fdo
+    );
+
+extern PCHAR
+FdoGetName(
+    IN  PXENVKBD_FDO    Fdo
+    );
+
+extern NTSTATUS
+FdoAddPhysicalDeviceObject(
+    IN  PXENVKBD_FDO    Fdo,
+    IN  PXENVKBD_PDO    Pdo
+    );
+
+extern VOID
+FdoRemovePhysicalDeviceObject(
+    IN  PXENVKBD_FDO    Fdo,
+    IN  PXENVKBD_PDO    Pdo
+    );
+
+extern VOID
+FdoAcquireMutex(
+    IN  PXENVKBD_FDO    Fdo
+    );
+
+extern VOID
+FdoReleaseMutex(
+    IN  PXENVKBD_FDO    Fdo
+    );
+
+extern PDEVICE_OBJECT
+FdoGetPhysicalDeviceObject(
+    IN  PXENVKBD_FDO    Fdo
+    );
+
+extern PDMA_ADAPTER
+FdoGetDmaAdapter(
+    IN  PXENVKBD_FDO        Fdo,
+    IN  PDEVICE_DESCRIPTION DeviceDescriptor,
+    OUT PULONG              NumberOfMapRegisters
+    );
+
+extern BOOLEAN
+FdoTranslateBusAddress(
+    IN      PXENVKBD_FDO        Fdo,
+    IN      PHYSICAL_ADDRESS    BusAddress,
+    IN      ULONG               Length,
+    IN OUT  PULONG              AddressSpace,
+    OUT     PPHYSICAL_ADDRESS   TranslatedAddress
+    );
+
+extern ULONG
+FdoSetBusData(
+    IN  PXENVKBD_FDO    Fdo,
+    IN  ULONG           DataType,
+    IN  PVOID           Buffer,
+    IN  ULONG           Offset,
+    IN  ULONG           Length
+    );
+
+extern ULONG
+FdoGetBusData(
+    IN  PXENVKBD_FDO    Fdo,
+    IN  ULONG           DataType,
+    IN  PVOID           Buffer,
+    IN  ULONG           Offset,
+    IN  ULONG           Length
+    );
+
+extern NTSTATUS
+FdoDelegateIrp(
+    IN  PXENVKBD_FDO    Fdo,
+    IN  PIRP            Irp
+    );
+
+extern NTSTATUS
+FdoDispatch(
+    IN  PXENVKBD_FDO    Fdo,
+    IN  PIRP            Irp
+    );
+
+#define DECLARE_FDO_GET_INTERFACE(_Interface, _Type)    \
+extern VOID                                             \
+FdoGet ## _Interface ## Interface(                      \
+    IN  PXENVKBD_FDO Fdo,                                \
+    OUT _Type       _Interface ## Interface             \
+    );
+
+DECLARE_FDO_GET_INTERFACE(Debug, PXENBUS_DEBUG_INTERFACE)
+DECLARE_FDO_GET_INTERFACE(Suspend, PXENBUS_SUSPEND_INTERFACE)
+DECLARE_FDO_GET_INTERFACE(Evtchn, PXENBUS_EVTCHN_INTERFACE)
+DECLARE_FDO_GET_INTERFACE(Store, PXENBUS_STORE_INTERFACE)
+DECLARE_FDO_GET_INTERFACE(RangeSet, PXENBUS_RANGE_SET_INTERFACE)
+DECLARE_FDO_GET_INTERFACE(Cache, PXENBUS_CACHE_INTERFACE)
+DECLARE_FDO_GET_INTERFACE(Gnttab, PXENBUS_GNTTAB_INTERFACE)
+DECLARE_FDO_GET_INTERFACE(Unplug, PXENBUS_UNPLUG_INTERFACE)
+
+extern NTSTATUS
+FdoCreate(
+    IN  PDEVICE_OBJECT  PhysicalDeviceObject
+    );
+
+extern VOID
+FdoDestroy(
+    IN  PXENVKBD_FDO    Fdo
+    );
+
+#endif  // _XENVKBD_FDO_H
diff --git a/src/xenvkbd/frontend.c b/src/xenvkbd/frontend.c
new file mode 100644 (file)
index 0000000..ea85cd2
--- /dev/null
@@ -0,0 +1,1249 @@
+/* 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 <procgrp.h>
+#include <ntstrsafe.h>
+#include <stdlib.h>
+#include <xen.h>
+
+#include "driver.h"
+#include "registry.h"
+#include "fdo.h"
+#include "pdo.h"
+#include "thread.h"
+#include "frontend.h"
+#include "names.h"
+#include "ring.h"
+#include "dbg_print.h"
+#include "assert.h"
+#include "util.h"
+
+#define DOMID_INVALID   (0x7FF4U)
+
+struct _XENVKBD_FRONTEND {
+    PXENVKBD_PDO                Pdo;
+    PCHAR                       Path;
+    XENVKBD_FRONTEND_STATE      State;
+    BOOLEAN                     Online;
+    KSPIN_LOCK                  Lock;
+    PXENVKBD_THREAD             EjectThread;
+    KEVENT                      EjectEvent;
+
+    PCHAR                       BackendPath;
+    USHORT                      BackendDomain;
+
+    PXENVKBD_RING               Ring;
+
+    XENBUS_SUSPEND_INTERFACE    SuspendInterface;
+    XENBUS_STORE_INTERFACE      StoreInterface;
+
+    PXENBUS_SUSPEND_CALLBACK    SuspendCallbackEarly;
+    PXENBUS_SUSPEND_CALLBACK    SuspendCallbackLate;
+    PXENBUS_STORE_WATCH         Watch;
+};
+
+static const PCHAR
+FrontendStateName(
+    IN  XENVKBD_FRONTEND_STATE  State
+    )
+{
+#define _STATE_NAME(_State)     \
+    case  FRONTEND_ ## _State:  \
+        return #_State;
+
+    switch (State) {
+    _STATE_NAME(UNKNOWN);
+    _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 __AllocatePoolWithTag(NonPagedPool, Length, FRONTEND_POOL);
+}
+
+static FORCEINLINE VOID
+__FrontendFree(
+    IN  PVOID   Buffer
+    )
+{
+    __FreePoolWithTag(Buffer, FRONTEND_POOL);
+}
+
+static FORCEINLINE PXENVKBD_PDO
+__FrontendGetPdo(
+    IN  PXENVKBD_FRONTEND   Frontend
+    )
+{
+    return Frontend->Pdo;
+}
+
+PXENVKBD_PDO
+FrontendGetPdo(
+    IN  PXENVKBD_FRONTEND   Frontend
+    )
+{
+    return __FrontendGetPdo(Frontend);
+}
+
+static FORCEINLINE PCHAR
+__FrontendGetPath(
+    IN  PXENVKBD_FRONTEND   Frontend
+    )
+{
+    return Frontend->Path;
+}
+
+PCHAR
+FrontendGetPath(
+    IN  PXENVKBD_FRONTEND   Frontend
+    )
+{
+    return __FrontendGetPath(Frontend);
+}
+
+static FORCEINLINE PCHAR
+__FrontendGetBackendPath(
+    IN  PXENVKBD_FRONTEND   Frontend
+    )
+{
+    return Frontend->BackendPath;
+}
+
+PCHAR
+FrontendGetBackendPath(
+    IN  PXENVKBD_FRONTEND   Frontend
+    )
+{
+    return __FrontendGetBackendPath(Frontend);
+}
+
+static FORCEINLINE USHORT
+__FrontendGetBackendDomain(
+    IN  PXENVKBD_FRONTEND   Frontend
+    )
+{
+    return Frontend->BackendDomain;
+}
+
+USHORT
+FrontendGetBackendDomain(
+    IN  PXENVKBD_FRONTEND   Frontend
+    )
+{
+    return __FrontendGetBackendDomain(Frontend);
+}
+
+#define DEFINE_FRONTEND_GET_FUNCTION(_Function, _Type)  \
+static FORCEINLINE _Type                                \
+__FrontendGet ## _Function(                             \
+    IN  PXENVKBD_FRONTEND   Frontend                    \
+    )                                                   \
+{                                                       \
+    return Frontend-> ## _Function;                     \
+}                                                       \
+                                                        \
+_Type                                                   \
+FrontendGet ## _Function(                               \
+    IN  PXENVKBD_FRONTEND   Frontend                    \
+    )                                                   \
+{                                                       \
+    return __FrontendGet ## _Function ## (Frontend);    \
+}
+
+DEFINE_FRONTEND_GET_FUNCTION(Ring, PXENVKBD_RING)
+
+static BOOLEAN
+FrontendIsOnline(
+    IN  PXENVKBD_FRONTEND   Frontend
+    )
+{
+    return Frontend->Online;
+}
+
+static BOOLEAN
+FrontendIsBackendOnline(
+    IN  PXENVKBD_FRONTEND   Frontend
+    )
+{
+    PCHAR                   Buffer;
+    BOOLEAN                 Online;
+    NTSTATUS                status;
+
+    status = XENBUS_STORE(Read,
+                          &Frontend->StoreInterface,
+                          NULL,
+                          __FrontendGetBackendPath(Frontend),
+                          "online",
+                          &Buffer);
+    if (!NT_SUCCESS(status)) {
+        Online = FALSE;
+    } else {
+        Online = (BOOLEAN)strtol(Buffer, NULL, 2);
+
+        XENBUS_STORE(Free,
+                     &Frontend->StoreInterface,
+                     Buffer);
+    }
+
+    return Online;
+}
+
+static DECLSPEC_NOINLINE NTSTATUS
+FrontendEject(
+    IN  PXENVKBD_THREAD Self,
+    IN  PVOID           Context
+    )
+{
+    PXENVKBD_FRONTEND    Frontend = Context;
+    PKEVENT             Event;
+
+    Trace("%s: ====>\n", __FrontendGetPath(Frontend));
+
+    Event = ThreadGetEvent(Self);
+
+    for (;;) {
+        KIRQL       Irql;
+
+        KeWaitForSingleObject(Event,
+                              Executive,
+                              KernelMode,
+                              FALSE,
+                              NULL);
+        KeClearEvent(Event);
+
+        if (ThreadIsAlerted(Self))
+            break;
+
+        KeAcquireSpinLock(&Frontend->Lock, &Irql);
+
+        // It is not safe to use interfaces before this point
+        if (Frontend->State == FRONTEND_UNKNOWN ||
+            Frontend->State == FRONTEND_CLOSED)
+            goto loop;
+
+        if (!FrontendIsOnline(Frontend))
+            goto loop;
+
+        if (!FrontendIsBackendOnline(Frontend))
+            PdoRequestEject(__FrontendGetPdo(Frontend));
+
+loop:
+        KeReleaseSpinLock(&Frontend->Lock, Irql);
+
+        KeSetEvent(&Frontend->EjectEvent, IO_NO_INCREMENT, FALSE);
+    }
+
+    KeSetEvent(&Frontend->EjectEvent, IO_NO_INCREMENT, FALSE);
+
+    Trace("%s: <====\n", __FrontendGetPath(Frontend));
+
+    return STATUS_SUCCESS;
+}
+
+VOID
+FrontendEjectFailed(
+    IN PXENVKBD_FRONTEND    Frontend
+    )
+{
+    KIRQL                   Irql;
+    ULONG                   Length;
+    PCHAR                   Path;
+    NTSTATUS                status;
+
+    KeAcquireSpinLock(&Frontend->Lock, &Irql);
+
+    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) XENBUS_STORE(Printf,
+                        &Frontend->StoreInterface,
+                        NULL,
+                        Path,
+                        "error",
+                        "UNPLUG FAILED: device is still in use");
+
+    __FrontendFree(Path);
+
+    KeReleaseSpinLock(&Frontend->Lock, Irql);
+    return;
+
+fail2:
+    Error("fail2\n");
+
+    __FrontendFree(Path);
+
+fail1:
+    Error("fail1 (%08x)\n", status);
+
+    KeReleaseSpinLock(&Frontend->Lock, Irql);
+}
+
+static VOID
+FrontendSetOnline(
+    IN  PXENVKBD_FRONTEND   Frontend
+    )
+{
+    Trace("====>\n");
+
+    Frontend->Online = TRUE;
+
+    Trace("<====\n");
+}
+
+static VOID
+FrontendSetOffline(
+    IN  PXENVKBD_FRONTEND   Frontend
+    )
+{
+    Trace("====>\n");
+
+    Frontend->Online = FALSE;
+    PdoRequestEject(__FrontendGetPdo(Frontend));
+
+    Trace("<====\n");
+}
+
+static VOID
+FrontendSetXenbusState(
+    IN  PXENVKBD_FRONTEND   Frontend,
+    IN  XenbusState         State
+    )
+{
+    BOOLEAN                 Online;
+
+    Trace("%s: ====> %s\n",
+          __FrontendGetPath(Frontend),
+          XenbusStateName(State));
+
+    ASSERT(FrontendIsOnline(Frontend));
+
+    Online = FrontendIsBackendOnline(Frontend);
+
+    (VOID) XENBUS_STORE(Printf,
+                        &Frontend->StoreInterface,
+                        NULL,
+                        __FrontendGetPath(Frontend),
+                        "state",
+                        "%u",
+                        State);
+
+    if (State == XenbusStateClosed && !Online)
+        FrontendSetOffline(Frontend);
+
+    Trace("%s: <==== %s\n",
+          __FrontendGetPath(Frontend),
+          XenbusStateName(State));
+}
+
+static VOID
+FrontendWaitForBackendXenbusStateChange(
+    IN      PXENVKBD_FRONTEND   Frontend,
+    IN OUT  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",
+          __FrontendGetBackendPath(Frontend),
+          XenbusStateName(*State));
+
+    ASSERT(FrontendIsOnline(Frontend));
+
+    KeInitializeEvent(&Event, NotificationEvent, FALSE);
+
+    status = XENBUS_STORE(WatchAdd,
+                          &Frontend->StoreInterface,
+                          __FrontendGetBackendPath(Frontend),
+                          "state",
+                          &Event,
+                          &Watch);
+    if (!NT_SUCCESS(status))
+        Watch = NULL;
+
+    KeQuerySystemTime(&Start);
+    TimeDelta = 0;
+
+    Timeout.QuadPart = 0;
+
+    while (*State == Old && TimeDelta < 120000) {
+        PCHAR           Buffer;
+        LARGE_INTEGER   Now;
+
+        if (Watch != NULL) {
+            ULONG   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.
+                XENBUS_STORE(Poll,
+                             &Frontend->StoreInterface);
+
+                KeStallExecutionProcessor(1000);   // 1ms
+            }
+
+            KeClearEvent(&Event);
+        }
+
+        status = XENBUS_STORE(Read,
+                              &Frontend->StoreInterface,
+                              NULL,
+                              __FrontendGetBackendPath(Frontend),
+                              "state",
+                              &Buffer);
+        if (!NT_SUCCESS(status)) {
+            *State = XenbusStateUnknown;
+        } else {
+            *State = (XenbusState)strtol(Buffer, NULL, 10);
+
+            XENBUS_STORE(Free,
+                         &Frontend->StoreInterface,
+                         Buffer);
+        }
+
+        KeQuerySystemTime(&Now);
+
+        TimeDelta = (Now.QuadPart - Start.QuadPart) / 10000ull;
+    }
+
+    if (Watch != NULL)
+        (VOID) XENBUS_STORE(WatchRemove,
+                            &Frontend->StoreInterface,
+                            Watch);
+
+    Trace("%s: <==== (%s)\n",
+          __FrontendGetBackendPath(Frontend),
+          XenbusStateName(*State));
+}
+
+static NTSTATUS
+FrontendUpdatePath(
+    IN  PXENVKBD_FRONTEND   Frontend
+    )
+{
+    ULONG                   Length;
+    PCHAR                   Buffer;
+    NTSTATUS                status;
+
+    status = XENBUS_STORE(Read,
+                          &Frontend->StoreInterface,
+                          NULL,
+                          Frontend->Path,
+                          "backend-id",
+                          &Buffer);
+    if (NT_SUCCESS(status)) {
+        Frontend->BackendDomain = (USHORT)strtoul(Buffer, NULL, 10);
+
+        XENBUS_STORE(Free,
+                     &Frontend->StoreInterface,
+                     Buffer);
+    } else {
+        Frontend->BackendDomain = 0;
+    }
+
+    status = XENBUS_STORE(Read,
+                          &Frontend->StoreInterface,
+                          NULL,
+                          Frontend->Path,
+                          "backend",
+                          &Buffer);
+    if (!NT_SUCCESS(status))
+        goto fail1;
+
+    Length = (ULONG)strlen(Buffer);
+
+    status = STATUS_NO_MEMORY;
+    if (Frontend->BackendPath)
+        __FrontendFree(Frontend->BackendPath);
+
+    Frontend->BackendPath = __FrontendAllocate((Length + 1) * sizeof(CHAR));
+    if (Frontend->BackendPath == NULL)
+        goto fail2;
+
+    RtlCopyMemory(Frontend->BackendPath,
+                  Buffer,
+                  Length * sizeof(CHAR));
+
+    XENBUS_STORE(Free,
+                 &Frontend->StoreInterface,
+                 Buffer);
+
+    return STATUS_SUCCESS;
+
+fail2:
+    Error("fail2\n");
+
+    XENBUS_STORE(Free,
+                 &Frontend->StoreInterface,
+                 Buffer);
+
+fail1:
+    Error("fail1 %08x\n", status);
+    return status;
+}
+
+static NTSTATUS
+FrontendClose(
+    IN  PXENVKBD_FRONTEND   Frontend
+    )
+{
+    XenbusState             State;
+    NTSTATUS                status;
+
+    Trace("=====>\n");
+    if (Frontend->Watch)
+        XENBUS_STORE(WatchRemove,
+                     &Frontend->StoreInterface,
+                     Frontend->Watch);
+    Frontend->Watch = NULL;
+
+    status = FrontendUpdatePath(Frontend);
+    if (!NT_SUCCESS(status))
+        goto fail1;
+
+    State = XenbusStateUnknown;
+    do {
+        FrontendWaitForBackendXenbusStateChange(Frontend,
+                                                &State);
+        if (State == XenbusStateUnknown)
+            goto fail2;
+    } while (State == XenbusStateInitialising);
+
+    FrontendSetXenbusState(Frontend, XenbusStateClosing);
+
+    status = STATUS_UNSUCCESSFUL;
+    do {
+        FrontendWaitForBackendXenbusStateChange(Frontend,
+                                                &State);
+        if (State == XenbusStateUnknown)
+            goto fail3;
+    } while (State != XenbusStateClosing &&
+             State != XenbusStateClosed);
+
+    FrontendSetXenbusState(Frontend, XenbusStateClosed);
+
+    do {
+        FrontendWaitForBackendXenbusStateChange(Frontend,
+                                                &State);
+        if (State == XenbusStateUnknown)
+            goto fail4;
+    } while (State != XenbusStateClosed);
+
+    __FrontendFree(Frontend->BackendPath);
+    Frontend->BackendPath = NULL;
+    Frontend->BackendDomain = DOMID_INVALID;
+
+    Trace("<=====\n");
+    return STATUS_SUCCESS;
+
+fail4:
+    Error("fail4\n");
+fail3:
+    Error("fail3\n");
+fail2:
+    Error("fail2\n");
+fail1:
+    Error("fail1 %08x\n", status);
+    return status;
+}
+
+static NTSTATUS
+FrontendPrepare(
+    IN  PXENVKBD_FRONTEND   Frontend
+    )
+{
+    XenbusState             State;
+    NTSTATUS                status;
+
+    Trace("=====>\n");
+
+    status = FrontendUpdatePath(Frontend);
+    if (!NT_SUCCESS(status))
+        goto fail1;
+
+    status = XENBUS_STORE(WatchAdd,
+                          &Frontend->StoreInterface,
+                          NULL,
+                          Frontend->BackendPath,
+                          ThreadGetEvent(Frontend->EjectThread),
+                          &Frontend->Watch);
+    if (!NT_SUCCESS(status))
+        goto fail2;
+
+    FrontendSetXenbusState(Frontend, XenbusStateInitialising);
+
+    status = STATUS_UNSUCCESSFUL;
+    do {
+        FrontendWaitForBackendXenbusStateChange(Frontend,
+                                                &State);
+        if (State == XenbusStateUnknown)
+            goto fail3;
+    } while (State != XenbusStateInitWait);
+
+    Trace("<=====\n");
+    return STATUS_SUCCESS;
+
+fail3:
+    Error("fail3\n");
+    XENBUS_STORE(WatchRemove,
+                 &Frontend->StoreInterface,
+                 Frontend->Watch);
+    Frontend->Watch = NULL;
+fail2:
+    Error("fail2\n");
+fail1:
+    Error("fail1 %08x\n", status);
+    return status;
+}
+
+static NTSTATUS
+FrontendConnect(
+    IN  PXENVKBD_FRONTEND   Frontend
+    )
+{
+    XenbusState             State;
+    NTSTATUS                status;
+
+    Trace("=====>\n");
+
+    status = RingConnect(Frontend->Ring);
+    if (!NT_SUCCESS(status))
+        goto fail1;
+
+    for (;;) {
+        PXENBUS_STORE_TRANSACTION   Transaction;
+
+        status = XENBUS_STORE(TransactionStart,
+                              &Frontend->StoreInterface,
+                              &Transaction);
+        if (!NT_SUCCESS(status))
+            break;
+
+        status = RingStoreWrite(Frontend->Ring,
+                                Transaction);
+        if (!NT_SUCCESS(status))
+            goto abort;
+
+        status = XENBUS_STORE(TransactionEnd,
+                              &Frontend->StoreInterface,
+                              Transaction,
+                              TRUE);
+        if (status == STATUS_RETRY)
+            continue;
+        break;
+
+abort:
+        (VOID) XENBUS_STORE(TransactionEnd,
+                            &Frontend->StoreInterface,
+                            Transaction,
+                            FALSE);
+        break;
+    }
+    if (!NT_SUCCESS(status))
+        goto fail2;
+
+    FrontendSetXenbusState(Frontend, XenbusStateInitialised);
+
+    status = STATUS_UNSUCCESSFUL;
+    do {
+        FrontendWaitForBackendXenbusStateChange(Frontend,
+                                                &State);
+        if (State == XenbusStateUnknown)
+            goto fail3;
+    } while (State == XenbusStateInitWait ||
+             State == XenbusStateInitialising ||
+             State == XenbusStateInitialised);
+
+    if (State != XenbusStateConnected)
+        goto fail4;
+
+    FrontendSetXenbusState(Frontend, XenbusStateConnected);
+
+    Trace("<=====\n");
+    return STATUS_SUCCESS;
+
+fail4:
+    Error("fail4\n");
+fail3:
+    Error("fail3\n");
+fail2:
+    Error("fail2\n");
+fail1:
+    Error("fail1 %08x\n", status);
+    return status;
+}
+
+static VOID
+FrontendDisconnect(
+    IN  PXENVKBD_FRONTEND   Frontend
+    )
+{
+    Trace("====>\n");
+
+    RingDisconnect(__FrontendGetRing(Frontend));
+
+    Trace("<====\n");
+}
+
+static NTSTATUS
+FrontendEnable(
+    IN  PXENVKBD_FRONTEND   Frontend
+    )
+{
+    NTSTATUS                status;
+
+    Trace("====>\n");
+
+    status = RingEnable(__FrontendGetRing(Frontend));
+    if (!NT_SUCCESS(status))
+        goto fail1;
+
+    Trace("<====\n");
+    return STATUS_SUCCESS;
+
+fail1:
+    Error("fail1 (%08x)\n", status);
+
+    return status;
+}
+
+static VOID
+FrontendDisable(
+    IN  PXENVKBD_FRONTEND    Frontend
+    )
+{
+    Trace("====>\n");
+
+    RingDisable(__FrontendGetRing(Frontend));
+
+    Trace("<====\n");
+}
+
+NTSTATUS
+FrontendSetState(
+    IN  PXENVKBD_FRONTEND       Frontend,
+    IN  XENVKBD_FRONTEND_STATE  State
+    )
+{
+    BOOLEAN                     Failed;
+    KIRQL                       Irql;
+
+    KeAcquireSpinLock(&Frontend->Lock, &Irql);
+
+    Info("%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_UNKNOWN:
+            switch (State) {
+            case FRONTEND_CLOSING:
+            case FRONTEND_CLOSED:
+            case FRONTEND_PREPARED:
+            case FRONTEND_CONNECTED:
+            case FRONTEND_ENABLED:
+                status = FrontendClose(Frontend);
+                if (NT_SUCCESS(status)) {
+                    Frontend->State = FRONTEND_CLOSED;
+                } else {
+                    Failed = TRUE;
+                }
+                break;
+
+            default:
+                ASSERT(FALSE);
+                break;
+            }
+            break;
+
+        case FRONTEND_CLOSING:
+            switch (State) {
+            case FRONTEND_UNKNOWN:
+            case FRONTEND_CLOSED:
+            case FRONTEND_PREPARED:
+            case FRONTEND_CONNECTED:
+            case FRONTEND_ENABLED:
+                FrontendDisconnect(Frontend);
+                Frontend->State = FRONTEND_CLOSED;
+                break;
+            default:
+                ASSERT(FALSE);
+                break;
+            }
+            break;
+
+        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 {
+                    FrontendClose(Frontend);
+                    Frontend->State = FRONTEND_CLOSED;
+                }
+                break;
+            default:
+                ASSERT(FALSE);
+                break;
+            }
+            break;
+
+        case FRONTEND_PREPARED:
+            switch (State) {
+            case FRONTEND_CLOSING:
+            case FRONTEND_CLOSED:
+                status = FrontendClose(Frontend);
+                if (NT_SUCCESS(status))
+                    Frontend->State = FRONTEND_CLOSED;
+                break;
+            case FRONTEND_CONNECTED:
+            case FRONTEND_ENABLED:
+                status = FrontendConnect(Frontend);
+                if (NT_SUCCESS(status)) {
+                    Frontend->State = FRONTEND_CONNECTED;
+                } else {
+                    FrontendClose(Frontend);
+                    Frontend->State = FRONTEND_CLOSED;
+                }
+                break;
+            default:
+                ASSERT(FALSE);
+                break;
+            }
+            break;
+
+        case FRONTEND_CONNECTED:
+            switch (State) {
+            case FRONTEND_ENABLED:
+                FrontendEnable(Frontend);
+                Frontend->State = FRONTEND_ENABLED;
+                break;
+            case FRONTEND_CLOSING:
+            case FRONTEND_CLOSED:
+                FrontendClose(Frontend);
+                Frontend->State = FRONTEND_CLOSING;
+                break;
+            default:
+                ASSERT(FALSE);
+                break;
+            }
+            break;
+
+        case FRONTEND_ENABLED:
+            switch (State) {
+            case FRONTEND_CLOSING:
+            case FRONTEND_CLOSED:
+            case FRONTEND_CONNECTED:
+                FrontendDisable(Frontend);
+                Frontend->State = FRONTEND_CONNECTED;
+                break;
+            default:
+                ASSERT(FALSE);
+                break;
+            }
+            break;
+
+        default:
+            ASSERT(FALSE);
+            break;
+        }
+
+        Info("%s in state '%s'\n",
+             __FrontendGetPath(Frontend),
+             FrontendStateName(Frontend->State));
+    }
+
+    KeReleaseSpinLock(&Frontend->Lock, Irql);
+
+    Info("%s: <=====\n", __FrontendGetPath(Frontend));
+
+    return (!Failed) ? STATUS_SUCCESS : STATUS_UNSUCCESSFUL;
+}
+
+static FORCEINLINE VOID
+__FrontendResume(
+    IN  PXENVKBD_FRONTEND   Frontend
+    )
+{
+    ASSERT3U(KeGetCurrentIrql(), ==, DISPATCH_LEVEL);
+
+    ASSERT3U(Frontend->State, ==, FRONTEND_UNKNOWN);
+    (VOID) FrontendSetState(Frontend, FRONTEND_CLOSED);
+}
+
+static FORCEINLINE VOID
+__FrontendSuspend(
+    IN  PXENVKBD_FRONTEND   Frontend
+    )
+{
+    ASSERT3U(KeGetCurrentIrql(), ==, DISPATCH_LEVEL);
+
+    (VOID) FrontendSetState(Frontend, FRONTEND_UNKNOWN);
+}
+
+static DECLSPEC_NOINLINE VOID
+FrontendSuspendCallbackEarly(
+    IN  PVOID           Argument
+    )
+{
+    PXENVKBD_FRONTEND   Frontend = Argument;
+
+    Frontend->Online = FALSE;
+}
+
+static DECLSPEC_NOINLINE VOID
+FrontendSuspendCallbackLate(
+    IN  PVOID           Argument
+    )
+{
+    PXENVKBD_FRONTEND   Frontend = Argument;
+
+    __FrontendSuspend(Frontend);
+    __FrontendResume(Frontend);
+}
+
+NTSTATUS
+FrontendResume(
+    IN  PXENVKBD_FRONTEND   Frontend
+    )
+{
+    KIRQL                   Irql;
+    NTSTATUS                status;
+
+    Trace("====>\n");
+
+    KeRaiseIrql(DISPATCH_LEVEL, &Irql);
+
+    status = XENBUS_SUSPEND(Acquire, &Frontend->SuspendInterface);
+    if (!NT_SUCCESS(status))
+        goto fail1;
+
+    __FrontendResume(Frontend);
+
+    status = XENBUS_SUSPEND(Register,
+                            &Frontend->SuspendInterface,
+                            SUSPEND_CALLBACK_EARLY,
+                            FrontendSuspendCallbackEarly,
+                            Frontend,
+                            &Frontend->SuspendCallbackEarly);
+    if (!NT_SUCCESS(status))
+        goto fail2;
+
+    status = XENBUS_SUSPEND(Register,
+                            &Frontend->SuspendInterface,
+                            SUSPEND_CALLBACK_LATE,
+                            FrontendSuspendCallbackLate,
+                            Frontend,
+                            &Frontend->SuspendCallbackLate);
+    if (!NT_SUCCESS(status))
+        goto fail3;
+
+    KeLowerIrql(Irql);
+
+    KeClearEvent(&Frontend->EjectEvent);
+    ThreadWake(Frontend->EjectThread);
+
+    Trace("waiting for eject thread\n");
+
+    (VOID) KeWaitForSingleObject(&Frontend->EjectEvent,
+                                 Executive,
+                                 KernelMode,
+                                 FALSE,
+                                 NULL);
+
+    Trace("<====\n");
+
+    return STATUS_SUCCESS;
+    
+fail3:
+    Error("fail3\n");
+
+    XENBUS_SUSPEND(Deregister,
+                   &Frontend->SuspendInterface,
+                   Frontend->SuspendCallbackEarly);
+    Frontend->SuspendCallbackEarly = NULL;
+
+fail2:
+    Error("fail2\n");
+
+    __FrontendSuspend(Frontend);
+
+    XENBUS_SUSPEND(Release, &Frontend->SuspendInterface);
+
+fail1:
+    Error("fail1 (%08x)\n", status);
+
+    KeLowerIrql(Irql);
+
+    return status;
+}
+
+VOID
+FrontendSuspend(
+    IN  PXENVKBD_FRONTEND   Frontend
+    )
+{
+    KIRQL                   Irql;
+
+    Trace("====>\n");
+
+    KeRaiseIrql(DISPATCH_LEVEL, &Irql);
+
+    XENBUS_SUSPEND(Deregister,
+                   &Frontend->SuspendInterface,
+                   Frontend->SuspendCallbackLate);
+    Frontend->SuspendCallbackLate = NULL;
+
+    XENBUS_SUSPEND(Deregister,
+                   &Frontend->SuspendInterface,
+                   Frontend->SuspendCallbackEarly);
+    Frontend->SuspendCallbackEarly = NULL;
+
+    __FrontendSuspend(Frontend);
+
+    XENBUS_SUSPEND(Release, &Frontend->SuspendInterface);
+
+    KeLowerIrql(Irql);
+
+    KeClearEvent(&Frontend->EjectEvent);
+    ThreadWake(Frontend->EjectThread);
+
+    Trace("waiting for eject thread\n");
+
+    (VOID) KeWaitForSingleObject(&Frontend->EjectEvent,
+                                 Executive,
+                                 KernelMode,
+                                 FALSE,
+                                 NULL);
+
+    Trace("<====\n");
+}
+
+__drv_requiresIRQL(PASSIVE_LEVEL)
+NTSTATUS
+FrontendInitialize(
+    IN  PXENVKBD_PDO        Pdo,
+    OUT PXENVKBD_FRONTEND   *Frontend
+    )
+{
+    PCHAR                   Name;
+    ULONG                   Length;
+    PCHAR                   Path;
+    NTSTATUS                status;
+
+    Trace("====>\n");
+
+    ASSERT3U(KeGetCurrentIrql(), ==, PASSIVE_LEVEL);
+
+    Name = PdoGetName(Pdo);
+
+    Length = sizeof ("devices/vkbd/") + (ULONG)strlen(Name);
+    Path = __FrontendAllocate(Length);
+
+    status = STATUS_NO_MEMORY;
+    if (Path == NULL)
+        goto fail1;
+
+    status = RtlStringCbPrintfA(Path, 
+                                Length,
+                                "device/vkbd/%s", 
+                                Name);
+    if (!NT_SUCCESS(status))
+        goto fail2;
+
+    *Frontend = __FrontendAllocate(sizeof (XENVKBD_FRONTEND));
+
+    status = STATUS_NO_MEMORY;
+    if (*Frontend == NULL)
+        goto fail3;
+
+    (*Frontend)->Pdo = Pdo;
+    (*Frontend)->Path = Path;
+    (*Frontend)->BackendDomain = DOMID_INVALID;
+
+    KeInitializeSpinLock(&(*Frontend)->Lock);
+
+    (*Frontend)->Online = TRUE;
+
+    FdoGetSuspendInterface(PdoGetFdo(Pdo), &(*Frontend)->SuspendInterface);
+    FdoGetStoreInterface(PdoGetFdo(Pdo), &(*Frontend)->StoreInterface);
+
+    status = RingInitialize(*Frontend, &(*Frontend)->Ring);
+    if (!NT_SUCCESS(status))
+        goto fail4;
+
+    KeInitializeEvent(&(*Frontend)->EjectEvent, NotificationEvent, FALSE);
+
+    status = ThreadCreate(FrontendEject, *Frontend, &(*Frontend)->EjectThread);
+    if (!NT_SUCCESS(status))
+        goto fail5;
+
+    Trace("<====\n");
+
+    return STATUS_SUCCESS;
+
+fail5:
+    Error("fail5\n");
+
+    RtlZeroMemory(&(*Frontend)->EjectEvent, sizeof (KEVENT));
+
+    RingTeardown(__FrontendGetRing(*Frontend));
+    (*Frontend)->Ring = NULL;
+
+fail4:
+    Error("fail4\n");
+
+    RtlZeroMemory(&(*Frontend)->StoreInterface,
+                  sizeof (XENBUS_STORE_INTERFACE));
+
+    RtlZeroMemory(&(*Frontend)->SuspendInterface,
+                  sizeof (XENBUS_SUSPEND_INTERFACE));
+
+    (*Frontend)->Online = FALSE;
+
+    RtlZeroMemory(&(*Frontend)->Lock, sizeof (KSPIN_LOCK));
+
+    (*Frontend)->BackendDomain = 0;
+    (*Frontend)->Path = NULL;
+    (*Frontend)->Pdo = NULL;
+
+    ASSERT(IsZeroMemory(*Frontend, sizeof (XENVKBD_FRONTEND)));
+
+    __FrontendFree(*Frontend);
+    *Frontend = NULL;
+
+fail3:
+    Error("fail3\n");
+
+fail2:
+    Error("fail2\n");
+
+    __FrontendFree(Path);
+
+fail1:
+    Error("fail1 (%08x)\n", status);
+
+    return status;
+}
+
+VOID
+FrontendTeardown(
+    IN  PXENVKBD_FRONTEND   Frontend
+    )
+{
+    Trace("====>\n");
+
+    ASSERT3U(KeGetCurrentIrql(), ==, PASSIVE_LEVEL);
+
+    ASSERT(Frontend->State == FRONTEND_UNKNOWN);
+
+    ThreadAlert(Frontend->EjectThread);
+    ThreadJoin(Frontend->EjectThread);
+    Frontend->EjectThread = NULL;
+
+    RtlZeroMemory(&Frontend->EjectEvent, sizeof (KEVENT));
+
+    RingTeardown(__FrontendGetRing(Frontend));
+    Frontend->Ring = NULL;
+
+    RtlZeroMemory(&Frontend->StoreInterface,
+                  sizeof (XENBUS_STORE_INTERFACE));
+
+    RtlZeroMemory(&Frontend->SuspendInterface,
+                  sizeof (XENBUS_SUSPEND_INTERFACE));
+
+    Frontend->Online = FALSE;
+
+    RtlZeroMemory(&Frontend->Lock, sizeof (KSPIN_LOCK));
+
+    Frontend->BackendDomain = 0;
+
+    __FrontendFree(Frontend->Path);
+    Frontend->Path = NULL;
+
+    Frontend->Pdo = NULL;
+
+    ASSERT(IsZeroMemory(Frontend, sizeof (XENVKBD_FRONTEND)));
+
+    __FrontendFree(Frontend);
+
+    Trace("<====\n");
+}
diff --git a/src/xenvkbd/frontend.h b/src/xenvkbd/frontend.h
new file mode 100644 (file)
index 0000000..f4e338f
--- /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 _XENVKBD_FRONTEND_H
+#define _XENVKBD_FRONTEND_H
+
+#include <ntddk.h>
+#include <debug_interface.h>
+#include <suspend_interface.h>
+#include <evtchn_interface.h>
+#include <store_interface.h>
+#include <range_set_interface.h>
+#include <cache_interface.h>
+#include <gnttab_interface.h>
+#include <hid_interface.h>
+
+#include "pdo.h"
+
+typedef struct _XENVKBD_FRONTEND XENVKBD_FRONTEND, *PXENVKBD_FRONTEND;
+
+typedef enum _XENVKBD_FRONTEND_STATE {
+    FRONTEND_UNKNOWN,
+    FRONTEND_CLOSING,
+    FRONTEND_CLOSED,
+    FRONTEND_PREPARED,
+    FRONTEND_CONNECTED,
+    FRONTEND_ENABLED
+} XENVKBD_FRONTEND_STATE, *PXENVKBD_FRONTEND_STATE;
+
+__drv_requiresIRQL(PASSIVE_LEVEL)
+extern NTSTATUS
+FrontendInitialize(
+    IN  PXENVKBD_PDO        Pdo,
+    OUT PXENVKBD_FRONTEND   *Frontend
+    );
+
+extern VOID
+FrontendTeardown(
+    IN  PXENVKBD_FRONTEND   Frontend
+    );
+
+extern VOID
+FrontendEjectFailed(
+    IN PXENVKBD_FRONTEND    Frontend
+    );
+
+extern NTSTATUS
+FrontendSetState(
+    IN  PXENVKBD_FRONTEND       Frontend,
+    IN  XENVKBD_FRONTEND_STATE  State
+    );
+
+extern NTSTATUS
+FrontendResume(
+    IN  PXENVKBD_FRONTEND   Frontend
+    );
+
+extern VOID
+FrontendSuspend(
+    IN  PXENVKBD_FRONTEND   Frontend
+    );
+
+extern PXENVKBD_PDO
+FrontendGetPdo(
+    IN  PXENVKBD_FRONTEND   Frontend
+    );
+
+extern PCHAR
+FrontendGetPath(
+    IN  PXENVKBD_FRONTEND   Frontend
+    );
+
+extern PCHAR
+FrontendGetBackendPath(
+    IN  PXENVKBD_FRONTEND   Frontend
+    );
+
+extern USHORT
+FrontendGetBackendDomain(
+    IN  PXENVKBD_FRONTEND   Frontend
+    );
+
+#include "ring.h"
+
+extern PXENVKBD_RING
+FrontendGetRing(
+    IN  PXENVKBD_FRONTEND   Frontend
+    );
+
+#endif  // _XENVKBD_FRONTEND_H
diff --git a/src/xenvkbd/hid.c b/src/xenvkbd/hid.c
new file mode 100644 (file)
index 0000000..6db6613
--- /dev/null
@@ -0,0 +1,981 @@
+/* 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 <stdlib.h>
+#include <xen.h>
+
+#include <debug_interface.h>
+#include <suspend_interface.h>
+
+#include "pdo.h"
+#include "hid.h"
+#include "mrsw.h"
+#include "thread.h"
+#include "vkbd.h"
+#include "dbg_print.h"
+#include "assert.h"
+#include "util.h"
+
+struct _XENVKBD_HID_CONTEXT {
+    PXENVKBD_PDO                Pdo;
+    XENVKBD_MRSW_LOCK           Lock;
+    LONG                        References;
+    PXENVKBD_FRONTEND           Frontend;
+    BOOLEAN                     Enabled;
+    ULONG                       Version;
+    XENHID_HID_CALLBACK         Callback;
+    PVOID                       Argument;
+    XENBUS_DEBUG_INTERFACE      DebugInterface;
+    XENBUS_SUSPEND_INTERFACE    SuspendInterface;
+    PXENBUS_DEBUG_CALLBACK      DebugCallback;
+    PXENBUS_SUSPEND_CALLBACK    SuspendCallbackLate;
+
+    XENVKBD_HID_KEYBOARD        KeyboardReport;
+    XENVKBD_HID_ABSMOUSE        AbsMouseReport;
+    BOOLEAN                     KeyboardPending;
+    BOOLEAN                     AbsMousePending;
+};
+
+#define XENVKBD_VKBD_TAG  'FIV'
+
+static FORCEINLINE NTSTATUS
+HidCopyBuffer(
+    IN  PVOID       Buffer,
+    IN  ULONG       Length,
+    IN  const VOID  *Source,
+    IN  ULONG       SourceLength,
+    OUT PULONG      Returned
+    )
+{
+    if (Buffer == NULL)
+        return STATUS_INVALID_PARAMETER;
+    if (Length < SourceLength)
+        return STATUS_NO_MEMORY;
+
+    RtlCopyMemory(Buffer,
+                  Source,
+                  SourceLength);
+    if (Returned)
+        *Returned = SourceLength;
+    return STATUS_SUCCESS;
+}
+
+static FORCEINLINE PVOID
+__HidAllocate(
+    IN  ULONG   Length
+    )
+{
+    return __AllocatePoolWithTag(NonPagedPool, Length, XENVKBD_VKBD_TAG);
+}
+
+static FORCEINLINE VOID
+__HidFree(
+    IN  PVOID   Buffer
+    )
+{
+    __FreePoolWithTag(Buffer, XENVKBD_VKBD_TAG);
+}
+
+static DECLSPEC_NOINLINE VOID
+HidSuspendCallbackLate(
+    IN  PVOID               Argument
+    )
+{
+    PXENVKBD_HID_CONTEXT    Context = Argument;
+    NTSTATUS                status;
+
+    RtlZeroMemory(&Context->KeyboardReport, sizeof(XENVKBD_HID_KEYBOARD));
+    RtlZeroMemory(&Context->AbsMouseReport, sizeof(XENVKBD_HID_ABSMOUSE));
+    Context->KeyboardReport.ReportId = 1;
+    Context->AbsMouseReport.ReportId = 2;
+
+    if (!Context->Enabled)
+        return;
+
+    status = FrontendSetState(Context->Frontend, FRONTEND_ENABLED);
+    ASSERT(NT_SUCCESS(status));
+}
+
+static DECLSPEC_NOINLINE VOID
+HidDebugCallback(
+    IN  PVOID               Argument,
+    IN  BOOLEAN             Crashing
+    )
+{
+    PXENVKBD_HID_CONTEXT    Context = Argument;
+
+    UNREFERENCED_PARAMETER(Crashing);
+
+    XENBUS_DEBUG(Printf,
+                 &Context->DebugInterface,
+                 "%u 0x%p(0x%p)%s\n",
+                 Context->Version,
+                 Context->Callback,
+                 Context->Argument,
+                 Context->Enabled ? " ENABLED" : "");
+
+    XENBUS_DEBUG(Printf,
+                 &Context->DebugInterface,
+                 "KBD: %02x %02x %02x %02x %02x %02x %02x %02x%s\n",
+                 Context->KeyboardReport.ReportId,
+                 Context->KeyboardReport.Modifiers,
+                 Context->KeyboardReport.Keys[0],
+                 Context->KeyboardReport.Keys[1],
+                 Context->KeyboardReport.Keys[2],
+                 Context->KeyboardReport.Keys[3],
+                 Context->KeyboardReport.Keys[4],
+                 Context->KeyboardReport.Keys[5],
+                 Context->KeyboardPending ? " PENDING" : "");
+
+    XENBUS_DEBUG(Printf,
+                 &Context->DebugInterface,
+                 "MOU: %02x %02x %04x %04x %02x%s\n",
+                 Context->AbsMouseReport.ReportId,
+                 Context->AbsMouseReport.Buttons,
+                 Context->AbsMouseReport.X,
+                 Context->AbsMouseReport.Y,
+                 Context->AbsMouseReport.dZ,
+                 Context->AbsMousePending ? " PENDING" : "");
+}
+
+static NTSTATUS
+HidEnable(
+    IN  PINTERFACE          Interface,
+    IN  XENHID_HID_CALLBACK Callback,
+    IN  PVOID               Argument
+    )
+{
+    PXENVKBD_HID_CONTEXT    Context = Interface->Context;
+    KIRQL                   Irql;
+    BOOLEAN                 Exclusive;
+    NTSTATUS                status;
+
+    Trace("====>\n");
+
+    AcquireMrswLockExclusive(&Context->Lock, &Irql);
+    Exclusive = TRUE;
+
+    if (Context->Enabled)
+        goto done;
+
+    Context->Callback = Callback;
+    Context->Argument = Argument;
+
+    Context->Enabled = TRUE;
+
+    KeMemoryBarrier();
+
+    status = XENBUS_SUSPEND(Acquire, &Context->SuspendInterface);
+    if (!NT_SUCCESS(status))
+        goto fail1;
+
+    status = FrontendSetState(Context->Frontend, FRONTEND_ENABLED);
+    if (!NT_SUCCESS(status))
+        goto fail2;
+
+    status = XENBUS_SUSPEND(Register,
+                            &Context->SuspendInterface,
+                            SUSPEND_CALLBACK_LATE,
+                            HidSuspendCallbackLate,
+                            Context,
+                            &Context->SuspendCallbackLate);
+    if (!NT_SUCCESS(status))
+        goto fail3;
+
+    status = XENBUS_DEBUG(Acquire, &Context->DebugInterface);
+    if (!NT_SUCCESS(status))
+        goto fail4;
+
+    status = XENBUS_DEBUG(Register,
+                          &Context->DebugInterface,
+                          __MODULE__"|DEBUG",
+                          HidDebugCallback,
+                          Context,
+                          &Context->DebugCallback);
+    if (!NT_SUCCESS(status))
+        goto fail5;
+
+done:
+    ASSERT(Exclusive);
+    ReleaseMrswLockExclusive(&Context->Lock, Irql, FALSE);
+
+    Trace("<====\n");
+
+    return STATUS_SUCCESS;
+
+fail5:
+    Error("fail5\n");
+
+    XENBUS_DEBUG(Release, &Context->DebugInterface);
+
+fail4:
+    Error("fail4\n");
+
+    XENBUS_SUSPEND(Deregister,
+                   &Context->SuspendInterface,
+                   Context->SuspendCallbackLate);
+    Context->SuspendCallbackLate = NULL;
+
+fail3:
+    Error("fail3\n");
+
+    (VOID) FrontendSetState(Context->Frontend, FRONTEND_CONNECTED);
+
+    ReleaseMrswLockExclusive(&Context->Lock, Irql, TRUE);
+    Exclusive = FALSE;
+
+fail2:
+    Error("fail2\n");
+
+    XENBUS_SUSPEND(Release, &Context->SuspendInterface);
+
+fail1:
+    Error("fail1 (%08x)\n", status);
+
+    Context->Enabled = FALSE;
+
+    KeMemoryBarrier();
+
+    Context->Argument = NULL;
+    Context->Callback = NULL;
+
+    if (Exclusive)
+        ReleaseMrswLockExclusive(&Context->Lock, Irql, FALSE);
+    else
+        ReleaseMrswLockShared(&Context->Lock);
+
+    return status;
+}
+
+static VOID
+HidDisable(
+    IN  PINTERFACE          Interface
+    )
+{
+    PXENVKBD_HID_CONTEXT    Context = Interface->Context;
+    KIRQL                   Irql;
+
+    Trace("====>\n");
+
+    AcquireMrswLockExclusive(&Context->Lock, &Irql);
+
+    if (!Context->Enabled) {
+        ReleaseMrswLockExclusive(&Context->Lock, Irql, FALSE);
+        goto done;
+    }
+
+    Context->Enabled = FALSE;
+
+    KeMemoryBarrier();
+
+    XENBUS_DEBUG(Deregister,
+                 &Context->DebugInterface,
+                 Context->DebugCallback);
+    Context->DebugCallback = NULL;
+
+    XENBUS_DEBUG(Release, &Context->DebugInterface);
+
+    XENBUS_SUSPEND(Deregister,
+                   &Context->SuspendInterface,
+                   Context->SuspendCallbackLate);
+    Context->SuspendCallbackLate = NULL;
+
+    (VOID) FrontendSetState(Context->Frontend, FRONTEND_CONNECTED);
+
+    ReleaseMrswLockExclusive(&Context->Lock, Irql, TRUE);
+
+    XENBUS_SUSPEND(Release, &Context->SuspendInterface);
+
+    Context->Argument = NULL;
+    Context->Callback = NULL;
+
+    ReleaseMrswLockShared(&Context->Lock);
+
+done:
+    Trace("<====\n");
+}
+
+static NTSTATUS
+HidGetDeviceAttributes(
+    IN  PINTERFACE          Interface,
+    IN  PVOID               Buffer,
+    IN  ULONG               Length,
+    OUT PULONG              Returned
+    )
+{
+    PXENVKBD_HID_CONTEXT    Context = Interface->Context;
+    NTSTATUS                status;
+
+    Trace("=====>\n");
+    AcquireMrswLockShared(&Context->Lock);
+
+    status = HidCopyBuffer(Buffer,
+                           Length,
+                           &VkbdDeviceAttributes,
+                           sizeof(VkbdDeviceAttributes),
+                           Returned);
+
+    ReleaseMrswLockShared(&Context->Lock);
+    Trace("<=====\n");
+
+    return status;
+}
+
+static NTSTATUS
+HidGetDeviceDescriptor(
+    IN  PINTERFACE          Interface,
+    IN  PVOID               Buffer,
+    IN  ULONG               Length,
+    OUT PULONG              Returned
+    )
+{
+    PXENVKBD_HID_CONTEXT    Context = Interface->Context;
+    NTSTATUS                status;
+
+    Trace("=====>\n");
+    AcquireMrswLockShared(&Context->Lock);
+
+    status = HidCopyBuffer(Buffer,
+                           Length,
+                           &VkbdDeviceDescriptor,
+                           sizeof(VkbdDeviceDescriptor),
+                           Returned);
+
+    ReleaseMrswLockShared(&Context->Lock);
+    Trace("<=====\n");
+
+    return status;
+}
+
+static NTSTATUS
+HidGetReportDescriptor(
+    IN  PINTERFACE          Interface,
+    IN  PVOID               Buffer,
+    IN  ULONG               Length,
+    OUT PULONG              Returned
+    )
+{
+    PXENVKBD_HID_CONTEXT    Context = Interface->Context;
+    NTSTATUS                status;
+
+    Trace("=====>\n");
+    AcquireMrswLockShared(&Context->Lock);
+
+    status = HidCopyBuffer(Buffer,
+                           Length,
+                           VkbdReportDescriptor,
+                           sizeof(VkbdReportDescriptor),
+                           Returned);
+
+    ReleaseMrswLockShared(&Context->Lock);
+    Trace("<=====\n");
+
+    return status;
+}
+
+static NTSTATUS
+HidGetString(
+    IN  PINTERFACE          Interface,
+    IN  ULONG               Identifier,
+    IN  PVOID               Buffer,
+    IN  ULONG               Length,
+    OUT PULONG              Returned
+    )
+{
+    PXENVKBD_HID_CONTEXT    Context = Interface->Context;
+    PXENVKBD_PDO            Pdo;
+    PXENVKBD_FDO            Fdo;
+    NTSTATUS                status;
+
+    Trace("=====>\n");
+    AcquireMrswLockShared(&Context->Lock);
+
+    Pdo = FrontendGetPdo(Context->Frontend);
+    Fdo = PdoGetFdo(Pdo);
+
+    // Ignore LangID
+    switch (Identifier & 0xFF) {
+    case HID_STRING_ID_IMANUFACTURER:
+        status = HidCopyBuffer(Buffer,
+                               Length,
+                               FdoGetVendorName(Fdo),
+                               (ULONG)strlen(FdoGetVendorName(Fdo)),
+                               Returned);
+        break;
+    case HID_STRING_ID_IPRODUCT:
+        status = HidCopyBuffer(Buffer,
+                               Length,
+                               "PV HID Device",
+                               sizeof("PV HID Device"),
+                               Returned);
+        break;
+    //case HID_STRING_ID_ISERIALNUMBER:
+    default:
+        status = STATUS_NOT_SUPPORTED;
+        break;
+    }
+  
+    ReleaseMrswLockShared(&Context->Lock);
+    Trace("<=====\n");
+
+    return status;
+}
+
+static NTSTATUS
+HidGetIndexedString(
+    IN  PINTERFACE          Interface,
+    IN  ULONG               Index,
+    IN  PVOID               Buffer,
+    IN  ULONG               Length,
+    OUT PULONG              Returned
+    )
+{
+    PXENVKBD_HID_CONTEXT    Context = Interface->Context;
+    NTSTATUS                status;
+
+    Trace("=====>\n");
+    AcquireMrswLockShared(&Context->Lock);
+
+    status = STATUS_NOT_SUPPORTED;
+  
+    ReleaseMrswLockShared(&Context->Lock);
+    Trace("<=====\n");
+
+    UNREFERENCED_PARAMETER(Index);
+    UNREFERENCED_PARAMETER(Buffer);
+    UNREFERENCED_PARAMETER(Length);
+    UNREFERENCED_PARAMETER(Returned);
+    return status;
+}
+
+static NTSTATUS
+HidGetFeature(
+    IN  PINTERFACE          Interface,
+    IN  ULONG               ReportId,
+    IN  PVOID               Buffer,
+    IN  ULONG               Length,
+    OUT PULONG              Returned
+    )
+{
+    PXENVKBD_HID_CONTEXT    Context = Interface->Context;
+    NTSTATUS                status;
+
+    Trace("=====>\n");
+    AcquireMrswLockShared(&Context->Lock);
+
+    status = STATUS_NOT_SUPPORTED;
+  
+    ReleaseMrswLockShared(&Context->Lock);
+    Trace("<=====\n");
+
+    UNREFERENCED_PARAMETER(ReportId);
+    UNREFERENCED_PARAMETER(Buffer);
+    UNREFERENCED_PARAMETER(Length);
+    UNREFERENCED_PARAMETER(Returned);
+    return status;
+}
+
+static NTSTATUS
+HidSetFeature(
+    IN  PINTERFACE          Interface,
+    IN  ULONG               ReportId,
+    IN  PVOID               Buffer,
+    IN  ULONG               Length
+    )
+{
+    PXENVKBD_HID_CONTEXT    Context = Interface->Context;
+    NTSTATUS                status;
+
+    Trace("=====>\n");
+    AcquireMrswLockShared(&Context->Lock);
+
+    status = STATUS_NOT_SUPPORTED;
+  
+    ReleaseMrswLockShared(&Context->Lock);
+    Trace("<=====\n");
+
+    UNREFERENCED_PARAMETER(ReportId);
+    UNREFERENCED_PARAMETER(Buffer);
+    UNREFERENCED_PARAMETER(Length);
+    return status;
+}
+
+static NTSTATUS
+HidGetInputReport(
+    IN  PINTERFACE          Interface,
+    IN  ULONG               ReportId,
+    IN  PVOID               Buffer,
+    IN  ULONG               Length,
+    OUT PULONG              Returned
+    )
+{
+    PXENVKBD_HID_CONTEXT    Context = Interface->Context;
+    NTSTATUS                status;
+
+    Trace("=====>\n");
+    AcquireMrswLockShared(&Context->Lock);
+
+    switch (ReportId) {
+    case 1:
+        status = HidCopyBuffer(Buffer,
+                               Length,
+                               &Context->KeyboardReport,
+                               sizeof(XENVKBD_HID_KEYBOARD),
+                               Returned);
+        break;
+    case 2:
+        status = HidCopyBuffer(Buffer,
+                               Length,
+                               &Context->AbsMouseReport,
+                               sizeof(XENVKBD_HID_ABSMOUSE),
+                               Returned);
+        break;
+    default:
+        status = STATUS_NOT_SUPPORTED;
+        break;
+    }
+  
+    ReleaseMrswLockShared(&Context->Lock);
+    Trace("<=====\n");
+
+    UNREFERENCED_PARAMETER(ReportId);
+    UNREFERENCED_PARAMETER(Buffer);
+    UNREFERENCED_PARAMETER(Length);
+    return status;
+}
+
+static NTSTATUS
+HidSetOutputReport(
+    IN  PINTERFACE          Interface,
+    IN  ULONG               ReportId,
+    IN  PVOID               Buffer,
+    IN  ULONG               Length
+    )
+{
+    PXENVKBD_HID_CONTEXT    Context = Interface->Context;
+    NTSTATUS                status;
+
+    Trace("=====>\n");
+    AcquireMrswLockShared(&Context->Lock);
+
+    status = STATUS_NOT_SUPPORTED;
+  
+    ReleaseMrswLockShared(&Context->Lock);
+    Trace("<=====\n");
+
+    UNREFERENCED_PARAMETER(ReportId);
+    UNREFERENCED_PARAMETER(Buffer);
+    UNREFERENCED_PARAMETER(Length);
+    return status;
+}
+
+static BOOLEAN
+HidSendReadReport(
+    IN  PXENVKBD_HID_CONTEXT    Context,
+    IN  PVOID                   Buffer,
+    IN  ULONG                   Length
+    )
+{
+    if (!Context->Enabled)
+        return TRUE; // flag as pending
+
+    // Callback returns TRUE on success, FALSE when Irp could not be completed
+    // Invert the result to indicate Pending state
+    return !Context->Callback(Context->Argument,
+                              Buffer,
+                              Length);
+}
+
+static VOID
+HidReadReport(
+    IN  PINTERFACE          Interface
+    )
+{
+    PXENVKBD_HID_CONTEXT    Context = Interface->Context;
+
+    AcquireMrswLockShared(&Context->Lock);
+
+    // Check for pending reports, push 1 pending report to subscriber
+    if (Context->KeyboardPending)
+        Context->KeyboardPending = HidSendReadReport(Context,
+                                                     &Context->KeyboardReport,
+                                                     sizeof(XENVKBD_HID_KEYBOARD));
+    else if (Context->AbsMousePending)
+        Context->AbsMousePending = HidSendReadReport(Context,
+                                                     &Context->AbsMouseReport,
+                                                     sizeof(XENVKBD_HID_ABSMOUSE));
+
+    ReleaseMrswLockShared(&Context->Lock);
+}
+
+static NTSTATUS
+HidWriteReport(
+    IN  PINTERFACE          Interface,
+    IN  ULONG               ReportId,
+    IN  PVOID               Buffer,
+    IN  ULONG               Length
+    )
+{
+    PXENVKBD_HID_CONTEXT    Context = Interface->Context;
+    NTSTATUS                status;
+
+    Trace("=====>\n");
+    AcquireMrswLockShared(&Context->Lock);
+
+    status = STATUS_NOT_SUPPORTED;
+  
+    ReleaseMrswLockShared(&Context->Lock);
+    Trace("<=====\n");
+
+    UNREFERENCED_PARAMETER(ReportId);
+    UNREFERENCED_PARAMETER(Buffer);
+    UNREFERENCED_PARAMETER(Length);
+    return status;
+}
+
+static NTSTATUS
+HidAcquire(
+    PINTERFACE              Interface
+    )
+{
+    PXENVKBD_HID_CONTEXT    Context = Interface->Context;
+    KIRQL                   Irql;
+
+    AcquireMrswLockExclusive(&Context->Lock, &Irql);
+
+    if (Context->References++ != 0)
+        goto done;
+
+    Trace("====>\n");
+
+    Context->Frontend = PdoGetFrontend(Context->Pdo);
+    Context->Version = Interface->Version;
+
+    Trace("<====\n");
+
+done:
+    ReleaseMrswLockExclusive(&Context->Lock, Irql, FALSE);
+
+    return STATUS_SUCCESS;
+}
+
+VOID
+HidRelease(
+    IN  PINTERFACE          Interface
+    )
+{
+    PXENVKBD_HID_CONTEXT    Context = Interface->Context;
+    KIRQL                   Irql;
+
+    AcquireMrswLockExclusive(&Context->Lock, &Irql);
+
+    if (--Context->References > 0)
+        goto done;
+
+    Trace("====>\n");
+
+    ASSERT(!Context->Enabled);
+
+    Context->Version = 0;
+    Context->Frontend = NULL;
+
+    Trace("<====\n");
+
+done:
+    ReleaseMrswLockExclusive(&Context->Lock, Irql, FALSE);
+}
+
+static struct _XENHID_HID_INTERFACE_V1 HidInterfaceVersion1 = {
+    { sizeof (struct _XENHID_HID_INTERFACE_V1), 1, NULL, NULL, NULL },
+    HidAcquire,
+    HidRelease,
+    HidEnable,
+    HidDisable,
+    HidGetDeviceAttributes,
+    HidGetDeviceDescriptor,
+    HidGetReportDescriptor,
+    HidGetString,
+    HidGetIndexedString,
+    HidGetFeature,
+    HidSetFeature,
+    HidGetInputReport,
+    HidSetOutputReport,
+    HidReadReport,
+    HidWriteReport
+};
+
+NTSTATUS
+HidInitialize(
+    IN  PXENVKBD_PDO            Pdo,
+    OUT PXENVKBD_HID_CONTEXT    *Context
+    )
+{
+    NTSTATUS                    status;
+
+    Trace("====>\n");
+
+    *Context = __HidAllocate(sizeof (XENVKBD_HID_CONTEXT));
+
+    status = STATUS_NO_MEMORY;
+    if (*Context == NULL)
+        goto fail1;
+
+    InitializeMrswLock(&(*Context)->Lock);
+
+    FdoGetDebugInterface(PdoGetFdo(Pdo),&(*Context)->DebugInterface);
+    FdoGetSuspendInterface(PdoGetFdo(Pdo),&(*Context)->SuspendInterface);
+
+    (*Context)->Pdo = Pdo;
+    (*Context)->KeyboardReport.ReportId = 1;
+    (*Context)->AbsMouseReport.ReportId = 2;
+
+    Trace("<====\n");
+
+    return STATUS_SUCCESS;
+
+fail1:
+    Error("fail1 (%08x)\n", status);
+
+    return status;
+}
+
+NTSTATUS
+HidGetInterface(
+    IN      PXENVKBD_HID_CONTEXT    Context,
+    IN      ULONG                   Version,
+    IN OUT  PINTERFACE              Interface,
+    IN      ULONG                   Size
+    )
+{
+    NTSTATUS                        status;
+
+    switch (Version) {
+    case 1: {
+        struct _XENHID_HID_INTERFACE_V1 *HidInterface;
+
+        HidInterface = (struct _XENHID_HID_INTERFACE_V1 *)Interface;
+
+        status = STATUS_BUFFER_OVERFLOW;
+        if (Size < sizeof (struct _XENHID_HID_INTERFACE_V1))
+            break;
+
+        *HidInterface = HidInterfaceVersion1;
+
+        ASSERT3U(Interface->Version, ==, Version);
+        Interface->Context = Context;
+
+        status = STATUS_SUCCESS;
+        break;
+    }
+    default:
+        status = STATUS_NOT_SUPPORTED;
+        break;
+    }
+
+    return status;
+}   
+
+VOID
+HidTeardown(
+    IN  PXENVKBD_HID_CONTEXT    Context
+    )
+{
+    Trace("====>\n");
+
+    RtlZeroMemory(&Context->KeyboardReport, sizeof(XENVKBD_HID_KEYBOARD));
+    RtlZeroMemory(&Context->AbsMouseReport, sizeof(XENVKBD_HID_ABSMOUSE));
+    Context->KeyboardPending = FALSE;
+    Context->AbsMousePending = FALSE;
+
+    Context->Pdo = NULL;
+    Context->Version = 0;
+
+    RtlZeroMemory(&Context->SuspendInterface,
+                  sizeof (XENBUS_SUSPEND_INTERFACE));
+    RtlZeroMemory(&Context->DebugInterface,
+                  sizeof (XENBUS_DEBUG_INTERFACE));
+
+    RtlZeroMemory(&Context->Lock, sizeof (XENVKBD_MRSW_LOCK));
+
+    ASSERT(IsZeroMemory(Context, sizeof (XENVKBD_HID_CONTEXT)));
+    __HidFree(Context);
+
+    Trace("<====\n");
+}
+
+static FORCEINLINE LONG
+Constrain(
+    IN  LONG    Value,
+    IN  LONG    Min,
+    IN  LONG    Max
+    )
+{
+    if (Value < Min)
+        return Min;
+    if (Value > Max)
+        return Max;
+    return Value;
+}
+
+static FORCEINLINE UCHAR
+SetBit(
+    IN  UCHAR   Value,
+    IN  UCHAR   BitIdx,
+    IN  BOOLEAN Pressed
+    )
+{
+    if (Pressed) {
+        return Value | (1 << BitIdx);
+    } else {
+        return Value & ~(1 << BitIdx);
+    }
+}
+
+static FORCEINLINE VOID
+SetArray(
+    IN  PUCHAR  Array,
+    IN  ULONG   Size,
+    IN  UCHAR   Value,
+    IN  BOOLEAN Pressed
+    )
+{
+    ULONG       Idx;
+    if (Pressed) {
+        for (Idx = 0; Idx < Size; ++Idx) {
+            if (Array[Idx] == Value)
+                break;
+            if (Array[Idx] != 0)
+                continue;
+            Array[Idx] = Value;
+            break;
+        }
+    } else {
+        for (Idx = 0; Idx < Size; ++Idx) {
+            if (Array[Idx] == 0)
+                break;
+            if (Array[Idx] != Value)
+                continue;
+            for (; Idx < Size - 1; ++Idx)
+                Array[Idx] = Array[Idx + 1];
+            Array[Size - 1] = 0;
+            break;
+        }
+    }
+}
+
+static FORCEINLINE USHORT
+KeyCodeToUsage(
+    IN  ULONG   KeyCode
+    )
+{
+    if (KeyCode < sizeof(VkbdKeyCodeToUsage)/sizeof(VkbdKeyCodeToUsage[0]))
+        return VkbdKeyCodeToUsage[KeyCode];
+    return 0;
+}
+
+VOID
+HidEventMotion(
+    IN  PXENVKBD_HID_CONTEXT    Context,
+    IN  LONG                    dX,
+    IN  LONG                    dY,
+    IN  LONG                    dZ
+    )
+{
+    Context->AbsMouseReport.X = (USHORT)Constrain(Context->AbsMouseReport.X + dX, 0, 32767);
+    Context->AbsMouseReport.Y = (USHORT)Constrain(Context->AbsMouseReport.Y + dY, 0, 32767);
+    Context->AbsMouseReport.dZ = -(CHAR)Constrain(dZ, -127, 127);
+
+    Context->AbsMousePending = HidSendReadReport(Context,
+                                                 &Context->AbsMouseReport,
+                                                 sizeof(XENVKBD_HID_ABSMOUSE));
+}
+
+VOID
+HidEventKeypress(
+    IN  PXENVKBD_HID_CONTEXT    Context,
+    IN  ULONG                   KeyCode,
+    IN  BOOLEAN                 Pressed
+    )
+{
+    if (KeyCode >= 0x110 && KeyCode <= 0x114) {
+        // Mouse Buttons
+        Context->AbsMouseReport.Buttons = SetBit(Context->AbsMouseReport.Buttons,
+                                                 (UCHAR)(KeyCode - 0x110),
+                                                 Pressed);
+
+        Context->AbsMousePending = HidSendReadReport(Context,
+                                                    &Context->AbsMouseReport,
+                                                    sizeof(XENVKBD_HID_ABSMOUSE));
+
+    } else {
+        // map KeyCode to Usage
+        USHORT  Usage = KeyCodeToUsage(KeyCode);
+        if (Usage == 0)
+            return; // non-standard key
+
+        if (Usage >= 0xE0 && Usage <= 0xE7) {
+            // Modifier
+            Context->KeyboardReport.Modifiers = SetBit(Context->KeyboardReport.Modifiers,
+                                                       (UCHAR)(Usage - 0xE0),
+                                                       Pressed);
+        } else {
+            // Standard Key
+            SetArray(Context->KeyboardReport.Keys,
+                     6,
+                     (UCHAR)Usage,
+                     Pressed);
+        }
+        Context->KeyboardPending = HidSendReadReport(Context,
+                                                    &Context->KeyboardReport,
+                                                    sizeof(XENVKBD_HID_KEYBOARD));
+
+    }
+}
+
+VOID
+HidEventPosition(
+    IN  PXENVKBD_HID_CONTEXT    Context,
+    IN  ULONG                   X,
+    IN  ULONG                   Y,
+    IN  LONG                    dZ
+    )
+{
+    Context->AbsMouseReport.X = (USHORT)Constrain(X, 0, 32767);
+    Context->AbsMouseReport.Y = (USHORT)Constrain(Y, 0, 32767);
+    Context->AbsMouseReport.dZ = -(CHAR)Constrain(dZ, -127, 127);
+
+    Context->AbsMousePending = HidSendReadReport(Context,
+                                                 &Context->AbsMouseReport,
+                                                 sizeof(XENVKBD_HID_ABSMOUSE));
+}
diff --git a/src/xenvkbd/hid.h b/src/xenvkbd/hid.h
new file mode 100644 (file)
index 0000000..ec38e28
--- /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 _XENVKBD_HID_H
+#define _XENVKBD_HID_H
+
+#include <ntddk.h>
+#include <hid_interface.h>
+
+#include "thread.h"
+
+typedef struct _XENVKBD_HID_CONTEXT  XENVKBD_HID_CONTEXT, *PXENVKBD_HID_CONTEXT;
+
+#include "fdo.h"
+
+extern NTSTATUS
+HidInitialize(
+    IN  PXENVKBD_PDO            Pdo,
+    OUT PXENVKBD_HID_CONTEXT    *Context
+    );
+
+extern NTSTATUS
+HidGetInterface(
+    IN      PXENVKBD_HID_CONTEXT    Context,
+    IN      ULONG                   Version,
+    IN OUT  PINTERFACE              Interface,
+    IN      ULONG                   Size
+    );
+
+extern VOID
+HidTeardown(
+    IN  PXENVKBD_HID_CONTEXT    Context
+    );
+
+// CALLBACKS
+
+extern VOID
+HidEventMotion(
+    IN  PXENVKBD_HID_CONTEXT    Context,
+    IN  LONG                    dX,
+    IN  LONG                    dY,
+    IN  LONG                    dZ
+    );
+
+extern VOID
+HidEventKeypress(
+    IN  PXENVKBD_HID_CONTEXT    Context,
+    IN  ULONG                   KeyCode,
+    IN  BOOLEAN                 Pressed
+    );
+
+extern VOID
+HidEventPosition(
+    IN  PXENVKBD_HID_CONTEXT    Context,
+    IN  ULONG                   X,
+    IN  ULONG                   Y,
+    IN  LONG                    dZ
+    );
+
+#endif  // _XENVKBD_VKBD_H
+
diff --git a/src/xenvkbd/mrsw.h b/src/xenvkbd/mrsw.h
new file mode 100644 (file)
index 0000000..ee480a3
--- /dev/null
@@ -0,0 +1,288 @@
+/* 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 _XENVKBD_MRSW_H
+#define _XENVKBD_MRSW_H
+
+#include <ntddk.h>
+
+#include "assert.h"
+#include "util.h"
+
+#pragma warning(disable:4127)   // conditional expression is constant
+
+typedef struct _XENVKBD_MRSW_HOLDER {
+    PKTHREAD    Thread;
+    LONG        Level;
+} XENVKBD_MRSW_HOLDER, *PXENVKBD_MRSW_HOLDER;
+
+typedef struct _XENVKBD_MRSW_LOCK {
+    volatile LONG64 Mask;
+    XENVKBD_MRSW_HOLDER     Holder[64];
+    KEVENT          Event;
+} XENVKBD_MRSW_LOCK, *PXENVKBD_MRSW_LOCK;
+
+C_ASSERT(RTL_FIELD_SIZE(XENVKBD_MRSW_LOCK, Holder) == RTL_FIELD_SIZE(XENVKBD_MRSW_LOCK, Mask) * 8 * sizeof (XENVKBD_MRSW_HOLDER));
+
+#define XENVKBD_MRSW_EXCLUSIVE_SLOT  0
+
+static FORCEINLINE VOID
+InitializeMrswLock(
+    IN  PXENVKBD_MRSW_LOCK  Lock
+    )
+{
+    LONG                    Slot;
+
+    RtlZeroMemory(Lock, sizeof (XENVKBD_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  PXENVKBD_MRSW_LOCK  Lock
+    )
+{
+    LONG64                  Old;
+    LONG64                  New;
+
+    Old = 0;
+    New = 1ll << XENVKBD_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  PXENVKBD_MRSW_LOCK  Lock
+    )
+{
+    KIRQL                   Irql;
+    LONG                    Slot;
+    PKTHREAD                Self;
+    PXENVKBD_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[XENVKBD_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  PXENVKBD_MRSW_LOCK          Lock,
+    IN  __drv_restoresIRQL KIRQL    Irql,
+    IN  BOOLEAN                     Shared
+    )
+{
+    LONG                            Slot;
+    PKTHREAD                        Self;
+    LONG64                          Old;
+    LONG64                          New;
+    PXENVKBD_MRSW_HOLDER            Holder;
+
+    ASSERT3U(KeGetCurrentIrql(), ==, DISPATCH_LEVEL);
+
+    Slot = XENVKBD_MRSW_EXCLUSIVE_SLOT + 1; // Choose any slot other than the exclusive slot
+
+    Old = 1ll << XENVKBD_MRSW_EXCLUSIVE_SLOT;
+    New = (Shared) ? (1ll << Slot) : 0;
+
+    Old = InterlockedCompareExchange64(&Lock->Mask, New, Old);
+    ASSERT3U(Old, == , 1ll << XENVKBD_MRSW_EXCLUSIVE_SLOT);
+
+    Self = KeGetCurrentThread();
+
+    ASSERT3P(Lock->Holder[XENVKBD_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[XENVKBD_MRSW_EXCLUSIVE_SLOT];
+
+    Holder = &Lock->Holder[XENVKBD_MRSW_EXCLUSIVE_SLOT];
+
+    Holder->Thread = NULL;
+    Holder->Level = -1;
+
+    KeLowerIrql(Irql);
+}
+
+static FORCEINLINE LONG
+__ClaimShared(
+    IN  PXENVKBD_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 << XENVKBD_MRSW_EXCLUSIVE_SLOT);
+
+    Slot = __ffu((ULONG64)Old);
+    ASSERT(Slot >= 0);
+    ASSERT3U(Slot, != , XENVKBD_MRSW_EXCLUSIVE_SLOT);
+
+    Old &= ~(1ll << XENVKBD_MRSW_EXCLUSIVE_SLOT);
+    New = Old | (1ll << Slot);
+
+    return (InterlockedCompareExchange64(&Lock->Mask, New, Old) == Old) ? Slot : -1;
+}
+
+static FORCEINLINE VOID
+AcquireMrswLockShared(
+    IN  PXENVKBD_MRSW_LOCK  Lock
+    )
+{
+    KIRQL                   Irql;
+    LONG                    Level;
+    LONG                    Slot;
+    PKTHREAD                Self;
+    PXENVKBD_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  PXENVKBD_MRSW_LOCK  Lock
+    )
+{
+    KIRQL                   Irql;
+    PKTHREAD                Self;
+    LONG                    Level;
+    LONG                    Deepest;
+    LONG                    Slot;
+    LONG64                  Old;
+    LONG64                  New;
+    PXENVKBD_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, !=, XENVKBD_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  // _XENVKBD_MRSW_H
diff --git a/src/xenvkbd/mutex.h b/src/xenvkbd/mutex.h
new file mode 100644 (file)
index 0000000..de98ab7
--- /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 _XENVKBD_MUTEX_H
+#define _XENVKBD_MUTEX_H
+
+#include <ntddk.h>
+
+#include "assert.h"
+
+typedef struct _MUTEX {
+    PKTHREAD    Owner;
+    KEVENT      Event;
+} MUTEX, *PMUTEX;
+
+static FORCEINLINE VOID
+InitializeMutex(
+    IN  PMUTEX  Mutex
+    )
+{
+    RtlZeroMemory(Mutex, sizeof (MUTEX));
+
+    KeInitializeEvent(&Mutex->Event, SynchronizationEvent, TRUE);
+}
+
+static FORCEINLINE VOID
+__drv_maxIRQL(PASSIVE_LEVEL)
+AcquireMutex(
+    IN  PMUTEX  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  PMUTEX  Mutex
+    )
+{
+    ASSERT3P(Mutex->Owner, ==, KeGetCurrentThread());
+    Mutex->Owner = NULL;
+
+    KeSetEvent(&Mutex->Event, IO_NO_INCREMENT, FALSE);
+}
+
+#endif  // _XENVKBD_MUTEX_H
diff --git a/src/xenvkbd/names.h b/src/xenvkbd/names.h
new file mode 100644 (file)
index 0000000..58ef694
--- /dev/null
@@ -0,0 +1,366 @@
+/* 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 _XENVKBD_NAMES_H_
+#define _XENVKBD_NAMES_H_
+
+#include <ntddk.h>
+#include <xen.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:
+        break;
+    }
+
+    return "UNKNOWN";
+
+#undef  _POWER_MINOR_FUNCTION_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 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
+}
+
+static FORCEINLINE const CHAR *
+InterfaceTypeName(
+    IN  INTERFACE_TYPE  Type
+    )
+{
+#define _INTERFACE_TYPE_NAME(_Type) \
+    case _Type:                     \
+        return #_Type;
+
+    switch (Type) {
+    _INTERFACE_TYPE_NAME(InterfaceTypeUndefined);
+    _INTERFACE_TYPE_NAME(Internal);
+    _INTERFACE_TYPE_NAME(Isa);
+    _INTERFACE_TYPE_NAME(Eisa);
+    _INTERFACE_TYPE_NAME(MicroChannel);
+    _INTERFACE_TYPE_NAME(TurboChannel);
+    _INTERFACE_TYPE_NAME(PCIBus);
+    _INTERFACE_TYPE_NAME(VMEBus);
+    _INTERFACE_TYPE_NAME(NuBus);
+    _INTERFACE_TYPE_NAME(PCMCIABus);
+    _INTERFACE_TYPE_NAME(CBus);
+    _INTERFACE_TYPE_NAME(MPIBus);
+    _INTERFACE_TYPE_NAME(MPSABus);
+    _INTERFACE_TYPE_NAME(ProcessorInternal);
+    _INTERFACE_TYPE_NAME(InternalPowerBus);
+    _INTERFACE_TYPE_NAME(PNPISABus);
+    _INTERFACE_TYPE_NAME(PNPBus);
+    _INTERFACE_TYPE_NAME(Vmcs);
+    _INTERFACE_TYPE_NAME(ACPIBus);
+    default:
+        break;
+    }
+
+    return "UNKNOWN";
+
+#undef  _INTERFACE_TYPE_NAME
+}
+
+static FORCEINLINE const CHAR *
+DmaWidthName(
+    IN  DMA_WIDTH   Width
+    )
+{
+#define _DMA_WIDTH_NAME(_Width) \
+    case Width ## _Width:       \
+        return #_Width;
+
+    switch (Width) {
+    _DMA_WIDTH_NAME(8Bits);
+    _DMA_WIDTH_NAME(16Bits);
+    _DMA_WIDTH_NAME(32Bits);
+    _DMA_WIDTH_NAME(64Bits);
+    _DMA_WIDTH_NAME(NoWrap);
+    default:
+        break;
+    }
+
+    return "UNKNOWN";
+
+#undef  _DMA_WIDTH_NAME
+}
+
+static FORCEINLINE const CHAR *
+DmaSpeedName(
+    IN  DMA_SPEED   Speed
+    )
+{
+#define _DMA_SPEED_NAME(_Speed) \
+    case _Speed:                \
+        return #_Speed;
+
+    switch (Speed) {
+    _DMA_SPEED_NAME(Compatible);
+    _DMA_SPEED_NAME(TypeA);
+    _DMA_SPEED_NAME(TypeB);
+    _DMA_SPEED_NAME(TypeC);
+    _DMA_SPEED_NAME(TypeF);
+    _DMA_SPEED_NAME(MaximumDmaSpeed);
+    default:
+        break;
+    }
+
+    return "UNKNOWN";
+
+#undef  _DMA_SPEED_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
+}
+
+#endif // _XENVKBD_NAMES_H_
diff --git a/src/xenvkbd/pdo.c b/src/xenvkbd/pdo.c
new file mode 100644 (file)
index 0000000..00fee0c
--- /dev/null
@@ -0,0 +1,2147 @@
+/* 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 <devguid.h>
+#include <ntstrsafe.h>
+#include <stdlib.h>
+#include <xen.h>
+
+#include <suspend_interface.h>
+#include <unplug_interface.h>
+#include <debug_interface.h>
+
+#include "names.h"
+#include "fdo.h"
+#include "pdo.h"
+#include "bus.h"
+#include "frontend.h"
+#include "hid.h"
+#include "driver.h"
+#include "registry.h"
+#include "thread.h"
+#include "dbg_print.h"
+#include "assert.h"
+#include "util.h"
+#include "version.h"
+#include "revision.h"
+
+#define PDO_POOL 'ODP'
+
+#define MAXNAMELEN  128
+
+struct _XENVKBD_PDO {
+    PXENVKBD_DX                 Dx;
+
+    PXENVKBD_THREAD             SystemPowerThread;
+    PIRP                        SystemPowerIrp;
+    PXENVKBD_THREAD             DevicePowerThread;
+    PIRP                        DevicePowerIrp;
+
+    PXENVKBD_FDO                Fdo;
+    BOOLEAN                     Missing;
+    const CHAR                  *Reason;
+    LONG                        Eject;
+
+    BUS_INTERFACE_STANDARD      BusInterface;
+
+    XENBUS_SUSPEND_INTERFACE    SuspendInterface;
+    PXENBUS_SUSPEND_CALLBACK    SuspendCallbackLate;
+
+    XENBUS_UNPLUG_INTERFACE     UnplugInterface;
+    BOOLEAN                     UnplugRequested;
+
+    PXENVKBD_FRONTEND           Frontend;
+
+    PXENVKBD_HID_CONTEXT        HidContext;
+    XENHID_HID_INTERFACE        HidInterface;
+};
+
+static FORCEINLINE PVOID
+__PdoAllocate(
+    IN  ULONG   Length
+    )
+{
+    return __AllocatePoolWithTag(NonPagedPool, Length, PDO_POOL);
+}
+
+static FORCEINLINE VOID
+__PdoFree(
+    IN  PVOID   Buffer
+    )
+{
+    __FreePoolWithTag(Buffer, PDO_POOL);
+}
+
+static FORCEINLINE VOID
+__PdoSetDevicePnpState(
+    IN  PXENVKBD_PDO        Pdo,
+    IN  DEVICE_PNP_STATE    State
+    )
+{
+    PXENVKBD_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  PXENVKBD_PDO        Pdo,
+    IN  DEVICE_PNP_STATE    State
+    )
+{
+    __PdoSetDevicePnpState(Pdo, State);
+}
+
+static FORCEINLINE VOID
+__PdoRestoreDevicePnpState(
+    IN  PXENVKBD_PDO        Pdo,
+    IN  DEVICE_PNP_STATE    State
+    )
+{
+    PXENVKBD_DX             Dx = Pdo->Dx;
+
+    if (Dx->DevicePnpState == State)
+        Dx->DevicePnpState = Dx->PreviousDevicePnpState;
+}
+
+static FORCEINLINE DEVICE_PNP_STATE
+__PdoGetDevicePnpState(
+    IN  PXENVKBD_PDO    Pdo
+    )
+{
+    PXENVKBD_DX         Dx = Pdo->Dx;
+
+    return Dx->DevicePnpState;
+}
+
+DEVICE_PNP_STATE
+PdoGetDevicePnpState(
+    IN  PXENVKBD_PDO    Pdo
+    )
+{
+    return __PdoGetDevicePnpState(Pdo);
+}
+
+static FORCEINLINE VOID
+__PdoSetSystemPowerState(
+    IN  PXENVKBD_PDO        Pdo,
+    IN  SYSTEM_POWER_STATE  State
+    )
+{
+    PXENVKBD_DX             Dx = Pdo->Dx;
+
+    Dx->SystemPowerState = State;
+}
+
+static FORCEINLINE SYSTEM_POWER_STATE
+__PdoGetSystemPowerState(
+    IN  PXENVKBD_PDO    Pdo
+    )
+{
+    PXENVKBD_DX         Dx = Pdo->Dx;
+
+    return Dx->SystemPowerState;
+}
+
+static FORCEINLINE VOID
+__PdoSetDevicePowerState(
+    IN  PXENVKBD_PDO        Pdo,
+    IN  DEVICE_POWER_STATE  State
+    )
+{
+    PXENVKBD_DX             Dx = Pdo->Dx;
+
+    Dx->DevicePowerState = State;
+}
+
+static FORCEINLINE DEVICE_POWER_STATE
+__PdoGetDevicePowerState(
+    IN  PXENVKBD_PDO    Pdo
+    )
+{
+    PXENVKBD_DX         Dx = Pdo->Dx;
+
+    return Dx->DevicePowerState;
+}
+
+static FORCEINLINE VOID
+__PdoSetMissing(
+    IN  PXENVKBD_PDO    Pdo,
+    IN  const CHAR      *Reason
+    )
+{
+    Pdo->Reason = Reason;
+    Pdo->Missing = TRUE;
+}
+
+VOID
+PdoSetMissing(
+    IN  PXENVKBD_PDO    Pdo,
+    IN  const CHAR      *Reason
+    )
+{
+    __PdoSetMissing(Pdo, Reason);
+}
+
+static FORCEINLINE BOOLEAN
+__PdoIsMissing(
+    IN  PXENVKBD_PDO    Pdo
+    )
+{
+    return Pdo->Missing;
+}
+
+BOOLEAN
+PdoIsMissing(
+    IN  PXENVKBD_PDO    Pdo
+    )
+{
+    return __PdoIsMissing(Pdo);
+}
+
+static FORCEINLINE PXENVKBD_FDO
+__PdoGetFdo(
+    IN  PXENVKBD_PDO    Pdo
+    )
+{
+    return Pdo->Fdo;
+}
+
+PXENVKBD_FDO
+PdoGetFdo(
+    IN  PXENVKBD_PDO    Pdo
+    )
+{
+    return __PdoGetFdo(Pdo);
+}
+
+static FORCEINLINE VOID
+__PdoSetName(
+    IN  PXENVKBD_PDO    Pdo,
+    IN  PCHAR           Path
+    )
+{
+    PXENVKBD_DX         Dx = Pdo->Dx;
+    NTSTATUS            status;
+
+    status = RtlStringCbPrintfA(Dx->Name, 
+                                MAX_DEVICE_ID_LEN, 
+                                "%s", 
+                                Path);
+    ASSERT(NT_SUCCESS(status));
+}
+
+static FORCEINLINE PCHAR
+__PdoGetName(
+    IN  PXENVKBD_PDO    Pdo
+    )
+{
+    PXENVKBD_DX         Dx = Pdo->Dx;
+
+    return Dx->Name;
+}
+
+PCHAR
+PdoGetName(
+    IN  PXENVKBD_PDO    Pdo
+    )
+{
+    return __PdoGetName(Pdo);
+}
+
+static FORCEINLINE BOOLEAN
+__PdoSetEjectRequested(
+    IN  PXENVKBD_PDO    Pdo
+    )
+{
+    return (InterlockedBitTestAndSet(&Pdo->Eject, 0) == 0) ? TRUE : FALSE;
+}
+
+VOID
+PdoRequestEject(
+    IN  PXENVKBD_PDO    Pdo
+    )
+{
+    PXENVKBD_DX         Dx = Pdo->Dx;
+    PDEVICE_OBJECT      PhysicalDeviceObject = Dx->DeviceObject;
+    PXENVKBD_FDO        Fdo = __PdoGetFdo(Pdo);
+
+    if (!__PdoSetEjectRequested(Pdo))
+        return;
+
+    Info("%p (%s)\n",
+         PhysicalDeviceObject,
+         __PdoGetName(Pdo));
+
+    IoInvalidateDeviceRelations(FdoGetPhysicalDeviceObject(Fdo),
+                                BusRelations);
+}
+
+static FORCEINLINE BOOLEAN
+__PdoClearEjectRequested(
+    IN  PXENVKBD_PDO    Pdo
+    )
+{
+    return (InterlockedBitTestAndReset(&Pdo->Eject, 0) != 0) ? TRUE : FALSE;
+}
+
+static FORCEINLINE BOOLEAN
+__PdoIsEjectRequested(
+    IN  PXENVKBD_PDO    Pdo
+    )
+{
+    KeMemoryBarrier();
+    return (Pdo->Eject & 1) ? TRUE : FALSE;
+}
+
+BOOLEAN
+PdoIsEjectRequested(
+    IN  PXENVKBD_PDO    Pdo
+    )
+{
+    return __PdoIsEjectRequested(Pdo);
+}
+
+#define MAXTEXTLEN  1024
+
+typedef struct _XENVKBD_PDO_REVISION {
+    ULONG   Number;
+    ULONG   HidInterfaceVersion;
+} XENVKBD_PDO_REVISION, *PXENVKBD_PDO_REVISION;
+
+#define DEFINE_REVISION(_N, _H) \
+    { (_N), (_H) }
+
+static XENVKBD_PDO_REVISION PdoRevision[] = {
+    DEFINE_REVISION_TABLE
+};
+
+#undef DEFINE_REVISION
+
+static VOID
+PdoDumpRevisions(
+    IN  PXENVKBD_PDO    Pdo
+    )
+{
+    ULONG               Index;
+
+    UNREFERENCED_PARAMETER(Pdo);
+
+    for (Index = 0; Index < ARRAYSIZE(PdoRevision); Index++) {
+        PXENVKBD_PDO_REVISION Revision = &PdoRevision[Index];
+
+        ASSERT3U(Revision->HidInterfaceVersion, >=, XENHID_HID_INTERFACE_VERSION_MIN);
+        ASSERT3U(Revision->HidInterfaceVersion, <=, XENHID_HID_INTERFACE_VERSION_MAX);
+        ASSERT(IMPLY(Index == ARRAYSIZE(PdoRevision) - 1,
+                     Revision->HidInterfaceVersion == XENHID_HID_INTERFACE_VERSION_MAX));
+
+        Info("%08X -> "
+             "HID v%u\n",
+             Revision->Number,
+             Revision->HidInterfaceVersion);
+    }
+}
+
+static FORCEINLINE PDEVICE_OBJECT
+__PdoGetDeviceObject(
+    IN  PXENVKBD_PDO    Pdo
+    )
+{
+    PXENVKBD_DX         Dx = Pdo->Dx;
+
+    return (Dx->DeviceObject);
+}
+    
+PDEVICE_OBJECT
+PdoGetDeviceObject(
+    IN  PXENVKBD_PDO    Pdo
+    )
+{
+    return __PdoGetDeviceObject(Pdo);
+}
+
+static FORCEINLINE PCHAR
+__PdoGetVendorName(
+    IN  PXENVKBD_PDO    Pdo
+    )
+{
+    return FdoGetVendorName(__PdoGetFdo(Pdo));
+}
+
+static FORCEINLINE PXENVKBD_FRONTEND
+__PdoGetFrontend(
+    IN  PXENVKBD_PDO    Pdo
+    )
+{
+    return Pdo->Frontend;
+}
+
+PXENVKBD_FRONTEND
+PdoGetFrontend(
+    IN  PXENVKBD_PDO    Pdo
+    )
+{
+    return __PdoGetFrontend(Pdo);
+}
+
+static FORCEINLINE PXENVKBD_HID_CONTEXT
+__PdoGetHidContext(
+    IN  PXENVKBD_PDO Pdo
+    )
+{
+    return Pdo->HidContext;
+}
+
+PXENVKBD_HID_CONTEXT
+PdoGetHidContext(
+    IN  PXENVKBD_PDO    Pdo
+    )
+{
+    return __PdoGetHidContext(Pdo);
+}
+
+PDMA_ADAPTER
+PdoGetDmaAdapter(
+    IN  PXENVKBD_PDO        Pdo,
+    IN  PDEVICE_DESCRIPTION DeviceDescriptor,
+    OUT PULONG              NumberOfMapRegisters
+    )
+{
+    Trace("<===>\n");
+
+    return FdoGetDmaAdapter(__PdoGetFdo(Pdo),
+                            DeviceDescriptor,
+                            NumberOfMapRegisters);
+}
+
+BOOLEAN
+PdoTranslateBusAddress(
+    IN      PXENVKBD_PDO        Pdo,
+    IN      PHYSICAL_ADDRESS    BusAddress,
+    IN      ULONG               Length,
+    IN OUT  PULONG              AddressSpace,
+    OUT     PPHYSICAL_ADDRESS   TranslatedAddress
+    )
+{
+    Trace("<===>\n");
+
+    return FdoTranslateBusAddress(__PdoGetFdo(Pdo),
+                                  BusAddress,
+                                  Length,
+                                  AddressSpace,
+                                  TranslatedAddress);
+}
+
+ULONG
+PdoSetBusData(
+    IN  PXENVKBD_PDO    Pdo,
+    IN  ULONG           DataType,
+    IN  PVOID           Buffer,
+    IN  ULONG           Offset,
+    IN  ULONG           Length
+    )
+{
+    Trace("<===>\n");
+
+    return FdoSetBusData(__PdoGetFdo(Pdo),
+                         DataType,
+                         Buffer,
+                         Offset,
+                         Length);
+}
+
+ULONG
+PdoGetBusData(
+    IN  PXENVKBD_PDO    Pdo,
+    IN  ULONG           DataType,
+    IN  PVOID           Buffer,
+    IN  ULONG           Offset,
+    IN  ULONG           Length
+    )
+{
+    Trace("<===>\n");
+
+    return FdoGetBusData(__PdoGetFdo(Pdo),
+                         DataType,
+                         Buffer,
+                         Offset,
+                         Length);
+}
+
+static FORCEINLINE NTSTATUS
+__PdoD3ToD0(
+    IN  PXENVKBD_PDO            Pdo
+    )
+{
+    POWER_STATE                 PowerState;
+    NTSTATUS                    status;
+
+    Trace("(%s) ====>\n", __PdoGetName(Pdo));
+
+    ASSERT3U(KeGetCurrentIrql(), ==, DISPATCH_LEVEL);
+    ASSERT3U(__PdoGetDevicePowerState(Pdo), ==, PowerDeviceD3);
+
+    // Dont take over keyboard/mouse until XenHid starts
+    // XenHid calling XENHID_HID(Enable...) sets state to ENABLED
+    status = FrontendSetState(__PdoGetFrontend(Pdo), FRONTEND_CLOSED);
+    if (!NT_SUCCESS(status))
+        goto fail1;
+
+    __PdoSetDevicePowerState(Pdo, PowerDeviceD0);
+
+    PowerState.DeviceState = PowerDeviceD0;
+    PoSetPowerState(__PdoGetDeviceObject(Pdo),
+                    DevicePowerState,
+                    PowerState);
+
+    Trace("(%s) <====\n", __PdoGetName(Pdo));
+
+    return STATUS_SUCCESS;
+
+fail1:
+    Error("fail1 (%08x)\n", status);
+
+    return status;
+}
+
+static FORCEINLINE VOID
+__PdoD0ToD3(
+    IN  PXENVKBD_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
+    )
+{
+    PXENVKBD_PDO        Pdo = Argument;
+    NTSTATUS            status;
+
+    __PdoD0ToD3(Pdo);
+
+    status = __PdoD3ToD0(Pdo);
+    ASSERT(NT_SUCCESS(status));
+}
+
+// This function must not touch pageable code or data
+static DECLSPEC_NOINLINE NTSTATUS
+PdoD3ToD0(
+    IN  PXENVKBD_PDO    Pdo
+    )
+{
+    KIRQL               Irql;
+    NTSTATUS            status;
+
+    ASSERT3U(KeGetCurrentIrql(), ==, PASSIVE_LEVEL);
+
+    KeRaiseIrql(DISPATCH_LEVEL, &Irql);
+
+    status = XENBUS_SUSPEND(Acquire, &Pdo->SuspendInterface);
+    if (!NT_SUCCESS(status))
+        goto fail1;
+
+    status = __PdoD3ToD0(Pdo);
+    if (!NT_SUCCESS(status))
+        goto fail2;
+
+    status = XENBUS_SUSPEND(Register,
+                            &Pdo->SuspendInterface,
+                            SUSPEND_CALLBACK_LATE,
+                            PdoSuspendCallbackLate,
+                            Pdo,
+                            &Pdo->SuspendCallbackLate);
+    if (!NT_SUCCESS(status))
+        goto fail3;
+
+    KeLowerIrql(Irql);
+
+    return STATUS_SUCCESS;
+
+fail3:
+    Error("fail3\n");
+
+    __PdoD0ToD3(Pdo);
+
+fail2:
+    Error("fail2\n");
+
+    XENBUS_SUSPEND(Release, &Pdo->SuspendInterface);
+
+fail1:
+    Error("fail1 (%08x)\n", status);
+
+    KeLowerIrql(Irql);
+
+    return status;
+}
+
+// This function must not touch pageable code or data
+static DECLSPEC_NOINLINE VOID
+PdoD0ToD3(
+    IN  PXENVKBD_PDO    Pdo
+    )
+{
+    KIRQL               Irql;
+
+    ASSERT3U(KeGetCurrentIrql(), ==, PASSIVE_LEVEL);
+
+    KeRaiseIrql(DISPATCH_LEVEL, &Irql);
+
+    XENBUS_SUSPEND(Deregister,
+                   &Pdo->SuspendInterface,
+                   Pdo->SuspendCallbackLate);
+    Pdo->SuspendCallbackLate = NULL;
+
+    __PdoD0ToD3(Pdo);
+
+    XENBUS_SUSPEND(Release, &Pdo->SuspendInterface);
+
+    KeLowerIrql(Irql);
+}
+
+// This function must not touch pageable code or data
+static DECLSPEC_NOINLINE VOID
+PdoS4ToS3(
+    IN  PXENVKBD_PDO    Pdo
+    )
+{
+    Trace("(%s) ====>\n", __PdoGetName(Pdo));
+
+    ASSERT3U(KeGetCurrentIrql(), ==, PASSIVE_LEVEL);
+    ASSERT3U(__PdoGetSystemPowerState(Pdo), ==, PowerSystemHibernate);
+
+    __PdoSetSystemPowerState(Pdo, PowerSystemSleeping3);
+
+    Trace("(%s) <====\n", __PdoGetName(Pdo));
+}
+
+// This function must not touch pageable code or data
+static DECLSPEC_NOINLINE VOID
+PdoS3ToS4(
+    IN  PXENVKBD_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 NTSTATUS
+PdoStartDevice(
+    IN  PXENVKBD_PDO    Pdo,
+    IN  PIRP            Irp
+    )
+{
+    NTSTATUS            status;
+
+    status = PdoD3ToD0(Pdo);
+    if (!NT_SUCCESS(status))
+        goto fail1;
+
+    __PdoSetDevicePnpState(Pdo, Started);
+
+    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;
+}
+
+static DECLSPEC_NOINLINE NTSTATUS
+PdoQueryStopDevice(
+    IN  PXENVKBD_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  PXENVKBD_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  PXENVKBD_PDO    Pdo,
+    IN  PIRP            Irp
+    )
+{
+    NTSTATUS            status;
+
+    if (__PdoGetDevicePowerState(Pdo) != PowerDeviceD0)
+        goto done;
+
+    PdoD0ToD3(Pdo);
+
+done:
+    __PdoSetDevicePnpState(Pdo, Stopped);
+    status = STATUS_SUCCESS;
+
+    Irp->IoStatus.Status = status;
+    IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+    return status;
+}
+
+static DECLSPEC_NOINLINE NTSTATUS
+PdoQueryRemoveDevice(
+    IN  PXENVKBD_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  PXENVKBD_PDO    Pdo,
+    IN  PIRP            Irp
+    )
+{
+    NTSTATUS            status;
+
+    if (__PdoClearEjectRequested(Pdo))
+        FrontendEjectFailed(__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  PXENVKBD_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  PXENVKBD_PDO    Pdo,
+    IN  PIRP            Irp
+    )
+{
+    PXENVKBD_FDO        Fdo = __PdoGetFdo(Pdo);
+    BOOLEAN             NeedInvalidate;
+    NTSTATUS            status;
+
+    if (__PdoGetDevicePowerState(Pdo) != PowerDeviceD0)
+        goto done;
+
+    PdoD0ToD3(Pdo);
+
+done:
+    NeedInvalidate = FALSE;
+
+    FdoAcquireMutex(Fdo);
+
+    if (__PdoIsMissing(Pdo)) {
+        DEVICE_PNP_STATE    State = __PdoGetDevicePnpState(Pdo);
+
+        __PdoSetDevicePnpState(Pdo, Deleted);
+
+        if (State == SurpriseRemovePending)
+            PdoDestroy(Pdo);
+        else
+            NeedInvalidate = TRUE;
+    } else {
+        __PdoSetDevicePnpState(Pdo, Enumerated);
+    }
+
+    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  PXENVKBD_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 NTSTATUS
+PdoDelegateIrp(
+    IN  PXENVKBD_PDO    Pdo,
+    IN  PIRP            Irp
+    )
+{
+    return FdoDelegateIrp(Pdo->Fdo, Irp);
+}
+
+static NTSTATUS
+PdoQueryBusInterface(
+    IN  PXENVKBD_PDO        Pdo,
+    IN  PIRP                Irp
+    )
+{
+    PIO_STACK_LOCATION      StackLocation;
+    USHORT                  Size;
+    USHORT                  Version;
+    PBUS_INTERFACE_STANDARD BusInterface;
+    NTSTATUS                status;
+
+    status = Irp->IoStatus.Status;        
+
+    StackLocation = IoGetCurrentIrpStackLocation(Irp);
+    Size = StackLocation->Parameters.QueryInterface.Size;
+    Version = StackLocation->Parameters.QueryInterface.Version;
+    BusInterface = (PBUS_INTERFACE_STANDARD)StackLocation->Parameters.QueryInterface.Interface;
+
+    if (Version != 1)
+        goto done;
+
+    status = STATUS_BUFFER_TOO_SMALL;        
+    if (Size < sizeof (BUS_INTERFACE_STANDARD))
+        goto done;
+
+    *BusInterface = Pdo->BusInterface;
+    BusInterface->InterfaceReference(BusInterface->Context);
+
+    Irp->IoStatus.Information = 0;
+    status = STATUS_SUCCESS;
+
+done:
+    return status;
+}
+
+static NTSTATUS
+PdoQueryHidInterface(
+    IN  PXENVKBD_PDO        Pdo,
+    IN  PIRP                Irp
+    )
+{
+    PIO_STACK_LOCATION      StackLocation;
+    USHORT                  Size;
+    USHORT                  Version;
+    PINTERFACE              Interface;
+    PVOID                   Context;
+    NTSTATUS                status;
+
+    status = Irp->IoStatus.Status;        
+
+    StackLocation = IoGetCurrentIrpStackLocation(Irp);
+    Size = StackLocation->Parameters.QueryInterface.Size;
+    Version = StackLocation->Parameters.QueryInterface.Version;
+    Interface = StackLocation->Parameters.QueryInterface.Interface;
+
+    Context = __PdoGetHidContext(Pdo);
+
+    status = HidGetInterface(Context,
+                             Version,
+                             Interface,
+                             Size);
+    if (!NT_SUCCESS(status))
+        goto done;
+
+    Irp->IoStatus.Information = 0;
+    status = STATUS_SUCCESS;
+
+done:
+    return status;
+}
+
+struct _INTERFACE_ENTRY {
+    const GUID  *Guid;
+    const CHAR  *Name;
+    NTSTATUS    (*Query)(PXENVKBD_PDO, PIRP);
+};
+
+struct _INTERFACE_ENTRY PdoInterfaceTable[] = {
+    { &GUID_BUS_INTERFACE_STANDARD, "BUS_INTERFACE", PdoQueryBusInterface },
+    { &GUID_XENHID_HID_INTERFACE, "HID_INTERFACE", PdoQueryHidInterface },
+    { &GUID_XENBUS_CACHE_INTERFACE, "CACHE_INTERFACE", PdoDelegateIrp },
+    { &GUID_XENBUS_STORE_INTERFACE, "STORE_INTERFACE", PdoDelegateIrp },
+    { &GUID_XENBUS_SUSPEND_INTERFACE, "SUSPEND_INTERFACE", PdoDelegateIrp },
+    { NULL, NULL, NULL }
+};
+
+static DECLSPEC_NOINLINE NTSTATUS
+PdoQueryInterface(
+    IN  PXENVKBD_PDO        Pdo,
+    IN  PIRP                Irp
+    )
+{
+    PIO_STACK_LOCATION      StackLocation;
+    const GUID              *InterfaceType;
+    struct _INTERFACE_ENTRY *Entry;
+    USHORT                  Version;
+    NTSTATUS                status;
+
+    status = Irp->IoStatus.Status;        
+
+    if (status != STATUS_NOT_SUPPORTED)
+        goto done;
+
+    StackLocation = IoGetCurrentIrpStackLocation(Irp);
+    InterfaceType = StackLocation->Parameters.QueryInterface.InterfaceType;
+    Version = StackLocation->Parameters.QueryInterface.Version;
+
+    for (Entry = PdoInterfaceTable; Entry->Guid != NULL; Entry++) {
+        if (IsEqualGUID(InterfaceType, Entry->Guid)) {
+            Info("%s: %s (VERSION %d)\n",
+                 __PdoGetName(Pdo),
+                 Entry->Name,
+                 Version);
+            status = Entry->Query(Pdo, Irp);
+            goto done;
+        }
+    }
+
+done:
+    Irp->IoStatus.Status = status;
+    IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+    return status;
+}
+
+static DECLSPEC_NOINLINE NTSTATUS
+PdoQueryCapabilities(
+    IN  PXENVKBD_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
+PdoQueryDeviceText(
+    IN  PXENVKBD_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(__PdoGetFdo(Pdo)),
+                                    __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  PXENVKBD_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  PXENVKBD_PDO    Pdo,
+    IN  PIRP            Irp
+    )
+{
+    UNREFERENCED_PARAMETER(Pdo);
+
+    Irp->IoStatus.Status = STATUS_NOT_SUPPORTED;
+    IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+    return STATUS_NOT_SUPPORTED;
+}
+
+#define REGSTR_VAL_MAX_HCID_LEN 1024
+
+static DECLSPEC_NOINLINE NTSTATUS
+PdoQueryId(
+    IN  PXENVKBD_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");
+        Id.MaximumLength = (USHORT)(strlen(__PdoGetName(Pdo)) + 1) * sizeof (WCHAR);
+        break;
+
+    case BusQueryDeviceID:
+        Trace("BusQueryDeviceID\n");
+        Id.MaximumLength = (MAX_DEVICE_ID_LEN - 2) * sizeof (WCHAR);
+        break;
+
+    case BusQueryHardwareIDs:
+        Trace("BusQueryHardwareIDs\n");
+        Id.MaximumLength = (USHORT)(MAX_DEVICE_ID_LEN * ARRAYSIZE(PdoRevision)) * sizeof (WCHAR);
+        break;
+
+    case BusQueryCompatibleIDs:
+        Trace("BusQueryCompatibleIDs\n");
+        Id.MaximumLength = (USHORT)(MAX_DEVICE_ID_LEN * ARRAYSIZE(PdoRevision)) * sizeof (WCHAR);
+        break;
+
+    default:
+        Irp->IoStatus.Information = 0;
+        status = STATUS_NOT_SUPPORTED;
+        goto done;
+    }
+
+    Buffer = ExAllocatePoolWithTag(PagedPool, Id.MaximumLength, 'FIV');
+
+    status = STATUS_NO_MEMORY;
+    if (Buffer == NULL)
+        goto done;
+
+    RtlZeroMemory(Buffer, Id.MaximumLength);
+
+    Id.Buffer = Buffer;
+    Id.Length = 0;
+
+    switch (StackLocation->Parameters.QueryId.IdType) {
+    case BusQueryInstanceID:
+        Type = REG_SZ;
+
+        status = RtlStringCbPrintfW(Buffer,
+                                    Id.MaximumLength,
+                                    L"%hs",
+                                    __PdoGetName(Pdo));
+        ASSERT(NT_SUCCESS(status));
+
+        Buffer += wcslen(Buffer);
+
+        break;
+
+    case BusQueryDeviceID: {
+        ULONG                   Index;
+        PXENVKBD_PDO_REVISION    Revision;
+
+        Type = REG_SZ;
+        Index = ARRAYSIZE(PdoRevision) - 1;
+        Revision = &PdoRevision[Index];
+
+        status = RtlStringCbPrintfW(Buffer,
+                                    Id.MaximumLength,
+                                    L"XENVKBD\\VEN_%hs&DEV_HID&REV_%08X",
+                                    __PdoGetVendorName(Pdo),
+                                    Revision->Number);
+        ASSERT(NT_SUCCESS(status));
+
+        Buffer += wcslen(Buffer);
+
+        break;
+    }
+    case BusQueryHardwareIDs:
+    case BusQueryCompatibleIDs: {
+        LONG    Index;
+        ULONG   Length;
+
+        Type = REG_MULTI_SZ;
+        Index = ARRAYSIZE(PdoRevision) - 1;
+
+        Length = Id.MaximumLength;
+
+        while (Index >= 0) {
+            PXENVKBD_PDO_REVISION    Revision = &PdoRevision[Index];
+
+            status = RtlStringCbPrintfW(Buffer,
+                                        Length,
+                                        L"XENVKBD\\VEN_%hs&DEV_HID&REV_%08X",
+                                        __PdoGetVendorName(Pdo),
+                                        Revision->Number);
+            ASSERT(NT_SUCCESS(status));
+
+            Buffer += wcslen(Buffer);
+            Length -= (ULONG)(wcslen(Buffer) * sizeof (WCHAR));
+
+            Buffer++;
+            Length -= sizeof (WCHAR);
+
+            --Index;
+        }
+
+        status = RtlStringCbPrintfW(Buffer,
+                                    Length,
+                                    L"XENDEVICE");
+        ASSERT(NT_SUCCESS(status));
+
+        Buffer += wcslen(Buffer);
+        Buffer++;
+
+        ASSERT3U((ULONG_PTR)Buffer - (ULONG_PTR)Id.Buffer, <,
+                 REGSTR_VAL_MAX_HCID_LEN);
+        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  PXENVKBD_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  PXENVKBD_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  PXENVKBD_PDO    Pdo,
+    IN  PIRP            Irp
+    )
+{
+    PXENVKBD_FDO        Fdo = __PdoGetFdo(Pdo);
+    NTSTATUS            status;
+
+    Trace("%s\n", __PdoGetName(Pdo));
+
+    FdoAcquireMutex(Fdo);
+
+    __PdoSetDevicePnpState(Pdo, Deleted);
+    __PdoSetMissing(Pdo, "device ejected");
+
+    FdoReleaseMutex(Fdo);
+
+    IoInvalidateDeviceRelations(FdoGetPhysicalDeviceObject(Fdo),
+                                BusRelations);
+
+    status = STATUS_SUCCESS;
+
+    Irp->IoStatus.Status = status;
+    IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+    return status;
+}
+
+static DECLSPEC_NOINLINE NTSTATUS
+PdoDispatchPnp(
+    IN  PXENVKBD_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_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  PXENVKBD_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  PXENVKBD_THREAD Self,
+    IN  PVOID           Context
+    )
+{
+    PXENVKBD_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  PXENVKBD_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  PXENVKBD_THREAD Self,
+    IN  PVOID           Context
+    )
+{
+    PXENVKBD_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  PXENVKBD_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  PXENVKBD_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  PXENVKBD_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  PXENVKBD_PDO    Pdo,
+    IN  PIRP            Irp
+    )
+{
+    NTSTATUS            status;
+
+    UNREFERENCED_PARAMETER(Pdo);
+
+    status = Irp->IoStatus.Status;
+    IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+    return status;
+}
+
+NTSTATUS
+PdoDispatch(
+    IN  PXENVKBD_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;
+}
+
+NTSTATUS
+PdoResume(
+    IN  PXENVKBD_PDO    Pdo
+    )
+{
+    return FrontendResume(__PdoGetFrontend(Pdo));
+}
+
+VOID
+PdoSuspend(
+    IN  PXENVKBD_PDO    Pdo
+    )
+{
+    FrontendSuspend(__PdoGetFrontend(Pdo));
+}
+
+NTSTATUS
+PdoCreate(
+    IN  PXENVKBD_FDO    Fdo,
+    IN  PANSI_STRING    Path
+    )
+{
+    PDEVICE_OBJECT      PhysicalDeviceObject;
+    PXENVKBD_DX         Dx;
+    PXENVKBD_PDO        Pdo;
+    NTSTATUS            status;
+
+#pragma prefast(suppress:28197) // Possibly leaking memory 'PhysicalDeviceObject'
+    status = IoCreateDevice(DriverGetDriverObject(),
+                            sizeof(XENVKBD_DX),
+                            NULL,
+                            FILE_DEVICE_UNKNOWN,
+                            FILE_DEVICE_SECURE_OPEN | FILE_AUTOGENERATED_DEVICE_NAME,
+                            FALSE,
+                            &PhysicalDeviceObject);
+    if (!NT_SUCCESS(status))
+        goto fail1;
+
+    Dx = (PXENVKBD_DX)PhysicalDeviceObject->DeviceExtension;
+    RtlZeroMemory(Dx, sizeof (XENVKBD_DX));
+
+    Dx->Type = PHYSICAL_DEVICE_OBJECT;
+    Dx->DeviceObject = PhysicalDeviceObject;
+    Dx->DevicePnpState = Present;
+    Dx->SystemPowerState = PowerSystemWorking;
+    Dx->DevicePowerState = PowerDeviceD3;
+
+    Pdo = __PdoAllocate(sizeof (XENVKBD_PDO));
+
+    status = STATUS_NO_MEMORY;
+    if (Pdo == NULL)
+        goto fail2;
+
+    Pdo->Dx = Dx;
+    Pdo->Fdo = Fdo;
+
+    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, Path->Buffer);
+
+    status = BusInitialize(Pdo, &Pdo->BusInterface);
+    if (!NT_SUCCESS(status))
+        goto fail5;
+
+    status = HidInitialize(Pdo, &Pdo->HidContext);
+    if (!NT_SUCCESS(status))
+        goto fail6;
+
+    status = FrontendInitialize(Pdo, &Pdo->Frontend);
+    if (!NT_SUCCESS(status))
+        goto fail7;
+
+    FdoGetSuspendInterface(Fdo, &Pdo->SuspendInterface);
+    FdoGetUnplugInterface(Fdo, &Pdo->UnplugInterface);
+
+    Dx->Pdo = Pdo;
+
+    status = FdoAddPhysicalDeviceObject(Fdo, Pdo);
+    if (!NT_SUCCESS(status))
+        goto fail8;
+
+    status = STATUS_UNSUCCESSFUL;
+    if (__PdoIsEjectRequested(Pdo))
+        goto fail9;
+
+    Info("%p (%s)\n",
+         PhysicalDeviceObject,
+         __PdoGetName(Pdo));
+
+    PdoDumpRevisions(Pdo);
+
+    PhysicalDeviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
+    return STATUS_SUCCESS;
+
+fail9:
+    Error("fail9\n");
+
+    FdoRemovePhysicalDeviceObject(Fdo, Pdo);
+
+fail8:
+    Error("fail8\n");
+
+    (VOID) __PdoClearEjectRequested(Pdo);
+
+    Dx->Pdo = NULL;
+
+    RtlZeroMemory(&Pdo->UnplugInterface,
+                  sizeof (XENBUS_UNPLUG_INTERFACE));
+
+    RtlZeroMemory(&Pdo->SuspendInterface,
+                  sizeof (XENBUS_SUSPEND_INTERFACE));
+
+    FrontendTeardown(__PdoGetFrontend(Pdo));
+    Pdo->Frontend = NULL;
+
+fail7:
+    Error("fail7\n");
+
+    HidTeardown(Pdo->HidContext);
+    Pdo->HidContext = NULL;
+
+fail6:
+    Error("fail6\n");
+
+    BusTeardown(&Pdo->BusInterface);
+
+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->Fdo = NULL;
+    Pdo->Dx = NULL;
+
+    ASSERT(IsZeroMemory(Pdo, sizeof (XENVKBD_PDO)));
+    __PdoFree(Pdo);
+
+fail2:
+    Error("fail2\n");
+
+    IoDeleteDevice(PhysicalDeviceObject);
+
+fail1:
+    Error("fail1 (%08x)\n", status);
+
+    return status;
+}
+
+VOID
+PdoDestroy(
+    IN  PXENVKBD_PDO    Pdo
+    )
+{
+    PXENVKBD_DX         Dx = Pdo->Dx;
+    PDEVICE_OBJECT      PhysicalDeviceObject = Dx->DeviceObject;
+    PXENVKBD_FDO        Fdo = __PdoGetFdo(Pdo);
+
+    Pdo->UnplugRequested = FALSE;
+
+    ASSERT3U(__PdoGetDevicePnpState(Pdo), ==, Deleted);
+
+    ASSERT(__PdoIsMissing(Pdo));
+    Pdo->Missing = FALSE;
+
+    Info("%p (%s) (%s)\n",
+         PhysicalDeviceObject,
+         __PdoGetName(Pdo),
+         Pdo->Reason);
+
+    Pdo->Reason = NULL;
+
+    FdoRemovePhysicalDeviceObject(Fdo, Pdo);
+
+    (VOID) __PdoClearEjectRequested(Pdo);
+
+    Dx->Pdo = NULL;
+
+    RtlZeroMemory(&Pdo->UnplugInterface,
+                  sizeof (XENBUS_UNPLUG_INTERFACE));
+
+    RtlZeroMemory(&Pdo->SuspendInterface,
+                  sizeof (XENBUS_SUSPEND_INTERFACE));
+
+    FrontendTeardown(__PdoGetFrontend(Pdo));
+    Pdo->Frontend = NULL;    
+
+    HidTeardown(Pdo->HidContext);
+    Pdo->HidContext = NULL;
+
+    BusTeardown(&Pdo->BusInterface);
+
+    ThreadAlert(Pdo->DevicePowerThread);
+    ThreadJoin(Pdo->DevicePowerThread);
+    Pdo->DevicePowerThread = NULL;
+
+    ThreadAlert(Pdo->SystemPowerThread);
+    ThreadJoin(Pdo->SystemPowerThread);
+    Pdo->SystemPowerThread = NULL;
+
+    Pdo->Fdo = NULL;
+    Pdo->Dx = NULL;
+
+    ASSERT(IsZeroMemory(Pdo, sizeof (XENVKBD_PDO)));
+    __PdoFree(Pdo);
+
+    IoDeleteDevice(PhysicalDeviceObject);
+}
diff --git a/src/xenvkbd/pdo.h b/src/xenvkbd/pdo.h
new file mode 100644 (file)
index 0000000..a23811e
--- /dev/null
@@ -0,0 +1,198 @@
+/* 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 _XENVKBD_PDO_H
+#define _XENVKBD_PDO_H
+
+#include <ntddk.h>
+#include <ifdef.h>
+
+#include "driver.h"
+#include "types.h"
+
+extern VOID
+PdoSetDevicePnpState(
+    IN  PXENVKBD_PDO        Pdo,
+    IN  DEVICE_PNP_STATE    State
+    );
+
+extern DEVICE_PNP_STATE
+PdoGetDevicePnpState(
+    IN  PXENVKBD_PDO    Pdo
+    );
+
+extern VOID
+PdoSetMissing(
+    IN  PXENVKBD_PDO    Pdo,
+    IN  const CHAR      *Reason
+    );
+
+extern BOOLEAN
+PdoIsMissing(
+    IN  PXENVKBD_PDO    Pdo
+    );
+
+extern VOID
+PdoRequestEject(
+    IN  PXENVKBD_PDO    Pdo
+    );
+
+extern BOOLEAN
+PdoIsEjectRequested(
+    IN  PXENVKBD_PDO    Pdo
+    );
+
+extern PCHAR
+PdoGetName(
+    IN  PXENVKBD_PDO    Pdo
+    );
+
+extern PXENVKBD_FDO
+PdoGetFdo(
+    IN  PXENVKBD_PDO    Pdo
+    );
+
+extern PDEVICE_OBJECT
+PdoGetDeviceObject(
+    IN  PXENVKBD_PDO    Pdo
+    );
+
+extern PDMA_ADAPTER
+PdoGetDmaAdapter(
+    IN  PXENVKBD_PDO        Pdo,
+    IN  PDEVICE_DESCRIPTION DeviceDescriptor,
+    OUT PULONG              NumberOfMapRegisters
+    );
+
+extern BOOLEAN
+PdoTranslateBusAddress(
+    IN      PXENVKBD_PDO        Pdo,
+    IN      PHYSICAL_ADDRESS    BusAddress,
+    IN      ULONG               Length,
+    IN OUT  PULONG              AddressSpace,
+    OUT     PPHYSICAL_ADDRESS   TranslatedAddress
+    );
+
+extern ULONG
+PdoSetBusData(
+    IN  PXENVKBD_PDO    Pdo,
+    IN  ULONG           DataType,
+    IN  PVOID           Buffer,
+    IN  ULONG           Offset,
+    IN  ULONG           Length
+    );
+
+extern ULONG
+PdoGetBusData(
+    IN  PXENVKBD_PDO    Pdo,
+    IN  ULONG           DataType,
+    IN  PVOID           Buffer,
+    IN  ULONG           Offset,
+    IN  ULONG           Length
+    );
+
+extern NTSTATUS
+PdoCreate(
+    IN  PXENVKBD_FDO    Fdo,
+    IN  PANSI_STRING    Path
+    );
+
+extern NTSTATUS
+PdoResume(
+    IN  PXENVKBD_PDO    Pdo
+    );
+
+extern VOID
+PdoSuspend(
+    IN  PXENVKBD_PDO    Pdo
+    );
+
+extern VOID
+PdoDestroy(
+    IN  PXENVKBD_PDO    Pdo
+    );
+
+#include "frontend.h"
+
+extern PXENVKBD_FRONTEND
+PdoGetFrontend(
+    IN  PXENVKBD_PDO    Pdo
+    );
+
+extern PXENBUS_EVTCHN_INTERFACE
+PdoGetEvtchnInterface(
+    IN  PXENVKBD_PDO    Pdo
+    );
+
+extern PXENBUS_DEBUG_INTERFACE
+PdoGetDebugInterface(
+    IN  PXENVKBD_PDO    Pdo
+    );
+
+extern PXENBUS_STORE_INTERFACE
+PdoGetStoreInterface(
+    IN  PXENVKBD_PDO    Pdo
+    );
+
+extern PXENBUS_CACHE_INTERFACE
+PdoGetCacheInterface(
+    IN  PXENVKBD_PDO    Pdo
+    );
+
+extern PXENBUS_GNTTAB_INTERFACE
+PdoGetGnttabInterface(
+    IN  PXENVKBD_PDO    Pdo
+    );
+
+extern PXENBUS_SUSPEND_INTERFACE
+PdoGetSuspendInterface(
+    IN  PXENVKBD_PDO    Pdo
+    );
+
+#include "hid.h"
+
+extern PXENVKBD_HID_CONTEXT
+PdoGetHidContext(
+    IN  PXENVKBD_PDO    Pdo
+    );
+
+extern PXENHID_HID_INTERFACE
+PdoGetHidInterface(
+    IN  PXENVKBD_PDO    Pdo
+    );
+
+extern NTSTATUS
+PdoDispatch(
+    IN  PXENVKBD_PDO    Pdo,
+    IN  PIRP            Irp
+    );
+
+#endif  // _XENVKBD_PDO_H
diff --git a/src/xenvkbd/registry.c b/src/xenvkbd/registry.c
new file mode 100644 (file)
index 0000000..fb05691
--- /dev/null
@@ -0,0 +1,1492 @@
+/* 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 "registry.h"
+#include "assert.h"
+#include "util.h"
+
+#define REGISTRY_TAG 'GERX'
+
+static UNICODE_STRING   RegistryPath;
+
+static FORCEINLINE PVOID
+__RegistryAllocate(
+    IN  ULONG   Length
+    )
+{
+    return __AllocatePoolWithTag(NonPagedPool, Length, REGISTRY_TAG);
+}
+
+static FORCEINLINE VOID
+__RegistryFree(
+    IN  PVOID   Buffer
+    )
+{
+    __FreePoolWithTag(Buffer, REGISTRY_TAG);
+}
+
+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
+RegistryOpenKey(
+    IN  HANDLE          Parent,
+    IN  PUNICODE_STRING Path,
+    IN  ACCESS_MASK     DesiredAccess,
+    OUT PHANDLE         Key
+    )
+{
+    OBJECT_ATTRIBUTES   Attributes;
+    NTSTATUS            status;
+
+    InitializeObjectAttributes(&Attributes,
+                               Path,
+                               OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
+                               Parent,
+                               NULL);
+
+    status = ZwOpenKey(Key,
+                       DesiredAccess,
+                       &Attributes);
+    if (!NT_SUCCESS(status))
+        goto fail1;
+
+    return STATUS_SUCCESS;
+
+fail1:
+    return status;
+}
+
+static NTSTATUS
+RegistryOpenRoot(
+    IN  PWCHAR          Path,
+    OUT PHANDLE         Parent,
+    OUT PWCHAR          *ChildPath
+    )
+{
+    const WCHAR         Prefix[] = L"\\Registry\\Machine\\";
+    ULONG               Length;
+    UNICODE_STRING      Unicode;
+    NTSTATUS            status;
+
+    Length = (ULONG)wcslen(Prefix);
+
+    status = STATUS_INVALID_PARAMETER;
+    if (_wcsnicmp(Path, Prefix, Length) != 0)
+        goto fail1;
+
+    RtlInitUnicodeString(&Unicode, Prefix);
+
+    status = RegistryOpenKey(NULL, &Unicode, KEY_ALL_ACCESS, Parent);
+    if (!NT_SUCCESS(status))
+        goto fail2;
+
+    *ChildPath = Path + Length;
+
+    return STATUS_SUCCESS;
+
+fail2:
+fail1:
+    return status;
+}
+
+NTSTATUS
+RegistryCreateKey(
+    IN  HANDLE          Parent,
+    IN  PUNICODE_STRING Path,
+    IN  ULONG           Options,
+    OUT PHANDLE         Key
+    )
+{
+    PWCHAR              Buffer;
+    HANDLE              Root;
+    PWCHAR              ChildPath;
+    PWCHAR              ChildName;
+    PWCHAR              Context;
+    HANDLE              Child;
+    NTSTATUS            status;
+
+    //
+    // UNICODE_STRINGs are not guaranteed to have NUL terminated
+    // buffers.
+    //
+
+    Buffer = __RegistryAllocate(Path->MaximumLength + sizeof (WCHAR));
+
+    status = STATUS_NO_MEMORY;
+    if (Buffer == NULL)
+        goto fail1;
+
+    RtlCopyMemory(Buffer, Path->Buffer, Path->Length);
+
+    Root = Parent;
+
+    if (Parent != NULL) {
+        ChildPath = Buffer;
+    } else {
+        status = RegistryOpenRoot(Buffer, &Parent, &ChildPath);
+        if (!NT_SUCCESS(status))
+            goto fail2;
+    }
+
+    ChildName = __wcstok_r(ChildPath, L"\\", &Context);
+
+    status = STATUS_INVALID_PARAMETER;
+    if (ChildName == NULL)
+        goto fail3;
+
+    Child = NULL;
+
+    while (ChildName != NULL) {
+        UNICODE_STRING      Unicode;
+        OBJECT_ATTRIBUTES   Attributes;
+
+        RtlInitUnicodeString(&Unicode, ChildName);
+
+        InitializeObjectAttributes(&Attributes,
+                                   &Unicode,
+                                   OBJ_CASE_INSENSITIVE |
+                                   OBJ_KERNEL_HANDLE |
+                                   OBJ_OPENIF,
+                                   Parent,
+                                   NULL);
+
+        status = ZwCreateKey(&Child,
+                             KEY_ALL_ACCESS,
+                             &Attributes,
+                             0,
+                             NULL,
+                             Options,
+                             NULL
+                             );
+        if (!NT_SUCCESS(status))
+            goto fail4;
+
+        ChildName = __wcstok_r(NULL, L"\\", &Context);
+
+        if (Parent != Root)
+            ZwClose(Parent);
+
+        Parent = Child;
+    }
+
+    ASSERT(Child != NULL);
+
+    *Key = Child;
+
+    __RegistryFree(Buffer);
+
+    return STATUS_SUCCESS;
+
+fail4:
+fail3:
+    if (Parent != Root)
+        ZwClose(Parent);
+
+fail2:
+    __RegistryFree(Buffer);
+
+fail1:
+    return status;
+}
+
+NTSTATUS
+RegistryOpenServiceKey(
+    IN  ACCESS_MASK     DesiredAccess,
+    OUT PHANDLE         Key
+    )
+{
+    return RegistryOpenKey(NULL, &RegistryPath, DesiredAccess, Key);
+}
+
+NTSTATUS
+RegistryCreateServiceKey(
+    OUT PHANDLE         Key
+    )
+{
+    return RegistryCreateKey(NULL, &RegistryPath, REG_OPTION_NON_VOLATILE, Key);
+}
+
+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
+    )
+{
+    HANDLE                  SubKey;
+    ULONG                   Length;
+    PKEY_NAME_INFORMATION   Info;
+    PWCHAR                  Cursor;
+    UNICODE_STRING          Unicode;
+    NTSTATUS                status;
+
+    status = IoOpenDeviceRegistryKey(DeviceObject,
+                                     PLUGPLAY_REGKEY_DEVICE,
+                                     KEY_READ,
+                                     &SubKey);
+    if (!NT_SUCCESS(status))
+        goto fail1;
+
+    Length = 0;
+    status = ZwQueryKey(SubKey,
+                        KeyNameInformation,
+                        NULL,
+                        0,
+                        &Length);
+    if (status != STATUS_BUFFER_OVERFLOW &&
+        status != STATUS_BUFFER_TOO_SMALL)
+        goto fail2;
+
+#pragma prefast(suppress:6102)
+    Info = __RegistryAllocate(Length + sizeof (WCHAR));
+
+    status = STATUS_NO_MEMORY;
+    if (Info == NULL)
+        goto fail3;
+
+    status = ZwQueryKey(SubKey,
+                        KeyNameInformation,
+                        Info,
+                        Length,
+                        &Length);
+    if (!NT_SUCCESS(status))
+        goto fail4;
+
+    Info->Name[Info->NameLength / sizeof (WCHAR)] = '\0';
+
+    Cursor = wcsrchr(Info->Name, L'\\');
+    ASSERT(Cursor != NULL);
+
+    *Cursor = L'\0';
+    
+    RtlInitUnicodeString(&Unicode, Info->Name);
+
+    status = RegistryOpenKey(NULL, &Unicode, DesiredAccess, Key);
+    if (!NT_SUCCESS(status))
+        goto fail5;
+
+    __RegistryFree(Info);
+
+    RegistryCloseKey(SubKey);
+
+    return STATUS_SUCCESS;
+
+fail5:
+fail4:
+    __RegistryFree(Info);
+
+fail3:
+fail2:
+    RegistryCloseKey(SubKey);
+
+fail1:
+    return status;
+}
+
+NTSTATUS
+RegistryOpenSubKey(
+    IN  PHANDLE         Key,
+    IN  PCHAR           Name,
+    IN  ACCESS_MASK     DesiredAccess,
+    OUT PHANDLE         SubKey
+    )
+{
+    ANSI_STRING         Ansi;
+    UNICODE_STRING      Unicode;
+    NTSTATUS            status;
+
+    RtlInitAnsiString(&Ansi, Name);
+
+    status = RtlAnsiStringToUnicodeString(&Unicode, &Ansi, TRUE);
+    if (!NT_SUCCESS(status))
+        goto fail1;
+
+    status = RegistryOpenKey(Key, &Unicode, DesiredAccess, SubKey);
+    if (!NT_SUCCESS(status))
+        goto fail2;
+
+    RtlFreeUnicodeString(&Unicode);
+
+    return STATUS_SUCCESS;
+
+fail2:
+    RtlFreeUnicodeString(&Unicode);
+
+fail1:
+    return status;
+}
+
+NTSTATUS
+RegistryCreateSubKey(
+    IN  PHANDLE         Key,
+    IN  PCHAR           Name,
+    IN  ULONG           Options,
+    OUT PHANDLE         SubKey
+    )
+{
+    ANSI_STRING         Ansi;
+    UNICODE_STRING      Unicode;
+    NTSTATUS            status;
+
+    RtlInitAnsiString(&Ansi, Name);
+
+    status = RtlAnsiStringToUnicodeString(&Unicode, &Ansi, TRUE);
+    if (!NT_SUCCESS(status))
+        goto fail1;
+
+    status = RegistryCreateKey(Key, &Unicode, Options, SubKey);
+    if (!NT_SUCCESS(status))
+        goto fail2;
+
+    RtlFreeUnicodeString(&Unicode);
+
+    return STATUS_SUCCESS;
+
+fail2:
+    RtlFreeUnicodeString(&Unicode);
+
+fail1:
+    return status;
+}
+
+NTSTATUS
+RegistryDeleteSubKey(
+    IN  PHANDLE         Key,
+    IN  PCHAR           Name
+    )
+{
+    ANSI_STRING         Ansi;
+    UNICODE_STRING      Unicode;
+    HANDLE              SubKey;
+    NTSTATUS            status;
+
+    RtlInitAnsiString(&Ansi, Name);
+
+    status = RtlAnsiStringToUnicodeString(&Unicode, &Ansi, TRUE);
+    if (!NT_SUCCESS(status))
+        goto fail1;
+
+    status = RegistryOpenKey(Key, &Unicode, KEY_ALL_ACCESS, &SubKey);
+    if (!NT_SUCCESS(status))
+        goto fail2;
+
+    status = ZwDeleteKey(SubKey);
+    if (!NT_SUCCESS(status))
+        goto fail3;
+
+    ZwClose(SubKey);
+
+    (VOID) ZwFlushKey(Key);
+
+    RtlFreeUnicodeString(&Unicode);
+
+    return STATUS_SUCCESS;
+
+fail3:
+    ZwClose(SubKey);
+
+fail2:
+    RtlFreeUnicodeString(&Unicode);
+
+fail1:
+    return status;
+}
+
+NTSTATUS
+RegistryEnumerateSubKeys(
+    IN  HANDLE              Key,
+    IN  NTSTATUS            (*Callback)(PVOID, HANDLE, PANSI_STRING),
+    IN  PVOID               Context
+    )
+{
+    ULONG                   Size;
+    NTSTATUS                status;
+    PKEY_FULL_INFORMATION   Full;
+    PKEY_BASIC_INFORMATION  Basic;
+    ULONG                   Index;
+
+    status = ZwQueryKey(Key,
+                        KeyFullInformation,
+                        NULL,
+                        0,
+                        &Size);
+    if (status != STATUS_BUFFER_OVERFLOW &&
+        status != STATUS_BUFFER_TOO_SMALL)
+        goto fail1;
+
+#pragma prefast(suppress:6102)
+    Full = __RegistryAllocate(Size);
+
+    status = STATUS_NO_MEMORY;
+    if (Full == NULL)
+        goto fail2;
+
+    status = ZwQueryKey(Key,
+                        KeyFullInformation,
+                        Full,
+                        Size,
+                        &Size);
+    if (!NT_SUCCESS(status))
+        goto fail3;
+
+    Size = FIELD_OFFSET(KEY_BASIC_INFORMATION, Name) +
+           Full->MaxNameLen;
+
+    Basic = __RegistryAllocate(Size);
+    status = STATUS_NO_MEMORY;
+    if (Basic == NULL)
+        goto fail4;
+
+    for (Index = 0; Index < Full->SubKeys; Index++) {
+        ULONG           Ignore;
+        UNICODE_STRING  Unicode;
+        ANSI_STRING     Ansi;
+
+        status = ZwEnumerateKey(Key,
+                                Index,
+                                KeyBasicInformation,
+                                Basic,
+                                Size,
+                                &Ignore);
+        if (!NT_SUCCESS(status))
+            goto fail5;
+
+        Unicode.MaximumLength = (USHORT)Basic->NameLength;
+        Unicode.Buffer = Basic->Name;
+        Unicode.Length = (USHORT)Basic->NameLength;
+
+        Ansi.MaximumLength = (USHORT)((Basic->NameLength / sizeof (WCHAR)) + sizeof (CHAR));
+        Ansi.Buffer = __RegistryAllocate(Ansi.MaximumLength);
+
+        status = STATUS_NO_MEMORY;
+        if (Ansi.Buffer == NULL)
+            goto fail6;
+
+        status = RtlUnicodeStringToAnsiString(&Ansi, &Unicode, FALSE);
+        ASSERT(NT_SUCCESS(status));
+
+        Ansi.Length = (USHORT)(strlen(Ansi.Buffer) * sizeof (CHAR));        
+
+        status = Callback(Context, Key, &Ansi);
+
+        __RegistryFree(Ansi.Buffer);
+        Ansi.Buffer = NULL;
+
+        if (!NT_SUCCESS(status))
+            goto fail7;
+    }
+
+    __RegistryFree(Basic);
+
+    __RegistryFree(Full);
+
+    return STATUS_SUCCESS;
+
+fail7:
+fail6:
+fail5:
+    __RegistryFree(Basic);
+
+fail4:
+fail3:
+    __RegistryFree(Full);
+    
+fail2:
+fail1:
+    return status;
+}
+
+NTSTATUS
+RegistryEnumerateValues(
+    IN  HANDLE                      Key,
+    IN  NTSTATUS                    (*Callback)(PVOID, HANDLE, PANSI_STRING, ULONG),
+    IN  PVOID                       Context
+    )
+{
+    ULONG                           Size;
+    NTSTATUS                        status;
+    PKEY_FULL_INFORMATION           Full;
+    PKEY_VALUE_BASIC_INFORMATION    Basic;
+    ULONG                           Index;
+
+    status = ZwQueryKey(Key,
+                        KeyFullInformation,
+                        NULL,
+                        0,
+                        &Size);
+    if (status != STATUS_BUFFER_OVERFLOW &&
+        status != STATUS_BUFFER_TOO_SMALL)
+        goto fail1;
+
+#pragma prefast(suppress:6102)
+    Full = __RegistryAllocate(Size);
+
+    status = STATUS_NO_MEMORY;
+    if (Full == NULL)
+        goto fail2;
+
+    status = ZwQueryKey(Key,
+                        KeyFullInformation,
+                        Full,
+                        Size,
+                        &Size);
+    if (!NT_SUCCESS(status))
+        goto fail3;
+
+    Size = FIELD_OFFSET(KEY_VALUE_BASIC_INFORMATION, Name) +
+           Full->MaxValueNameLen;
+
+    Basic = __RegistryAllocate(Size);
+    status = STATUS_NO_MEMORY;
+    if (Basic == NULL)
+        goto fail4;
+
+    for (Index = 0; Index < Full->Values; Index++) {
+        ULONG           Ignore;
+        UNICODE_STRING  Unicode;
+        ANSI_STRING     Ansi;
+
+        status = ZwEnumerateValueKey(Key,
+                                     Index,
+                                     KeyValueBasicInformation,
+                                     Basic,
+                                     Size,
+                                     &Ignore);
+        if (!NT_SUCCESS(status))
+            goto fail5;
+
+        Unicode.MaximumLength = (USHORT)Basic->NameLength;
+        Unicode.Buffer = Basic->Name;
+        Unicode.Length = (USHORT)Basic->NameLength;
+
+        Ansi.MaximumLength = (USHORT)((Basic->NameLength / sizeof (WCHAR)) + sizeof (CHAR));
+        Ansi.Buffer = __RegistryAllocate(Ansi.MaximumLength);
+
+        status = RtlUnicodeStringToAnsiString(&Ansi, &Unicode, FALSE);
+        ASSERT(NT_SUCCESS(status));
+
+        Ansi.Length = (USHORT)(strlen(Ansi.Buffer) * sizeof (CHAR));        
+
+        status = Callback(Context, Key, &Ansi, Basic->Type);
+
+        __RegistryFree(Ansi.Buffer);
+
+        if (!NT_SUCCESS(status))
+            goto fail6;
+    }
+
+    __RegistryFree(Basic);
+
+    __RegistryFree(Full);
+
+    return STATUS_SUCCESS;
+
+fail6:
+fail5:
+    __RegistryFree(Basic);
+
+fail4:
+fail3:
+    __RegistryFree(Full);
+    
+fail2:
+fail1:
+    return status;
+}
+
+NTSTATUS
+RegistryDeleteValue(
+    IN  PHANDLE         Key,
+    IN  PCHAR           Name
+    )
+{
+    ANSI_STRING         Ansi;
+    UNICODE_STRING      Unicode;
+    NTSTATUS            status;
+
+    RtlInitAnsiString(&Ansi, Name);
+
+    status = RtlAnsiStringToUnicodeString(&Unicode, &Ansi, TRUE);
+    if (!NT_SUCCESS(status))
+        goto fail1;
+
+    status = ZwDeleteValueKey(Key, &Unicode);
+    if (!NT_SUCCESS(status))
+        goto fail2;
+
+    RtlFreeUnicodeString(&Unicode);
+
+    (VOID) ZwFlushKey(Key);
+
+    return STATUS_SUCCESS;
+
+fail2:
+    RtlFreeUnicodeString(&Unicode);
+
+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_OVERFLOW &&
+        status != STATUS_BUFFER_TOO_SMALL)
+        goto fail2;
+
+#pragma prefast(suppress:6102)
+    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);
+
+    (VOID) ZwFlushKey(Key);
+
+    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 PULONG                      Type OPTIONAL,
+    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_OVERFLOW &&
+        status != STATUS_BUFFER_TOO_SMALL)
+        goto fail2;
+
+#pragma prefast(suppress:6102)
+    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;
+
+    if (Type != NULL)
+        *Type = Value->Type;
+
+    __RegistryFree(Value);
+
+    RtlFreeUnicodeString(&Unicode);
+
+    return STATUS_SUCCESS;
+
+fail5:
+fail4:
+    __RegistryFree(Value);
+
+fail3:
+fail2:
+    RtlFreeUnicodeString(&Unicode);
+
+fail1:
+    return status;
+}
+
+NTSTATUS
+RegistryQueryBinaryValue(
+    IN  HANDLE                      Key,
+    IN  PCHAR                       Name,
+    OUT PVOID                       *Buffer,
+    OUT PULONG                      Length
+    )
+{
+    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_OVERFLOW &&
+        status != STATUS_BUFFER_TOO_SMALL)
+        goto fail2;
+
+#pragma prefast(suppress:6102)
+    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;
+
+    switch (Partial->Type) {
+    case REG_BINARY:
+        *Buffer = __RegistryAllocate(Partial->DataLength);
+
+        status = STATUS_NO_MEMORY;
+        if (*Buffer == NULL)
+            break;
+
+        *Length = Partial->DataLength;
+        RtlCopyMemory(*Buffer, Partial->Data, Partial->DataLength);
+        break;
+
+    default:
+        status = STATUS_INVALID_PARAMETER;
+        *Buffer = NULL;
+        break;
+    }
+
+    if (*Buffer == NULL)
+        goto fail5;
+
+    __RegistryFree(Partial);
+
+    RtlFreeUnicodeString(&Unicode);
+
+    return STATUS_SUCCESS;
+
+fail5:
+fail4:
+    __RegistryFree(Partial);
+
+fail3:
+fail2:
+    RtlFreeUnicodeString(&Unicode);
+
+fail1:
+    return status;
+}
+
+NTSTATUS
+RegistryUpdateBinaryValue(
+    IN  HANDLE                      Key,
+    IN  PCHAR                       Name,
+    IN  PVOID                       Buffer,
+    IN  ULONG                       Length
+    )
+{
+    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) +
+                                 Length);
+
+    status = STATUS_NO_MEMORY;
+    if (Partial == NULL)
+        goto fail2;
+
+    Partial->TitleIndex = 0;
+    Partial->Type = REG_BINARY;
+    Partial->DataLength = Length;
+    RtlCopyMemory(Partial->Data, Buffer, Partial->DataLength);
+
+    status = ZwSetValueKey(Key,
+                           &Unicode,
+                           Partial->TitleIndex,
+                           Partial->Type,
+                           Partial->Data,
+                           Partial->DataLength);
+    if (!NT_SUCCESS(status))
+        goto fail3;
+
+    __RegistryFree(Partial);
+
+    (VOID) ZwFlushKey(Key);
+
+    RtlFreeUnicodeString(&Unicode);
+
+    return STATUS_SUCCESS;
+
+fail3:
+    __RegistryFree(Partial);
+
+fail2:
+    RtlFreeUnicodeString(&Unicode);
+
+fail1:
+
+    return status;
+}
+
+NTSTATUS
+RegistryQueryKeyName(
+    IN  HANDLE              Key,
+    OUT PANSI_STRING        *Array
+    )
+{
+    PKEY_NAME_INFORMATION   Value;
+    ULONG                   Size;
+    NTSTATUS                status;
+
+    status = ZwQueryKey(Key,
+                        KeyNameInformation,
+                        NULL,
+                        0,
+                        &Size);
+    if (status != STATUS_BUFFER_OVERFLOW &&
+        status != STATUS_BUFFER_TOO_SMALL)
+        goto fail1;
+
+    // Name information is not intrinsically NULL terminated
+#pragma prefast(suppress:6102)
+    Value = __RegistryAllocate(Size + sizeof (WCHAR));
+
+    status = STATUS_NO_MEMORY;
+    if (Value == NULL)
+        goto fail2;
+
+    status = ZwQueryKey(Key,
+                        KeyNameInformation,
+                        Value,
+                        Size,
+                        &Size);
+    if (!NT_SUCCESS(status))
+        goto fail3;
+
+    Value->Name[Value->NameLength / sizeof (WCHAR)] = L'\0';
+    *Array = RegistrySzToAnsi((PWCHAR)Value->Name);
+
+    status = STATUS_NO_MEMORY;
+    if (*Array == NULL)
+        goto fail4;
+
+    __RegistryFree(Value);
+
+    return STATUS_SUCCESS;
+
+fail4:
+fail3:
+    __RegistryFree(Value);
+
+fail2:
+fail1:
+    return status;
+}
+
+NTSTATUS
+RegistryQuerySystemStartOption(
+    IN  PCHAR                       Prefix,
+    OUT PANSI_STRING                *Value
+    )
+{
+    UNICODE_STRING                  Unicode;
+    HANDLE                          Key;
+    PANSI_STRING                    Ansi;
+    ULONG                           Length;
+    PCHAR                           Option;
+    PCHAR                           Context;
+    NTSTATUS                        status;
+
+    RtlInitUnicodeString(&Unicode, L"\\Registry\\Machine\\SYSTEM\\CurrentControlSet\\Control");
+    
+    status = RegistryOpenKey(NULL, &Unicode, KEY_READ, &Key);
+    if (!NT_SUCCESS(status))
+        goto fail1;
+
+    status = RegistryQuerySzValue(Key, "SystemStartOptions", NULL, &Ansi);
+    if (!NT_SUCCESS(status))
+        goto fail2;
+
+    // SystemStartOptions is a space separated list of options.
+    // Scan it looking for the one we want.
+    Length = (ULONG)strlen(Prefix);
+
+    Option = __strtok_r(Ansi[0].Buffer, " ", &Context);
+    while (Option != NULL) {
+        if (strncmp(Prefix, Option, Length) == 0)
+            goto found;
+
+        Option = __strtok_r(NULL, " ", &Context);
+    }
+
+    status = STATUS_OBJECT_NAME_NOT_FOUND;
+    goto fail3;
+
+found:
+    *Value = __RegistryAllocate(sizeof (ANSI_STRING) * 2);
+
+    status = STATUS_NO_MEMORY;
+    if (*Value == NULL)
+        goto fail4;
+
+    Length = (ULONG)strlen(Option);
+    (*Value)[0].MaximumLength = (USHORT)(Length + 1) * sizeof (CHAR);
+    (*Value)[0].Buffer = __RegistryAllocate((*Value)[0].MaximumLength);
+
+    status = STATUS_NO_MEMORY;
+    if ((*Value)[0].Buffer == NULL)
+        goto fail5;
+
+    RtlCopyMemory((*Value)[0].Buffer, Option, Length * sizeof (CHAR));
+
+    (*Value)[0].Length = (USHORT)Length * sizeof (CHAR);
+
+    RegistryFreeSzValue(Ansi);
+
+    ZwClose(Key);
+
+    return STATUS_SUCCESS;
+
+fail5:
+    __RegistryFree(*Value);
+
+fail4:
+fail3:
+    RegistryFreeSzValue(Ansi);
+
+fail2:
+    ZwClose(Key);
+
+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,
+    IN  PANSI_STRING                Array
+    )
+{
+    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;
+
+    switch (Type) {
+    case REG_SZ:
+        status = STATUS_NO_MEMORY;
+        Partial = RegistryAnsiToSz(Array);
+        break;
+
+    case REG_MULTI_SZ:
+        status = STATUS_NO_MEMORY;
+        Partial = RegistryAnsiToMultiSz(Array);
+        break;
+
+    default:
+        status = STATUS_INVALID_PARAMETER;
+        Partial = NULL;
+        break;
+    }
+
+    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);
+
+    (VOID) ZwFlushKey(Key);
+
+    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
+RegistryFreeBinaryValue(
+    IN  PVOID   Buffer
+    )
+{
+    __RegistryFree(Buffer);
+}
+
+VOID
+RegistryCloseKey(
+    IN  HANDLE  Key
+    )
+{
+    ZwClose(Key);
+}
diff --git a/src/xenvkbd/registry.h b/src/xenvkbd/registry.h
new file mode 100644 (file)
index 0000000..4ade040
--- /dev/null
@@ -0,0 +1,203 @@
+/* Copyright (c) Citrix Systems Inc.
+ * All rights reserved.
+ * 
+ * Redistribution and use in source and binary forms, 
+ * with or without modification, are permitted provided 
+ * that the following conditions are met:
+ * 
+ * *   Redistributions of source code must retain the above 
+ *     copyright notice, this list of conditions and the 
+ *     following disclaimer.
+ * *   Redistributions in binary form must reproduce the above 
+ *     copyright notice, this list of conditions and the 
+ *     following disclaimer in the documentation and/or other 
+ *     materials provided with the distribution.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND 
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, 
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR 
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 
+ * SUCH DAMAGE.
+ */
+
+#ifndef _XENVKBD_REGISTRY_H
+#define _XENVKBD_REGISTRY_H
+
+#include <ntddk.h>
+
+extern NTSTATUS
+RegistryInitialize(
+    IN PUNICODE_STRING  Path
+    );
+
+extern VOID
+RegistryTeardown(
+    VOID
+    );
+
+extern NTSTATUS
+RegistryOpenKey(
+    IN  HANDLE          Parent,
+    IN  PUNICODE_STRING Path,
+    IN  ACCESS_MASK     DesiredAccess,
+    OUT PHANDLE         Key
+    );
+
+extern NTSTATUS
+RegistryCreateKey(
+    IN  HANDLE          Parent,
+    IN  PUNICODE_STRING Path,
+    IN  ULONG           Options,
+    OUT PHANDLE         Key
+    );
+
+extern NTSTATUS
+RegistryOpenServiceKey(
+    IN  ACCESS_MASK DesiredAccess,
+    OUT PHANDLE     Key
+    );
+
+extern NTSTATUS
+RegistryCreateServiceKey(
+    OUT PHANDLE     Key
+    );
+
+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
+RegistryOpenSubKey(
+    IN  HANDLE      Key,
+    IN  PCHAR       Name,
+    IN  ACCESS_MASK DesiredAccess,
+    OUT PHANDLE     SubKey
+    );
+
+extern NTSTATUS
+RegistryCreateSubKey(
+    IN  HANDLE      Key,
+    IN  PCHAR       Name,
+    IN  ULONG       Options,
+    OUT PHANDLE     SubKey
+    );
+
+extern NTSTATUS
+RegistryDeleteSubKey(
+    IN  HANDLE      Key,
+    IN  PCHAR       Name
+    );
+
+extern NTSTATUS
+RegistryEnumerateSubKeys(
+    IN  HANDLE      Key,
+    IN  NTSTATUS    (*Callback)(PVOID, HANDLE, PANSI_STRING),
+    IN  PVOID       Context
+    );
+
+extern NTSTATUS
+RegistryEnumerateValues(
+    IN  HANDLE      Key,
+    IN  NTSTATUS    (*Callback)(PVOID, HANDLE, PANSI_STRING, ULONG),
+    IN  PVOID       Context
+    );
+
+extern NTSTATUS
+RegistryDeleteValue(
+    IN  HANDLE      Key,
+    IN  PCHAR       Name
+    );
+
+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 PULONG          Type OPTIONAL,
+    OUT PANSI_STRING    *Array
+    );
+
+extern NTSTATUS
+RegistryQueryBinaryValue(
+    IN  HANDLE          Key,
+    IN  PCHAR           Name,
+    OUT PVOID           *Buffer,
+    OUT PULONG          Length
+    );
+
+extern NTSTATUS
+RegistryUpdateBinaryValue(
+    IN  HANDLE          Key,
+    IN  PCHAR           Name,
+    IN  PVOID           Buffer,
+    IN  ULONG           Length
+    );
+
+extern NTSTATUS
+RegistryQueryKeyName(
+    IN  HANDLE              Key,
+    OUT PANSI_STRING        *Array
+    );
+
+extern NTSTATUS
+RegistryQuerySystemStartOption(
+    IN  PCHAR           Name,
+    OUT PANSI_STRING    *Option
+    );
+
+extern VOID
+RegistryFreeSzValue(
+    IN  PANSI_STRING    Array
+    );
+
+extern VOID
+RegistryFreeBinaryValue(
+    IN  PVOID           Buffer
+    );
+
+extern NTSTATUS
+RegistryUpdateSzValue(
+    IN  HANDLE          Key,
+    IN  PCHAR           Name,
+    IN  ULONG           Type,
+    IN  PANSI_STRING    Array
+    );
+
+extern VOID
+RegistryCloseKey(
+    IN  HANDLE  Key
+    );
+
+#endif  // _XENVKBD_REGISTRY_H
diff --git a/src/xenvkbd/ring.c b/src/xenvkbd/ring.c
new file mode 100644 (file)
index 0000000..dd15807
--- /dev/null
@@ -0,0 +1,647 @@
+/* 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 documetation 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 <procgrp.h>
+#include <ntstrsafe.h>
+#include <stdlib.h>
+#include <xen.h>
+
+#include <debug_interface.h>
+#include <store_interface.h>
+#include <cache_interface.h>
+#include <gnttab_interface.h>
+#include <range_set_interface.h>
+#include <evtchn_interface.h>
+
+#include "pdo.h"
+#include "frontend.h"
+#include "ring.h"
+#include "hid.h"
+#include "thread.h"
+#include "registry.h"
+#include "dbg_print.h"
+#include "assert.h"
+#include "util.h"
+
+#define MAXNAMELEN  128
+
+struct _XENVKBD_RING {
+    PXENVKBD_FRONTEND       Frontend;
+    PXENVKBD_HID_CONTEXT    Hid;
+
+    XENBUS_DEBUG_INTERFACE  DebugInterface;
+    XENBUS_STORE_INTERFACE  StoreInterface;
+    XENBUS_GNTTAB_INTERFACE GnttabInterface;
+    XENBUS_EVTCHN_INTERFACE EvtchnInterface;
+
+    PXENBUS_DEBUG_CALLBACK  DebugCallback;
+
+    PXENBUS_GNTTAB_CACHE    GnttabCache;
+    PMDL                    Mdl;
+    struct xenkbd_page      *Shared;
+    PXENBUS_GNTTAB_ENTRY    Entry;
+    PXENBUS_EVTCHN_CHANNEL  Channel;
+    KSPIN_LOCK              Lock;
+    KDPC                    Dpc;
+    ULONG                   Dpcs;
+    ULONG                   Events;
+    BOOLEAN                 Connected;
+    BOOLEAN                 Enabled;
+    BOOLEAN                 AbsPointer;
+};
+
+#define XENVKBD_RING_TAG    'gniR'
+
+static FORCEINLINE PVOID
+__RingAllocate(
+    IN  ULONG   Length
+    )
+{
+    return __AllocatePoolWithTag(NonPagedPool, Length, XENVKBD_RING_TAG);
+}
+
+static FORCEINLINE VOID
+__RingFree(
+    IN  PVOID   Buffer
+    )
+{
+    __FreePoolWithTag(Buffer, XENVKBD_RING_TAG);
+}
+
+__drv_functionClass(KDEFERRED_ROUTINE)
+__drv_maxIRQL(DISPATCH_LEVEL)
+__drv_minIRQL(DISPATCH_LEVEL)
+__drv_requiresIRQL(DISPATCH_LEVEL)
+__drv_sameIRQL
+static VOID
+RingDpc(
+    IN  PKDPC       Dpc,
+    IN  PVOID       Context,
+    IN  PVOID       Argument1,
+    IN  PVOID       Argument2
+    )
+{
+    PXENVKBD_RING   Ring = Context;
+
+    UNREFERENCED_PARAMETER(Dpc);
+    UNREFERENCED_PARAMETER(Argument1);
+    UNREFERENCED_PARAMETER(Argument2);
+
+    ASSERT(Ring != NULL);
+
+    for (;;) {
+        ULONG   in_cons;
+        ULONG   in_prod;
+
+        KeMemoryBarrier();
+
+        in_cons = Ring->Shared->in_cons;
+        in_prod = Ring->Shared->in_prod;
+
+        KeMemoryBarrier();
+
+        if (in_cons == in_prod)
+            break;
+
+        while (in_cons != in_prod) {
+            union xenkbd_in_event *in_evt;
+
+            in_evt = &XENKBD_IN_RING_REF(Ring->Shared, in_cons);
+            ++in_cons;
+
+            switch (in_evt->type) {
+            case XENKBD_TYPE_MOTION:
+                HidEventMotion(Ring->Hid,
+                               in_evt->motion.rel_x,
+                               in_evt->motion.rel_y,
+                               in_evt->motion.rel_z);
+                break;
+            case XENKBD_TYPE_KEY:
+                HidEventKeypress(Ring->Hid,
+                                 in_evt->key.keycode,
+                                 in_evt->key.pressed);                        
+                break;
+            case XENKBD_TYPE_POS:
+                HidEventPosition(Ring->Hid,
+                                 in_evt->pos.abs_x,
+                                 in_evt->pos.abs_y,
+                                 in_evt->pos.rel_z);
+                break;
+            case XENKBD_TYPE_MTOUCH:
+                Trace("MTOUCH: %u %u %u %u\n",
+                     in_evt->mtouch.event_type,
+                     in_evt->mtouch.contact_id,
+                     in_evt->mtouch.u.pos.abs_x,
+                     in_evt->mtouch.u.pos.abs_y);
+                // call Frontend
+                break;
+            default:
+                Trace("UNKNOWN: %u\n",
+                      in_evt->type);
+                break;
+            }
+        }
+
+        KeMemoryBarrier();
+
+        Ring->Shared->in_cons = in_cons;
+    }
+
+    XENBUS_EVTCHN(Unmask,
+                  &Ring->EvtchnInterface,
+                  Ring->Channel,
+                  FALSE);
+}
+
+static VOID
+RingAcquireLock(
+    IN  PVOID       Context
+    )
+{
+    PXENVKBD_RING   Ring = Context;
+    KeAcquireSpinLockAtDpcLevel(&Ring->Lock);
+}
+
+static VOID
+RingReleaseLock(
+    IN  PVOID       Context
+    )
+{
+    PXENVKBD_RING   Ring = Context;
+#pragma warning(suppress:26110)
+    KeReleaseSpinLockFromDpcLevel(&Ring->Lock);
+}
+
+KSERVICE_ROUTINE    RingEvtchnCallback;
+
+BOOLEAN
+RingEvtchnCallback(
+    IN  PKINTERRUPT     InterruptObject,
+    IN  PVOID           Argument
+    )
+{
+    PXENVKBD_RING       Ring = Argument;
+
+    UNREFERENCED_PARAMETER(InterruptObject);
+
+    ASSERT(Ring != NULL);
+    Ring->Events++;
+
+    if (KeInsertQueueDpc(&Ring->Dpc, NULL, NULL))
+        Ring->Dpcs++;
+
+    return TRUE;
+}
+
+static VOID
+RingDebugCallback(
+    IN  PVOID           Argument,
+    IN  BOOLEAN         Crashing
+    )
+{
+    PXENVKBD_RING       Ring = Argument;
+
+    UNREFERENCED_PARAMETER(Crashing);
+
+    XENBUS_DEBUG(Printf,
+                 &Ring->DebugInterface,
+                 "0x%p [%s]\n",
+                 Ring,
+                 (Ring->Enabled) ? "ENABLED" : "DISABLED");
+}
+
+NTSTATUS
+RingInitialize(
+    IN  PXENVKBD_FRONTEND   Frontend,
+    OUT PXENVKBD_RING       *Ring
+    )
+{
+    NTSTATUS                status;
+
+    Trace("=====>\n");
+    status = STATUS_NO_MEMORY;
+    *Ring = __RingAllocate(sizeof(XENVKBD_RING));
+    if (*Ring == NULL)
+        goto fail1;
+
+    (*Ring)->Frontend = Frontend;
+    (*Ring)->Hid = PdoGetHidContext(FrontendGetPdo(Frontend));
+    KeInitializeDpc(&(*Ring)->Dpc, RingDpc, *Ring);
+    KeInitializeSpinLock(&(*Ring)->Lock);
+
+    FdoGetDebugInterface(PdoGetFdo(FrontendGetPdo(Frontend)),
+                         &(*Ring)->DebugInterface);
+
+    FdoGetStoreInterface(PdoGetFdo(FrontendGetPdo(Frontend)),
+                         &(*Ring)->StoreInterface);
+
+    FdoGetGnttabInterface(PdoGetFdo(FrontendGetPdo(Frontend)),
+                          &(*Ring)->GnttabInterface);
+
+    FdoGetEvtchnInterface(PdoGetFdo(FrontendGetPdo(Frontend)),
+                          &(*Ring)->EvtchnInterface);
+
+    return STATUS_SUCCESS;
+
+fail1:
+    Error("fail1 %08x\n", status);
+    return status;
+}
+
+static FORCEINLINE VOID
+RingReadFeatures(
+    IN  PXENVKBD_RING   Ring
+    )
+{
+    PCHAR               Buffer;
+    NTSTATUS            status;
+
+    status = XENBUS_STORE(Read,
+                          &Ring->StoreInterface,
+                          NULL,
+                          FrontendGetBackendPath(Ring->Frontend),
+                          "feature-abs-pointer",
+                          &Buffer);
+    if (NT_SUCCESS(status)) {
+        Ring->AbsPointer = (BOOLEAN)strtoul(Buffer, NULL, 2);
+
+        XENBUS_STORE(Free,
+                     &Ring->StoreInterface,
+                     Buffer);
+    } else {
+        Ring->AbsPointer = FALSE;
+    }
+}
+
+NTSTATUS
+RingConnect(
+    IN  PXENVKBD_RING   Ring
+    )
+{
+    PFN_NUMBER          Pfn;
+    PXENVKBD_FRONTEND   Frontend;
+    NTSTATUS            status;
+
+    Trace("=====>\n");
+    Frontend = Ring->Frontend;
+
+    status = XENBUS_DEBUG(Acquire, &Ring->DebugInterface);
+    if (!NT_SUCCESS(status))
+        goto fail1;
+
+    status = XENBUS_STORE(Acquire, &Ring->StoreInterface);
+    if (!NT_SUCCESS(status))
+        goto fail2;
+
+    status = XENBUS_EVTCHN(Acquire, &Ring->EvtchnInterface);
+    if (!NT_SUCCESS(status))
+        goto fail3;
+
+    status = XENBUS_GNTTAB(Acquire, &Ring->GnttabInterface);
+    if (!NT_SUCCESS(status))
+        goto fail4;
+
+    status = XENBUS_GNTTAB(CreateCache,
+                           &Ring->GnttabInterface,
+                           "VKBD_Ring_Gnttab",
+                           0,
+                           RingAcquireLock,
+                           RingReleaseLock,
+                           Ring,
+                           &Ring->GnttabCache);
+    if (!NT_SUCCESS(status))
+        goto fail5;
+
+    RingReadFeatures(Ring);
+
+    Ring->Mdl = __AllocatePage();
+    
+    status = STATUS_NO_MEMORY;
+    if (Ring->Mdl == NULL)
+        goto fail6;
+
+    ASSERT(Ring->Mdl->MdlFlags & MDL_MAPPED_TO_SYSTEM_VA);
+    Ring->Shared = Ring->Mdl->MappedSystemVa;
+    ASSERT(Ring->Shared != NULL);
+
+    ASSERT(Ring->Mdl->MdlFlags & MDL_MAPPED_TO_SYSTEM_VA);
+    RtlZeroMemory(Ring->Mdl->MappedSystemVa, PAGE_SIZE);
+
+    Pfn = MmGetMdlPfnArray(Ring->Mdl)[0];
+
+    status = XENBUS_GNTTAB(PermitForeignAccess,
+                           &Ring->GnttabInterface,
+                           Ring->GnttabCache,
+                           TRUE,
+                           FrontendGetBackendDomain(Frontend),
+                           Pfn,
+                           FALSE,
+                           &Ring->Entry);
+    if (!NT_SUCCESS(status))
+        goto fail7;
+
+    Ring->Channel = XENBUS_EVTCHN(Open,
+                                  &Ring->EvtchnInterface,
+                                  XENBUS_EVTCHN_TYPE_UNBOUND,
+                                  RingEvtchnCallback,
+                                  Ring,
+                                  FrontendGetBackendDomain(Frontend),
+                                  TRUE);
+
+    status = STATUS_UNSUCCESSFUL;
+    if (Ring->Channel == NULL)
+        goto fail8;
+
+    XENBUS_EVTCHN(Unmask,
+                  &Ring->EvtchnInterface,
+                  Ring->Channel,
+                  FALSE);
+
+    status = XENBUS_DEBUG(Register,
+                          &Ring->DebugInterface,
+                          __MODULE__ "|RING",
+                          RingDebugCallback,
+                          Ring,
+                          &Ring->DebugCallback);
+    if (!NT_SUCCESS(status))
+        goto fail9;
+
+    Ring->Connected = TRUE;
+    return STATUS_SUCCESS;
+
+fail9:
+    Error("fail9\n");
+
+    XENBUS_EVTCHN(Close,
+                  &Ring->EvtchnInterface,
+                  Ring->Channel);
+    Ring->Channel = NULL;
+
+    Ring->Events = 0;
+
+fail8:
+    Error("fail8\n");
+
+    (VOID) XENBUS_GNTTAB(RevokeForeignAccess,
+                         &Ring->GnttabInterface,
+                         Ring->GnttabCache,
+                         TRUE,
+                         Ring->Entry);
+    Ring->Entry = NULL;
+
+fail7:
+    Error("fail7\n");
+
+    Ring->Shared = NULL;
+    __FreePage(Ring->Mdl);
+    Ring->Mdl = NULL;
+
+fail6:
+    Error("fail6\n");
+
+    XENBUS_GNTTAB(DestroyCache,
+                  &Ring->GnttabInterface,
+                  Ring->GnttabCache);
+    Ring->GnttabCache = NULL;
+
+fail5:
+    Error("fail5\n");
+
+    XENBUS_GNTTAB(Release, &Ring->GnttabInterface);
+
+fail4:
+    Error("fail4\n");
+
+    XENBUS_EVTCHN(Release, &Ring->EvtchnInterface);
+
+fail3:
+    Error("fail3\n");
+
+    XENBUS_STORE(Release, &Ring->StoreInterface);
+
+fail2:
+    Error("fail2\n");
+
+    XENBUS_DEBUG(Release, &Ring->DebugInterface);
+
+fail1:
+    Error("fail1 %08x\n", status);
+
+    return status;
+}
+
+NTSTATUS
+RingStoreWrite(
+    IN  PXENVKBD_RING               Ring,
+    IN  PXENBUS_STORE_TRANSACTION   Transaction
+    )
+{
+    ULONG                           Port;
+    NTSTATUS                        status;
+
+    Trace("=====>\n");
+    status = XENBUS_STORE(Printf,
+                          &Ring->StoreInterface,
+                          Transaction,
+                          FrontendGetPath(Ring->Frontend),
+                          "page-gref",
+                          "%u",
+                          XENBUS_GNTTAB(GetReference,
+                                        &Ring->GnttabInterface,
+                                        Ring->Entry));
+    if (!NT_SUCCESS(status))
+        goto fail1;
+
+    // this should not be required - QEMU should use grant references
+    status = XENBUS_STORE(Printf,
+                          &Ring->StoreInterface,
+                          Transaction,
+                          FrontendGetPath(Ring->Frontend),
+                          "page-ref",
+                          "%llu",
+                          (ULONG64)MmGetMdlPfnArray(Ring->Mdl)[0]);
+    if (!NT_SUCCESS(status))
+        goto fail2;
+
+    Port = XENBUS_EVTCHN(GetPort,
+                         &Ring->EvtchnInterface,
+                         Ring->Channel);
+
+    status = XENBUS_STORE(Printf,
+                          &Ring->StoreInterface,
+                          Transaction,
+                          FrontendGetPath(Ring->Frontend),
+                          "event-channel",
+                          "%u",
+                          Port);
+    if (!NT_SUCCESS(status))
+        goto fail3;
+
+    status = XENBUS_STORE(Printf,
+                          &Ring->StoreInterface,
+                          Transaction,
+                          FrontendGetPath(Ring->Frontend),
+                          "request-abs-pointer",
+                          "%u",
+                          Ring->AbsPointer);
+    if (!NT_SUCCESS(status))
+        goto fail4;
+
+    Trace("<=====\n");
+    return STATUS_SUCCESS;
+
+fail4:
+    Error("fail4\n");
+fail3:
+    Error("fail3\n");
+fail2:
+    Error("fail2\n");
+fail1:
+    Error("fail1 %08x\n", status);
+    return status;
+}
+
+NTSTATUS
+RingEnable(
+    IN  PXENVKBD_RING   Ring
+    )
+{
+    Trace("=====>\n");
+
+    ASSERT(!Ring->Enabled);
+    Ring->Enabled = TRUE;
+
+    KeInsertQueueDpc(&Ring->Dpc, NULL, NULL);
+
+    Trace("<=====\n");
+    return STATUS_SUCCESS;
+}
+
+VOID
+RingDisable(
+    IN  PXENVKBD_RING   Ring
+    )
+{
+    Trace("=====>\n");
+
+    ASSERT(Ring->Enabled);
+    Ring->Enabled = FALSE;
+
+    Trace("<=====\n");
+}
+
+VOID
+RingDisconnect(
+    IN  PXENVKBD_RING   Ring
+    )
+{
+    Trace("=====>\n");
+
+    XENBUS_DEBUG(Deregister,
+                 &Ring->DebugInterface,
+                 Ring->DebugCallback);
+    Ring->DebugCallback = NULL;
+
+    XENBUS_EVTCHN(Close,
+                  &Ring->EvtchnInterface,
+                  Ring->Channel);
+    Ring->Channel = NULL;
+
+    Ring->Events = 0;
+
+    (VOID) XENBUS_GNTTAB(RevokeForeignAccess,
+                         &Ring->GnttabInterface,
+                         Ring->GnttabCache,
+                         TRUE,
+                         Ring->Entry);
+    Ring->Entry = NULL;
+
+    Ring->Shared = NULL;
+    __FreePage(Ring->Mdl);
+    Ring->Mdl = NULL;
+
+    XENBUS_GNTTAB(DestroyCache,
+                  &Ring->GnttabInterface,
+                  Ring->GnttabCache);
+    Ring->GnttabCache = NULL;
+
+    XENBUS_GNTTAB(Release, &Ring->GnttabInterface);
+    XENBUS_EVTCHN(Release, &Ring->EvtchnInterface);
+    XENBUS_STORE(Release, &Ring->StoreInterface);
+    XENBUS_DEBUG(Release, &Ring->DebugInterface);
+
+    Trace("<=====\n");
+}
+
+VOID
+RingTeardown(
+    IN  PXENVKBD_RING   Ring
+    )
+{
+    Trace("=====>\n");
+    Ring->Dpcs = 0;
+
+    Ring->AbsPointer = FALSE;
+
+    RtlZeroMemory(&Ring->Dpc, sizeof (KDPC));
+
+    RtlZeroMemory(&Ring->Lock,
+                  sizeof (KSPIN_LOCK));
+
+    RtlZeroMemory(&Ring->GnttabInterface,
+                  sizeof (XENBUS_GNTTAB_INTERFACE));
+
+    RtlZeroMemory(&Ring->StoreInterface,
+                  sizeof (XENBUS_STORE_INTERFACE));
+
+    RtlZeroMemory(&Ring->DebugInterface,
+                  sizeof (XENBUS_DEBUG_INTERFACE));
+
+    RtlZeroMemory(&Ring->EvtchnInterface,
+                  sizeof (XENBUS_EVTCHN_INTERFACE));
+
+    Ring->Frontend = NULL;
+    Ring->Hid = NULL;
+
+    ASSERT(IsZeroMemory(Ring, sizeof (XENVKBD_RING)));
+    __RingFree(Ring);
+    Trace("<=====\n");
+}
+
+VOID
+RingNotify(
+    IN  PXENVKBD_RING   Ring
+    )
+{
+    if (KeInsertQueueDpc(&Ring->Dpc, NULL, NULL))
+        Ring->Dpcs++;
+}
diff --git a/src/xenvkbd/ring.h b/src/xenvkbd/ring.h
new file mode 100644 (file)
index 0000000..09d1f8e
--- /dev/null
@@ -0,0 +1,85 @@
+/* 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 _XENVKBD_RING_H
+#define _XENVKBD_RING_H
+
+#include <ntddk.h>
+
+#include <hid_interface.h>
+
+#include "frontend.h"
+
+typedef struct _XENVKBD_RING XENVKBD_RING, *PXENVKBD_RING;
+
+extern NTSTATUS
+RingInitialize(
+    IN  PXENVKBD_FRONTEND   Frontend,
+    OUT PXENVKBD_RING       *Ring
+    );
+
+extern NTSTATUS
+RingConnect(
+    IN  PXENVKBD_RING   Ring
+    );
+
+extern NTSTATUS
+RingStoreWrite(
+    IN  PXENVKBD_RING               Ring,
+    IN  PXENBUS_STORE_TRANSACTION   Transaction
+    );
+
+extern NTSTATUS
+RingEnable(
+    IN  PXENVKBD_RING   Ring
+    );
+
+extern VOID
+RingDisable(
+    IN  PXENVKBD_RING   Ring
+    );
+
+extern VOID
+RingDisconnect(
+    IN  PXENVKBD_RING   Ring
+    );
+
+extern VOID
+RingTeardown(
+    IN  PXENVKBD_RING   Ring
+    );
+
+extern VOID
+RingNotify(
+    IN  PXENVKBD_RING   Ring
+    );
+
+#endif  // _XENVKBD_RING_H
diff --git a/src/xenvkbd/thread.c b/src/xenvkbd/thread.c
new file mode 100644 (file)
index 0000000..65e14ae
--- /dev/null
@@ -0,0 +1,226 @@
+/* Copyright (c) Citrix Systems Inc.
+ * All rights reserved.
+ * 
+ * Redistribution and use in source and binary forms, 
+ * with or without modification, are permitted provided 
+ * that the following conditions are met:
+ * 
+ * *   Redistributions of source code must retain the above 
+ *     copyright notice, this list of conditions and the 
+ *     following disclaimer.
+ * *   Redistributions in binary form must reproduce the above 
+ *     copyright notice, this list of conditions and the 
+ *     following disclaimer in the documentation and/or other 
+ *     materials provided with the distribution.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND 
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, 
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR 
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 
+ * SUCH DAMAGE.
+ */
+
+#include <ntddk.h>
+
+#include "thread.h"
+#include "dbg_print.h"
+#include "assert.h"
+#include "util.h"
+
+#define THREAD_POOL 'ERHT'
+
+struct _XENVKBD_THREAD {
+    XENVKBD_THREAD_FUNCTION Function;
+    PVOID                   Context;
+    KEVENT                  Event;
+    BOOLEAN                 Alerted;
+    LONG                    References;
+    PKTHREAD                Thread;
+};
+
+static FORCEINLINE PVOID
+__ThreadAllocate(
+    IN  ULONG   Length
+    )
+{
+    return __AllocatePoolWithTag(NonPagedPool, Length, THREAD_POOL);
+}
+
+static FORCEINLINE VOID
+__ThreadFree(
+    IN  PVOID   Buffer
+    )
+{
+    __FreePoolWithTag(Buffer, THREAD_POOL);
+}
+
+static FORCEINLINE VOID
+__ThreadWake(
+    IN  PXENVKBD_THREAD Thread
+    )
+{
+    KeSetEvent(&Thread->Event, IO_NO_INCREMENT, FALSE);
+}
+
+VOID
+ThreadWake(
+    IN  PXENVKBD_THREAD Thread
+    )
+{
+    __ThreadWake(Thread);
+}
+
+static FORCEINLINE VOID
+__ThreadAlert(
+    IN  PXENVKBD_THREAD Thread
+    )
+{
+    Thread->Alerted = TRUE;
+    __ThreadWake(Thread);
+}
+
+VOID
+ThreadAlert(
+    IN  PXENVKBD_THREAD Thread
+    )
+{
+    __ThreadAlert(Thread);
+}
+
+KSTART_ROUTINE  ThreadFunction;
+
+VOID
+ThreadFunction(
+    IN  PVOID       Argument
+    )
+{
+    PXENVKBD_THREAD Self = Argument;
+    NTSTATUS        status;
+
+    status = Self->Function(Self, Self->Context);
+
+    if (InterlockedDecrement(&Self->References) == 0)
+        __ThreadFree(Self);
+
+    PsTerminateSystemThread(status);
+    // NOT REACHED
+}
+
+__drv_requiresIRQL(PASSIVE_LEVEL)
+NTSTATUS
+ThreadCreate(
+    IN  XENVKBD_THREAD_FUNCTION Function,
+    IN  PVOID                   Context,
+    OUT PXENVKBD_THREAD         *Thread
+    )
+{
+    HANDLE                      Handle;
+    NTSTATUS                    status;
+
+    ASSERT3U(KeGetCurrentIrql(), ==, PASSIVE_LEVEL);
+
+    (*Thread) = __ThreadAllocate(sizeof (XENVKBD_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  PXENVKBD_THREAD Thread
+    )
+{
+    return &Thread->Event;
+}
+
+BOOLEAN
+ThreadIsAlerted(
+    IN  PXENVKBD_THREAD Thread
+    )
+{
+    return Thread->Alerted;
+}
+
+VOID
+ThreadJoin(
+    IN  PXENVKBD_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/xenvkbd/thread.h b/src/xenvkbd/thread.h
new file mode 100644 (file)
index 0000000..5db568b
--- /dev/null
@@ -0,0 +1,75 @@
+/* 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 _XENVKBD_THREAD_H
+#define _XENVKBD_THREAD_H
+
+#include <ntddk.h>
+
+typedef struct _XENVKBD_THREAD XENVKBD_THREAD, *PXENVKBD_THREAD;
+
+typedef NTSTATUS (*XENVKBD_THREAD_FUNCTION)(PXENVKBD_THREAD, PVOID);
+
+__drv_requiresIRQL(PASSIVE_LEVEL)
+extern NTSTATUS
+ThreadCreate(
+    IN  XENVKBD_THREAD_FUNCTION Function,
+    IN  PVOID                   Context,
+    OUT PXENVKBD_THREAD         *Thread
+    );
+
+extern PKEVENT
+ThreadGetEvent(
+    IN  PXENVKBD_THREAD Self
+    );
+
+extern BOOLEAN
+ThreadIsAlerted(
+    IN  PXENVKBD_THREAD Self
+    );
+
+extern VOID
+ThreadWake(
+    IN  PXENVKBD_THREAD Thread
+    );
+
+extern VOID
+ThreadAlert(
+    IN  PXENVKBD_THREAD Thread
+    );
+
+extern VOID
+ThreadJoin(
+    IN  PXENVKBD_THREAD Thread
+    );
+
+#endif  // _XENVKBD_THREAD_H
+
diff --git a/src/xenvkbd/types.h b/src/xenvkbd/types.h
new file mode 100644 (file)
index 0000000..9ec16dc
--- /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 _XENVKBD_TYPES_H
+#define _XENVKBD_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  // _XENVKBD_TYPES_H
diff --git a/src/xenvkbd/util.h b/src/xenvkbd/util.h
new file mode 100644 (file)
index 0000000..2d4d1c9
--- /dev/null
@@ -0,0 +1,361 @@
+/* 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 _XENVKBD_UTIL_H
+#define _XENVKBD_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 VOID
+__CpuId(
+    IN  ULONG   Leaf,
+    OUT PULONG  EAX OPTIONAL,
+    OUT PULONG  EBX OPTIONAL,
+    OUT PULONG  ECX OPTIONAL,
+    OUT PULONG  EDX OPTIONAL
+    )
+{
+    ULONG       Value[4] = {0};
+
+    __cpuid(Value, Leaf);
+
+    if (EAX)
+        *EAX = Value[0];
+
+    if (EBX)
+        *EBX = Value[1];
+
+    if (ECX)
+        *ECX = Value[2];
+
+    if (EDX)
+        *EDX = Value[3];
+}
+
+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;
+}
+
+__checkReturn
+static FORCEINLINE PVOID
+__AllocatePoolWithTag(
+    IN  POOL_TYPE   PoolType,
+    IN  SIZE_T      NumberOfBytes,
+    IN  ULONG       Tag
+    )
+{
+    PUCHAR          Buffer;
+
+    __analysis_assume(PoolType == NonPagedPool ||
+                      PoolType == PagedPool);
+
+#pragma warning(suppress:28160) // annotation error
+    Buffer = ExAllocatePoolWithTag(PoolType, NumberOfBytes, Tag);
+    if (Buffer == NULL)
+        return NULL;
+
+    RtlZeroMemory(Buffer, NumberOfBytes);
+    return Buffer;
+}
+
+static FORCEINLINE VOID
+__FreePoolWithTag(
+    IN  PVOID   Buffer,
+    IN  ULONG   Tag
+    )
+{
+    ExFreePoolWithTag(Buffer, Tag);
+}
+
+static FORCEINLINE PMDL
+__AllocatePages(
+    IN  ULONG           Count
+    )
+{
+    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 * Count;
+
+    Mdl = MmAllocatePagesForMdlEx(LowAddress,
+                                  HighAddress,
+                                  SkipBytes,
+                                  TotalBytes,
+                                  MmCached,
+                                  MM_DONT_ZERO_ALLOCATION);
+
+    status = STATUS_NO_MEMORY;
+    if (Mdl == NULL)
+        goto fail1;
+
+    if (Mdl->ByteCount < TotalBytes)
+        goto fail2;
+
+    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 fail3;
+
+    ASSERT3P(MdlMappedSystemVa, ==, Mdl->MappedSystemVa);
+
+    RtlZeroMemory(MdlMappedSystemVa, Mdl->ByteCount);
+
+    return Mdl;
+
+fail3:
+    Error("fail3\n");
+
+fail2:
+    Error("fail2\n");
+
+    MmFreePagesFromMdl(Mdl);
+    ExFreePool(Mdl);
+
+fail1:
+    Error("fail1 (%08x)\n", status);
+
+    return NULL;
+}
+
+#define __AllocatePage()    __AllocatePages(1)
+
+static FORCEINLINE VOID
+__FreePages(
+    IN PMDL    Mdl
+    )
+{
+    PUCHAR     MdlMappedSystemVa;
+
+    ASSERT(Mdl->MdlFlags & MDL_MAPPED_TO_SYSTEM_VA);
+    MdlMappedSystemVa = Mdl->MappedSystemVa;
+
+    MmUnmapLockedPages(MdlMappedSystemVa, Mdl);
+
+    MmFreePagesFromMdl(Mdl);
+    ExFreePool(Mdl);
+}
+
+#define __FreePage(_Mdl)    __FreePages(_Mdl)
+
+static FORCEINLINE PCHAR
+__strtok_r(
+    IN      PCHAR   Buffer,
+    IN      PCHAR   Delimiter,
+    IN OUT  PCHAR   *Context
+    )
+{
+    PCHAR           Token;
+    PCHAR           End;
+
+    if (Buffer != NULL)
+        *Context = Buffer;
+
+    Token = *Context;
+
+    if (Token == NULL)
+        return NULL;
+
+    while (*Token != '\0' &&
+           strchr(Delimiter, *Token) != NULL)
+        Token++;
+
+    if (*Token == '\0')
+        return NULL;
+
+    End = Token + 1;
+    while (*End != '\0' &&
+           strchr(Delimiter, *End) == NULL)
+        End++;
+
+    if (*End != '\0')
+        *End++ = '\0';
+
+    *Context = End;
+
+    return Token;
+}
+
+static FORCEINLINE PWCHAR
+__wcstok_r(
+    IN      PWCHAR  Buffer,
+    IN      PWCHAR  Delimiter,
+    IN OUT  PWCHAR  *Context
+    )
+{
+    PWCHAR          Token;
+    PWCHAR          End;
+
+    if (Buffer != NULL)
+        *Context = Buffer;
+
+    Token = *Context;
+
+    if (Token == NULL)
+        return NULL;
+
+    while (*Token != L'\0' &&
+           wcschr(Delimiter, *Token) != NULL)
+        Token++;
+
+    if (*Token == L'\0')
+        return NULL;
+
+    End = Token + 1;
+    while (*End != L'\0' &&
+           wcschr(Delimiter, *End) == NULL)
+        End++;
+
+    if (*End != L'\0')
+        *End++ = L'\0';
+
+    *Context = End;
+
+    return Token;
+}
+
+static FORCEINLINE CHAR
+__toupper(
+    IN  CHAR    Character
+    )
+{
+    if (Character < 'a' || Character > 'z')
+        return Character;
+
+    return 'A' + Character - 'a';
+}
+
+static FORCEINLINE CHAR
+__tolower(
+    IN  CHAR    Character
+    )
+{
+    if (Character < 'A' || Character > 'Z')
+        return Character;
+
+    return 'a' + Character - 'A';
+}
+
+#endif  // _XENVKBD_UTIL_H
diff --git a/src/xenvkbd/vkbd.h b/src/xenvkbd/vkbd.h
new file mode 100644 (file)
index 0000000..0105238
--- /dev/null
@@ -0,0 +1,259 @@
+/* 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 _XENVKBD_VKBD_H
+#define _XENVKBD_VKBD_H
+
+#include <ntddk.h>
+#include <hidport.h>
+
+typedef struct _XENVKBD_HID_KEYBOARD {
+    UCHAR   ReportId; // = 1
+    UCHAR   Modifiers;
+    UCHAR   Keys[6];
+} XENVKBD_HID_KEYBOARD;
+
+typedef struct _XENVKBD_HID_ABSMOUSE {
+    UCHAR   ReportId; // = 2
+    UCHAR   Buttons;
+    USHORT  X;
+    USHORT  Y;
+    CHAR    dZ;
+} XENVKBD_HID_ABSMOUSE;
+
+static const UCHAR VkbdReportDescriptor[] = {
+    /* ReportId 1 : Keyboard                                               */
+    0x05, 0x01,         /* USAGE_PAGE (Generic Desktop)                    */\r
+    0x09, 0x06,         /* USAGE (Keyboard 6)                              */\r
+    0xa1, 0x01,         /* COLLECTION (Application)                        */\r
+    0x85, 0x01,         /*   REPORT_ID (1)                                 */\r
+    0x05, 0x07,         /*   USAGE_PAGE (Keyboard)                         */\r
+    0x19, 0xe0,         /*   USAGE_MINIMUM (Keyboard LeftControl)          */\r
+    0x29, 0xe7,         /*   USAGE_MAXIMUM (Keyboard Right GUI)            */\r
+    0x15, 0x00,         /*   LOGICAL_MINIMUM (0)                           */\r
+    0x25, 0x01,         /*   LOGICAL_MAXIMUM (1)                           */\r
+    0x75, 0x01,         /*   REPORT_SIZE (1)                               */\r
+    0x95, 0x08,         /*   REPORT_COUNT (8)                              */\r
+    0x81, 0x02,         /*   INPUT (Data,Var,Abs)                          */\r
+    0x95, 0x06,         /*   REPORT_COUNT (6)                              */\r
+    0x75, 0x08,         /*   REPORT_SIZE (8)                               */\r
+    0x15, 0x00,         /*   LOGICAL_MINIMUM (0)                           */\r
+    0x25, 0x65,         /*   LOGICAL_MAXIMUM (101)                         */\r
+    0x05, 0x07,         /*   USAGE_PAGE (Keyboard)                         */\r
+    0x19, 0x00,         /*   USAGE_MINIMUM (Reserved (no event indicated)) */\r
+    0x29, 0x65,         /*   USAGE_MAXIMUM (Keyboard Application)          */\r
+    0x81, 0x00,         /*   INPUT (Data,Ary,Abs)                          */\r
+    0xc0,               /* END_COLLECTION                                  */
+    /* Report Id 2 : Absolute Mouse                                        */
+    0x05, 0x01,         /* USAGE_PAGE (Generic Desktop)                    */\r
+    0x09, 0x02,         /* USAGE (Mouse 2)                                 */\r
+    0xa1, 0x01,         /* COLLECTION (Application)                        */\r
+    0x85, 0x02,         /*   REPORT_ID (2)                                 */\r
+    0x09, 0x01,         /*   USAGE (Pointer)                               */\r
+    0xa1, 0x00,         /*   COLLECTION (Physical)                         */\r
+    0x05, 0x09,         /*     USAGE_PAGE (Button)                         */\r
+    0x19, 0x01,         /*     USAGE_MINIMUM (Button 1)                    */\r
+    0x29, 0x05,         /*     USAGE_MAXIMUM (Button 5)                    */\r
+    0x15, 0x00,         /*     LOGICAL_MINIMUM (0)                         */\r
+    0x25, 0x01,         /*     LOGICAL_MAXIMUM (1)                         */\r
+    0x95, 0x05,         /*     REPORT_COUNT (5)                            */\r
+    0x75, 0x01,         /*     REPORT_SIZE (1)                             */\r
+    0x81, 0x02,         /*     INPUT (Data,Var,Abs)                        */\r
+    0x95, 0x01,         /*     REPORT_COUNT (1)                            */\r
+    0x75, 0x03,         /*     REPORT_SIZE (3)                             */\r
+    0x81, 0x03,         /*     INPUT (Cnst,Var,Abs)                        */\r
+    0x05, 0x01,         /*     USAGE_PAGE (Generic Desktop)                */\r
+    0x09, 0x30,         /*     USAGE (X)                                   */\r
+    0x09, 0x31,         /*     USAGE (Y)                                   */\r
+    0x16, 0x00, 0x00,   /*     LOGICAL_MINIMUM (0)                         */\r
+    0x26, 0xff, 0x7f,   /*     LOGICAL_MAXIMUM (32767)                     */\r
+    0x75, 0x10,         /*     REPORT_SIZE (16)                            */\r
+    0x95, 0x02,         /*     REPORT_COUNT (2)                            */\r
+    0x81, 0x02,         /*     INPUT (Data,Var,Abs)                        */\r
+    0x09, 0x38,         /*     USAGE (Z)                                   */\r
+    0x15, 0x81,         /*     LOGICAL_MINIMUM (-127)                      */\r
+    0x25, 0x7f,         /*     LOGICAL_MAXIMUM (127)                       */\r
+    0x75, 0x08,         /*     REPORT_SIZE (8)                             */\r
+    0x95, 0x01,         /*     REPORT_COUNT (1)                            */\r
+    0x81, 0x06,         /*     INPUT (Data,Var,Rel)                        */\r
+    0xc0,               /*   END_COLLECTION                                */\r
+    0xc0                /* END_COLLECTION                                  */\r
+
+};
+
+static const HID_DESCRIPTOR VkbdDeviceDescriptor = {
+    sizeof(HID_DESCRIPTOR),
+    0x09,
+    0x0101,
+    0x00,
+    0x01,
+    { 0x22, sizeof(VkbdReportDescriptor) }
+};
+
+static const HID_DEVICE_ATTRIBUTES VkbdDeviceAttributes = {
+    sizeof(HID_DEVICE_ATTRIBUTES),
+    0xF001, // Random Vendor ID - this may need changing to a valid USBIF designation
+    0xF001, // Random Product ID
+    0x0101
+};
+
+static const USHORT VkbdKeyCodeToUsage[] = {
+    0x00, // KEY_RESERVED\r
+    0x29, // KEY_ESC\r
+    0x1E, // KEY_1\r
+    0x1F, // KEY_2\r
+    0x20, // KEY_3\r
+    0x21, // KEY_4\r
+    0x22, // KEY_5\r
+    0x23, // KEY_6\r
+    0x24, // KEY_7\r
+    0x25, // KEY_8\r
+    0x26, // KEY_9\r
+    0x27, // KEY_0\r
+    0x2D, // KEY_MINUS\r
+    0x2E, // KEY_EQUAL\r
+    0x2A, // KEY_BACKSPACE\r
+    0x2B, // KEY_TAB\r
+    0x14, // KEY_Q\r
+    0x1A, // KEY_W\r
+    0x08, // KEY_E\r
+    0x15, // KEY_R\r
+    0x17, // KEY_T\r
+    0x1C, // KEY_Y\r
+    0x18, // KEY_U\r
+    0x0C, // KEY_I\r
+    0x12, // KEY_O\r
+    0x13, // KEY_P\r
+    0x2F, // KEY_LEFTBRACE\r
+    0x30, // KEY_RIGHTBRACE\r
+    0x29, // KEY_ENTER\r
+    0xE0, // KEY_LEFTCTRL\r
+    0x04, // KEY_A\r
+    0x16, // KEY_S\r
+    0x07, // KEY_D\r
+    0x09, // KEY_F\r
+    0x0A, // KEY_G\r
+    0x0B, // KEY_H\r
+    0x0D, // KEY_J\r
+    0x0E, // KEY_K\r
+    0x0F, // KEY_L\r
+    0x33, // KEY_SEMICOLON\r
+    0x24, // KEY_APOSTROPHE\r
+    0x35, // KEY_GRAVE\r
+    0xE1, // KEY_LEFTSHIFT\r
+    0x31, // KEY_BACKSLASH\r
+    0x1D, // KEY_Z\r
+    0x1B, // KEY_X\r
+    0x06, // KEY_C\r
+    0x19, // KEY_V\r
+    0x05, // KEY_B\r
+    0x11, // KEY_N\r
+    0x10, // KEY_M\r
+    0x36, // KEY_COMMA\r
+    0x37, // KEY_DOT\r
+    0x38, // KEY_SLASH\r
+    0xE5, // KEY_RIGHTSHIFT\r
+    0x55, // KEY_KPASTERISK\r
+    0xE2, // KEY_LEFTALT\r
+    0x2C, // KEY_SPACE\r
+    0x39, // KEY_CAPSLOCK\r
+    0x3A, // KEY_F1\r
+    0x3B, // KEY_F2\r
+    0x3C, // KEY_F3\r
+    0x3D, // KEY_F4\r
+    0x3E, // KEY_F5\r
+    0x3F, // KEY_F6\r
+    0x40, // KEY_F7\r
+    0x41, // KEY_F8\r
+    0x42, // KEY_F9\r
+    0x43, // KEY_F10\r
+    0x53, // KEY_NUMLOCK\r
+    0x47, // KEY_SCROLLLOCK\r
+    0x5F, // KEY_KP7\r
+    0x60, // KEY_KP8\r
+    0x61, // KEY_KP9\r
+    0x56, // KEY_KPMINUS\r
+    0x5C, // KEY_KP4\r
+    0x5D, // KEY_KP5\r
+    0x5E, // KEY_KP6\r
+    0x57, // KEY_KPPLUS\r
+    0x59, // KEY_KP1\r
+    0x5A, // KEY_KP2\r
+    0x5B, // KEY_KP3\r
+    0x62, // KEY_KP0\r
+    0x63, // KEY_KPDOT\r
+    0x00, // gap in sequence\r
+    0x8F, // KEY_ZENKAKUHANKAKU\r
+    0x64, // KEY_102ND\r
+    0x44, // KEY_F11\r
+    0x45, // KEY_F12\r
+    0x87, // KEY_RO\r
+    0x88, // KEY_KATAKANA\r
+    0x8A, // KEY_HIRAGANA\r
+    0x8B, // KEY_HENKAN\r
+    0x8C, // KEY_KATAKANAHIRAGANA\r
+    0x8D, // KEY_MUHENKAN\r
+    0x8E, // KEY_KPJPCOMMA\r
+    0x58, // KEY_KPENTER\r
+    0xE4, // KEY_RIGHTCTRL\r
+    0x54, // KEY_KPSLASH\r
+    0x48, // KEY_SYSRQ\r
+    0xE6, // KEY_RIGHTALT\r
+    0x00, // gap in sequence\r
+    0x4A, // KEY_HOME\r
+    0x52, // KEY_UP\r
+    0x4B, // KEY_PAGEUP\r
+    0x50, // KEY_LEFT\r
+    0x4F, // KEY_RIGHT\r
+    0x4D, // KEY_END\r
+    0x51, // KEY_DOWN\r
+    0x4E, // KEY_PAGEDOWN\r
+    0x49, // KEY_INSERT\r
+    0x4D, // KEY_DELETE\r
+    0x00, // gap in sequence\r
+    0x7F, // KEY_MUTE\r
+    0x81, // KEY_VOLUMEDOWN\r
+    0x80, // KEY_VOLUMEUP\r
+    0x66, // KEY_POWER\r
+    0x67, // KEY_KPEQUAL\r
+    0x00, // KEY_KPPLUSMINUS\r
+    0x00, // gap in sequence\r
+    0x00, // gap in sequence\r
+    0x00, // gap in sequence\r
+    0x85, // KEY_KPCOMMA\r
+    0x90, // KEY_HANGEUL\r
+    0x91, // KEY_HANJA\r
+    0x89, // KEY_YEN\r
+    0xE3, // KEY_LEFTMETA\r
+    0xE7, // KEY_RIGHTMETA\r
+};
+
+#endif  // _XENVKBD_VKBD_H
diff --git a/src/xenvkbd/xenvkbd.rc b/src/xenvkbd/xenvkbd.rc
new file mode 100644 (file)
index 0000000..1ce56c5
--- /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
+
+#include <version.h>
+
+#define        VER_COMPANYNAME_STR         VENDOR_NAME_STR
+#define VER_LEGALCOPYRIGHT_STR      "Copyright (c) Citrix Systems Inc."
+
+#define VER_PRODUCTNAME_STR         "XENVKBD"
+#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        "XENVKBD.SYS"
+#define VER_FILEDESCRIPTION_STR     "XENVKBD"
+
+#define VER_FILETYPE                VFT_DRV
+#define VER_FILESUBTYPE             VFT2_DRV_SYSTEM
+
+#include <common.ver>
diff --git a/vs2013/configs.props b/vs2013/configs.props
new file mode 100644 (file)
index 0000000..43987fb
--- /dev/null
@@ -0,0 +1,53 @@
+<?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 8 Debug|Win32"> 
+                       <Configuration>Windows 8 Debug</Configuration> 
+                       <Platform>Win32</Platform> 
+               </ProjectConfiguration> 
+               <ProjectConfiguration Include="Windows 7 Debug|Win32"> 
+                       <Configuration>Windows 7 Debug</Configuration> 
+                       <Platform>Win32</Platform> 
+               </ProjectConfiguration> 
+               <ProjectConfiguration Include="Windows Vista Debug|Win32"> 
+                       <Configuration>Windows Vista Debug</Configuration> 
+                       <Platform>Win32</Platform> 
+               </ProjectConfiguration> 
+               <ProjectConfiguration Include="Windows 8 Release|Win32"> 
+                       <Configuration>Windows 8 Release</Configuration> 
+                       <Platform>Win32</Platform> 
+               </ProjectConfiguration> 
+               <ProjectConfiguration Include="Windows 7 Release|Win32"> 
+                       <Configuration>Windows 7 Release</Configuration> 
+                       <Platform>Win32</Platform> 
+               </ProjectConfiguration> 
+               <ProjectConfiguration Include="Windows Vista Release|Win32"> 
+                       <Configuration>Windows Vista Release</Configuration> 
+                       <Platform>Win32</Platform> 
+               </ProjectConfiguration> 
+               <ProjectConfiguration Include="Windows 8 Debug|x64"> 
+                       <Configuration>Windows 8 Debug</Configuration> 
+                       <Platform>x64</Platform> 
+               </ProjectConfiguration> 
+               <ProjectConfiguration Include="Windows 7 Debug|x64"> 
+                       <Configuration>Windows 7 Debug</Configuration> 
+                       <Platform>x64</Platform> 
+               </ProjectConfiguration> 
+               <ProjectConfiguration Include="Windows Vista Debug|x64"> 
+                       <Configuration>Windows Vista Debug</Configuration> 
+                       <Platform>x64</Platform> 
+               </ProjectConfiguration> 
+               <ProjectConfiguration Include="Windows 8 Release|x64"> 
+                       <Configuration>Windows 8 Release</Configuration> 
+                       <Platform>x64</Platform> 
+               </ProjectConfiguration> 
+               <ProjectConfiguration Include="Windows 7 Release|x64"> 
+                       <Configuration>Windows 7 Release</Configuration> 
+                       <Platform>x64</Platform> 
+               </ProjectConfiguration> 
+               <ProjectConfiguration Include="Windows Vista Release|x64"> 
+                       <Configuration>Windows Vista Release</Configuration> 
+                       <Platform>x64</Platform> 
+               </ProjectConfiguration> 
+       </ItemGroup> 
+</Project>
diff --git a/vs2013/package/package.vcxproj b/vs2013/package/package.vcxproj
new file mode 100644 (file)
index 0000000..d84fe2e
--- /dev/null
@@ -0,0 +1,54 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <Import Project="..\configs.props" />
+  <PropertyGroup Label="PropertySheets">
+    <DriverType>WDM</DriverType>
+    <PlatformToolset>WindowsApplicationForDrivers8.1</PlatformToolset>
+    <ConfigurationType>Utility</ConfigurationType>
+    <DriverType>Package</DriverType>
+    <DisableFastUpToDateCheck>true</DisableFastUpToDateCheck>
+  </PropertyGroup>
+  <PropertyGroup Label="Globals">
+    <Configuration>Windows Vista Debug</Configuration>
+    <Platform Condition="'$(Platform)' == ''">Win32</Platform>
+    <DebuggerFlavor>DbgengKernelDebugger</DebuggerFlavor>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+  <PropertyGroup Label="Globals">
+    <ProjectGuid>{73768CC9-DB26-4297-9EC8-1042F815EB15}</ProjectGuid>
+  </PropertyGroup>
+  <Import Project="..\targets.props" />
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+  <PropertyGroup>
+    <EnableInf2cat>true</EnableInf2cat>
+    <Inf2CatWindowsVersionList Condition="'$(Platform)'=='x64'">Vista_x64;7_x64;Server2008_x64;Server2008R2_x64;Server8_x64</Inf2CatWindowsVersionList>
+    <Inf2CatWindowsVersionList Condition="'$(Platform)'=='Win32'">Vista_x86;7_x86;Server2008_x86;8_x86</Inf2CatWindowsVersionList>
+    <DebuggerFlavor>DbgengKernelDebugger</DebuggerFlavor>
+    <EnableDeployment>False</EnableDeployment>
+    <ImportToStore>False</ImportToStore>
+    <InstallMode>None</InstallMode>
+    <ScriptDeviceQuery>%PathToInf%</ScriptDeviceQuery>
+    <EnableVerifier>False</EnableVerifier>
+    <AllDrivers>False</AllDrivers>
+    <VerifyProjectOutput>True</VerifyProjectOutput>
+    <VerifyFlags>133563</VerifyFlags>
+    <IntDir>..\$(ProjectName)\$(ConfigurationName)\$(Platform)\</IntDir>
+    <OutDir>..\$(ConfigurationName)\$(Platform)\</OutDir>
+    <PackageDir>..\..\xenvkbd\$(DDKPlatform)</PackageDir>
+  </PropertyGroup>
+  <ItemGroup>
+    <ProjectReference Include="..\xenvkbd\xenvkbd.vcxproj">
+      <Project>{C3F96D4C-E441-47F7-A44C-D2D0543C1D18}</Project>
+    </ProjectReference>
+    <ProjectReference Include="..\xenvkbd_coinst\xenvkbd_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/vs2013/package/package.vcxproj.user b/vs2013/package/package.vcxproj.user
new file mode 100644 (file)
index 0000000..81ffe6c
--- /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>TestSign</SignMode>
+    <TestCertificate>..\..\src\xenvkbd.pfx</TestCertificate>
+    <TimeStampServer>http://timestamp.verisign.com/scripts/timstamp.dll</TimeStampServer>
+  </PropertyGroup>
+</Project>
diff --git a/vs2013/targets.props b/vs2013/targets.props
new file mode 100644 (file)
index 0000000..c8aba92
--- /dev/null
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+       <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Windows 8 Debug|Win32'"> 
+               <TargetVersion>Windows8</TargetVersion> 
+               <UseDebugLibraries>true</UseDebugLibraries> 
+       </PropertyGroup> 
+       <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Windows 8 Release|Win32'"> 
+               <TargetVersion>Windows8</TargetVersion> 
+               <UseDebugLibraries>false</UseDebugLibraries> 
+       </PropertyGroup> 
+       <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Windows 8 Debug|x64'"> 
+               <TargetVersion>Windows8</TargetVersion> 
+               <UseDebugLibraries>true</UseDebugLibraries> 
+       </PropertyGroup> 
+       <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Windows 8 Release|x64'"> 
+               <TargetVersion>Windows8</TargetVersion> 
+               <UseDebugLibraries>false</UseDebugLibraries> 
+       </PropertyGroup>
+       <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Windows 7 Debug|Win32'"> 
+               <TargetVersion>Windows7</TargetVersion> 
+               <UseDebugLibraries>true</UseDebugLibraries> 
+       </PropertyGroup> 
+       <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Windows 7 Release|Win32'"> 
+               <TargetVersion>Windows7</TargetVersion> 
+               <UseDebugLibraries>false</UseDebugLibraries> 
+       </PropertyGroup> 
+       <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Windows 7 Debug|x64'"> 
+               <TargetVersion>Windows7</TargetVersion> 
+               <UseDebugLibraries>true</UseDebugLibraries> 
+       </PropertyGroup> 
+       <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Windows 7 Release|x64'"> 
+               <TargetVersion>Windows7</TargetVersion> 
+               <UseDebugLibraries>false</UseDebugLibraries> 
+       </PropertyGroup>
+       <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Windows Vista Debug|Win32'"> 
+               <TargetVersion>Vista</TargetVersion> 
+               <UseDebugLibraries>true</UseDebugLibraries> 
+       </PropertyGroup> 
+       <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Windows Vista Release|Win32'"> 
+               <TargetVersion>Vista</TargetVersion> 
+               <UseDebugLibraries>false</UseDebugLibraries> 
+       </PropertyGroup> 
+       <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Windows Vista Debug|x64'"> 
+               <TargetVersion>Vista</TargetVersion> 
+               <UseDebugLibraries>true</UseDebugLibraries> 
+       </PropertyGroup> 
+       <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Windows Vista Release|x64'"> 
+               <TargetVersion>Vista</TargetVersion> 
+               <UseDebugLibraries>false</UseDebugLibraries> 
+       </PropertyGroup>
+</Project>
diff --git a/vs2013/xenvkbd.sln b/vs2013/xenvkbd.sln
new file mode 100644 (file)
index 0000000..c213cda
--- /dev/null
@@ -0,0 +1,138 @@
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio 2012
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "xenvkbd", "xenvkbd\xenvkbd.vcxproj", "{C3F96D4C-E441-47F7-A44C-D2D0543C1D18}"
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "xenvkbd_coinst", "xenvkbd_coinst\xenvkbd_coinst.vcxproj", "{2BFAC7E6-3420-47A5-A092-BDC5C9D78A44}"
+       ProjectSection(ProjectDependencies) = postProject
+               {C3F96D4C-E441-47F7-A44C-D2D0543C1D18} = {C3F96D4C-E441-47F7-A44C-D2D0543C1D18}
+       EndProjectSection
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "package", "package\package.vcxproj", "{73768CC9-DB26-4297-9EC8-1042F815EB15}"
+       ProjectSection(ProjectDependencies) = postProject
+               {C3F96D4C-E441-47F7-A44C-D2D0543C1D18} = {C3F96D4C-E441-47F7-A44C-D2D0543C1D18}
+               {2BFAC7E6-3420-47A5-A092-BDC5C9D78A44} = {2BFAC7E6-3420-47A5-A092-BDC5C9D78A44}
+       EndProjectSection
+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 8 Debug|Win32 = Windows 8 Debug|Win32
+               Windows 8 Debug|x64 = Windows 8 Debug|x64
+               Windows 8 Release|Win32 = Windows 8 Release|Win32
+               Windows 8 Release|x64 = Windows 8 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 8 Debug|Win32.ActiveCfg = Windows 8 Debug|Win32
+               {2BFAC7E6-3420-47A5-A092-BDC5C9D78A44}.Windows 8 Debug|Win32.Build.0 = Windows 8 Debug|Win32
+               {2BFAC7E6-3420-47A5-A092-BDC5C9D78A44}.Windows 8 Debug|Win32.Deploy.0 = Windows 8 Debug|Win32
+               {2BFAC7E6-3420-47A5-A092-BDC5C9D78A44}.Windows 8 Debug|x64.ActiveCfg = Windows 8 Debug|x64
+               {2BFAC7E6-3420-47A5-A092-BDC5C9D78A44}.Windows 8 Debug|x64.Build.0 = Windows 8 Debug|x64
+               {2BFAC7E6-3420-47A5-A092-BDC5C9D78A44}.Windows 8 Release|Win32.ActiveCfg = Windows 8 Release|Win32
+               {2BFAC7E6-3420-47A5-A092-BDC5C9D78A44}.Windows 8 Release|Win32.Build.0 = Windows 8 Release|Win32
+               {2BFAC7E6-3420-47A5-A092-BDC5C9D78A44}.Windows 8 Release|Win32.Deploy.0 = Windows 8 Release|Win32
+               {2BFAC7E6-3420-47A5-A092-BDC5C9D78A44}.Windows 8 Release|x64.ActiveCfg = Windows 8 Release|x64
+               {2BFAC7E6-3420-47A5-A092-BDC5C9D78A44}.Windows 8 Release|x64.Build.0 = Windows 8 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 8 Debug|Win32.ActiveCfg = Windows 8 Debug|Win32
+               {73768CC9-DB26-4297-9EC8-1042F815EB15}.Windows 8 Debug|Win32.Build.0 = Windows 8 Debug|Win32
+               {73768CC9-DB26-4297-9EC8-1042F815EB15}.Windows 8 Debug|Win32.Deploy.0 = Windows 8 Debug|Win32
+               {73768CC9-DB26-4297-9EC8-1042F815EB15}.Windows 8 Debug|x64.ActiveCfg = Windows 8 Debug|x64
+               {73768CC9-DB26-4297-9EC8-1042F815EB15}.Windows 8 Debug|x64.Build.0 = Windows 8 Debug|x64
+               {73768CC9-DB26-4297-9EC8-1042F815EB15}.Windows 8 Debug|x64.Deploy.0 = Windows 8 Debug|x64
+               {73768CC9-DB26-4297-9EC8-1042F815EB15}.Windows 8 Release|Win32.ActiveCfg = Windows 8 Release|Win32
+               {73768CC9-DB26-4297-9EC8-1042F815EB15}.Windows 8 Release|Win32.Build.0 = Windows 8 Release|Win32
+               {73768CC9-DB26-4297-9EC8-1042F815EB15}.Windows 8 Release|Win32.Deploy.0 = Windows 8 Release|Win32
+               {73768CC9-DB26-4297-9EC8-1042F815EB15}.Windows 8 Release|x64.ActiveCfg = Windows 8 Release|x64
+               {73768CC9-DB26-4297-9EC8-1042F815EB15}.Windows 8 Release|x64.Build.0 = Windows 8 Release|x64
+               {73768CC9-DB26-4297-9EC8-1042F815EB15}.Windows 8 Release|x64.Deploy.0 = Windows 8 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 8 Debug|Win32.ActiveCfg = Windows 8 Debug|Win32
+               {C3F96D4C-E441-47F7-A44C-D2D0543C1D18}.Windows 8 Debug|Win32.Build.0 = Windows 8 Debug|Win32
+               {C3F96D4C-E441-47F7-A44C-D2D0543C1D18}.Windows 8 Debug|Win32.Deploy.0 = Windows 8 Debug|Win32
+               {C3F96D4C-E441-47F7-A44C-D2D0543C1D18}.Windows 8 Debug|x64.ActiveCfg = Windows 8 Debug|x64
+               {C3F96D4C-E441-47F7-A44C-D2D0543C1D18}.Windows 8 Debug|x64.Build.0 = Windows 8 Debug|x64
+               {C3F96D4C-E441-47F7-A44C-D2D0543C1D18}.Windows 8 Debug|x64.Deploy.0 = Windows 8 Debug|x64
+               {C3F96D4C-E441-47F7-A44C-D2D0543C1D18}.Windows 8 Release|Win32.ActiveCfg = Windows 8 Release|Win32
+               {C3F96D4C-E441-47F7-A44C-D2D0543C1D18}.Windows 8 Release|Win32.Build.0 = Windows 8 Release|Win32
+               {C3F96D4C-E441-47F7-A44C-D2D0543C1D18}.Windows 8 Release|Win32.Deploy.0 = Windows 8 Release|Win32
+               {C3F96D4C-E441-47F7-A44C-D2D0543C1D18}.Windows 8 Release|x64.ActiveCfg = Windows 8 Release|x64
+               {C3F96D4C-E441-47F7-A44C-D2D0543C1D18}.Windows 8 Release|x64.Build.0 = Windows 8 Release|x64
+               {C3F96D4C-E441-47F7-A44C-D2D0543C1D18}.Windows 8 Release|x64.Deploy.0 = Windows 8 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/vs2013/xenvkbd/xenvkbd.vcxproj b/vs2013/xenvkbd/xenvkbd.vcxproj
new file mode 100644 (file)
index 0000000..b2a5c07
--- /dev/null
@@ -0,0 +1,92 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <Import Project="..\configs.props" />
+  <PropertyGroup Label="Globals">
+    <Configuration>Windows Vista Debug</Configuration>
+    <Platform Condition="'$(Platform)' == ''">Win32</Platform>
+    <DebuggerFlavor>DbgengKernelDebugger</DebuggerFlavor>
+  </PropertyGroup>
+  <PropertyGroup Label="PropertySheets">
+    <DriverType>WDM</DriverType>
+    <PlatformToolset>WindowsKernelModeDriver8.1</PlatformToolset>
+    <ConfigurationType>Driver</ConfigurationType>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+  <PropertyGroup Label="Globals">
+    <ProjectGuid>{C3F96D4C-E441-47F7-A44C-D2D0543C1D18}</ProjectGuid>
+  </PropertyGroup>
+  <Import Project="..\targets.props" />
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+  <PropertyGroup>
+    <RunCodeAnalysis>true</RunCodeAnalysis>
+    <EnableInf2cat>false</EnableInf2cat>
+    <IntDir>..\$(ProjectName)\$(ConfigurationName)\$(Platform)\</IntDir>
+    <OutDir>..\$(ConfigurationName)\$(Platform)\</OutDir>
+  </PropertyGroup>
+  <ItemDefinitionGroup>
+    <ClCompile>
+      <AdditionalIncludeDirectories>$(WindowsSdkDir)\include\km;..\..\include;..\..\include\xen;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <PreprocessorDefinitions>__MODULE__="XENVKBD";POOL_NX_OPTIN=1;NT_PROCESSOR_GROUPS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <WarningLevel>EnableAllWarnings</WarningLevel>
+      <DisableSpecificWarnings>4711;4548;4820;4668;4255;6001;6054;28196;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+      <MultiProcessorCompilation>true</MultiProcessorCompilation>
+      <EnablePREfast>true</EnablePREfast>
+    </ClCompile>
+    <ResourceCompile>
+      <AdditionalIncludeDirectories>..\..\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+    </ResourceCompile>
+    <Link>
+      <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
+      <AdditionalDependencies>$(DDK_LIB_PATH)/Rtlver.lib;$(DDK_LIB_PATH)/libcntpr.lib;$(DDK_LIB_PATH)/aux_klib.lib;$(DDK_LIB_PATH)/ksecdd.lib;$(DDK_LIB_PATH)/procgrp.lib;%(AdditionalDependencies)</AdditionalDependencies>
+      <EnableCOMDATFolding>false</EnableCOMDATFolding>
+    </Link>
+    <Inf>
+      <SpecifyArchitecture>true</SpecifyArchitecture>
+      <SpecifyDriverVerDirectiveVersion>true</SpecifyDriverVerDirectiveVersion>
+      <TimeStamp>$(MAJOR_VERSION).$(MINOR_VERSION).$(MICRO_VERSION).$(BUILD_NUMBER)</TimeStamp>
+      <EnableVerbose>true</EnableVerbose>
+    </Inf>
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Platform)'=='Win32'">
+    <ClCompile>
+      <PreprocessorDefinitions>__i386__;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+    </ClCompile>
+    <Inf>
+      <Architecture>x86</Architecture>
+    </Inf>
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Platform)'=='x64'">
+    <ClCompile>
+      <PreprocessorDefinitions>__x86_64__;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+    </ClCompile>
+    <Inf>
+      <Architecture>amd64</Architecture>
+    </Inf>
+  </ItemDefinitionGroup>
+  <ItemGroup>
+    <FilesToPackage Include="$(TargetPath)" />
+    <FilesToPackage Include="$(OutDir)$(TargetName).pdb" />
+    <FilesToPackage Include="@(Inf->'%(CopyOutput)')" Condition="'@(Inf)'!=''" />
+  </ItemGroup>
+  <ItemGroup>
+    <ClCompile Include="../../src/xenvkbd/bus.c" />
+    <ClCompile Include="../../src/xenvkbd/driver.c" />
+    <ClCompile Include="../../src/xenvkbd/fdo.c" />
+    <ClCompile Include="../../src/xenvkbd/frontend.c" />
+    <ClCompile Include="../../src/xenvkbd/pdo.c" />
+    <ClCompile Include="../../src/xenvkbd/registry.c" />
+    <ClCompile Include="../../src/xenvkbd/ring.c" />
+    <ClCompile Include="../../src/xenvkbd/thread.c" />
+    <ClCompile Include="../../src/xenvkbd/hid.c" />
+  </ItemGroup>
+  <ItemGroup>
+    <ResourceCompile Include="..\..\src\xenvkbd\xenvkbd.rc" />
+  </ItemGroup>
+  <ItemGroup>
+    <Inf Include="..\xenvkbd.inf" />
+  </ItemGroup>
+  <ItemGroup>
+    <None Include="..\package\package.vcxproj" />
+  </ItemGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+</Project>
diff --git a/vs2013/xenvkbd/xenvkbd.vcxproj.user b/vs2013/xenvkbd/xenvkbd.vcxproj.user
new file mode 100644 (file)
index 0000000..81ffe6c
--- /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>TestSign</SignMode>
+    <TestCertificate>..\..\src\xenvkbd.pfx</TestCertificate>
+    <TimeStampServer>http://timestamp.verisign.com/scripts/timstamp.dll</TimeStampServer>
+  </PropertyGroup>
+</Project>
diff --git a/vs2013/xenvkbd_coinst/xenvkbd_coinst.vcxproj b/vs2013/xenvkbd_coinst/xenvkbd_coinst.vcxproj
new file mode 100644 (file)
index 0000000..8a17bce
--- /dev/null
@@ -0,0 +1,65 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <Import Project="..\configs.props" />
+  <PropertyGroup Label="PropertySheets">
+    <DriverType>WDM</DriverType>
+    <PlatformToolset>WindowsApplicationForDrivers8.1</PlatformToolset>
+    <ConfigurationType>DynamicLibrary</ConfigurationType>
+  </PropertyGroup>
+  <PropertyGroup Label="Globals">
+    <Configuration>Windows Vista Debug</Configuration>
+    <Platform Condition="'$(Platform)' == ''">Win32</Platform>
+    <DebuggerFlavor>DbgengKernelDebugger</DebuggerFlavor>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+  <PropertyGroup Label="Globals">
+    <ProjectGuid>{2BFAC7E6-3420-47A5-A092-BDC5C9D78A44}</ProjectGuid>
+  </PropertyGroup>
+  <Import Project="..\targets.props" />
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+  <PropertyGroup>
+    <DebuggerFlavor>DbgengKernelDebugger</DebuggerFlavor>
+    <IncludePath>..\..\include;$(IncludePath)</IncludePath>
+    <RunCodeAnalysis>true</RunCodeAnalysis>
+    <EnableInf2cat>false</EnableInf2cat>
+    <IntDir>..\$(ProjectName)\$(ConfigurationName)\$(Platform)\</IntDir>
+    <OutDir>..\$(ConfigurationName)\$(Platform)\</OutDir>
+  </PropertyGroup>
+  <ItemDefinitionGroup>
+    <ClCompile>
+      <PreprocessorDefinitions>__MODULE__="XENVKBD_COINST";%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <WarningLevel>EnableAllWarnings</WarningLevel>
+      <DisableSpecificWarnings>4127;4548;4711;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/xenvkbd_coinst.def</ModuleDefinitionFile>
+      <AdditionalDependencies>setupapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
+    </Link>
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Platform)'=='Win32'">
+    <ClCompile>
+      <PreprocessorDefinitions>__i386__;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+    </ClCompile>
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Platform)'=='x64'">
+    <ClCompile>
+      <PreprocessorDefinitions>__x86_64__;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+    </ClCompile>
+  </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\xenvkbd_coinst.def" />
+  </ItemGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+</Project>
\ No newline at end of file
diff --git a/vs2013/xenvkbd_coinst/xenvkbd_coinst.vcxproj.user b/vs2013/xenvkbd_coinst/xenvkbd_coinst.vcxproj.user
new file mode 100644 (file)
index 0000000..81ffe6c
--- /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>TestSign</SignMode>
+    <TestCertificate>..\..\src\xenvkbd.pfx</TestCertificate>
+    <TimeStampServer>http://timestamp.verisign.com/scripts/timstamp.dll</TimeStampServer>
+  </PropertyGroup>
+</Project>
diff --git a/vs2015/configs.props b/vs2015/configs.props
new file mode 100644 (file)
index 0000000..cdbb3c8
--- /dev/null
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+       <ItemGroup Label="ProjectConfigurations">
+               <ProjectConfiguration Include="Windows 10 Debug|Win32">
+                       <Configuration>Windows 10 Debug</Configuration>
+                       <Platform>Win32</Platform>
+                       <WindowsTargetPlatformVersion>10</WindowsTargetPlatformVersion>
+               </ProjectConfiguration>
+               <ProjectConfiguration Include="Windows 10 Release|Win32">
+                       <Configuration>Windows 10 Release</Configuration>
+                       <Platform>Win32</Platform>
+                       <WindowsTargetPlatformVersion>10</WindowsTargetPlatformVersion>
+               </ProjectConfiguration>
+               <ProjectConfiguration Include="Windows 10 Debug|x64">
+                       <Configuration>Windows 10 Debug</Configuration>
+                       <Platform>x64</Platform>
+                       <WindowsTargetPlatformVersion>10</WindowsTargetPlatformVersion>
+               </ProjectConfiguration>
+               <ProjectConfiguration Include="Windows 10 Release|x64">
+                       <Configuration>Windows 10 Release</Configuration>
+                       <Platform>x64</Platform>
+                       <WindowsTargetPlatformVersion>10</WindowsTargetPlatformVersion>
+               </ProjectConfiguration>
+               <ProjectConfiguration Include="Windows 8 Debug|Win32">
+                       <Configuration>Windows 8 Debug</Configuration>
+                       <Platform>Win32</Platform>
+                       <WindowsTargetPlatformVersion>8.1</WindowsTargetPlatformVersion>
+               </ProjectConfiguration>
+               <ProjectConfiguration Include="Windows 8 Release|Win32">
+                       <Configuration>Windows 8 Release</Configuration>
+                       <Platform>Win32</Platform>
+                       <WindowsTargetPlatformVersion>8.1</WindowsTargetPlatformVersion>
+               </ProjectConfiguration>
+               <ProjectConfiguration Include="Windows 8 Debug|x64">
+                       <Configuration>Windows 8 Debug</Configuration>
+                       <Platform>x64</Platform>
+                       <WindowsTargetPlatformVersion>8.1</WindowsTargetPlatformVersion>
+               </ProjectConfiguration>
+               <ProjectConfiguration Include="Windows 8 Release|x64">
+                       <Configuration>Windows 8 Release</Configuration>
+                       <Platform>x64</Platform>
+                       <WindowsTargetPlatformVersion>8.1</WindowsTargetPlatformVersion>
+               </ProjectConfiguration>
+       </ItemGroup>
+</Project>
diff --git a/vs2015/package/package.vcxproj b/vs2015/package/package.vcxproj
new file mode 100644 (file)
index 0000000..27413c6
--- /dev/null
@@ -0,0 +1,49 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <Import Project="..\configs.props" />
+  <PropertyGroup Label="PropertySheets">
+    <PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset>
+    <ConfigurationType>Utility</ConfigurationType>
+    <DriverType>Package</DriverType>
+    <DisableFastUpToDateCheck>true</DisableFastUpToDateCheck>
+    <SupportsPackaging>true</SupportsPackaging>
+    <DriverTargetPlatform>Desktop</DriverTargetPlatform>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+  <PropertyGroup Label="Globals">
+    <ProjectGuid>{73768CC9-DB26-4297-9EC8-1042F815EB15}</ProjectGuid>
+  </PropertyGroup>
+  <Import Project="..\targets.props" />
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+  <PropertyGroup>
+    <EnableInf2cat>true</EnableInf2cat>
+    <Inf2CatWindowsVersionList Condition="'$(Platform)'=='x64'">8_x64;Server8_x64;10_x64;Server10_x64</Inf2CatWindowsVersionList>
+    <Inf2CatWindowsVersionList Condition="'$(Platform)'=='Win32'">8_x86;10_x86</Inf2CatWindowsVersionList>
+    <DebuggerFlavor>DbgengKernelDebugger</DebuggerFlavor>
+    <EnableDeployment>False</EnableDeployment>
+    <ImportToStore>False</ImportToStore>
+    <InstallMode>None</InstallMode>
+    <ScriptDeviceQuery>%PathToInf%</ScriptDeviceQuery>
+    <EnableVerifier>False</EnableVerifier>
+    <AllDrivers>False</AllDrivers>
+    <VerifyProjectOutput>True</VerifyProjectOutput>
+    <VerifyFlags>133563</VerifyFlags>
+    <IntDir>..\$(ProjectName)\$(ConfigurationName)\$(Platform)\</IntDir>
+    <OutDir>..\..\xenvkbd\$(DDKPlatform)\</OutDir>
+  </PropertyGroup>
+  <ItemGroup>
+    <ProjectReference Include="..\xenvkbd\xenvkbd.vcxproj">
+      <Project>{C3F96D4C-E441-47F7-A44C-D2D0543C1D18}</Project>
+    </ProjectReference>
+    <ProjectReference Include="..\xenvkbd_coinst\xenvkbd_coinst.vcxproj">
+      <Project>{2BFAC7E6-3420-47A5-A092-BDC5C9D78A44}</Project>
+    </ProjectReference>
+  </ItemGroup>
+  <ItemGroup>
+    <FilesToPackage Include="$(DPINST_REDIST)\x86\dpinst.exe" Condition="'$(Platform)'=='Win32'" />
+    <FilesToPackage Include="$(DPINST_REDIST)\x64\dpinst.exe" Condition="'$(Platform)'=='x64'" />
+  </ItemGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+  <ImportGroup Label="ExtensionTargets">
+  </ImportGroup>
+</Project>
diff --git a/vs2015/package/package.vcxproj.user b/vs2015/package/package.vcxproj.user
new file mode 100644 (file)
index 0000000..185ea97
--- /dev/null
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <PropertyGroup>
+    <SignMode>TestSign</SignMode>
+    <TestCertificate>..\..\src\xenvkbd.pfx</TestCertificate>
+    <TimeStampServer>http://timestamp.verisign.com/scripts/timstamp.dll</TimeStampServer>
+  </PropertyGroup>
+</Project>
diff --git a/vs2015/targets.props b/vs2015/targets.props
new file mode 100644 (file)
index 0000000..64598fc
--- /dev/null
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+       <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Windows 10 Debug|Win32'">
+               <TargetVersion>Windows10</TargetVersion>
+               <UseDebugLibraries>true</UseDebugLibraries>
+       </PropertyGroup>
+       <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Windows 10 Release|Win32'">
+               <TargetVersion>Windows10</TargetVersion>
+               <UseDebugLibraries>false</UseDebugLibraries>
+       </PropertyGroup>
+       <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Windows 10 Debug|x64'">
+               <TargetVersion>Windows10</TargetVersion>
+               <UseDebugLibraries>true</UseDebugLibraries>
+       </PropertyGroup>
+       <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Windows 10 Release|x64'">
+               <TargetVersion>Windows10</TargetVersion>
+               <UseDebugLibraries>false</UseDebugLibraries>
+       </PropertyGroup>
+       <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Windows 8 Debug|Win32'">
+               <TargetVersion>Windows8</TargetVersion>
+               <UseDebugLibraries>true</UseDebugLibraries>
+       </PropertyGroup>
+       <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Windows 8 Release|Win32'">
+               <TargetVersion>Windows8</TargetVersion>
+               <UseDebugLibraries>false</UseDebugLibraries>
+       </PropertyGroup>
+       <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Windows 8 Debug|x64'">
+               <TargetVersion>Windows8</TargetVersion>
+               <UseDebugLibraries>true</UseDebugLibraries>
+       </PropertyGroup>
+       <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Windows 8 Release|x64'">
+               <TargetVersion>Windows8</TargetVersion>
+               <UseDebugLibraries>false</UseDebugLibraries>
+       </PropertyGroup>
+</Project>
diff --git a/vs2015/xenvkbd.sln b/vs2015/xenvkbd.sln
new file mode 100644 (file)
index 0000000..c77ce42
--- /dev/null
@@ -0,0 +1,102 @@
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio 14
+VisualStudioVersion = 14.0.25420.1
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "xenvkbd", "xenvkbd\xenvkbd.vcxproj", "{C3F96D4C-E441-47F7-A44C-D2D0543C1D18}"
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "xenvkbd_coinst", "xenvkbd_coinst\xenvkbd_coinst.vcxproj", "{2BFAC7E6-3420-47A5-A092-BDC5C9D78A44}"
+       ProjectSection(ProjectDependencies) = postProject
+               {C3F96D4C-E441-47F7-A44C-D2D0543C1D18} = {C3F96D4C-E441-47F7-A44C-D2D0543C1D18}
+       EndProjectSection
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "package", "package\package.vcxproj", "{73768CC9-DB26-4297-9EC8-1042F815EB15}"
+       ProjectSection(ProjectDependencies) = postProject
+               {C3F96D4C-E441-47F7-A44C-D2D0543C1D18} = {C3F96D4C-E441-47F7-A44C-D2D0543C1D18}
+               {2BFAC7E6-3420-47A5-A092-BDC5C9D78A44} = {2BFAC7E6-3420-47A5-A092-BDC5C9D78A44}
+       EndProjectSection
+EndProject
+Global
+       GlobalSection(SolutionConfigurationPlatforms) = preSolution
+               Windows 8 Debug|Win32 = Windows 8 Debug|Win32
+               Windows 8 Debug|x64 = Windows 8 Debug|x64
+               Windows 8 Release|Win32 = Windows 8 Release|Win32
+               Windows 8 Release|x64 = Windows 8 Release|x64
+               Windows 10 Debug|Win32 = Windows 10 Debug|Win32
+               Windows 10 Debug|x64 = Windows 10 Debug|x64
+               Windows 10 Release|Win32 = Windows 10 Release|Win32
+               Windows 10 Release|x64 = Windows 10 Release|x64
+       EndGlobalSection
+       GlobalSection(ProjectConfigurationPlatforms) = postSolution
+               {2BFAC7E6-3420-47A5-A092-BDC5C9D78A44}.Windows 8 Debug|Win32.ActiveCfg = Windows 8 Debug|Win32
+               {2BFAC7E6-3420-47A5-A092-BDC5C9D78A44}.Windows 8 Debug|Win32.Build.0 = Windows 8 Debug|Win32
+               {2BFAC7E6-3420-47A5-A092-BDC5C9D78A44}.Windows 8 Debug|Win32.Deploy.0 = Windows 8 Debug|Win32
+               {2BFAC7E6-3420-47A5-A092-BDC5C9D78A44}.Windows 8 Debug|x64.ActiveCfg = Windows 8 Debug|x64
+               {2BFAC7E6-3420-47A5-A092-BDC5C9D78A44}.Windows 8 Debug|x64.Build.0 = Windows 8 Debug|x64
+               {2BFAC7E6-3420-47A5-A092-BDC5C9D78A44}.Windows 8 Release|Win32.ActiveCfg = Windows 8 Release|Win32
+               {2BFAC7E6-3420-47A5-A092-BDC5C9D78A44}.Windows 8 Release|Win32.Build.0 = Windows 8 Release|Win32
+               {2BFAC7E6-3420-47A5-A092-BDC5C9D78A44}.Windows 8 Release|Win32.Deploy.0 = Windows 8 Release|Win32
+               {2BFAC7E6-3420-47A5-A092-BDC5C9D78A44}.Windows 8 Release|x64.ActiveCfg = Windows 8 Release|x64
+               {2BFAC7E6-3420-47A5-A092-BDC5C9D78A44}.Windows 8 Release|x64.Build.0 = Windows 8 Release|x64
+               {2BFAC7E6-3420-47A5-A092-BDC5C9D78A44}.Windows 10 Debug|Win32.ActiveCfg = Windows 10 Debug|Win32
+               {2BFAC7E6-3420-47A5-A092-BDC5C9D78A44}.Windows 10 Debug|Win32.Build.0 = Windows 10 Debug|Win32
+               {2BFAC7E6-3420-47A5-A092-BDC5C9D78A44}.Windows 10 Debug|Win32.Deploy.0 = Windows 10 Debug|Win32
+               {2BFAC7E6-3420-47A5-A092-BDC5C9D78A44}.Windows 10 Debug|x64.ActiveCfg = Windows 10 Debug|x64
+               {2BFAC7E6-3420-47A5-A092-BDC5C9D78A44}.Windows 10 Debug|x64.Build.0 = Windows 10 Debug|x64
+               {2BFAC7E6-3420-47A5-A092-BDC5C9D78A44}.Windows 10 Release|Win32.ActiveCfg = Windows 10 Release|Win32
+               {2BFAC7E6-3420-47A5-A092-BDC5C9D78A44}.Windows 10 Release|Win32.Build.0 = Windows 10 Release|Win32
+               {2BFAC7E6-3420-47A5-A092-BDC5C9D78A44}.Windows 10 Release|Win32.Deploy.0 = Windows 10 Release|Win32
+               {2BFAC7E6-3420-47A5-A092-BDC5C9D78A44}.Windows 10 Release|x64.ActiveCfg = Windows 10 Release|x64
+               {2BFAC7E6-3420-47A5-A092-BDC5C9D78A44}.Windows 10 Release|x64.Build.0 = Windows 10 Release|x64
+               {73768CC9-DB26-4297-9EC8-1042F815EB15}.Windows 8 Debug|Win32.ActiveCfg = Windows 8 Debug|Win32
+               {73768CC9-DB26-4297-9EC8-1042F815EB15}.Windows 8 Debug|Win32.Build.0 = Windows 8 Debug|Win32
+               {73768CC9-DB26-4297-9EC8-1042F815EB15}.Windows 8 Debug|Win32.Deploy.0 = Windows 8 Debug|Win32
+               {73768CC9-DB26-4297-9EC8-1042F815EB15}.Windows 8 Debug|x64.ActiveCfg = Windows 8 Debug|x64
+               {73768CC9-DB26-4297-9EC8-1042F815EB15}.Windows 8 Debug|x64.Build.0 = Windows 8 Debug|x64
+               {73768CC9-DB26-4297-9EC8-1042F815EB15}.Windows 8 Debug|x64.Deploy.0 = Windows 8 Debug|x64
+               {73768CC9-DB26-4297-9EC8-1042F815EB15}.Windows 8 Release|Win32.ActiveCfg = Windows 8 Release|Win32
+               {73768CC9-DB26-4297-9EC8-1042F815EB15}.Windows 8 Release|Win32.Build.0 = Windows 8 Release|Win32
+               {73768CC9-DB26-4297-9EC8-1042F815EB15}.Windows 8 Release|Win32.Deploy.0 = Windows 8 Release|Win32
+               {73768CC9-DB26-4297-9EC8-1042F815EB15}.Windows 8 Release|x64.ActiveCfg = Windows 8 Release|x64
+               {73768CC9-DB26-4297-9EC8-1042F815EB15}.Windows 8 Release|x64.Build.0 = Windows 8 Release|x64
+               {73768CC9-DB26-4297-9EC8-1042F815EB15}.Windows 8 Release|x64.Deploy.0 = Windows 8 Release|x64
+               {73768CC9-DB26-4297-9EC8-1042F815EB15}.Windows 10 Debug|Win32.ActiveCfg = Windows 10 Debug|Win32
+               {73768CC9-DB26-4297-9EC8-1042F815EB15}.Windows 10 Debug|Win32.Build.0 = Windows 10 Debug|Win32
+               {73768CC9-DB26-4297-9EC8-1042F815EB15}.Windows 10 Debug|Win32.Deploy.0 = Windows 10 Debug|Win32
+               {73768CC9-DB26-4297-9EC8-1042F815EB15}.Windows 10 Debug|x64.ActiveCfg = Windows 10 Debug|x64
+               {73768CC9-DB26-4297-9EC8-1042F815EB15}.Windows 10 Debug|x64.Build.0 = Windows 10 Debug|x64
+               {73768CC9-DB26-4297-9EC8-1042F815EB15}.Windows 10 Debug|x64.Deploy.0 = Windows 10 Debug|x64
+               {73768CC9-DB26-4297-9EC8-1042F815EB15}.Windows 10 Release|Win32.ActiveCfg = Windows 10 Release|Win32
+               {73768CC9-DB26-4297-9EC8-1042F815EB15}.Windows 10 Release|Win32.Build.0 = Windows 10 Release|Win32
+               {73768CC9-DB26-4297-9EC8-1042F815EB15}.Windows 10 Release|Win32.Deploy.0 = Windows 10 Release|Win32
+               {73768CC9-DB26-4297-9EC8-1042F815EB15}.Windows 10 Release|x64.ActiveCfg = Windows 10 Release|x64
+               {73768CC9-DB26-4297-9EC8-1042F815EB15}.Windows 10 Release|x64.Build.0 = Windows 10 Release|x64
+               {73768CC9-DB26-4297-9EC8-1042F815EB15}.Windows 10 Release|x64.Deploy.0 = Windows 10 Release|x64
+               {C3F96D4C-E441-47F7-A44C-D2D0543C1D18}.Windows 8 Debug|Win32.ActiveCfg = Windows 8 Debug|Win32
+               {C3F96D4C-E441-47F7-A44C-D2D0543C1D18}.Windows 8 Debug|Win32.Build.0 = Windows 8 Debug|Win32
+               {C3F96D4C-E441-47F7-A44C-D2D0543C1D18}.Windows 8 Debug|Win32.Deploy.0 = Windows 8 Debug|Win32
+               {C3F96D4C-E441-47F7-A44C-D2D0543C1D18}.Windows 8 Debug|x64.ActiveCfg = Windows 8 Debug|x64
+               {C3F96D4C-E441-47F7-A44C-D2D0543C1D18}.Windows 8 Debug|x64.Build.0 = Windows 8 Debug|x64
+               {C3F96D4C-E441-47F7-A44C-D2D0543C1D18}.Windows 8 Debug|x64.Deploy.0 = Windows 8 Debug|x64
+               {C3F96D4C-E441-47F7-A44C-D2D0543C1D18}.Windows 8 Release|Win32.ActiveCfg = Windows 8 Release|Win32
+               {C3F96D4C-E441-47F7-A44C-D2D0543C1D18}.Windows 8 Release|Win32.Build.0 = Windows 8 Release|Win32
+               {C3F96D4C-E441-47F7-A44C-D2D0543C1D18}.Windows 8 Release|Win32.Deploy.0 = Windows 8 Release|Win32
+               {C3F96D4C-E441-47F7-A44C-D2D0543C1D18}.Windows 8 Release|x64.ActiveCfg = Windows 8 Release|x64
+               {C3F96D4C-E441-47F7-A44C-D2D0543C1D18}.Windows 8 Release|x64.Build.0 = Windows 8 Release|x64
+               {C3F96D4C-E441-47F7-A44C-D2D0543C1D18}.Windows 8 Release|x64.Deploy.0 = Windows 8 Release|x64
+               {C3F96D4C-E441-47F7-A44C-D2D0543C1D18}.Windows 10 Debug|Win32.ActiveCfg = Windows 10 Debug|Win32
+               {C3F96D4C-E441-47F7-A44C-D2D0543C1D18}.Windows 10 Debug|Win32.Build.0 = Windows 10 Debug|Win32
+               {C3F96D4C-E441-47F7-A44C-D2D0543C1D18}.Windows 10 Debug|Win32.Deploy.0 = Windows 10 Debug|Win32
+               {C3F96D4C-E441-47F7-A44C-D2D0543C1D18}.Windows 10 Debug|x64.ActiveCfg = Windows 10 Debug|x64
+               {C3F96D4C-E441-47F7-A44C-D2D0543C1D18}.Windows 10 Debug|x64.Build.0 = Windows 10 Debug|x64
+               {C3F96D4C-E441-47F7-A44C-D2D0543C1D18}.Windows 10 Debug|x64.Deploy.0 = Windows 10 Debug|x64
+               {C3F96D4C-E441-47F7-A44C-D2D0543C1D18}.Windows 10 Release|Win32.ActiveCfg = Windows 10 Release|Win32
+               {C3F96D4C-E441-47F7-A44C-D2D0543C1D18}.Windows 10 Release|Win32.Build.0 = Windows 10 Release|Win32
+               {C3F96D4C-E441-47F7-A44C-D2D0543C1D18}.Windows 10 Release|Win32.Deploy.0 = Windows 10 Release|Win32
+               {C3F96D4C-E441-47F7-A44C-D2D0543C1D18}.Windows 10 Release|x64.ActiveCfg = Windows 10 Release|x64
+               {C3F96D4C-E441-47F7-A44C-D2D0543C1D18}.Windows 10 Release|x64.Build.0 = Windows 10 Release|x64
+               {C3F96D4C-E441-47F7-A44C-D2D0543C1D18}.Windows 10 Release|x64.Deploy.0 = Windows 10 Release|x64
+       EndGlobalSection
+       GlobalSection(SolutionProperties) = preSolution
+               HideSolutionNode = FALSE
+       EndGlobalSection
+EndGlobal
diff --git a/vs2015/xenvkbd/xenvkbd.vcxproj b/vs2015/xenvkbd/xenvkbd.vcxproj
new file mode 100644 (file)
index 0000000..0814db2
--- /dev/null
@@ -0,0 +1,87 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <Import Project="..\configs.props" />
+  <PropertyGroup Label="PropertySheets">
+    <DriverType>WDM</DriverType>
+    <PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset>
+    <ConfigurationType>Driver</ConfigurationType>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+  <PropertyGroup Label="Globals">
+    <ProjectGuid>{C3F96D4C-E441-47F7-A44C-D2D0543C1D18}</ProjectGuid>
+  </PropertyGroup>
+  <Import Project="..\targets.props" />
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+  <PropertyGroup>
+    <RunCodeAnalysis>true</RunCodeAnalysis>
+    <EnableInf2cat>false</EnableInf2cat>
+    <IntDir>..\$(ProjectName)\$(ConfigurationName)\$(Platform)\</IntDir>
+    <OutDir>..\$(ConfigurationName)\$(Platform)\</OutDir>
+  </PropertyGroup>
+  <ItemDefinitionGroup>
+    <ClCompile>
+      <AdditionalIncludeDirectories>$(WindowsSdkDir)\include\km;..\..\include;..\..\include\xen;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+      <PreprocessorDefinitions>__MODULE__="XENVKBD";POOL_NX_OPTIN=1;NT_PROCESSOR_GROUPS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <WarningLevel>EnableAllWarnings</WarningLevel>
+      <DisableSpecificWarnings>4464;4711;4548;4820;4668;4255;6001;6054;28196;30030;30029;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+      <MultiProcessorCompilation>true</MultiProcessorCompilation>
+      <EnablePREfast>true</EnablePREfast>
+    </ClCompile>
+    <ResourceCompile>
+      <AdditionalIncludeDirectories>..\..\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+    </ResourceCompile>
+    <Link>
+      <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
+      <AdditionalDependencies>$(DDK_LIB_PATH)/Rtlver.lib;$(DDK_LIB_PATH)/libcntpr.lib;$(DDK_LIB_PATH)/aux_klib.lib;$(DDK_LIB_PATH)/ksecdd.lib;$(DDK_LIB_PATH)/procgrp.lib;%(AdditionalDependencies)</AdditionalDependencies>
+      <EnableCOMDATFolding>false</EnableCOMDATFolding>
+    </Link>
+    <Inf>
+      <SpecifyArchitecture>true</SpecifyArchitecture>
+      <SpecifyDriverVerDirectiveVersion>true</SpecifyDriverVerDirectiveVersion>
+      <TimeStamp>$(MAJOR_VERSION).$(MINOR_VERSION).$(MICRO_VERSION).$(BUILD_NUMBER)</TimeStamp>
+      <EnableVerbose>true</EnableVerbose>
+    </Inf>
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Platform)'=='Win32'">
+    <ClCompile>
+      <PreprocessorDefinitions>__i386__;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+    </ClCompile>
+    <Inf>
+      <Architecture>x86</Architecture>
+    </Inf>
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Platform)'=='x64'">
+    <ClCompile>
+      <PreprocessorDefinitions>__x86_64__;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+    </ClCompile>
+    <Inf>
+      <Architecture>amd64</Architecture>
+    </Inf>
+  </ItemDefinitionGroup>
+  <ItemGroup>
+    <FilesToPackage Include="$(TargetPath)" />
+    <FilesToPackage Include="$(OutDir)$(TargetName).pdb" />
+    <FilesToPackage Include="@(Inf->'%(CopyOutput)')" Condition="'@(Inf)'!=''" />
+  </ItemGroup>
+  <ItemGroup>
+    <ClCompile Include="../../src/xenvkbd/bus.c" />
+    <ClCompile Include="../../src/xenvkbd/driver.c" />
+    <ClCompile Include="../../src/xenvkbd/fdo.c" />
+    <ClCompile Include="../../src/xenvkbd/frontend.c" />
+    <ClCompile Include="../../src/xenvkbd/pdo.c" />
+    <ClCompile Include="../../src/xenvkbd/registry.c" />
+    <ClCompile Include="../../src/xenvkbd/ring.c" />
+    <ClCompile Include="../../src/xenvkbd/thread.c" />
+    <ClCompile Include="../../src/xenvkbd/hid.c" />
+  </ItemGroup>
+  <ItemGroup>
+    <ResourceCompile Include="..\..\src\xenvkbd\xenvkbd.rc" />
+  </ItemGroup>
+  <ItemGroup>
+    <Inf Include="..\xenvkbd.inf" />
+  </ItemGroup>
+  <ItemGroup>
+    <None Include="..\package\package.vcxproj" />
+  </ItemGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+</Project>
diff --git a/vs2015/xenvkbd/xenvkbd.vcxproj.user b/vs2015/xenvkbd/xenvkbd.vcxproj.user
new file mode 100644 (file)
index 0000000..185ea97
--- /dev/null
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <PropertyGroup>
+    <SignMode>TestSign</SignMode>
+    <TestCertificate>..\..\src\xenvkbd.pfx</TestCertificate>
+    <TimeStampServer>http://timestamp.verisign.com/scripts/timstamp.dll</TimeStampServer>
+  </PropertyGroup>
+</Project>
diff --git a/vs2015/xenvkbd_coinst/xenvkbd_coinst.vcxproj b/vs2015/xenvkbd_coinst/xenvkbd_coinst.vcxproj
new file mode 100644 (file)
index 0000000..ad5c7b5
--- /dev/null
@@ -0,0 +1,60 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <Import Project="..\configs.props" />
+  <PropertyGroup Label="PropertySheets">
+    <DriverType>WDM</DriverType>
+    <PlatformToolset>WindowsApplicationForDrivers10.0</PlatformToolset>
+    <ConfigurationType>DynamicLibrary</ConfigurationType>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+  <PropertyGroup Label="Globals">
+    <ProjectGuid>{2BFAC7E6-3420-47A5-A092-BDC5C9D78A44}</ProjectGuid>
+  </PropertyGroup>
+  <Import Project="..\targets.props" />
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+  <PropertyGroup>
+    <DebuggerFlavor>DbgengKernelDebugger</DebuggerFlavor>
+    <IncludePath>..\..\include;$(IncludePath)</IncludePath>
+    <RunCodeAnalysis>true</RunCodeAnalysis>
+    <EnableInf2cat>false</EnableInf2cat>
+    <IntDir>..\$(ProjectName)\$(ConfigurationName)\$(Platform)\</IntDir>
+    <OutDir>..\$(ConfigurationName)\$(Platform)\</OutDir>
+  </PropertyGroup>
+  <ItemDefinitionGroup>
+    <ClCompile>
+      <PreprocessorDefinitions>__MODULE__="XENVKBD_COINST";%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <WarningLevel>EnableAllWarnings</WarningLevel>
+      <DisableSpecificWarnings>4127;4548;4711;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/xenvkbd_coinst.def</ModuleDefinitionFile>
+      <AdditionalDependencies>setupapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
+    </Link>
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Platform)'=='Win32'">
+    <ClCompile>
+      <PreprocessorDefinitions>__i386__;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+    </ClCompile>
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Platform)'=='x64'">
+    <ClCompile>
+      <PreprocessorDefinitions>__x86_64__;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+    </ClCompile>
+  </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\xenvkbd_coinst.def" />
+  </ItemGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+</Project>
diff --git a/vs2015/xenvkbd_coinst/xenvkbd_coinst.vcxproj.user b/vs2015/xenvkbd_coinst/xenvkbd_coinst.vcxproj.user
new file mode 100644 (file)
index 0000000..185ea97
--- /dev/null
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <PropertyGroup>
+    <SignMode>TestSign</SignMode>
+    <TestCertificate>..\..\src\xenvkbd.pfx</TestCertificate>
+    <TimeStampServer>http://timestamp.verisign.com/scripts/timstamp.dll</TimeStampServer>
+  </PropertyGroup>
+</Project>