--- /dev/null
+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.
+
--- /dev/null
+#!python -u
+
+import os, sys
+import datetime
+import re
+import glob
+import tarfile
+import subprocess
+
+def next_build_number():
+ try:
+ file = open('.build_number', 'r')
+ build_number = file.read()
+ file.close()
+ except IOError:
+ build_number = '0'
+
+ file = open('.build_number', 'w')
+ file.write(str(int(build_number) + 1))
+ file.close()
+
+ return build_number
+
+
+def make_header():
+ now = datetime.datetime.now()
+
+ file = open('include\\version.h', 'w')
+ file.write('#define MAJOR_VERSION\t' + os.environ['MAJOR_VERSION'] + '\n')
+ file.write('#define MAJOR_VERSION_STR\t"' + os.environ['MAJOR_VERSION'] + '"\n')
+ file.write('\n')
+
+ file.write('#define MINOR_VERSION\t' + os.environ['MINOR_VERSION'] + '\n')
+ file.write('#define MINOR_VERSION_STR\t"' + os.environ['MINOR_VERSION'] + '"\n')
+ file.write('\n')
+
+ file.write('#define MICRO_VERSION\t' + os.environ['MICRO_VERSION'] + '\n')
+ file.write('#define MICRO_VERSION_STR\t"' + os.environ['MICRO_VERSION'] + '"\n')
+ file.write('\n')
+
+ file.write('#define BUILD_NUMBER\t' + os.environ['BUILD_NUMBER'] + '\n')
+ file.write('#define BUILD_NUMBER_STR\t"' + os.environ['BUILD_NUMBER'] + '"\n')
+ file.write('\n')
+
+ file.write('#define YEAR\t' + str(now.year) + '\n')
+ file.write('#define YEAR_STR\t"' + str(now.year) + '"\n')
+
+ file.write('#define MONTH\t' + str(now.month) + '\n')
+ file.write('#define MONTH_STR\t"' + str(now.month) + '"\n')
+
+ file.write('#define DAY\t' + str(now.day) + '\n')
+ file.write('#define DAY_STR\t"' + str(now.day) + '"\n')
+
+ file.close()
+
+
+def copy_inf(name):
+ src = open('src\\%s.inf' % name, 'r')
+ dst = open('proj\\%s.inf' % name, 'w')
+
+ for line in src:
+ line = re.sub('@MAJOR_VERSION@', os.environ['MAJOR_VERSION'], line)
+ line = re.sub('@MINOR_VERSION@', os.environ['MINOR_VERSION'], line)
+ line = re.sub('@MICRO_VERSION@', os.environ['MICRO_VERSION'], line)
+ line = re.sub('@BUILD_NUMBER@', os.environ['BUILD_NUMBER'], line)
+ dst.write(line)
+
+ dst.close()
+ src.close()
+
+
+def get_expired_symbols(name, age = 30):
+ path = os.path.join(os.environ['SYMBOL_SERVER'], '000Admin\\history.txt')
+
+ try:
+ file = open(path, 'r')
+ except IOError:
+ return []
+
+ threshold = datetime.datetime.utcnow() - datetime.timedelta(days = age)
+
+ expired = []
+
+ for line in file:
+ item = line.split(',')
+
+ if (re.match('add', item[1])):
+ id = item[0]
+ date = item[3].split('/')
+ time = item[4].split(':')
+ tag = item[5].strip('"')
+
+ age = datetime.datetime(year = int(date[2]),
+ month = int(date[0]),
+ day = int(date[1]),
+ hour = int(time[0]),
+ minute = int(time[1]),
+ second = int(time[2]))
+ if (tag == name and age < threshold):
+ expired.append(id)
+
+ elif (re.match('del', item[1])):
+ id = item[2].rstrip()
+ try:
+ expired.remove(id)
+ except ValueError:
+ pass
+
+ file.close()
+
+ return expired
+
+
+def get_configuration(debug):
+ configuration = 'Windows Vista'
+
+ if debug:
+ configuration += ' Debug'
+ else:
+ configuration += ' Release'
+
+ return configuration
+
+def get_configuration_name(debug):
+ configuration = 'WindowsVista'
+
+ if debug:
+ configuration += 'Debug'
+ else:
+ configuration += 'Release'
+
+ return configuration
+
+def get_target_path(arch, debug):
+ configuration = get_configuration_name(debug)
+
+ target = { 'x86': 'proj', 'x64': os.sep.join(['proj', 'x64']) }
+ target_path = os.sep.join([target[arch], configuration])
+
+ return target_path
+
+
+def shell(command):
+ print(command)
+ sys.stdout.flush()
+
+ pipe = os.popen(command, 'r', 1)
+
+ for line in pipe:
+ print(line.rstrip())
+
+ return pipe.close()
+
+
+class msbuild_failure(Exception):
+ def __init__(self, value):
+ self.value = value
+ def __str__(self):
+ return repr(self.value)
+
+def msbuild(name, arch, debug):
+ cwd = os.getcwd()
+ configuration = get_configuration(debug)
+
+ os.environ['SOLUTION'] = name
+
+ if arch == 'x86':
+ os.environ['PLATFORM'] = 'Win32'
+ elif arch == 'x64':
+ os.environ['PLATFORM'] = 'x64'
+
+ os.environ['CONFIGURATION'] = configuration
+ os.environ['TARGET'] = 'Build'
+
+ os.chdir('proj')
+ status = shell('msbuild.bat')
+ os.chdir(cwd)
+
+ if (status != None):
+ raise msbuild_failure(configuration)
+
+
+def symstore_del(name, age):
+ symstore_path = [os.environ['KIT'], 'Debuggers']
+ if os.environ['PROCESSOR_ARCHITECTURE'] == 'x86':
+ symstore_path.append('x86')
+ else:
+ symstore_path.append('x64')
+ symstore_path.append('symstore.exe')
+
+ symstore = os.path.join(*symstore_path)
+
+ for id in get_expired_symbols(name, age):
+ command=['"' + symstore + '"']
+ command.append('del')
+ command.append('/i')
+ command.append(str(id))
+ command.append('/s')
+ command.append(os.environ['SYMBOL_SERVER'])
+
+ shell(' '.join(command))
+
+def symstore_add(name, arch, debug):
+ cwd = os.getcwd()
+ configuration = get_configuration_name(debug)
+ target_path = get_target_path(arch, debug)
+
+ symstore_path = [os.environ['KIT'], 'Debuggers']
+ if os.environ['PROCESSOR_ARCHITECTURE'] == 'x86':
+ symstore_path.append('x86')
+ else:
+ symstore_path.append('x64')
+ symstore_path.append('symstore.exe')
+
+ symstore = os.path.join(*symstore_path)
+
+ version = '.'.join([os.environ['MAJOR_VERSION'],
+ os.environ['MINOR_VERSION'],
+ os.environ['MICRO_VERSION'],
+ os.environ['BUILD_NUMBER']])
+
+ os.chdir(target_path)
+ command=['"' + symstore + '"']
+ command.append('add')
+ command.append('/s')
+ command.append(os.environ['SYMBOL_SERVER'])
+ command.append('/r')
+ command.append('/f')
+ command.append('*.pdb')
+ command.append('/t')
+ command.append(name)
+ command.append('/v')
+ command.append(version)
+
+ shell(' '.join(command))
+
+ os.chdir(cwd)
+
+
+def callfnout(cmd):
+ print(cmd)
+
+ sub = subprocess.Popen(cmd, stdout=subprocess.PIPE)
+ output = sub.communicate()[0]
+ ret = sub.returncode
+
+ if ret != 0:
+ raise(Exception("Error %d in : %s" % (ret, cmd)))
+
+ return output.decode('utf-8')
+
+
+def archive(filename, files, tgz=False):
+ access='w'
+ if tgz:
+ access='w:gz'
+ tar = tarfile.open(filename, access)
+ for name in files :
+ try:
+ print('adding '+name)
+ tar.add(name)
+ except:
+ pass
+ tar.close()
+
+
+if __name__ == '__main__':
+ debug = { 'checked': True, 'free': False }
+ driver = 'xenvif'
+
+ os.environ['MAJOR_VERSION'] = '7'
+ os.environ['MINOR_VERSION'] = '0'
+ os.environ['MICRO_VERSION'] = '0'
+
+ if 'BUILD_NUMBER' not in os.environ.keys():
+ os.environ['BUILD_NUMBER'] = next_build_number()
+
+ print("BUILD_NUMBER=%s" % os.environ['BUILD_NUMBER'])
+
+ if 'MERCURIAL_REVISION' in os.environ.keys():
+ revision = open('revision', 'w')
+ print(os.environ['MERCURIAL_REVISION'], file=revision)
+ revision.close()
+
+ make_header()
+
+ copy_inf(driver)
+
+ symstore_del(driver, 30)
+
+ msbuild(driver, 'x86', debug[sys.argv[1]])
+ msbuild(driver, 'x64', debug[sys.argv[1]])
+
+ symstore_add(driver, 'x86', debug[sys.argv[1]])
+ symstore_add(driver, 'x64', debug[sys.argv[1]])
+
+ listfile = callfnout(['hg','manifest'])
+ archive(driver + '\\source.tgz', listfile.splitlines(), tgz=True)
+ archive(driver + '.tar', [driver,'revision'])
+
--- /dev/null
+#!/usr/bin/env python
+
+import os, sys
+
+file = os.popen('hg status')
+
+for line in file:
+ item = line.split(' ')
+ if item[0] == '?':
+ path = ' '.join(item[1:]).rstrip()
+ print(path)
+ os.remove(path)
+
+file.close()
--- /dev/null
+/* Copyright (c) Citrix Systems Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms,
+ * with or without modification, are permitted provided
+ * that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer in the documentation and/or other
+ * materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef _XENBUS_DEBUG_INTERFACE_H
+#define _XENBUS_DEBUG_INTERFACE_H
+
+typedef struct _XENBUS_DEBUG_CALLBACK XENBUS_DEBUG_CALLBACK, *PXENBUS_DEBUG_CALLBACK;
+
+#define DEFINE_DEBUG_OPERATIONS \
+ DEBUG_OPERATION(VOID, \
+ Acquire, \
+ ( \
+ IN PXENBUS_DEBUG_CONTEXT Context \
+ ) \
+ ) \
+ DEBUG_OPERATION(VOID, \
+ Release, \
+ ( \
+ IN PXENBUS_DEBUG_CONTEXT Context \
+ ) \
+ ) \
+ DEBUG_OPERATION(NTSTATUS, \
+ Register, \
+ ( \
+ IN PXENBUS_DEBUG_CONTEXT Context, \
+ IN const CHAR *Prefix, \
+ IN VOID (*Function)(PVOID, BOOLEAN), \
+ IN PVOID Argument OPTIONAL, \
+ OUT PXENBUS_DEBUG_CALLBACK *Callback \
+ ) \
+ ) \
+ DEBUG_OPERATION(VOID, \
+ Printf, \
+ ( \
+ IN PXENBUS_DEBUG_CONTEXT Context, \
+ IN PXENBUS_DEBUG_CALLBACK Callback, \
+ IN const CHAR *Format, \
+ ... \
+ ) \
+ ) \
+ DEBUG_OPERATION(VOID, \
+ Deregister, \
+ ( \
+ IN PXENBUS_DEBUG_CONTEXT Context, \
+ IN PXENBUS_DEBUG_CALLBACK Callback \
+ ) \
+ )
+
+typedef struct _XENBUS_DEBUG_CONTEXT XENBUS_DEBUG_CONTEXT, *PXENBUS_DEBUG_CONTEXT;
+
+#define DEBUG_OPERATION(_Type, _Name, _Arguments) \
+ _Type (*DEBUG_ ## _Name) _Arguments;
+
+typedef struct _XENBUS_DEBUG_OPERATIONS {
+ DEFINE_DEBUG_OPERATIONS
+} XENBUS_DEBUG_OPERATIONS, *PXENBUS_DEBUG_OPERATIONS;
+
+#undef DEBUG_OPERATION
+
+typedef struct _XENBUS_DEBUG_INTERFACE XENBUS_DEBUG_INTERFACE, *PXENBUS_DEBUG_INTERFACE;
+
+// {54AAE52C-1838-49d3-AA43-9DDDD891603B}
+DEFINE_GUID(GUID_DEBUG_INTERFACE,
+ 0x54aae52c,
+ 0x1838,
+ 0x49d3,
+ 0xaa,
+ 0x43,
+ 0x9d,
+ 0xdd,
+ 0xd8,
+ 0x91,
+ 0x60,
+ 0x3b);
+
+#define DEBUG_INTERFACE_VERSION 4
+
+#define DEBUG_OPERATIONS(_Interface) \
+ (PXENBUS_DEBUG_OPERATIONS *)((ULONG_PTR)(_Interface))
+
+#define DEBUG_CONTEXT(_Interface) \
+ (PXENBUS_DEBUG_CONTEXT *)((ULONG_PTR)(_Interface) + sizeof (PVOID))
+
+#define DEBUG(_Operation, _Interface, ...) \
+ (*DEBUG_OPERATIONS(_Interface))->DEBUG_ ## _Operation((*DEBUG_CONTEXT(_Interface)), __VA_ARGS__)
+
+#endif // _XENBUS_DEBUG_INTERFACE_H
+
--- /dev/null
+/* Copyright (c) Citrix Systems Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms,
+ * with or without modification, are permitted provided
+ * that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer in the documentation and/or other
+ * materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef _XENFILT_EMULATED_INTERFACE_H
+#define _XENFILT_EMULATED_INTERFACE_H
+
+#define DEFINE_EMULATED_OPERATIONS \
+ EMULATED_OPERATION(VOID, \
+ Acquire, \
+ ( \
+ IN PXENFILT_EMULATED_CONTEXT Context \
+ ) \
+ ) \
+ EMULATED_OPERATION(VOID, \
+ Release, \
+ ( \
+ IN PXENFILT_EMULATED_CONTEXT Context \
+ ) \
+ ) \
+ EMULATED_OPERATION(BOOLEAN, \
+ IsPresent, \
+ ( \
+ IN PXENFILT_EMULATED_CONTEXT Context, \
+ IN PCHAR Class, \
+ IN PCHAR Device \
+ ) \
+ )
+
+typedef struct _XENFILT_EMULATED_CONTEXT XENFILT_EMULATED_CONTEXT, *PXENFILT_EMULATED_CONTEXT;
+
+#define EMULATED_OPERATION(_Type, _Name, _Arguments) \
+ _Type (*EMULATED_ ## _Name) _Arguments;
+
+typedef struct _XENFILT_EMULATED_OPERATIONS {
+ DEFINE_EMULATED_OPERATIONS
+} XENFILT_EMULATED_OPERATIONS, *PXENFILT_EMULATED_OPERATIONS;
+
+#undef EMULATED_OPERATION
+
+typedef struct _XENFILT_EMULATED_INTERFACE XENFILT_EMULATED_INTERFACE, *PXENFILT_EMULATED_INTERFACE;
+
+// {062AAC96-2BF8-4A69-AD6B-154CF051E977}
+DEFINE_GUID(GUID_EMULATED_INTERFACE,
+ 0x62aac96,
+ 0x2bf8,
+ 0x4a69,
+ 0xad,
+ 0x6b,
+ 0x15,
+ 0x4c,
+ 0xf0,
+ 0x51,
+ 0xe9,
+ 0x77);
+
+#define EMULATED_INTERFACE_VERSION 3
+
+#define EMULATED_OPERATIONS(_Interface) \
+ (PXENFILT_EMULATED_OPERATIONS *)((ULONG_PTR)(_Interface))
+
+#define EMULATED_CONTEXT(_Interface) \
+ (PXENFILT_EMULATED_CONTEXT *)((ULONG_PTR)(_Interface) + sizeof (PVOID))
+
+#define EMULATED(_Operation, _Interface, ...) \
+ (*EMULATED_OPERATIONS(_Interface))->EMULATED_ ## _Operation((*EMULATED_CONTEXT(_Interface)), __VA_ARGS__)
+
+#endif // _XENFILT_EMULATED_INTERFACE_H
+
--- /dev/null
+/* Copyright (c) Citrix Systems Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms,
+ * with or without modification, are permitted provided
+ * that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer in the documentation and/or other
+ * materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef _ETHERNET_H_
+#define _ETHERNET_H_
+
+#pragma warning(push)
+#pragma warning(disable:4201) // nonstandard extension used : nameless struct/union
+
+#define NTOHS(_Value) _byteswap_ushort(_Value)
+#define HTONS(_Value) _byteswap_ushort(_Value)
+#define NTOHL(_Value) _byteswap_ulong(_Value)
+#define HTONL(_Value) _byteswap_ulong(_Value)
+
+#pragma pack(push, 1)
+
+// Ethernet data structures
+//
+// NOTE: Fields are in network byte order
+
+#define ETHERNET_MTU 1500
+#define ETHERNET_MIN 60
+#define ETHERNET_MAX 1514
+
+typedef struct _ETHERNET_ADDRESS {
+ UCHAR Byte[6];
+} ETHERNET_ADDRESS, *PETHERNET_ADDRESS;
+
+#define ETHERNET_ADDRESS_LENGTH (sizeof (ETHERNET_ADDRESS))
+
+typedef enum _ETHERNET_ADDRESS_TYPE {
+ ETHERNET_ADDRESS_TYPE_INVALID = 0,
+ ETHERNET_ADDRESS_UNICAST,
+ ETHERNET_ADDRESS_MULTICAST,
+ ETHERNET_ADDRESS_BROADCAST,
+ ETHERNET_ADDRESS_TYPE_COUNT
+} ETHERNET_ADDRESS_TYPE, *PETHERNET_ADDRESS_TYPE;
+
+#define GET_ETHERNET_ADDRESS_TYPE(_Address) \
+ (((_Address)->Byte[0] & 0x01) ? \
+ (((((_Address)->Byte[0] & ~0x03) == 0xFC) && \
+ (((_Address)->Byte[1] ) == 0xFF) && \
+ (((_Address)->Byte[2] ) == 0xFF) && \
+ (((_Address)->Byte[3] ) == 0xFF) && \
+ (((_Address)->Byte[4] ) == 0xFF) && \
+ (((_Address)->Byte[5] ) == 0xFF) \
+ ) ? \
+ ETHERNET_ADDRESS_BROADCAST : \
+ ETHERNET_ADDRESS_MULTICAST \
+ ) : \
+ ETHERNET_ADDRESS_UNICAST \
+ )
+
+typedef struct _ETHERNET_UNTAGGED_HEADER {
+ ETHERNET_ADDRESS DestinationAddress;
+ ETHERNET_ADDRESS SourceAddress;
+ USHORT TypeOrLength;
+
+#define ETHERTYPE_INVALID 0x0000
+#define ETHERTYPE_IPV4 0x0800
+#define ETHERTYPE_IPV6 0x86DD
+#define ETHERTYPE_ARP 0x0806
+#define ETHERTYPE_RARP 0x0835
+#define ETHERTYPE_TPID 0x8100
+#define ETHERTYPE_IPX 0xFFFF
+
+} ETHERNET_UNTAGGED_HEADER, *PETHERNET_UNTAGGED_HEADER;
+
+typedef struct _ETHERNET_TAG {
+ USHORT ProtocolID; // == ETHERTYPE_TPID
+ USHORT ControlInformation;
+
+#define PACK_TAG_CONTROL_INFORMATION(_ControlInformation, _UserPriority, _CanonicalFormatId, _VlanId) \
+ do { \
+ (_ControlInformation) = (USHORT)(_VlanId) & 0x0FFF; \
+ (_ControlInformation) |= (USHORT)((_CanonicalFormatId) << 12) & 0x1000; \
+ (_ControlInformation) |= (USHORT)((_UserPriority) << 13) & 0xE000; \
+ } while (FALSE)
+
+#define UNPACK_TAG_CONTROL_INFORMATION(_ControlInformation, _UserPriority, _CanonicalFormatId, _VlanId) \
+ do { \
+ (_VlanId) = (_ControlInformation) & 0xFFF; \
+ (_CanonicalFormatId) = ((_ControlInformation) & 0x1000) >> 12; \
+ (_UserPriority) = ((_ControlInformation) & 0xE000) >> 13; \
+ } while (FALSE)
+
+} ETHERNET_TAG, *PETHERNET_TAG;
+
+typedef struct _ETHERNET_TAGGED_HEADER {
+ ETHERNET_ADDRESS DestinationAddress;
+ ETHERNET_ADDRESS SourceAddress;
+ ETHERNET_TAG Tag;
+ USHORT TypeOrLength;
+} ETHERNET_TAGGED_HEADER, *PETHERNET_TAGGED_HEADER;
+
+typedef union _ETHERNET_HEADER {
+ struct {
+ ETHERNET_ADDRESS DestinationAddress;
+ ETHERNET_ADDRESS SourceAddress;
+ };
+ ETHERNET_UNTAGGED_HEADER Untagged;
+ ETHERNET_TAGGED_HEADER Tagged;
+} ETHERNET_HEADER, *PETHERNET_HEADER;
+
+#define ETHERNET_HEADER_IS_TAGGED(_Header) \
+ ((_Header)->Untagged.TypeOrLength == NTOHS(ETHERTYPE_TPID))
+
+#define ETHERNET_HEADER_LENGTH(_Header) \
+ ETHERNET_HEADER_IS_TAGGED(_Header) ? \
+ sizeof (ETHERNET_TAGGED_HEADER) : \
+ sizeof (ETHERNET_UNTAGGEDHEADER))
+
+#pragma pack(pop)
+
+#pragma warning(pop)
+
+#endif // _ETHERNET_H
--- /dev/null
+/* Copyright (c) Citrix Systems Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms,
+ * with or without modification, are permitted provided
+ * that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer in the documentation and/or other
+ * materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef _XENBUS_EVTCHN_INTERFACE_H
+#define _XENBUS_EVTCHN_INTERFACE_H
+
+typedef enum _XENBUS_EVTCHN_TYPE {
+ EVTCHN_TYPE_INVALID = 0,
+ EVTCHN_FIXED,
+ EVTCHN_UNBOUND,
+ EVTCHN_INTER_DOMAIN,
+ EVTCHN_VIRQ
+} XENBUS_EVTCHN_TYPE, *PXENBUS_EVTCHN_TYPE;
+
+typedef struct _XENBUS_EVTCHN_DESCRIPTOR XENBUS_EVTCHN_DESCRIPTOR, *PXENBUS_EVTCHN_DESCRIPTOR;
+
+#define PORT_INVALID 0xFFFFFFFF
+
+#define DEFINE_EVTCHN_OPERATIONS \
+ EVTCHN_OPERATION(VOID, \
+ Acquire, \
+ ( \
+ IN PXENBUS_EVTCHN_CONTEXT Context \
+ ) \
+ ) \
+ EVTCHN_OPERATION(VOID, \
+ Release, \
+ ( \
+ IN PXENBUS_EVTCHN_CONTEXT Context \
+ ) \
+ ) \
+ EVTCHN_OPERATION(PXENBUS_EVTCHN_DESCRIPTOR, \
+ Open, \
+ ( \
+ IN PXENBUS_EVTCHN_CONTEXT Context, \
+ IN XENBUS_EVTCHN_TYPE Type, \
+ IN PKSERVICE_ROUTINE Function, \
+ IN PVOID Argument OPTIONAL, \
+ ... \
+ ) \
+ ) \
+ EVTCHN_OPERATION(BOOLEAN, \
+ Unmask, \
+ ( \
+ IN PXENBUS_EVTCHN_CONTEXT Context, \
+ IN PXENBUS_EVTCHN_DESCRIPTOR Descriptor, \
+ IN BOOLEAN Locked \
+ ) \
+ ) \
+ EVTCHN_OPERATION(NTSTATUS, \
+ Send, \
+ ( \
+ IN PXENBUS_EVTCHN_CONTEXT Context, \
+ IN PXENBUS_EVTCHN_DESCRIPTOR Descriptor \
+ ) \
+ ) \
+ EVTCHN_OPERATION(BOOLEAN, \
+ Trigger, \
+ ( \
+ IN PXENBUS_EVTCHN_CONTEXT Context, \
+ IN PXENBUS_EVTCHN_DESCRIPTOR Descriptor \
+ ) \
+ ) \
+ EVTCHN_OPERATION(VOID, \
+ Close, \
+ ( \
+ IN PXENBUS_EVTCHN_CONTEXT Context, \
+ IN PXENBUS_EVTCHN_DESCRIPTOR Descriptor \
+ ) \
+ ) \
+ EVTCHN_OPERATION(ULONG, \
+ Port, \
+ ( \
+ IN PXENBUS_EVTCHN_CONTEXT Context, \
+ IN PXENBUS_EVTCHN_DESCRIPTOR Descriptor \
+ ) \
+ )
+
+typedef struct _XENBUS_EVTCHN_CONTEXT XENBUS_EVTCHN_CONTEXT, *PXENBUS_EVTCHN_CONTEXT;
+
+#define EVTCHN_OPERATION(_Type, _Name, _Arguments) \
+ _Type (*EVTCHN_ ## _Name) _Arguments;
+
+typedef struct _XENBUS_EVTCHN_OPERATIONS {
+ DEFINE_EVTCHN_OPERATIONS
+} XENBUS_EVTCHN_OPERATIONS, *PXENBUS_EVTCHN_OPERATIONS;
+
+#undef EVTCHN_OPERATION
+
+typedef struct _XENBUS_EVTCHN_INTERFACE XENBUS_EVTCHN_INTERFACE, *PXENBUS_EVTCHN_INTERFACE;
+
+// {F87E8751-D6FB-44e8-85E3-DAC19FFA17A6}
+DEFINE_GUID(GUID_EVTCHN_INTERFACE,
+ 0xf87e8751,
+ 0xd6fb,
+ 0x44e8,
+ 0x85,
+ 0xe3,
+ 0xda,
+ 0xc1,
+ 0x9f,
+ 0xfa,
+ 0x17,
+ 0xa6);
+
+#define EVTCHN_INTERFACE_VERSION 4
+
+#define EVTCHN_OPERATIONS(_Interface) \
+ (PXENBUS_EVTCHN_OPERATIONS *)((ULONG_PTR)(_Interface))
+
+#define EVTCHN_CONTEXT(_Interface) \
+ (PXENBUS_EVTCHN_CONTEXT *)((ULONG_PTR)(_Interface) + sizeof (PVOID))
+
+#define EVTCHN(_Operation, _Interface, ...) \
+ (*EVTCHN_OPERATIONS(_Interface))->EVTCHN_ ## _Operation((*EVTCHN_CONTEXT(_Interface)), __VA_ARGS__)
+
+#endif // _XENBUS_EVTCHN_INTERFACE_H
+
--- /dev/null
+#!/bin/sh -x
+
+REPO="$1"
+TAG="$2"
+
+copy()
+{
+ SRCDIR="$1"
+ DSTDIR="$2"
+ HEADER="$3"
+
+ CWD=$(pwd)
+
+ mkdir -p ${DSTDIR}
+ cd ${DSTDIR}
+
+ URL="${REPO}/raw-file/${TAG}/xen/include/${SRCDIR}/${HEADER}"
+
+ wget ${URL}
+
+ mv ${HEADER} ${HEADER}.orig
+ sed -e 's/ unsigned long/ ULONG_PTR/g' \
+ -e 's/(unsigned long/(ULONG_PTR/g' \
+ -e 's/ long/ LONG_PTR/g' \
+ -e 's/(long/(LONG_PTR/g' \
+ < ${HEADER}.orig > ${HEADER}
+
+ cd ${CWD}
+}
+
+rm -rf xen
+mkdir -p xen
+cd xen
+
+copy public . xen.h
+copy public . xen-compat.h
+copy public . grant_table.h
+
+copy public/arch-x86 arch-x86 xen.h
+copy public/arch-x86 arch-x86 xen-x86_32.h
+copy public/arch-x86 arch-x86 xen-x86_64.h
+
+copy public/io io netif.h
+copy public/io io ring.h
+copy public/io io xenbus.h
+
+
--- /dev/null
+/* Copyright (c) Citrix Systems Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms,
+ * with or without modification, are permitted provided
+ * that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer in the documentation and/or other
+ * materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef _XENBUS_GNTTAB_INTERFACE_H
+#define _XENBUS_GNTTAB_INTERFACE_H
+
+typedef enum _XENBUS_GNTTAB_ENTRY_TYPE {
+ GNTTAB_ENTRY_TYPE_INVALID = 0,
+ GNTTAB_ENTRY_FULL_PAGE,
+ GNTTAB_ENTRY_SUB_PAGE,
+ GNTTAB_ENTRY_TRANSITIVE
+} XENBUS_GNTTAB_ENTRY_TYPE, *PXENBUS_GNTTAB_ENTRY_TYPE;
+
+typedef struct _XENBUS_GNTTAB_COPY_OPERATION {
+ LIST_ENTRY ListEntry;
+ USHORT RemoteDomain;
+ ULONG RemoteReference;
+ ULONG RemoteOffset;
+ PFN_NUMBER Pfn;
+ ULONG Offset;
+ ULONG Length;
+} XENBUS_GNTTAB_COPY_OPERATION, *PXENBUS_GNTTAB_COPY_OPERATION;
+
+#define DEFINE_GNTTAB_OPERATIONS \
+ GNTTAB_OPERATION(VOID, \
+ Acquire, \
+ ( \
+ IN PXENBUS_GNTTAB_CONTEXT Context \
+ ) \
+ ) \
+ GNTTAB_OPERATION(VOID, \
+ Release, \
+ ( \
+ IN PXENBUS_GNTTAB_CONTEXT Context \
+ ) \
+ ) \
+ GNTTAB_OPERATION(NTSTATUS, \
+ Get, \
+ ( \
+ IN PXENBUS_GNTTAB_CONTEXT Context, \
+ OUT PULONG Reference \
+ ) \
+ ) \
+ GNTTAB_OPERATION(VOID, \
+ Put, \
+ ( \
+ IN PXENBUS_GNTTAB_CONTEXT Context, \
+ IN ULONG Reference \
+ ) \
+ ) \
+ GNTTAB_OPERATION(VOID, \
+ PermitForeignAccess, \
+ ( \
+ IN PXENBUS_GNTTAB_CONTEXT Context, \
+ IN ULONG Reference, \
+ IN USHORT Domain, \
+ IN XENBUS_GNTTAB_ENTRY_TYPE Type, \
+ ... \
+ ) \
+ ) \
+ GNTTAB_OPERATION(VOID, \
+ RevokeForeignAccess, \
+ ( \
+ IN PXENBUS_GNTTAB_CONTEXT Context, \
+ IN ULONG Reference \
+ ) \
+ ) \
+ GNTTAB_OPERATION(NTSTATUS, \
+ Copy, \
+ ( \
+ IN PXENBUS_GNTTAB_CONTEXT Context, \
+ IN PLIST_ENTRY List, \
+ IN ULONG Count \
+ ) \
+ )
+
+typedef struct _XENBUS_GNTTAB_CONTEXT XENBUS_GNTTAB_CONTEXT, *PXENBUS_GNTTAB_CONTEXT;
+
+#define GNTTAB_OPERATION(_Type, _Name, _Arguments) \
+ _Type (*GNTTAB_ ## _Name) _Arguments;
+
+typedef struct _XENBUS_GNTTAB_OPERATIONS {
+ DEFINE_GNTTAB_OPERATIONS
+} XENBUS_GNTTAB_OPERATIONS, *PXENBUS_GNTTAB_OPERATIONS;
+
+#undef GNTTAB_OPERATION
+
+typedef struct _XENBUS_GNTTAB_INTERFACE XENBUS_GNTTAB_INTERFACE, *PXENBUS_GNTTAB_INTERFACE;
+
+// {CC32D7DF-88BE-4248-9A53-1F178BE9D60E}
+DEFINE_GUID(GUID_GNTTAB_INTERFACE,
+ 0xcc32d7df,
+ 0x88be,
+ 0x4248,
+ 0x9a,
+ 0x53,
+ 0x1f,
+ 0x17,
+ 0x8b,
+ 0xe9,
+ 0xd6,
+ 0xe);
+
+#define GNTTAB_INTERFACE_VERSION 2
+
+#define GNTTAB_OPERATIONS(_Interface) \
+ (PXENBUS_GNTTAB_OPERATIONS *)((ULONG_PTR)(_Interface))
+
+#define GNTTAB_CONTEXT(_Interface) \
+ (PXENBUS_GNTTAB_CONTEXT *)((ULONG_PTR)(_Interface) + sizeof (PVOID))
+
+#define GNTTAB(_Operation, _Interface, ...) \
+ (*GNTTAB_OPERATIONS(_Interface))->GNTTAB_ ## _Operation((*GNTTAB_CONTEXT(_Interface)), __VA_ARGS__)
+
+#endif // _XENBUS_GNTTAB_INTERFACE_H
+
--- /dev/null
+/* Copyright (c) Citrix Systems Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms,
+ * with or without modification, are permitted provided
+ * that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer in the documentation and/or other
+ * materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef _IPX_H
+#define _IPX_H
+
+#pragma warning(push)
+#pragma warning(disable:4214) // nonstandard extension used : bit field types other than int
+#pragma warning(disable:4201) // nonstandard extension used : nameless struct/union
+
+#pragma pack(push, 1)
+
+// IPX data structures
+//
+// NOTE: Fields are in network byte order
+
+typedef struct _IPX_NODE {
+ UCHAR Byte[6];
+} IPX_NODE, *PIPX_NODE;
+
+typedef struct _IPX_NETWORK {
+ UCHAR Byte[4];
+} IPX_NETWORK, *PIPX_NETWORK;
+
+typedef struct _IPX_HEADER {
+ USHORT Checksum;
+ USHORT PacketLength;
+ UCHAR TransportControl;
+ UCHAR PacketType;
+ IPX_NETWORK DestinationNetwork;
+ IPX_NODE DestinationNode;
+ UCHAR DestinationSocket;
+ IPX_NETWORK SourceNetwork;
+ IPX_NODE SourceNode;
+ UCHAR SourceSocket;
+} IPX_HEADER, *PIPX_HEADER;
+
+#pragma pack(pop)
+
+#pragma warning(pop)
+
+#endif //_IPX_H
--- /dev/null
+/* Copyright (c) Citrix Systems Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms,
+ * with or without modification, are permitted provided
+ * that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer in the documentation and/or other
+ * materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef _LLC_H
+#define _LLC_H
+
+#pragma warning(push)
+#pragma warning(disable:4214) // nonstandard extension used : bit field types other than int
+#pragma warning(disable:4201) // nonstandard extension used : nameless struct/union
+
+#pragma pack(push, 1)
+
+// LLC data structures
+//
+// NOTE: Fields are in network byte order
+
+typedef struct _LLC_U_HEADER {
+ UCHAR DestinationSAP;
+ UCHAR SourceSAP;
+ UCHAR Control;
+} LLC_U_HEADER, *PLLC_U_HEADER;
+
+#define LLC_SAP_MASK (~(UCHAR)1)
+#define LLC_U_FRAME 0x03
+
+typedef struct _LLC_SNAP_HEADER {
+ UCHAR DestinationSAP;
+ UCHAR SourceSAP;
+ UCHAR Control;
+ UCHAR OUI[3];
+ USHORT Type;
+} LLC_SNAP_HEADER, *PLLC_SNAP_HEADER;
+
+#define SNAPTYPE_IPX 0x8137
+#define SNAPTYPE_IPV4 ETHERTYPE_IPV4
+#define SNAPTYPE_IPV6 ETHERTYPE_IPV6
+
+#pragma pack(pop)
+
+#pragma warning(pop)
+
+#endif //_LLC_H
--- /dev/null
+/* Copyright (c) Citrix Systems Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms,
+ * with or without modification, are permitted provided
+ * that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer in the documentation and/or other
+ * materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef _XENBUS_STORE_INTERFACE_H
+#define _XENBUS_STORE_INTERFACE_H
+
+typedef struct _XENBUS_STORE_TRANSACTION XENBUS_STORE_TRANSACTION, *PXENBUS_STORE_TRANSACTION;
+typedef struct _XENBUS_STORE_WATCH XENBUS_STORE_WATCH, *PXENBUS_STORE_WATCH;
+
+#define DEFINE_STORE_OPERATIONS \
+ STORE_OPERATION(VOID, \
+ Acquire, \
+ ( \
+ IN PXENBUS_STORE_CONTEXT Context \
+ ) \
+ ) \
+ STORE_OPERATION(VOID, \
+ Release, \
+ ( \
+ IN PXENBUS_STORE_CONTEXT Context \
+ ) \
+ ) \
+ STORE_OPERATION(VOID, \
+ Free, \
+ ( \
+ IN PXENBUS_STORE_CONTEXT Context, \
+ IN PCHAR Value \
+ ) \
+ ) \
+ STORE_OPERATION(NTSTATUS, \
+ Read, \
+ ( \
+ IN PXENBUS_STORE_CONTEXT Context, \
+ IN PXENBUS_STORE_TRANSACTION Transaction OPTIONAL, \
+ IN PCHAR Prefix OPTIONAL, \
+ IN PCHAR Node, \
+ OUT PCHAR *Value \
+ ) \
+ ) \
+ STORE_OPERATION(NTSTATUS, \
+ Write, \
+ ( \
+ IN PXENBUS_STORE_CONTEXT Context, \
+ IN PXENBUS_STORE_TRANSACTION Transaction OPTIONAL, \
+ IN PCHAR Prefix OPTIONAL, \
+ IN PCHAR Node, \
+ IN PCHAR Value \
+ ) \
+ ) \
+ STORE_OPERATION(NTSTATUS, \
+ Printf, \
+ ( \
+ IN PXENBUS_STORE_CONTEXT Context, \
+ IN PXENBUS_STORE_TRANSACTION Transaction OPTIONAL, \
+ IN PCHAR Prefix OPTIONAL, \
+ IN PCHAR Node, \
+ IN const CHAR *Format, \
+ ... \
+ ) \
+ ) \
+ STORE_OPERATION(NTSTATUS, \
+ Remove, \
+ ( \
+ IN PXENBUS_STORE_CONTEXT Context, \
+ IN PXENBUS_STORE_TRANSACTION Transaction OPTIONAL, \
+ IN PCHAR Prefix OPTIONAL, \
+ IN PCHAR Node \
+ ) \
+ ) \
+ STORE_OPERATION(NTSTATUS, \
+ Directory, \
+ ( \
+ IN PXENBUS_STORE_CONTEXT Context, \
+ IN PXENBUS_STORE_TRANSACTION Transaction OPTIONAL, \
+ IN PCHAR Prefix OPTIONAL, \
+ IN PCHAR Node, \
+ OUT PCHAR *Value \
+ ) \
+ ) \
+ STORE_OPERATION(NTSTATUS, \
+ TransactionStart, \
+ ( \
+ IN PXENBUS_STORE_CONTEXT Context, \
+ OUT PXENBUS_STORE_TRANSACTION *Transaction \
+ ) \
+ ) \
+ STORE_OPERATION(NTSTATUS, \
+ TransactionEnd, \
+ ( \
+ IN PXENBUS_STORE_CONTEXT Context, \
+ IN PXENBUS_STORE_TRANSACTION Transaction, \
+ IN BOOLEAN Commit \
+ ) \
+ ) \
+ STORE_OPERATION(NTSTATUS, \
+ Watch, \
+ ( \
+ IN PXENBUS_STORE_CONTEXT Context, \
+ IN PCHAR Prefix OPTIONAL, \
+ IN PCHAR Node, \
+ IN PKEVENT Event, \
+ OUT PXENBUS_STORE_WATCH *Watch \
+ ) \
+ ) \
+ STORE_OPERATION(NTSTATUS, \
+ Unwatch, \
+ ( \
+ IN PXENBUS_STORE_CONTEXT Context, \
+ IN PXENBUS_STORE_WATCH Watch \
+ ) \
+ ) \
+ STORE_OPERATION(VOID, \
+ Poll, \
+ ( \
+ IN PXENBUS_STORE_CONTEXT Context \
+ ) \
+ )
+
+typedef struct _XENBUS_STORE_CONTEXT XENBUS_STORE_CONTEXT, *PXENBUS_STORE_CONTEXT;
+
+#define STORE_OPERATION(_Type, _Name, _Arguments) \
+ _Type (*STORE_ ## _Name) _Arguments;
+
+typedef struct _XENBUS_STORE_OPERATIONS {
+ DEFINE_STORE_OPERATIONS
+} XENBUS_STORE_OPERATIONS, *PXENBUS_STORE_OPERATIONS;
+
+#undef STORE_OPERATION
+
+typedef struct _XENBUS_STORE_INTERFACE XENBUS_STORE_INTERFACE, *PXENBUS_STORE_INTERFACE;
+
+// {916920F1-F9EE-465d-8137-5CC61786B840}
+DEFINE_GUID(GUID_STORE_INTERFACE,
+ 0x916920f1,
+ 0xf9ee,
+ 0x465d,
+ 0x81,
+ 0x37,
+ 0x5c,
+ 0xc6,
+ 0x17,
+ 0x86,
+ 0xb8,
+ 0x40);
+
+#define STORE_INTERFACE_VERSION 4
+
+#define STORE_OPERATIONS(_Interface) \
+ (PXENBUS_STORE_OPERATIONS *)((ULONG_PTR)(_Interface))
+
+#define STORE_CONTEXT(_Interface) \
+ (PXENBUS_STORE_CONTEXT *)((ULONG_PTR)(_Interface) + sizeof (PVOID))
+
+#define STORE(_Operation, _Interface, ...) \
+ (*STORE_OPERATIONS(_Interface))->STORE_ ## _Operation((*STORE_CONTEXT(_Interface)), __VA_ARGS__)
+
+#endif // _XENBUS_STORE_INTERFACE_H
+
--- /dev/null
+/* Copyright (c) Citrix Systems Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms,
+ * with or without modification, are permitted provided
+ * that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer in the documentation and/or other
+ * materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef _XENBUS_SUSPEND_INTERFACE_H
+#define _XENBUS_SUSPEND_INTERFACE_H
+
+typedef enum _XENBUS_SUSPEND_CALLBACK_TYPE {
+ SUSPEND_CALLBACK_TYPE_INVALID = 0,
+ SUSPEND_CALLBACK_EARLY,
+ SUSPEND_CALLBACK_LATE
+} XENBUS_SUSPEND_CALLBACK_TYPE, *PXENBUS_SUSPEND_CALLBACK_TYPE;
+
+typedef struct _XENBUS_SUSPEND_CALLBACK XENBUS_SUSPEND_CALLBACK, *PXENBUS_SUSPEND_CALLBACK;
+
+#define DEFINE_SUSPEND_OPERATIONS \
+ SUSPEND_OPERATION(VOID, \
+ Acquire, \
+ ( \
+ IN PXENBUS_SUSPEND_CONTEXT Context \
+ ) \
+ ) \
+ SUSPEND_OPERATION(VOID, \
+ Release, \
+ ( \
+ IN PXENBUS_SUSPEND_CONTEXT Context \
+ ) \
+ ) \
+ SUSPEND_OPERATION(NTSTATUS, \
+ Register, \
+ ( \
+ IN PXENBUS_SUSPEND_CONTEXT Context, \
+ IN XENBUS_SUSPEND_CALLBACK_TYPE Type, \
+ IN VOID (*Function)(PVOID), \
+ IN PVOID Argument OPTIONAL, \
+ OUT PXENBUS_SUSPEND_CALLBACK *Callback \
+ ) \
+ ) \
+ SUSPEND_OPERATION(VOID, \
+ Deregister, \
+ ( \
+ IN PXENBUS_SUSPEND_CONTEXT Context, \
+ IN PXENBUS_SUSPEND_CALLBACK Callback \
+ ) \
+ ) \
+ SUSPEND_OPERATION(ULONG, \
+ Count, \
+ ( \
+ IN PXENBUS_SUSPEND_CONTEXT Context \
+ ) \
+ )
+
+typedef struct _XENBUS_SUSPEND_CONTEXT XENBUS_SUSPEND_CONTEXT, *PXENBUS_SUSPEND_CONTEXT;
+
+#define SUSPEND_OPERATION(_Type, _Name, _Arguments) \
+ _Type (*SUSPEND_ ## _Name) _Arguments;
+
+typedef struct _XENBUS_SUSPEND_OPERATIONS {
+ DEFINE_SUSPEND_OPERATIONS
+} XENBUS_SUSPEND_OPERATIONS, *PXENBUS_SUSPEND_OPERATIONS;
+
+#undef SUSPEND_OPERATION
+
+typedef struct _XENBUS_SUSPEND_INTERFACE XENBUS_SUSPEND_INTERFACE, *PXENBUS_SUSPEND_INTERFACE;
+
+// 104f0a14-e2d5-42b6-b10f-a669ccd410a1
+
+DEFINE_GUID(GUID_SUSPEND_INTERFACE,
+ 0x104f0a14,
+ 0xe2d5,
+ 0x42b6,
+ 0xb1,
+ 0x0f,
+ 0xa6,
+ 0x69,
+ 0xcc,
+ 0xd4,
+ 0x10,
+ 0xa1);
+
+#define SUSPEND_INTERFACE_VERSION 2
+
+#define SUSPEND_OPERATIONS(_Interface) \
+ (PXENBUS_SUSPEND_OPERATIONS *)((ULONG_PTR)(_Interface))
+
+#define SUSPEND_CONTEXT(_Interface) \
+ (PXENBUS_SUSPEND_CONTEXT *)((ULONG_PTR)(_Interface) + sizeof (PVOID))
+
+#define SUSPEND(_Operation, _Interface, ...) \
+ (*SUSPEND_OPERATIONS(_Interface))->SUSPEND_ ## _Operation((*SUSPEND_CONTEXT(_Interface)), __VA_ARGS__)
+
+#endif // _XENBUS_SUSPEND_INTERFACE_H
+
--- /dev/null
+/* Copyright (c) Citrix Systems Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms,
+ * with or without modification, are permitted provided
+ * that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer in the documentation and/or other
+ * materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "ethernet.h"
+
+#ifndef _TCPIP_H
+#define _TCPIP_H
+
+#pragma warning(push)
+#pragma warning(disable:4214) // nonstandard extension used : bit field types other than int
+#pragma warning(disable:4201) // nonstandard extension used : nameless struct/union
+
+#pragma pack(push, 1)
+
+// TCP/IP data structures
+//
+// NOTE: Fields are in network byte order
+
+// IPv4
+
+typedef struct _IPV4_ADDRESS {
+ union {
+ ULONG Dword[1];
+ UCHAR Byte[4];
+ };
+} IPV4_ADDRESS, *PIPV4_ADDRESS;
+
+#define IPV4_ADDRESS_LENGTH (sizeof (IPV4_ADDRESS))
+
+typedef struct _IPV4_HEADER {
+ UCHAR HeaderLength:4;
+ UCHAR Version:4;
+ UCHAR TypeOfService;
+ USHORT PacketLength;
+ USHORT PacketID;
+ USHORT FragmentOffsetAndFlags;
+
+#define IPV4_FRAGMENT_OFFSET(_FragmentOffsetAndFlags) \
+ ((_FragmentOffsetAndFlags) & 0x1fff)
+#define IPV4_DONT_FRAGMENT(_FragmentOffsetAndFlags) \
+ ((_FragmentOffsetAndFlags) & 0x4000)
+#define IPV4_MORE_FRAGMENTS(_FragmentOffsetAndFlags) \
+ ((_FragmentOffsetAndFlags) & 0x2000)
+#define IPV4_IS_A_FRAGMENT(_FragmentOffsetAndFlags) \
+ ((_FragmentOffsetAndFlags) & 0x3fff)
+
+ UCHAR TimeToLive;
+ UCHAR Protocol;
+ USHORT Checksum;
+ IPV4_ADDRESS SourceAddress;
+ IPV4_ADDRESS DestinationAddress;
+} IPV4_HEADER, *PIPV4_HEADER;
+
+#define IPV4_HEADER_LENGTH(_Header) \
+ (((ULONG)((_Header)->HeaderLength)) << 2)
+
+#define MAXIMUM_IPV4_HEADER_LENGTH \
+ (0xF << 2)
+
+// IPv6
+
+typedef struct _IPV6_ADDRESS {
+ union {
+ ULONG Dword[4];
+ USHORT Word[8];
+ UCHAR Byte[16];
+ };
+} IPV6_ADDRESS, *PIPV6_ADDRESS;
+
+#define IPV6_ADDRESS_LENGTH (sizeof (IPV6_ADDRESS))
+
+#define IS_IPV6_UNSPECIFIED_ADDRESS(_Address) \
+ (((_Address)->Byte[0] == 0) && \
+ ((_Address)->Byte[1] == 0) && \
+ ((_Address)->Byte[2] == 0) && \
+ ((_Address)->Byte[3] == 0) && \
+ ((_Address)->Byte[4] == 0) && \
+ ((_Address)->Byte[5] == 0) && \
+ ((_Address)->Byte[6] == 0) && \
+ ((_Address)->Byte[7] == 0) && \
+ ((_Address)->Byte[8] == 0) && \
+ ((_Address)->Byte[9] == 0) && \
+ ((_Address)->Byte[10] == 0) && \
+ ((_Address)->Byte[11] == 0) && \
+ ((_Address)->Byte[12] == 0) && \
+ ((_Address)->Byte[13] == 0) && \
+ ((_Address)->Byte[14] == 0) && \
+ ((_Address)->Byte[15] == 0))
+
+typedef struct _IPV6_HEADER {
+ union {
+ struct {
+ UCHAR __Pad:4;
+ UCHAR Version:4;
+ };
+ ULONG VCF;
+ };
+ USHORT PayloadLength;
+ UCHAR NextHeader;
+ UCHAR HopLimit;
+ IPV6_ADDRESS SourceAddress;
+ IPV6_ADDRESS DestinationAddress;
+} IPV6_HEADER, *PIPV6_HEADER;
+
+#define IPV6_HEADER_LENGTH(_Header) \
+ (ULONG)(sizeof (IPV6_HEADER))
+
+#define MAXIMUM_IPV6_HEADER_LENGTH \
+ (ULONG)(sizeof (IPV6_HEADER))
+
+// IP
+
+typedef union _IP_ADDRESS {
+ IPV4_ADDRESS Version4;
+ IPV6_ADDRESS Version6;
+} IP_ADDRESS, *PIP_ADDRESS;
+
+typedef union _IP_HEADER {
+ struct {
+ UCHAR __Pad:4;
+ UCHAR Version:4;
+ };
+ IPV4_HEADER Version4;
+ IPV6_HEADER Version6;
+} IP_HEADER, *PIP_HEADER;
+
+#define IP_HEADER_LENGTH(_Header) \
+ (((_Header)->Version == 4) ? \
+ IPV4_HEADER_LENGTH(&(_Header)->Version4) : \
+ IPV6_HEADER_LENGTH(&(_Header)->Version6))
+
+#define IPPROTO_HOP_OPTIONS 0
+#define IPPROTO_DST_OPTIONS 60
+#define IPPROTO_ROUTING 43
+#define IPPROTO_FRAGMENT 44
+
+// Options
+
+typedef struct _IPV6_OPTION_HEADER {
+ UCHAR NextHeader;
+ UCHAR Length;
+} IPV6_OPTION_HEADER, *PIPV6_OPTION_HEADER;
+
+#define IPV6_OPTION_HEADER_LENGTH(_Header) \
+ (ULONG)(sizeof (IPV6_OPTION_HEADER) + (_Header)->PayloadLength)
+
+typedef struct _IPV6_FRAGMENT_HEADER {
+ UCHAR NextHeader;
+ UCHAR Reserved;
+ USHORT OffsetAndFlags;
+ ULONG ID;
+} IPV6_FRAGMENT_HEADER, *PIPV6_FRAGMENT_HEADER;
+
+#define IPV6_FRAGMENT_OFFSET(_FragmentOffsetAndFlags) \
+ ((_FragmentOffsetAndFlags) & 0x1fff)
+#define IPV6_MORE_FRAGMENTS(_FragmentOffsetAndFlags) \
+ ((_FragmentOffsetAndFlags) & 0x8000)
+#define IPV6_IS_A_FRAGMENT(_FragmentOffsetAndFlags) \
+ ((_FragmentOffsetAndFlags) & 0x9fff)
+
+#define IPPROTO_TCP 6
+
+// TCP
+
+typedef struct _TCP_HEADER {
+ USHORT SourcePort;
+ USHORT DestinationPort;
+ ULONG Seq;
+ ULONG Ack;
+ UCHAR Reserved:4;
+ UCHAR HeaderLength:4;
+ UCHAR Flags;
+
+#define TCP_FIN 0x01
+#define TCP_SYN 0x02
+#define TCP_RST 0x04
+#define TCP_PSH 0x08
+#define TCP_ACK 0x10
+#define TCP_URG 0x20
+#define TCP_ECE 0x40
+#define TCP_CWR 0x80
+
+ USHORT Window;
+ USHORT Checksum;
+ USHORT UrgentPointer;
+} TCP_HEADER, *PTCP_HEADER;
+
+#define TCP_HEADER_LENGTH(_Header) \
+ (((ULONG)((_Header)->HeaderLength)) << 2)
+
+#define MAXIMUM_TCP_HEADER_LENGTH \
+ (0xF << 2)
+
+#define TCPOPT_NOP 1
+#define TCPOPT_TIMESTAMP 8
+#define TCPOLEN_TIMESTAMP 10
+
+#define IPPROTO_UDP 17
+
+// UDP
+
+typedef struct _UDP_HEADER {
+ USHORT SourcePort;
+ USHORT DestinationPort;
+ USHORT PacketLength;
+ USHORT Checksum;
+} UDP_HEADER, *PUDP_HEADER;
+
+#define UDP_HEADER_LENGTH(_Header) \
+ (ULONG)(sizeof (UDP_HEADER))
+
+#define IPPROTO_ICMPV6 58
+
+// ICMPV6
+
+typedef struct _ICMPV6_HEADER {
+ UCHAR Type;
+ UCHAR Code;
+ USHORT Checksum;
+ ULONG Data;
+} ICMPV6_HEADER, *PICMPV6_HEADER;
+
+#define ICMPV6_TYPE_RS 133
+#define ICMPV6_TYPE_RA 134
+#define ICMPV6_TYPE_NS 135
+#define ICMPV6_TYPE_NA 136
+
+#define IPPROTO_NONE 59
+
+// Checksum
+
+typedef struct _IPV4_PSEUDO_HEADER {
+ IPV4_ADDRESS SourceAddress;
+ IPV4_ADDRESS DestinationAddress;
+ UCHAR Zero;
+ UCHAR Protocol; // TCP or UDP
+ USHORT Length; // Including TCP/UDP header
+} IPV4_PSEUDO_HEADER, *PIPV4_PSEUDO_HEADER;
+
+typedef struct _IPV6_PSEUDO_HEADER {
+ IPV6_ADDRESS SourceAddress;
+ IPV6_ADDRESS DestinationAddress;
+ USHORT Length; // Including TCP/UDP header
+ UCHAR Zero[3];
+ UCHAR NextHeader; // TCP or UDP
+} IPV6_PSEUDO_HEADER, *PIPV6_PSEUDO_HEADER;
+
+typedef union _PSEUDO_HEADER {
+ IPV4_PSEUDO_HEADER Version4;
+ IPV6_PSEUDO_HEADER Version6;
+} PSEUDO_HEADER, *PPSEUDO_HEADER;
+
+// ARP
+
+typedef struct _ARP_HEADER {
+ USHORT HardwareType;
+
+#define HARDWARE_ETHER 1
+
+ USHORT ProtocolType;
+
+#define PROTOCOL_IPV4 ETHERTYPE_IPV4
+
+ UCHAR HardwareAddressLength;
+ UCHAR ProtocolAddressLength;
+ USHORT Operation;
+
+#define ARP_REQUEST 1
+#define ARP_REPLY 2
+#define RARP_REQUEST 3
+#define RARP_REPLY 4
+
+} ARP_HEADER, *PARP_HEADER;
+
+#define ARP_HEADER_LENGTH(_Header) \
+ (ULONG)(sizeof (ARP_HEADER))
+
+#pragma pack(pop)
+
+#pragma warning(pop)
+
+#endif //_TCPIP_H
--- /dev/null
+/* Copyright (c) Citrix Systems Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms,
+ * with or without modification, are permitted provided
+ * that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer in the documentation and/or other
+ * materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef _UTIL_H
+#define _UTIL_H
+
+#include <ntddk.h>
+
+#include "assert.h"
+
+#define P2ROUNDUP(_x, _a) \
+ (-(-(_x) & -(_a)))
+
+static FORCEINLINE LONG
+__ffs(
+ IN unsigned long long mask
+ )
+{
+ unsigned char *array = (unsigned char *)&mask;
+ unsigned int byte;
+ unsigned int bit;
+ unsigned char val;
+
+ val = 0;
+
+ byte = 0;
+ while (byte < 8) {
+ val = array[byte];
+
+ if (val != 0)
+ break;
+
+ byte++;
+ }
+ if (byte == 8)
+ return -1;
+
+ bit = 0;
+ while (bit < 8) {
+ if (val & 0x01)
+ break;
+
+ val >>= 1;
+ bit++;
+ }
+
+ return (byte * 8) + bit;
+}
+
+#define __ffu(_mask) \
+ __ffs(~(_mask))
+
+static FORCEINLINE LONG
+__InterlockedAdd(
+ IN LONG *Value,
+ IN LONG Delta
+ )
+{
+ LONG New;
+ LONG Old;
+
+ do {
+ Old = *Value;
+ New = Old + Delta;
+ } while (InterlockedCompareExchange(Value, New, Old) != Old);
+
+ return New;
+}
+
+static FORCEINLINE LONG
+__InterlockedSubtract(
+ IN LONG *Value,
+ IN LONG Delta
+ )
+{
+ LONG New;
+ LONG Old;
+
+ do {
+ Old = *Value;
+ New = Old - Delta;
+ } while (InterlockedCompareExchange(Value, New, Old) != Old);
+
+ return New;
+}
+
+typedef struct _NON_PAGED_BUFFER_HEADER {
+ SIZE_T Length;
+ ULONG Tag;
+} NON_PAGED_BUFFER_HEADER, *PNON_PAGED_BUFFER_HEADER;
+
+typedef struct _NON_PAGED_BUFFER_TRAILER {
+ ULONG Tag;
+} NON_PAGED_BUFFER_TRAILER, *PNON_PAGED_BUFFER_TRAILER;
+
+static FORCEINLINE PVOID
+__AllocateNonPagedPoolWithTag(
+ IN SIZE_T Length,
+ IN ULONG Tag
+ )
+{
+ PUCHAR Buffer;
+ PNON_PAGED_BUFFER_HEADER Header;
+ PNON_PAGED_BUFFER_TRAILER Trailer;
+
+ ASSERT(Length != 0);
+
+ Buffer = ExAllocatePoolWithTag(NonPagedPool,
+ sizeof (NON_PAGED_BUFFER_HEADER) +
+ Length +
+ sizeof (NON_PAGED_BUFFER_TRAILER),
+ Tag);
+ if (Buffer == NULL)
+ goto done;
+
+ RtlZeroMemory(Buffer,
+ sizeof (NON_PAGED_BUFFER_HEADER) +
+ Length +
+ sizeof (NON_PAGED_BUFFER_TRAILER));
+
+ Header = (PNON_PAGED_BUFFER_HEADER)Buffer;
+ Header->Length = Length;
+ Header->Tag = Tag;
+
+ Buffer += sizeof (NON_PAGED_BUFFER_HEADER);
+
+ Trailer = (PNON_PAGED_BUFFER_TRAILER)(Buffer + Length);
+ Trailer->Tag = Tag;
+
+done:
+ return Buffer;
+}
+
+static FORCEINLINE VOID
+__FreePoolWithTag(
+ IN PVOID _Buffer,
+ IN ULONG Tag
+ )
+{
+ PUCHAR Buffer = _Buffer;
+ SIZE_T Length;
+ PNON_PAGED_BUFFER_HEADER Header;
+ PNON_PAGED_BUFFER_TRAILER Trailer;
+
+ ASSERT(Buffer != NULL);
+
+ Buffer -= sizeof (NON_PAGED_BUFFER_HEADER);
+
+ Header = (PNON_PAGED_BUFFER_HEADER)Buffer;
+ ASSERT3U(Tag, ==, Header->Tag);
+ Length = Header->Length;
+
+ Buffer += sizeof (NON_PAGED_BUFFER_HEADER);
+
+ Trailer = (PNON_PAGED_BUFFER_TRAILER)(Buffer + Length);
+ ASSERT3U(Tag, ==, Trailer->Tag);
+
+ Buffer -= sizeof (NON_PAGED_BUFFER_HEADER);
+
+ RtlFillMemory(Buffer,
+ sizeof (NON_PAGED_BUFFER_HEADER) +
+ Length +
+ sizeof (NON_PAGED_BUFFER_TRAILER),
+ 0xAA);
+
+ ExFreePoolWithTag(Buffer, Tag);
+}
+
+static FORCEINLINE PMDL
+__AllocatePage(
+ VOID
+ )
+{
+ PHYSICAL_ADDRESS LowAddress;
+ PHYSICAL_ADDRESS HighAddress;
+ LARGE_INTEGER SkipBytes;
+ SIZE_T TotalBytes;
+ PMDL Mdl;
+ PUCHAR MdlMappedSystemVa;
+ NTSTATUS status;
+
+ LowAddress.QuadPart = 0ull;
+ HighAddress.QuadPart = ~0ull;
+ SkipBytes.QuadPart = 0ull;
+ TotalBytes = (SIZE_T)PAGE_SIZE;
+
+ Mdl = MmAllocatePagesForMdlEx(LowAddress,
+ HighAddress,
+ SkipBytes,
+ TotalBytes,
+ MmCached,
+ 0);
+
+ status = STATUS_NO_MEMORY;
+ if (Mdl == NULL)
+ goto fail1;
+
+ ASSERT((Mdl->MdlFlags & (MDL_MAPPED_TO_SYSTEM_VA |
+ MDL_PARTIAL_HAS_BEEN_MAPPED |
+ MDL_PARTIAL |
+ MDL_PARENT_MAPPED_SYSTEM_VA |
+ MDL_SOURCE_IS_NONPAGED_POOL |
+ MDL_IO_SPACE)) == 0);
+
+ MdlMappedSystemVa = MmMapLockedPagesSpecifyCache(Mdl,
+ KernelMode,
+ MmCached,
+ NULL,
+ FALSE,
+ NormalPagePriority);
+
+ status = STATUS_UNSUCCESSFUL;
+ if (MdlMappedSystemVa == NULL)
+ goto fail2;
+
+ ASSERT3P(MdlMappedSystemVa, ==, Mdl->MappedSystemVa);
+
+ RtlZeroMemory(MdlMappedSystemVa, PAGE_SIZE);
+
+ return Mdl;
+
+fail2:
+ Error("fail2\n");
+
+ MmFreePagesFromMdl(Mdl);
+ ExFreePool(Mdl);
+
+fail1:
+ Error("fail1 (%08x)\n", status);
+
+ return NULL;
+}
+
+static FORCEINLINE VOID
+__FreePage(
+ IN PMDL Mdl
+ )
+{
+ PUCHAR MdlMappedSystemVa;
+
+ ASSERT(Mdl->MdlFlags & MDL_MAPPED_TO_SYSTEM_VA);
+ MdlMappedSystemVa = Mdl->MappedSystemVa;
+
+ RtlFillMemory(MdlMappedSystemVa, PAGE_SIZE, 0xAA);
+
+ MmUnmapLockedPages(MdlMappedSystemVa, Mdl);
+
+ MmFreePagesFromMdl(Mdl);
+}
+
+#endif // _UTIL_H
--- /dev/null
+/* Copyright (c) Citrix Systems Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms,
+ * with or without modification, are permitted provided
+ * that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer in the documentation and/or other
+ * materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef _XENVIF_VIF_INTERFACE_H
+#define _XENVIF_VIF_INTERFACE_H
+
+#include <ifdef.h>
+#include <ethernet.h>
+
+#define MAX_SKB_FRAGS ((65536/PAGE_SIZE) + 2)
+
+typedef UCHAR XENVIF_PACKET_STATUS, *PXENVIF_PACKET_STATUS;
+
+#define PACKET_STATUS_INVALID 0
+#define PACKET_PENDING 1
+#define PACKET_OK 2
+#define PACKET_DROPPED 3
+#define PACKET_ERROR 4
+
+typedef struct _XENVIF_PACKET_HEADER {
+ ULONG Offset;
+ ULONG Length;
+} XENVIF_PACKET_HEADER, *PXENVIF_PACKET_HEADER;
+
+typedef struct _XENVIF_PACKET_INFO {
+ XENVIF_PACKET_HEADER EthernetHeader;
+ XENVIF_PACKET_HEADER LLCSnapHeader;
+ XENVIF_PACKET_HEADER IpHeader;
+ XENVIF_PACKET_HEADER IpOptions;
+ XENVIF_PACKET_HEADER TcpHeader;
+ XENVIF_PACKET_HEADER TcpOptions;
+ XENVIF_PACKET_HEADER UdpHeader;
+ ULONG Length;
+} XENVIF_PACKET_INFO, *PXENVIF_PACKET_INFO;
+
+#pragma warning(push)
+#pragma warning(disable:4214) // nonstandard extension used : bit field types other than int
+#pragma warning(disable:4201) // nonstandard extension used : nameless struct/union
+
+typedef struct _XENVIF_CHECKSUM_FLAGS {
+ struct {
+ ULONG IpChecksumSucceeded:1;
+ ULONG IpChecksumFailed:1;
+ ULONG IpChecksumPresent:1;
+ ULONG TcpChecksumSucceeded:1;
+ ULONG TcpChecksumFailed:1;
+ ULONG TcpChecksumPresent:1;
+ ULONG UdpChecksumSucceeded:1;
+ ULONG UdpChecksumFailed:1;
+ ULONG UdpChecksumPresent:1;
+ ULONG Reserved:23;
+ };
+} XENVIF_CHECKSUM_FLAGS, *PXENVIF_CHECKSUM_FLAGS;
+
+#pragma warning(pop)
+
+typedef struct _XENVIF_RECEIVER_PACKET {
+ LIST_ENTRY ListEntry;
+ ULONG Offset;
+ ULONG Length;
+ XENVIF_PACKET_INFO Info;
+ XENVIF_CHECKSUM_FLAGS Flags;
+ USHORT TagControlInformation;
+ USHORT MaximumSegmentSize;
+ PVOID Cookie;
+ MDL Mdl;
+ PFN_NUMBER __Pfn;
+} XENVIF_RECEIVER_PACKET, *PXENVIF_RECEIVER_PACKET;
+
+typedef struct _XENVIF_RECEIVER_PACKET_STATISTICS {
+ ULONGLONG Drop;
+ ULONGLONG BackendError;
+ ULONGLONG FrontendError;
+ ULONGLONG Unicast;
+ ULONGLONG UnicastBytes;
+ ULONGLONG Multicast;
+ ULONGLONG MulticastBytes;
+ ULONGLONG Broadcast;
+ ULONGLONG BroadcastBytes;
+} XENVIF_RECEIVER_PACKET_STATISTICS, *PXENVIF_RECEIVER_PACKET_STATISTICS;
+
+#pragma warning(push)
+#pragma warning(disable:4214) // nonstandard extension used : bit field types other than int
+#pragma warning(disable:4201) // nonstandard extension used : nameless struct/union
+
+typedef struct _XENVIF_OFFLOAD_OPTIONS {
+ union {
+ struct {
+ USHORT OffloadTagManipulation:1;
+ USHORT OffloadIpVersion4LargePacket:1;
+ USHORT OffloadIpVersion4HeaderChecksum:1;
+ USHORT OffloadIpVersion4TcpChecksum:1;
+ USHORT OffloadIpVersion4UdpChecksum:1;
+ USHORT OffloadIpVersion6LargePacket:1;
+ USHORT OffloadIpVersion6TcpChecksum:1;
+ USHORT OffloadIpVersion6UdpChecksum:1;
+ USHORT NeedChecksumValue:1;
+ USHORT NeedLargePacketSplit:1;
+ USHORT Reserved:6;
+ };
+
+ USHORT Value;
+ };
+} XENVIF_OFFLOAD_OPTIONS, *PXENVIF_OFFLOAD_OPTIONS;
+
+#pragma warning(pop)
+
+typedef struct _XENVIF_TRANSMITTER_PACKET XENVIF_TRANSMITTER_PACKET, *PXENVIF_TRANSMITTER_PACKET;
+
+// To fit into the reserved space in NDIS_PACKET and NET_BUFFER structures the XENVIF_TRANSMITTER_PACKET
+// structure must be at most the size of 3 pointer types.
+
+#pragma pack(push, 1)
+typedef struct _XENVIF_SEND_INFO {
+ XENVIF_OFFLOAD_OPTIONS OffloadOptions;
+ USHORT MaximumSegmentSize; // Only used if OffloadOptions.OffloadIpVersion[4|6}LargePacket is set
+ USHORT TagControlInformation; // Only used if OffloadOptions.OffloadTagManipulation is set
+} XENVIF_SEND_INFO, *PXENVIF_SEND_INFO;
+
+typedef struct _XENVIF_COMPLETION_INFO {
+ UCHAR Type;
+ XENVIF_PACKET_STATUS Status;
+ USHORT PacketLength;
+ USHORT PayloadLength;
+} XENVIF_COMPLETION_INFO, *PXENVIF_COMPLETION_INFO;
+
+#pragma warning(push)
+#pragma warning(disable:4201) // nonstandard extension used : nameless struct/union
+
+struct _XENVIF_TRANSMITTER_PACKET {
+ PXENVIF_TRANSMITTER_PACKET Next;
+ union {
+ XENVIF_SEND_INFO Send;
+ XENVIF_COMPLETION_INFO Completion;
+ };
+};
+
+#pragma warning(pop)
+
+#pragma pack(pop)
+
+C_ASSERT(sizeof (XENVIF_TRANSMITTER_PACKET) <= (3 * sizeof (PVOID)));
+
+// Because we're so tight on space in the XENVIF_TRANSMITTER_PACKET structure, certain packet metadata
+// needs to be accessed via magic offsets
+typedef struct _XENVIF_TRANSMITTER_PACKET_METADATA {
+ LONG_PTR OffsetOffset;
+ LONG_PTR LengthOffset;
+ LONG_PTR MdlOffset;
+} XENVIF_TRANSMITTER_PACKET_METADATA, *PXENVIF_TRANSMITTER_PACKET_METADATA;
+
+typedef struct _XENVIF_TRANSMITTER_PACKET_STATISTICS {
+ ULONG Drop;
+ ULONG BackendError;
+ ULONG FrontendError;
+ ULONG Unicast;
+ ULONG UnicastBytes;
+ ULONG Multicast;
+ ULONG MulticastBytes;
+ ULONG Broadcast;
+ ULONG BroadcastBytes;
+} XENVIF_TRANSMITTER_PACKET_STATISTICS, *PXENVIF_TRANSMITTER_PACKET_STATISTICS;
+
+typedef struct _XENVIF_PACKET_STATISTICS {
+ XENVIF_RECEIVER_PACKET_STATISTICS Receiver;
+ XENVIF_TRANSMITTER_PACKET_STATISTICS Transmitter;
+} XENVIF_PACKET_STATISTICS, *PXENVIF_PACKET_STATISTICS;
+
+#define MAXIMUM_MULTICAST_ADDRESS_COUNT 32 // Minimum number to pass WHQL
+
+typedef enum _XENVIF_MAC_FILTER_LEVEL {
+ MAC_FILTER_NONE = 0,
+ MAC_FILTER_MATCHING = 1,
+ MAC_FILTER_ALL = 2
+} XENVIF_MAC_FILTER_LEVEL, *PXENVIF_MAC_FILTER_LEVEL;
+
+typedef struct _XENVIF_MEDIA_STATE {
+ NET_IF_MEDIA_CONNECT_STATE MediaConnectState;
+ ULONG64 LinkSpeed;
+ NET_IF_MEDIA_DUPLEX_STATE MediaDuplexState;
+} XENVIF_MEDIA_STATE, *PXENVIF_MEDIA_STATE;
+
+typedef enum XENVIF_CALLBACK_TYPE {
+ XENVIF_CALLBACK_TYPE_INVALID = 0,
+ XENVIF_CALLBACK_COMPLETE_PACKETS,
+ XENVIF_CALLBACK_RECEIVE_PACKETS,
+ XENVIF_CALLBACK_MEDIA_STATE_CHANGE
+} XENVIF_CALLBACK_TYPE, *PXENVIF_CALLBACK_TYPE;
+
+#define DEFINE_VIF_OPERATIONS \
+ VIF_OPERATION(VOID, \
+ Acquire, \
+ ( \
+ IN PXENVIF_VIF_CONTEXT Context \
+ ) \
+ ) \
+ VIF_OPERATION(VOID, \
+ Release, \
+ ( \
+ IN PXENVIF_VIF_CONTEXT Context \
+ ) \
+ ) \
+ VIF_OPERATION(NTSTATUS, \
+ Enable, \
+ ( \
+ IN PXENVIF_VIF_CONTEXT Context, \
+ IN VOID (*Function)(PVOID, XENVIF_CALLBACK_TYPE, ...), \
+ IN PVOID Argument OPTIONAL \
+ ) \
+ ) \
+ VIF_OPERATION(VOID, \
+ Disable, \
+ ( \
+ IN PXENVIF_VIF_CONTEXT Context \
+ ) \
+ ) \
+ VIF_OPERATION(VOID, \
+ QueryPacketStatistics, \
+ ( \
+ IN PXENVIF_VIF_CONTEXT Context, \
+ OUT PXENVIF_PACKET_STATISTICS Statistics \
+ ) \
+ ) \
+ VIF_OPERATION(VOID, \
+ UpdatePacketMetadata, \
+ ( \
+ IN PXENVIF_VIF_CONTEXT Context, \
+ IN PXENVIF_TRANSMITTER_PACKET_METADATA Metadata \
+ ) \
+ ) \
+ VIF_OPERATION(VOID, \
+ ReturnPacket, \
+ ( \
+ IN PXENVIF_VIF_CONTEXT Context, \
+ IN PXENVIF_RECEIVER_PACKET Packet \
+ ) \
+ ) \
+ VIF_OPERATION(NTSTATUS, \
+ QueuePackets, \
+ ( \
+ IN PXENVIF_VIF_CONTEXT Context, \
+ IN PXENVIF_TRANSMITTER_PACKET HeadPacket \
+ ) \
+ ) \
+ VIF_OPERATION(VOID, \
+ QueryOffloadOptions, \
+ ( \
+ IN PXENVIF_VIF_CONTEXT Context, \
+ OUT PXENVIF_OFFLOAD_OPTIONS Options \
+ ) \
+ ) \
+ VIF_OPERATION(VOID, \
+ UpdateOffloadOptions, \
+ ( \
+ IN PXENVIF_VIF_CONTEXT Context, \
+ IN XENVIF_OFFLOAD_OPTIONS Options \
+ ) \
+ ) \
+ VIF_OPERATION(VOID, \
+ QueryLargePacketSize, \
+ ( \
+ IN PXENVIF_VIF_CONTEXT Context, \
+ IN UCHAR Version, \
+ OUT PULONG Size \
+ ) \
+ ) \
+ VIF_OPERATION(VOID, \
+ QueryMediaState, \
+ ( \
+ IN PXENVIF_VIF_CONTEXT Context, \
+ OUT PNET_IF_MEDIA_CONNECT_STATE MediaConnectState OPTIONAL, \
+ OUT PULONG64 LinkSpeed OPTIONAL, \
+ OUT PNET_IF_MEDIA_DUPLEX_STATE MediaDuplexState OPTIONAL \
+ ) \
+ ) \
+ VIF_OPERATION(VOID, \
+ QueryMaximumFrameSize, \
+ ( \
+ IN PXENVIF_VIF_CONTEXT Context, \
+ OUT PULONG Size \
+ ) \
+ ) \
+ VIF_OPERATION(VOID, \
+ QueryPermanentAddress, \
+ ( \
+ IN PXENVIF_VIF_CONTEXT Context, \
+ OUT PETHERNET_ADDRESS Address \
+ ) \
+ ) \
+ VIF_OPERATION(VOID, \
+ QueryCurrentAddress, \
+ ( \
+ IN PXENVIF_VIF_CONTEXT Context, \
+ OUT PETHERNET_ADDRESS Address \
+ ) \
+ ) \
+ VIF_OPERATION(NTSTATUS, \
+ UpdateCurrentAddress, \
+ ( \
+ IN PXENVIF_VIF_CONTEXT Context, \
+ IN PETHERNET_ADDRESS Address \
+ ) \
+ ) \
+ VIF_OPERATION(NTSTATUS, \
+ QueryMulticastAddresses, \
+ ( \
+ IN PXENVIF_VIF_CONTEXT Context, \
+ OUT PETHERNET_ADDRESS Address OPTIONAL, \
+ OUT PULONG Count \
+ ) \
+ ) \
+ VIF_OPERATION(NTSTATUS, \
+ UpdateMulticastAddresses, \
+ ( \
+ IN PXENVIF_VIF_CONTEXT Context, \
+ IN PETHERNET_ADDRESS Address, \
+ IN ULONG Count \
+ ) \
+ ) \
+ VIF_OPERATION(VOID, \
+ QueryFilterLevel, \
+ ( \
+ IN PXENVIF_VIF_CONTEXT Context, \
+ IN ETHERNET_ADDRESS_TYPE Type, \
+ OUT PXENVIF_MAC_FILTER_LEVEL Level \
+ ) \
+ ) \
+ VIF_OPERATION(NTSTATUS, \
+ UpdateFilterLevel, \
+ ( \
+ IN PXENVIF_VIF_CONTEXT Context, \
+ IN ETHERNET_ADDRESS_TYPE Type, \
+ IN XENVIF_MAC_FILTER_LEVEL Level \
+ ) \
+ ) \
+ VIF_OPERATION(VOID, \
+ QueryReceiverRingSize, \
+ ( \
+ IN PXENVIF_VIF_CONTEXT Context, \
+ OUT PULONG Size \
+ ) \
+ ) \
+ VIF_OPERATION(VOID, \
+ QueryTransmitterRingSize, \
+ ( \
+ IN PXENVIF_VIF_CONTEXT Context, \
+ OUT PULONG Size \
+ ) \
+ )
+
+typedef struct _XENVIF_VIF_CONTEXT XENVIF_VIF_CONTEXT, *PXENVIF_VIF_CONTEXT;
+
+#define VIF_OPERATION(_Type, _Name, _Arguments) \
+ _Type (*VIF_ ## _Name) _Arguments;
+
+typedef struct _XENVIF_VIF_OPERATIONS {
+ DEFINE_VIF_OPERATIONS
+} XENVIF_VIF_OPERATIONS, *PXENVIF_VIF_OPERATIONS;
+
+#undef VIF_OPERATION
+
+typedef struct _XENVIF_VIF_INTERFACE XENVIF_VIF_INTERFACE, *PXENVIF_VIF_INTERFACE;
+
+// {BAA55367-D5CD-4fab-8A2D-BB40476795C3}
+DEFINE_GUID(GUID_VIF_INTERFACE,
+ 0xbaa55367,
+ 0xd5cd,
+ 0x4fab,
+ 0x8a,
+ 0x2d,
+ 0xbb,
+ 0x40,
+ 0x47,
+ 0x67,
+ 0x95,
+ 0xc3);
+
+#define VIF_INTERFACE_VERSION 12
+
+#define VIF_OPERATIONS(_Interface) \
+ (PXENVIF_VIF_OPERATIONS *)((ULONG_PTR)(_Interface))
+
+#define VIF_CONTEXT(_Interface) \
+ (PXENVIF_VIF_CONTEXT *)((ULONG_PTR)(_Interface) + sizeof (PVOID))
+
+#define VIF(_Operation, _Interface, ...) \
+ (*VIF_OPERATIONS(_Interface))->VIF_ ## _Operation((*VIF_CONTEXT(_Interface)), __VA_ARGS__)
+
+#endif // _XENVIF_INTERFACE_H
+
--- /dev/null
+/* 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
--- /dev/null
+/* 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
--- /dev/null
+/* 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
--- /dev/null
+/* Copyright (c) Citrix Systems Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms,
+ * with or without modification, are permitted provided
+ * that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer in the documentation and/or other
+ * materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef _XEN_H
+#define _XEN_H
+
+#include <ntddk.h>
+
+#include <xen-version.h>
+#include <xen-types.h>
+#include <xen-warnings.h>
+#include <xen/io/ring.h>
+#include <xen/io/netif.h>
+#include <xen/io/xenbus.h>
+
+#endif // _XEN_H
--- /dev/null
+/******************************************************************************
+ * xen-x86_32.h
+ *
+ * Guest OS interface to x86 32-bit Xen.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Copyright (c) 2004-2007, K A Fraser
+ */
+
+#ifndef __XEN_PUBLIC_ARCH_X86_XEN_X86_32_H__
+#define __XEN_PUBLIC_ARCH_X86_XEN_X86_32_H__
+
+/*
+ * Hypercall interface:
+ * Input: %ebx, %ecx, %edx, %esi, %edi (arguments 1-5)
+ * Output: %eax
+ * Access is via hypercall page (set up by guest loader or via a Xen MSR):
+ * call hypercall_page + hypercall-number * 32
+ * Clobbered: Argument registers (e.g., 2-arg hypercall clobbers %ebx,%ecx)
+ */
+
+#if __XEN_INTERFACE_VERSION__ < 0x00030203
+/*
+ * Legacy hypercall interface:
+ * As above, except the entry sequence to the hypervisor is:
+ * mov $hypercall-number*32,%eax ; int $0x82
+ */
+#define TRAP_INSTR "int $0x82"
+#endif
+
+/*
+ * These flat segments are in the Xen-private section of every GDT. Since these
+ * are also present in the initial GDT, many OSes will be able to avoid
+ * installing their own GDT.
+ */
+#define FLAT_RING1_CS 0xe019 /* GDT index 259 */
+#define FLAT_RING1_DS 0xe021 /* GDT index 260 */
+#define FLAT_RING1_SS 0xe021 /* GDT index 260 */
+#define FLAT_RING3_CS 0xe02b /* GDT index 261 */
+#define FLAT_RING3_DS 0xe033 /* GDT index 262 */
+#define FLAT_RING3_SS 0xe033 /* GDT index 262 */
+
+#define FLAT_KERNEL_CS FLAT_RING1_CS
+#define FLAT_KERNEL_DS FLAT_RING1_DS
+#define FLAT_KERNEL_SS FLAT_RING1_SS
+#define FLAT_USER_CS FLAT_RING3_CS
+#define FLAT_USER_DS FLAT_RING3_DS
+#define FLAT_USER_SS FLAT_RING3_SS
+
+#define __HYPERVISOR_VIRT_START_PAE 0xF5800000
+#define __MACH2PHYS_VIRT_START_PAE 0xF5800000
+#define __MACH2PHYS_VIRT_END_PAE 0xF6800000
+#define HYPERVISOR_VIRT_START_PAE \
+ mk_unsigned_long(__HYPERVISOR_VIRT_START_PAE)
+#define MACH2PHYS_VIRT_START_PAE \
+ mk_unsigned_long(__MACH2PHYS_VIRT_START_PAE)
+#define MACH2PHYS_VIRT_END_PAE \
+ mk_unsigned_long(__MACH2PHYS_VIRT_END_PAE)
+
+/* Non-PAE bounds are obsolete. */
+#define __HYPERVISOR_VIRT_START_NONPAE 0xFC000000
+#define __MACH2PHYS_VIRT_START_NONPAE 0xFC000000
+#define __MACH2PHYS_VIRT_END_NONPAE 0xFC400000
+#define HYPERVISOR_VIRT_START_NONPAE \
+ mk_unsigned_long(__HYPERVISOR_VIRT_START_NONPAE)
+#define MACH2PHYS_VIRT_START_NONPAE \
+ mk_unsigned_long(__MACH2PHYS_VIRT_START_NONPAE)
+#define MACH2PHYS_VIRT_END_NONPAE \
+ mk_unsigned_long(__MACH2PHYS_VIRT_END_NONPAE)
+
+#define __HYPERVISOR_VIRT_START __HYPERVISOR_VIRT_START_PAE
+#define __MACH2PHYS_VIRT_START __MACH2PHYS_VIRT_START_PAE
+#define __MACH2PHYS_VIRT_END __MACH2PHYS_VIRT_END_PAE
+
+#ifndef HYPERVISOR_VIRT_START
+#define HYPERVISOR_VIRT_START mk_unsigned_long(__HYPERVISOR_VIRT_START)
+#endif
+
+#define MACH2PHYS_VIRT_START mk_unsigned_long(__MACH2PHYS_VIRT_START)
+#define MACH2PHYS_VIRT_END mk_unsigned_long(__MACH2PHYS_VIRT_END)
+#define MACH2PHYS_NR_ENTRIES ((MACH2PHYS_VIRT_END-MACH2PHYS_VIRT_START)>>2)
+#ifndef machine_to_phys_mapping
+#define machine_to_phys_mapping ((ULONG_PTR *)MACH2PHYS_VIRT_START)
+#endif
+
+/* 32-/64-bit invariability for control interfaces (domctl/sysctl). */
+#if defined(__XEN__) || defined(__XEN_TOOLS__)
+#undef ___DEFINE_XEN_GUEST_HANDLE
+#define ___DEFINE_XEN_GUEST_HANDLE(name, type) \
+ typedef struct { type *p; } \
+ __guest_handle_ ## name; \
+ typedef struct { union { type *p; uint64_aligned_t q; }; } \
+ __guest_handle_64_ ## name
+#undef set_xen_guest_handle_raw
+#define set_xen_guest_handle_raw(hnd, val) \
+ do { if ( sizeof(hnd) == 8 ) *(uint64_t *)&(hnd) = 0; \
+ (hnd).p = val; \
+ } while ( 0 )
+#define uint64_aligned_t uint64_t __attribute__((aligned(8)))
+#define __XEN_GUEST_HANDLE_64(name) __guest_handle_64_ ## name
+#define XEN_GUEST_HANDLE_64(name) __XEN_GUEST_HANDLE_64(name)
+#endif
+
+#ifndef __ASSEMBLY__
+
+struct cpu_user_regs {
+ uint32_t ebx;
+ uint32_t ecx;
+ uint32_t edx;
+ uint32_t esi;
+ uint32_t edi;
+ uint32_t ebp;
+ uint32_t eax;
+ uint16_t error_code; /* private */
+ uint16_t entry_vector; /* private */
+ uint32_t eip;
+ uint16_t cs;
+ uint8_t saved_upcall_mask;
+ uint8_t _pad0;
+ uint32_t eflags; /* eflags.IF == !saved_upcall_mask */
+ uint32_t esp;
+ uint16_t ss, _pad1;
+ uint16_t es, _pad2;
+ uint16_t ds, _pad3;
+ uint16_t fs, _pad4;
+ uint16_t gs, _pad5;
+};
+typedef struct cpu_user_regs cpu_user_regs_t;
+DEFINE_XEN_GUEST_HANDLE(cpu_user_regs_t);
+
+/*
+ * Page-directory addresses above 4GB do not fit into architectural %cr3.
+ * When accessing %cr3, or equivalent field in vcpu_guest_context, guests
+ * must use the following accessor macros to pack/unpack valid MFNs.
+ */
+#define xen_pfn_to_cr3(pfn) (((unsigned)(pfn) << 12) | ((unsigned)(pfn) >> 20))
+#define xen_cr3_to_pfn(cr3) (((unsigned)(cr3) >> 12) | ((unsigned)(cr3) << 20))
+
+struct arch_vcpu_info {
+ ULONG_PTR cr2;
+ ULONG_PTR pad[5]; /* sizeof(vcpu_info_t) == 64 */
+};
+typedef struct arch_vcpu_info arch_vcpu_info_t;
+
+struct xen_callback {
+ ULONG_PTR cs;
+ ULONG_PTR eip;
+};
+typedef struct xen_callback xen_callback_t;
+
+#endif /* !__ASSEMBLY__ */
+
+#endif /* __XEN_PUBLIC_ARCH_X86_XEN_X86_32_H__ */
+
+/*
+ * Local variables:
+ * mode: C
+ * c-set-style: "BSD"
+ * c-basic-offset: 4
+ * tab-width: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
--- /dev/null
+/******************************************************************************
+ * xen-x86_64.h
+ *
+ * Guest OS interface to x86 64-bit Xen.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Copyright (c) 2004-2006, K A Fraser
+ */
+
+#ifndef __XEN_PUBLIC_ARCH_X86_XEN_X86_64_H__
+#define __XEN_PUBLIC_ARCH_X86_XEN_X86_64_H__
+
+/*
+ * Hypercall interface:
+ * Input: %rdi, %rsi, %rdx, %r10, %r8 (arguments 1-5)
+ * Output: %rax
+ * Access is via hypercall page (set up by guest loader or via a Xen MSR):
+ * call hypercall_page + hypercall-number * 32
+ * Clobbered: argument registers (e.g., 2-arg hypercall clobbers %rdi,%rsi)
+ */
+
+#if __XEN_INTERFACE_VERSION__ < 0x00030203
+/*
+ * Legacy hypercall interface:
+ * As above, except the entry sequence to the hypervisor is:
+ * mov $hypercall-number*32,%eax ; syscall
+ * Clobbered: %rcx, %r11, argument registers (as above)
+ */
+#define TRAP_INSTR "syscall"
+#endif
+
+/*
+ * 64-bit segment selectors
+ * These flat segments are in the Xen-private section of every GDT. Since these
+ * are also present in the initial GDT, many OSes will be able to avoid
+ * installing their own GDT.
+ */
+
+#define FLAT_RING3_CS32 0xe023 /* GDT index 260 */
+#define FLAT_RING3_CS64 0xe033 /* GDT index 261 */
+#define FLAT_RING3_DS32 0xe02b /* GDT index 262 */
+#define FLAT_RING3_DS64 0x0000 /* NULL selector */
+#define FLAT_RING3_SS32 0xe02b /* GDT index 262 */
+#define FLAT_RING3_SS64 0xe02b /* GDT index 262 */
+
+#define FLAT_KERNEL_DS64 FLAT_RING3_DS64
+#define FLAT_KERNEL_DS32 FLAT_RING3_DS32
+#define FLAT_KERNEL_DS FLAT_KERNEL_DS64
+#define FLAT_KERNEL_CS64 FLAT_RING3_CS64
+#define FLAT_KERNEL_CS32 FLAT_RING3_CS32
+#define FLAT_KERNEL_CS FLAT_KERNEL_CS64
+#define FLAT_KERNEL_SS64 FLAT_RING3_SS64
+#define FLAT_KERNEL_SS32 FLAT_RING3_SS32
+#define FLAT_KERNEL_SS FLAT_KERNEL_SS64
+
+#define FLAT_USER_DS64 FLAT_RING3_DS64
+#define FLAT_USER_DS32 FLAT_RING3_DS32
+#define FLAT_USER_DS FLAT_USER_DS64
+#define FLAT_USER_CS64 FLAT_RING3_CS64
+#define FLAT_USER_CS32 FLAT_RING3_CS32
+#define FLAT_USER_CS FLAT_USER_CS64
+#define FLAT_USER_SS64 FLAT_RING3_SS64
+#define FLAT_USER_SS32 FLAT_RING3_SS32
+#define FLAT_USER_SS FLAT_USER_SS64
+
+#define __HYPERVISOR_VIRT_START 0xFFFF800000000000
+#define __HYPERVISOR_VIRT_END 0xFFFF880000000000
+#define __MACH2PHYS_VIRT_START 0xFFFF800000000000
+#define __MACH2PHYS_VIRT_END 0xFFFF804000000000
+
+#ifndef HYPERVISOR_VIRT_START
+#define HYPERVISOR_VIRT_START mk_unsigned_long(__HYPERVISOR_VIRT_START)
+#define HYPERVISOR_VIRT_END mk_unsigned_long(__HYPERVISOR_VIRT_END)
+#endif
+
+#define MACH2PHYS_VIRT_START mk_unsigned_long(__MACH2PHYS_VIRT_START)
+#define MACH2PHYS_VIRT_END mk_unsigned_long(__MACH2PHYS_VIRT_END)
+#define MACH2PHYS_NR_ENTRIES ((MACH2PHYS_VIRT_END-MACH2PHYS_VIRT_START)>>3)
+#ifndef machine_to_phys_mapping
+#define machine_to_phys_mapping ((ULONG_PTR *)HYPERVISOR_VIRT_START)
+#endif
+
+/*
+ * int HYPERVISOR_set_segment_base(unsigned int which, ULONG_PTR base)
+ * @which == SEGBASE_* ; @base == 64-bit base address
+ * Returns 0 on success.
+ */
+#define SEGBASE_FS 0
+#define SEGBASE_GS_USER 1
+#define SEGBASE_GS_KERNEL 2
+#define SEGBASE_GS_USER_SEL 3 /* Set user %gs specified in base[15:0] */
+
+/*
+ * int HYPERVISOR_iret(void)
+ * All arguments are on the kernel stack, in the following format.
+ * Never returns if successful. Current kernel context is lost.
+ * The saved CS is mapped as follows:
+ * RING0 -> RING3 kernel mode.
+ * RING1 -> RING3 kernel mode.
+ * RING2 -> RING3 kernel mode.
+ * RING3 -> RING3 user mode.
+ * However RING0 indicates that the guest kernel should return to iteself
+ * directly with
+ * orb $3,1*8(%rsp)
+ * iretq
+ * If flags contains VGCF_in_syscall:
+ * Restore RAX, RIP, RFLAGS, RSP.
+ * Discard R11, RCX, CS, SS.
+ * Otherwise:
+ * Restore RAX, R11, RCX, CS:RIP, RFLAGS, SS:RSP.
+ * All other registers are saved on hypercall entry and restored to user.
+ */
+/* Guest exited in SYSCALL context? Return to guest with SYSRET? */
+#define _VGCF_in_syscall 8
+#define VGCF_in_syscall (1<<_VGCF_in_syscall)
+#define VGCF_IN_SYSCALL VGCF_in_syscall
+
+#ifndef __ASSEMBLY__
+
+struct iret_context {
+ /* Top of stack (%rsp at point of hypercall). */
+ uint64_t rax, r11, rcx, flags, rip, cs, rflags, rsp, ss;
+ /* Bottom of iret stack frame. */
+};
+
+#if defined(__GNUC__) && !defined(__STRICT_ANSI__)
+/* Anonymous union includes both 32- and 64-bit names (e.g., eax/rax). */
+#define __DECL_REG(name) union { \
+ uint64_t r ## name, e ## name; \
+ uint32_t _e ## name; \
+}
+#else
+/* Non-gcc sources must always use the proper 64-bit name (e.g., rax). */
+#define __DECL_REG(name) uint64_t r ## name
+#endif
+
+struct cpu_user_regs {
+ uint64_t r15;
+ uint64_t r14;
+ uint64_t r13;
+ uint64_t r12;
+ __DECL_REG(bp);
+ __DECL_REG(bx);
+ uint64_t r11;
+ uint64_t r10;
+ uint64_t r9;
+ uint64_t r8;
+ __DECL_REG(ax);
+ __DECL_REG(cx);
+ __DECL_REG(dx);
+ __DECL_REG(si);
+ __DECL_REG(di);
+ uint32_t error_code; /* private */
+ uint32_t entry_vector; /* private */
+ __DECL_REG(ip);
+ uint16_t cs, _pad0[1];
+ uint8_t saved_upcall_mask;
+ uint8_t _pad1[3];
+ __DECL_REG(flags); /* rflags.IF == !saved_upcall_mask */
+ __DECL_REG(sp);
+ uint16_t ss, _pad2[3];
+ uint16_t es, _pad3[3];
+ uint16_t ds, _pad4[3];
+ uint16_t fs, _pad5[3]; /* Non-zero => takes precedence over fs_base. */
+ uint16_t gs, _pad6[3]; /* Non-zero => takes precedence over gs_base_usr. */
+};
+typedef struct cpu_user_regs cpu_user_regs_t;
+DEFINE_XEN_GUEST_HANDLE(cpu_user_regs_t);
+
+#undef __DECL_REG
+
+#define xen_pfn_to_cr3(pfn) ((ULONG_PTR)(pfn) << 12)
+#define xen_cr3_to_pfn(cr3) ((ULONG_PTR)(cr3) >> 12)
+
+struct arch_vcpu_info {
+ ULONG_PTR cr2;
+ ULONG_PTR pad; /* sizeof(vcpu_info_t) == 64 */
+};
+typedef struct arch_vcpu_info arch_vcpu_info_t;
+
+typedef ULONG_PTR xen_callback_t;
+
+#endif /* !__ASSEMBLY__ */
+
+#endif /* __XEN_PUBLIC_ARCH_X86_XEN_X86_64_H__ */
+
+/*
+ * Local variables:
+ * mode: C
+ * c-set-style: "BSD"
+ * c-basic-offset: 4
+ * tab-width: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
--- /dev/null
+/******************************************************************************
+ * arch-x86/xen.h
+ *
+ * Guest OS interface to x86 Xen.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Copyright (c) 2004-2006, K A Fraser
+ */
+
+#include "../xen.h"
+
+#ifndef __XEN_PUBLIC_ARCH_X86_XEN_H__
+#define __XEN_PUBLIC_ARCH_X86_XEN_H__
+
+/* Structural guest handles introduced in 0x00030201. */
+#if __XEN_INTERFACE_VERSION__ >= 0x00030201
+#define ___DEFINE_XEN_GUEST_HANDLE(name, type) \
+ typedef struct { type *p; } __guest_handle_ ## name
+#else
+#define ___DEFINE_XEN_GUEST_HANDLE(name, type) \
+ typedef type * __guest_handle_ ## name
+#endif
+
+#define __DEFINE_XEN_GUEST_HANDLE(name, type) \
+ ___DEFINE_XEN_GUEST_HANDLE(name, type); \
+ ___DEFINE_XEN_GUEST_HANDLE(const_##name, const type)
+#define DEFINE_XEN_GUEST_HANDLE(name) __DEFINE_XEN_GUEST_HANDLE(name, name)
+#define __XEN_GUEST_HANDLE(name) __guest_handle_ ## name
+#define XEN_GUEST_HANDLE(name) __XEN_GUEST_HANDLE(name)
+#define set_xen_guest_handle_raw(hnd, val) do { (hnd).p = val; } while (0)
+#ifdef __XEN_TOOLS__
+#define get_xen_guest_handle(val, hnd) do { val = (hnd).p; } while (0)
+#endif
+#define set_xen_guest_handle(hnd, val) set_xen_guest_handle_raw(hnd, val)
+
+#if defined(__i386__)
+#include "xen-x86_32.h"
+#elif defined(__x86_64__)
+#include "xen-x86_64.h"
+#endif
+
+#ifndef __ASSEMBLY__
+typedef ULONG_PTR xen_pfn_t;
+#define PRI_xen_pfn "lx"
+#endif
+
+/*
+ * SEGMENT DESCRIPTOR TABLES
+ */
+/*
+ * A number of GDT entries are reserved by Xen. These are not situated at the
+ * start of the GDT because some stupid OSes export hard-coded selector values
+ * in their ABI. These hard-coded values are always near the start of the GDT,
+ * so Xen places itself out of the way, at the far end of the GDT.
+ */
+#define FIRST_RESERVED_GDT_PAGE 14
+#define FIRST_RESERVED_GDT_BYTE (FIRST_RESERVED_GDT_PAGE * 4096)
+#define FIRST_RESERVED_GDT_ENTRY (FIRST_RESERVED_GDT_BYTE / 8)
+
+/* Maximum number of virtual CPUs in legacy multi-processor guests. */
+#define XEN_LEGACY_MAX_VCPUS 32
+
+#ifndef __ASSEMBLY__
+
+typedef ULONG_PTR xen_ulong_t;
+
+/*
+ * Send an array of these to HYPERVISOR_set_trap_table().
+ * The privilege level specifies which modes may enter a trap via a software
+ * interrupt. On x86/64, since rings 1 and 2 are unavailable, we allocate
+ * privilege levels as follows:
+ * Level == 0: Noone may enter
+ * Level == 1: Kernel may enter
+ * Level == 2: Kernel may enter
+ * Level == 3: Everyone may enter
+ */
+#define TI_GET_DPL(_ti) ((_ti)->flags & 3)
+#define TI_GET_IF(_ti) ((_ti)->flags & 4)
+#define TI_SET_DPL(_ti,_dpl) ((_ti)->flags |= (_dpl))
+#define TI_SET_IF(_ti,_if) ((_ti)->flags |= ((!!(_if))<<2))
+struct trap_info {
+ uint8_t vector; /* exception vector */
+ uint8_t flags; /* 0-3: privilege level; 4: clear event enable? */
+ uint16_t cs; /* code selector */
+ ULONG_PTR address; /* code offset */
+};
+typedef struct trap_info trap_info_t;
+DEFINE_XEN_GUEST_HANDLE(trap_info_t);
+
+typedef uint64_t tsc_timestamp_t; /* RDTSC timestamp */
+
+/*
+ * The following is all CPU context. Note that the fpu_ctxt block is filled
+ * in by FXSAVE if the CPU has feature FXSR; otherwise FSAVE is used.
+ */
+struct vcpu_guest_context {
+ /* FPU registers come first so they can be aligned for FXSAVE/FXRSTOR. */
+ struct { char x[512]; } fpu_ctxt; /* User-level FPU registers */
+#define VGCF_I387_VALID (1<<0)
+#define VGCF_IN_KERNEL (1<<2)
+#define _VGCF_i387_valid 0
+#define VGCF_i387_valid (1<<_VGCF_i387_valid)
+#define _VGCF_in_kernel 2
+#define VGCF_in_kernel (1<<_VGCF_in_kernel)
+#define _VGCF_failsafe_disables_events 3
+#define VGCF_failsafe_disables_events (1<<_VGCF_failsafe_disables_events)
+#define _VGCF_syscall_disables_events 4
+#define VGCF_syscall_disables_events (1<<_VGCF_syscall_disables_events)
+#define _VGCF_online 5
+#define VGCF_online (1<<_VGCF_online)
+ ULONG_PTR flags; /* VGCF_* flags */
+ struct cpu_user_regs user_regs; /* User-level CPU registers */
+ struct trap_info trap_ctxt[256]; /* Virtual IDT */
+ ULONG_PTR ldt_base, ldt_ents; /* LDT (linear address, # ents) */
+ ULONG_PTR gdt_frames[16], gdt_ents; /* GDT (machine frames, # ents) */
+ ULONG_PTR kernel_ss, kernel_sp; /* Virtual TSS (only SS1/SP1) */
+ /* NB. User pagetable on x86/64 is placed in ctrlreg[1]. */
+ ULONG_PTR ctrlreg[8]; /* CR0-CR7 (control registers) */
+ ULONG_PTR debugreg[8]; /* DB0-DB7 (debug registers) */
+#ifdef __i386__
+ ULONG_PTR event_callback_cs; /* CS:EIP of event callback */
+ ULONG_PTR event_callback_eip;
+ ULONG_PTR failsafe_callback_cs; /* CS:EIP of failsafe callback */
+ ULONG_PTR failsafe_callback_eip;
+#else
+ ULONG_PTR event_callback_eip;
+ ULONG_PTR failsafe_callback_eip;
+#ifdef __XEN__
+ union {
+ ULONG_PTR syscall_callback_eip;
+ struct {
+ unsigned int event_callback_cs; /* compat CS of event cb */
+ unsigned int failsafe_callback_cs; /* compat CS of failsafe cb */
+ };
+ };
+#else
+ ULONG_PTR syscall_callback_eip;
+#endif
+#endif
+ ULONG_PTR vm_assist; /* VMASST_TYPE_* bitmap */
+#ifdef __x86_64__
+ /* Segment base addresses. */
+ uint64_t fs_base;
+ uint64_t gs_base_kernel;
+ uint64_t gs_base_user;
+#endif
+};
+typedef struct vcpu_guest_context vcpu_guest_context_t;
+DEFINE_XEN_GUEST_HANDLE(vcpu_guest_context_t);
+
+struct arch_shared_info {
+ ULONG_PTR max_pfn; /* max pfn that appears in table */
+ /* Frame containing list of mfns containing list of mfns containing p2m. */
+ xen_pfn_t pfn_to_mfn_frame_list_list;
+ ULONG_PTR nmi_reason;
+ uint64_t pad[32];
+};
+typedef struct arch_shared_info arch_shared_info_t;
+
+#endif /* !__ASSEMBLY__ */
+
+/*
+ * Prefix forces emulation of some non-trapping instructions.
+ * Currently only CPUID.
+ */
+#ifdef __ASSEMBLY__
+#define XEN_EMULATE_PREFIX .byte 0x0f,0x0b,0x78,0x65,0x6e ;
+#define XEN_CPUID XEN_EMULATE_PREFIX cpuid
+#else
+#define XEN_EMULATE_PREFIX ".byte 0x0f,0x0b,0x78,0x65,0x6e ; "
+#define XEN_CPUID XEN_EMULATE_PREFIX "cpuid"
+#endif
+
+#endif /* __XEN_PUBLIC_ARCH_X86_XEN_H__ */
+
+/*
+ * Local variables:
+ * mode: C
+ * c-set-style: "BSD"
+ * c-basic-offset: 4
+ * tab-width: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
--- /dev/null
+/******************************************************************************
+ * grant_table.h
+ *
+ * Interface for granting foreign access to page frames, and receiving
+ * page-ownership transfers.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Copyright (c) 2004, K A Fraser
+ */
+
+#ifndef __XEN_PUBLIC_GRANT_TABLE_H__
+#define __XEN_PUBLIC_GRANT_TABLE_H__
+
+#include "xen.h"
+
+/***********************************
+ * GRANT TABLE REPRESENTATION
+ */
+
+/* Some rough guidelines on accessing and updating grant-table entries
+ * in a concurrency-safe manner. For more information, Linux contains a
+ * reference implementation for guest OSes (arch/xen/kernel/grant_table.c).
+ *
+ * NB. WMB is a no-op on current-generation x86 processors. However, a
+ * compiler barrier will still be required.
+ *
+ * Introducing a valid entry into the grant table:
+ * 1. Write ent->domid.
+ * 2. Write ent->frame:
+ * GTF_permit_access: Frame to which access is permitted.
+ * GTF_accept_transfer: Pseudo-phys frame slot being filled by new
+ * frame, or zero if none.
+ * 3. Write memory barrier (WMB).
+ * 4. Write ent->flags, inc. valid type.
+ *
+ * Invalidating an unused GTF_permit_access entry:
+ * 1. flags = ent->flags.
+ * 2. Observe that !(flags & (GTF_reading|GTF_writing)).
+ * 3. Check result of SMP-safe CMPXCHG(&ent->flags, flags, 0).
+ * NB. No need for WMB as reuse of entry is control-dependent on success of
+ * step 3, and all architectures guarantee ordering of ctrl-dep writes.
+ *
+ * Invalidating an in-use GTF_permit_access entry:
+ * This cannot be done directly. Request assistance from the domain controller
+ * which can set a timeout on the use of a grant entry and take necessary
+ * action. (NB. This is not yet implemented!).
+ *
+ * Invalidating an unused GTF_accept_transfer entry:
+ * 1. flags = ent->flags.
+ * 2. Observe that !(flags & GTF_transfer_committed). [*]
+ * 3. Check result of SMP-safe CMPXCHG(&ent->flags, flags, 0).
+ * NB. No need for WMB as reuse of entry is control-dependent on success of
+ * step 3, and all architectures guarantee ordering of ctrl-dep writes.
+ * [*] If GTF_transfer_committed is set then the grant entry is 'committed'.
+ * The guest must /not/ modify the grant entry until the address of the
+ * transferred frame is written. It is safe for the guest to spin waiting
+ * for this to occur (detect by observing GTF_transfer_completed in
+ * ent->flags).
+ *
+ * Invalidating a committed GTF_accept_transfer entry:
+ * 1. Wait for (ent->flags & GTF_transfer_completed).
+ *
+ * Changing a GTF_permit_access from writable to read-only:
+ * Use SMP-safe CMPXCHG to set GTF_readonly, while checking !GTF_writing.
+ *
+ * Changing a GTF_permit_access from read-only to writable:
+ * Use SMP-safe bit-setting instruction.
+ */
+
+/*
+ * Reference to a grant entry in a specified domain's grant table.
+ */
+typedef uint32_t grant_ref_t;
+
+/*
+ * A grant table comprises a packed array of grant entries in one or more
+ * page frames shared between Xen and a guest.
+ * [XEN]: This field is written by Xen and read by the sharing guest.
+ * [GST]: This field is written by the guest and read by Xen.
+ */
+
+/*
+ * Version 1 of the grant table entry structure is maintained purely
+ * for backwards compatibility. New guests should use version 2.
+ */
+#if __XEN_INTERFACE_VERSION__ < 0x0003020a
+#define grant_entry_v1 grant_entry
+#define grant_entry_v1_t grant_entry_t
+#endif
+struct grant_entry_v1 {
+ /* GTF_xxx: various type and flag information. [XEN,GST] */
+ uint16_t flags;
+ /* The domain being granted foreign privileges. [GST] */
+ domid_t domid;
+ /*
+ * GTF_permit_access: Frame that @domid is allowed to map and access. [GST]
+ * GTF_accept_transfer: Frame whose ownership transferred by @domid. [XEN]
+ */
+ uint32_t frame;
+};
+typedef struct grant_entry_v1 grant_entry_v1_t;
+
+/*
+ * Type of grant entry.
+ * GTF_invalid: This grant entry grants no privileges.
+ * GTF_permit_access: Allow @domid to map/access @frame.
+ * GTF_accept_transfer: Allow @domid to transfer ownership of one page frame
+ * to this guest. Xen writes the page number to @frame.
+ * GTF_transitive: Allow @domid to transitively access a subrange of
+ * @trans_grant in @trans_domid. No mappings are allowed.
+ */
+#define GTF_invalid (0U<<0)
+#define GTF_permit_access (1U<<0)
+#define GTF_accept_transfer (2U<<0)
+#define GTF_transitive (3U<<0)
+#define GTF_type_mask (3U<<0)
+
+/*
+ * Subflags for GTF_permit_access.
+ * GTF_readonly: Restrict @domid to read-only mappings and accesses. [GST]
+ * GTF_reading: Grant entry is currently mapped for reading by @domid. [XEN]
+ * GTF_writing: Grant entry is currently mapped for writing by @domid. [XEN]
+ * GTF_PAT, GTF_PWT, GTF_PCD: (x86) cache attribute flags for the grant [GST]
+ * GTF_sub_page: Grant access to only a subrange of the page. @domid
+ * will only be allowed to copy from the grant, and not
+ * map it. [GST]
+ */
+#define _GTF_readonly (2)
+#define GTF_readonly (1U<<_GTF_readonly)
+#define _GTF_reading (3)
+#define GTF_reading (1U<<_GTF_reading)
+#define _GTF_writing (4)
+#define GTF_writing (1U<<_GTF_writing)
+#define _GTF_PWT (5)
+#define GTF_PWT (1U<<_GTF_PWT)
+#define _GTF_PCD (6)
+#define GTF_PCD (1U<<_GTF_PCD)
+#define _GTF_PAT (7)
+#define GTF_PAT (1U<<_GTF_PAT)
+#define _GTF_sub_page (8)
+#define GTF_sub_page (1U<<_GTF_sub_page)
+
+/*
+ * Subflags for GTF_accept_transfer:
+ * GTF_transfer_committed: Xen sets this flag to indicate that it is committed
+ * to transferring ownership of a page frame. When a guest sees this flag
+ * it must /not/ modify the grant entry until GTF_transfer_completed is
+ * set by Xen.
+ * GTF_transfer_completed: It is safe for the guest to spin-wait on this flag
+ * after reading GTF_transfer_committed. Xen will always write the frame
+ * address, followed by ORing this flag, in a timely manner.
+ */
+#define _GTF_transfer_committed (2)
+#define GTF_transfer_committed (1U<<_GTF_transfer_committed)
+#define _GTF_transfer_completed (3)
+#define GTF_transfer_completed (1U<<_GTF_transfer_completed)
+
+/*
+ * Version 2 grant table entries. These fulfil the same role as
+ * version 1 entries, but can represent more complicated operations.
+ * Any given domain will have either a version 1 or a version 2 table,
+ * and every entry in the table will be the same version.
+ *
+ * The interface by which domains use grant references does not depend
+ * on the grant table version in use by the other domain.
+ */
+#if __XEN_INTERFACE_VERSION__ >= 0x0003020a
+/*
+ * Version 1 and version 2 grant entries share a common prefix. The
+ * fields of the prefix are documented as part of struct
+ * grant_entry_v1.
+ */
+struct grant_entry_header {
+ uint16_t flags;
+ domid_t domid;
+};
+typedef struct grant_entry_header grant_entry_header_t;
+
+/*
+ * Version 2 of the grant entry structure.
+ */
+union grant_entry_v2 {
+ grant_entry_header_t hdr;
+
+ /*
+ * This member is used for V1-style full page grants, where either:
+ *
+ * -- hdr.type is GTF_accept_transfer, or
+ * -- hdr.type is GTF_permit_access and GTF_sub_page is not set.
+ *
+ * In that case, the frame field has the same semantics as the
+ * field of the same name in the V1 entry structure.
+ */
+ struct {
+ grant_entry_header_t hdr;
+ uint32_t pad0;
+ uint64_t frame;
+ } full_page;
+
+ /*
+ * If the grant type is GTF_grant_access and GTF_sub_page is set,
+ * @domid is allowed to access bytes [@page_off,@page_off+@length)
+ * in frame @frame.
+ */
+ struct {
+ grant_entry_header_t hdr;
+ uint16_t page_off;
+ uint16_t length;
+ uint64_t frame;
+ } sub_page;
+
+ /*
+ * If the grant is GTF_transitive, @domid is allowed to use the
+ * grant @gref in domain @trans_domid, as if it was the local
+ * domain. Obviously, the transitive access must be compatible
+ * with the original grant.
+ *
+ * The current version of Xen does not allow transitive grants
+ * to be mapped.
+ */
+ struct {
+ grant_entry_header_t hdr;
+ domid_t trans_domid;
+ uint16_t pad0;
+ grant_ref_t gref;
+ } transitive;
+
+ uint32_t __spacer[4]; /* Pad to a power of two */
+};
+typedef union grant_entry_v2 grant_entry_v2_t;
+
+typedef uint16_t grant_status_t;
+
+#endif /* __XEN_INTERFACE_VERSION__ */
+
+/***********************************
+ * GRANT TABLE QUERIES AND USES
+ */
+
+/*
+ * Handle to track a mapping created via a grant reference.
+ */
+typedef uint32_t grant_handle_t;
+
+/*
+ * GNTTABOP_map_grant_ref: Map the grant entry (<dom>,<ref>) for access
+ * by devices and/or host CPUs. If successful, <handle> is a tracking number
+ * that must be presented later to destroy the mapping(s). On error, <handle>
+ * is a negative status code.
+ * NOTES:
+ * 1. If GNTMAP_device_map is specified then <dev_bus_addr> is the address
+ * via which I/O devices may access the granted frame.
+ * 2. If GNTMAP_host_map is specified then a mapping will be added at
+ * either a host virtual address in the current address space, or at
+ * a PTE at the specified machine address. The type of mapping to
+ * perform is selected through the GNTMAP_contains_pte flag, and the
+ * address is specified in <host_addr>.
+ * 3. Mappings should only be destroyed via GNTTABOP_unmap_grant_ref. If a
+ * host mapping is destroyed by other means then it is *NOT* guaranteed
+ * to be accounted to the correct grant reference!
+ */
+#define GNTTABOP_map_grant_ref 0
+struct gnttab_map_grant_ref {
+ /* IN parameters. */
+ uint64_t host_addr;
+ uint32_t flags; /* GNTMAP_* */
+ grant_ref_t ref;
+ domid_t dom;
+ /* OUT parameters. */
+ int16_t status; /* GNTST_* */
+ grant_handle_t handle;
+ uint64_t dev_bus_addr;
+};
+typedef struct gnttab_map_grant_ref gnttab_map_grant_ref_t;
+DEFINE_XEN_GUEST_HANDLE(gnttab_map_grant_ref_t);
+
+/*
+ * GNTTABOP_unmap_grant_ref: Destroy one or more grant-reference mappings
+ * tracked by <handle>. If <host_addr> or <dev_bus_addr> is zero, that
+ * field is ignored. If non-zero, they must refer to a device/host mapping
+ * that is tracked by <handle>
+ * NOTES:
+ * 1. The call may fail in an undefined manner if either mapping is not
+ * tracked by <handle>.
+ * 3. After executing a batch of unmaps, it is guaranteed that no stale
+ * mappings will remain in the device or host TLBs.
+ */
+#define GNTTABOP_unmap_grant_ref 1
+struct gnttab_unmap_grant_ref {
+ /* IN parameters. */
+ uint64_t host_addr;
+ uint64_t dev_bus_addr;
+ grant_handle_t handle;
+ /* OUT parameters. */
+ int16_t status; /* GNTST_* */
+};
+typedef struct gnttab_unmap_grant_ref gnttab_unmap_grant_ref_t;
+DEFINE_XEN_GUEST_HANDLE(gnttab_unmap_grant_ref_t);
+
+/*
+ * GNTTABOP_setup_table: Set up a grant table for <dom> comprising at least
+ * <nr_frames> pages. The frame addresses are written to the <frame_list>.
+ * Only <nr_frames> addresses are written, even if the table is larger.
+ * NOTES:
+ * 1. <dom> may be specified as DOMID_SELF.
+ * 2. Only a sufficiently-privileged domain may specify <dom> != DOMID_SELF.
+ * 3. Xen may not support more than a single grant-table page per domain.
+ */
+#define GNTTABOP_setup_table 2
+struct gnttab_setup_table {
+ /* IN parameters. */
+ domid_t dom;
+ uint32_t nr_frames;
+ /* OUT parameters. */
+ int16_t status; /* GNTST_* */
+ XEN_GUEST_HANDLE(ulong) frame_list;
+};
+typedef struct gnttab_setup_table gnttab_setup_table_t;
+DEFINE_XEN_GUEST_HANDLE(gnttab_setup_table_t);
+
+/*
+ * GNTTABOP_dump_table: Dump the contents of the grant table to the
+ * xen console. Debugging use only.
+ */
+#define GNTTABOP_dump_table 3
+struct gnttab_dump_table {
+ /* IN parameters. */
+ domid_t dom;
+ /* OUT parameters. */
+ int16_t status; /* GNTST_* */
+};
+typedef struct gnttab_dump_table gnttab_dump_table_t;
+DEFINE_XEN_GUEST_HANDLE(gnttab_dump_table_t);
+
+/*
+ * GNTTABOP_transfer_grant_ref: Transfer <frame> to a foreign domain. The
+ * foreign domain has previously registered its interest in the transfer via
+ * <domid, ref>.
+ *
+ * Note that, even if the transfer fails, the specified page no LONG_PTRer belongs
+ * to the calling domain *unless* the error is GNTST_bad_page.
+ */
+#define GNTTABOP_transfer 4
+struct gnttab_transfer {
+ /* IN parameters. */
+ xen_pfn_t mfn;
+ domid_t domid;
+ grant_ref_t ref;
+ /* OUT parameters. */
+ int16_t status;
+};
+typedef struct gnttab_transfer gnttab_transfer_t;
+DEFINE_XEN_GUEST_HANDLE(gnttab_transfer_t);
+
+
+/*
+ * GNTTABOP_copy: Hypervisor based copy
+ * source and destinations can be eithers MFNs or, for foreign domains,
+ * grant references. the foreign domain has to grant read/write access
+ * in its grant table.
+ *
+ * The flags specify what type source and destinations are (either MFN
+ * or grant reference).
+ *
+ * Note that this can also be used to copy data between two domains
+ * via a third party if the source and destination domains had previously
+ * grant appropriate access to their pages to the third party.
+ *
+ * source_offset specifies an offset in the source frame, dest_offset
+ * the offset in the target frame and len specifies the number of
+ * bytes to be copied.
+ */
+
+#define _GNTCOPY_source_gref (0)
+#define GNTCOPY_source_gref (1<<_GNTCOPY_source_gref)
+#define _GNTCOPY_dest_gref (1)
+#define GNTCOPY_dest_gref (1<<_GNTCOPY_dest_gref)
+#define _GNTCOPY_can_fail (2)
+#define GNTCOPY_can_fail (1<<_GNTCOPY_can_fail)
+
+#define GNTTABOP_copy 5
+typedef struct gnttab_copy {
+ /* IN parameters. */
+ struct {
+ union {
+ grant_ref_t ref;
+ xen_pfn_t gmfn;
+ } u;
+ domid_t domid;
+ uint16_t offset;
+ } source, dest;
+ uint16_t len;
+ uint16_t flags; /* GNTCOPY_* */
+ /* OUT parameters. */
+ int16_t status;
+} gnttab_copy_t;
+DEFINE_XEN_GUEST_HANDLE(gnttab_copy_t);
+
+/*
+ * GNTTABOP_query_size: Query the current and maximum sizes of the shared
+ * grant table.
+ * NOTES:
+ * 1. <dom> may be specified as DOMID_SELF.
+ * 2. Only a sufficiently-privileged domain may specify <dom> != DOMID_SELF.
+ */
+#define GNTTABOP_query_size 6
+struct gnttab_query_size {
+ /* IN parameters. */
+ domid_t dom;
+ /* OUT parameters. */
+ uint32_t nr_frames;
+ uint32_t max_nr_frames;
+ int16_t status; /* GNTST_* */
+};
+typedef struct gnttab_query_size gnttab_query_size_t;
+DEFINE_XEN_GUEST_HANDLE(gnttab_query_size_t);
+
+/*
+ * GNTTABOP_unmap_and_replace: Destroy one or more grant-reference mappings
+ * tracked by <handle> but atomically replace the page table entry with one
+ * pointing to the machine address under <new_addr>. <new_addr> will be
+ * redirected to the null entry.
+ * NOTES:
+ * 1. The call may fail in an undefined manner if either mapping is not
+ * tracked by <handle>.
+ * 2. After executing a batch of unmaps, it is guaranteed that no stale
+ * mappings will remain in the device or host TLBs.
+ */
+#define GNTTABOP_unmap_and_replace 7
+struct gnttab_unmap_and_replace {
+ /* IN parameters. */
+ uint64_t host_addr;
+ uint64_t new_addr;
+ grant_handle_t handle;
+ /* OUT parameters. */
+ int16_t status; /* GNTST_* */
+};
+typedef struct gnttab_unmap_and_replace gnttab_unmap_and_replace_t;
+DEFINE_XEN_GUEST_HANDLE(gnttab_unmap_and_replace_t);
+
+#if __XEN_INTERFACE_VERSION__ >= 0x0003020a
+/*
+ * GNTTABOP_set_version: Request a particular version of the grant
+ * table shared table structure. This operation can only be performed
+ * once in any given domain. It must be performed before any grants
+ * are activated; otherwise, the domain will be stuck with version 1.
+ * The only defined versions are 1 and 2.
+ */
+#define GNTTABOP_set_version 8
+struct gnttab_set_version {
+ /* IN/OUT parameters */
+ uint32_t version;
+};
+typedef struct gnttab_set_version gnttab_set_version_t;
+DEFINE_XEN_GUEST_HANDLE(gnttab_set_version_t);
+
+
+/*
+ * GNTTABOP_get_status_frames: Get the list of frames used to store grant
+ * status for <dom>. In grant format version 2, the status is separated
+ * from the other shared grant fields to allow more efficient synchronization
+ * using barriers instead of atomic cmpexch operations.
+ * <nr_frames> specify the size of vector <frame_list>.
+ * The frame addresses are returned in the <frame_list>.
+ * Only <nr_frames> addresses are returned, even if the table is larger.
+ * NOTES:
+ * 1. <dom> may be specified as DOMID_SELF.
+ * 2. Only a sufficiently-privileged domain may specify <dom> != DOMID_SELF.
+ */
+#define GNTTABOP_get_status_frames 9
+struct gnttab_get_status_frames {
+ /* IN parameters. */
+ uint32_t nr_frames;
+ domid_t dom;
+ /* OUT parameters. */
+ int16_t status; /* GNTST_* */
+ XEN_GUEST_HANDLE(uint64_t) frame_list;
+};
+typedef struct gnttab_get_status_frames gnttab_get_status_frames_t;
+DEFINE_XEN_GUEST_HANDLE(gnttab_get_status_frames_t);
+
+/*
+ * GNTTABOP_get_version: Get the grant table version which is in
+ * effect for domain <dom>.
+ */
+#define GNTTABOP_get_version 10
+struct gnttab_get_version {
+ /* IN parameters */
+ domid_t dom;
+ uint16_t pad;
+ /* OUT parameters */
+ uint32_t version;
+};
+typedef struct gnttab_get_version gnttab_get_version_t;
+DEFINE_XEN_GUEST_HANDLE(gnttab_get_version_t);
+
+#endif /* __XEN_INTERFACE_VERSION__ */
+
+/*
+ * Bitfield values for gnttab_map_grant_ref.flags.
+ */
+ /* Map the grant entry for access by I/O devices. */
+#define _GNTMAP_device_map (0)
+#define GNTMAP_device_map (1<<_GNTMAP_device_map)
+ /* Map the grant entry for access by host CPUs. */
+#define _GNTMAP_host_map (1)
+#define GNTMAP_host_map (1<<_GNTMAP_host_map)
+ /* Accesses to the granted frame will be restricted to read-only access. */
+#define _GNTMAP_readonly (2)
+#define GNTMAP_readonly (1<<_GNTMAP_readonly)
+ /*
+ * GNTMAP_host_map subflag:
+ * 0 => The host mapping is usable only by the guest OS.
+ * 1 => The host mapping is usable by guest OS + current application.
+ */
+#define _GNTMAP_application_map (3)
+#define GNTMAP_application_map (1<<_GNTMAP_application_map)
+
+ /*
+ * GNTMAP_contains_pte subflag:
+ * 0 => This map request contains a host virtual address.
+ * 1 => This map request contains the machine addess of the PTE to update.
+ */
+#define _GNTMAP_contains_pte (4)
+#define GNTMAP_contains_pte (1<<_GNTMAP_contains_pte)
+
+#define _GNTMAP_can_fail (5)
+#define GNTMAP_can_fail (1<<_GNTMAP_can_fail)
+
+/*
+ * Bits to be placed in guest kernel available PTE bits (architecture
+ * dependent; only supported when XENFEAT_gnttab_map_avail_bits is set).
+ */
+#define _GNTMAP_guest_avail0 (16)
+#define GNTMAP_guest_avail_mask ((uint32_t)~0 << _GNTMAP_guest_avail0)
+
+/*
+ * Values for error status returns. All errors are -ve.
+ */
+#define GNTST_okay (0) /* Normal return. */
+#define GNTST_general_error (-1) /* General undefined error. */
+#define GNTST_bad_domain (-2) /* Unrecognsed domain id. */
+#define GNTST_bad_gntref (-3) /* Unrecognised or inappropriate gntref. */
+#define GNTST_bad_handle (-4) /* Unrecognised or inappropriate handle. */
+#define GNTST_bad_virt_addr (-5) /* Inappropriate virtual address to map. */
+#define GNTST_bad_dev_addr (-6) /* Inappropriate device address to unmap.*/
+#define GNTST_no_device_space (-7) /* Out of space in I/O MMU. */
+#define GNTST_permission_denied (-8) /* Not enough privilege for operation. */
+#define GNTST_bad_page (-9) /* Specified page was invalid for op. */
+#define GNTST_bad_copy_arg (-10) /* copy arguments cross page boundary. */
+#define GNTST_address_too_big (-11) /* transfer page address too large. */
+#define GNTST_eagain (-12) /* Could not map at the moment. Retry. */
+
+#define GNTTABOP_error_msgs { \
+ "okay", \
+ "undefined error", \
+ "unrecognised domain id", \
+ "invalid grant reference", \
+ "invalid mapping handle", \
+ "invalid virtual address", \
+ "invalid device address", \
+ "no spare translation slot in the I/O MMU", \
+ "permission denied", \
+ "bad page", \
+ "copy arguments cross page boundary", \
+ "page address size too large", \
+ "could not map at the moment, retry" \
+}
+
+#endif /* __XEN_PUBLIC_GRANT_TABLE_H__ */
+
+/*
+ * Local variables:
+ * mode: C
+ * c-set-style: "BSD"
+ * c-basic-offset: 4
+ * tab-width: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
--- /dev/null
+/******************************************************************************
+ * netif.h
+ *
+ * Unified network-device I/O interface for Xen guest OSes.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Copyright (c) 2003-2004, Keir Fraser
+ */
+
+#ifndef __XEN_PUBLIC_IO_NETIF_H__
+#define __XEN_PUBLIC_IO_NETIF_H__
+
+#include "ring.h"
+#include "../grant_table.h"
+
+/*
+ * Notifications after enqueuing any type of message should be conditional on
+ * the appropriate req_event or rsp_event field in the shared ring.
+ * If the client sends notification for rx requests then it should specify
+ * feature 'feature-rx-notify' via xenbus. Otherwise the backend will assume
+ * that it cannot safely queue packets (as it may not be kicked to send them).
+ */
+
+/*
+ * This is the 'wire' format for packets:
+ * Request 1: netif_tx_request -- NETTXF_* (any flags)
+ * [Request 2: netif_tx_extra] (only if request 1 has NETTXF_extra_info)
+ * [Request 3: netif_tx_extra] (only if request 2 has XEN_NETIF_EXTRA_MORE)
+ * Request 4: netif_tx_request -- NETTXF_more_data
+ * Request 5: netif_tx_request -- NETTXF_more_data
+ * ...
+ * Request N: netif_tx_request -- 0
+ */
+
+/* Protocol checksum field is blank in the packet (hardware offload)? */
+#define _NETTXF_csum_blank (0)
+#define NETTXF_csum_blank (1U<<_NETTXF_csum_blank)
+
+/* Packet data has been validated against protocol checksum. */
+#define _NETTXF_data_validated (1)
+#define NETTXF_data_validated (1U<<_NETTXF_data_validated)
+
+/* Packet continues in the next request descriptor. */
+#define _NETTXF_more_data (2)
+#define NETTXF_more_data (1U<<_NETTXF_more_data)
+
+/* Packet to be followed by extra descriptor(s). */
+#define _NETTXF_extra_info (3)
+#define NETTXF_extra_info (1U<<_NETTXF_extra_info)
+
+struct netif_tx_request {
+ grant_ref_t gref; /* Reference to buffer page */
+ uint16_t offset; /* Offset within buffer page */
+ uint16_t flags; /* NETTXF_* */
+ uint16_t id; /* Echoed in response message. */
+ uint16_t size; /* Packet size in bytes. */
+};
+typedef struct netif_tx_request netif_tx_request_t;
+
+/* Types of netif_extra_info descriptors. */
+#define XEN_NETIF_EXTRA_TYPE_NONE (0) /* Never used - invalid */
+#define XEN_NETIF_EXTRA_TYPE_GSO (1) /* u.gso */
+#define XEN_NETIF_EXTRA_TYPE_MCAST_ADD (2) /* u.mcast */
+#define XEN_NETIF_EXTRA_TYPE_MCAST_DEL (3) /* u.mcast */
+#define XEN_NETIF_EXTRA_TYPE_MAX (4)
+
+/* netif_extra_info flags. */
+#define _XEN_NETIF_EXTRA_FLAG_MORE (0)
+#define XEN_NETIF_EXTRA_FLAG_MORE (1U<<_XEN_NETIF_EXTRA_FLAG_MORE)
+
+/* GSO types */
+#define XEN_NETIF_GSO_TYPE_TCPV4 (1)
+#define XEN_NETIF_GSO_TYPE_TCPV6 (2)
+
+/*
+ * This structure needs to fit within both netif_tx_request and
+ * netif_rx_response for compatibility.
+ */
+struct netif_extra_info {
+ uint8_t type; /* XEN_NETIF_EXTRA_TYPE_* */
+ uint8_t flags; /* XEN_NETIF_EXTRA_FLAG_* */
+
+ union {
+ /*
+ * XEN_NETIF_EXTRA_TYPE_GSO:
+ */
+ struct {
+ /*
+ * Maximum payload size of each segment. For example, for TCP this
+ * is just the path MSS.
+ */
+ uint16_t size;
+
+ /*
+ * GSO type. This determines the protocol of the packet and any
+ * extra features required to segment the packet properly.
+ */
+ uint8_t type; /* XEN_NETIF_GSO_TYPE_* */
+
+ /* Future expansion. */
+ uint8_t pad;
+
+ /*
+ * GSO features. This specifies any extra GSO features required
+ * to process this packet, such as ECN support for TCPv4.
+ */
+ uint16_t features; /* XEN_NETIF_GSO_FEAT_* */
+ } gso;
+
+ /*
+ * XEN_NETIF_EXTRA_TYPE_MCAST_{ADD,DEL}:
+ * Backend advertises availability via 'feature-multicast-control'
+ * xenbus node containing value '1'.
+ * Frontend requests this feature by advertising
+ * 'request-multicast-control' xenbus node containing value '1'.
+ * If multicast control is requested then multicast flooding is
+ * disabled and the frontend must explicitly register its interest
+ * in multicast groups using dummy transmit requests containing
+ * MCAST_{ADD,DEL} extra-info fragments.
+ */
+ struct {
+ uint8_t addr[6]; /* Address to add/remove. */
+ } mcast;
+
+ uint16_t pad[3];
+ } u;
+};
+typedef struct netif_extra_info netif_extra_info_t;
+
+struct netif_tx_response {
+ uint16_t id;
+ int16_t status; /* NETIF_RSP_* */
+};
+typedef struct netif_tx_response netif_tx_response_t;
+
+struct netif_rx_request {
+ uint16_t id; /* Echoed in response message. */
+ grant_ref_t gref; /* Reference to incoming granted frame */
+};
+typedef struct netif_rx_request netif_rx_request_t;
+
+/* Packet data has been validated against protocol checksum. */
+#define _NETRXF_data_validated (0)
+#define NETRXF_data_validated (1U<<_NETRXF_data_validated)
+
+/* Protocol checksum field is blank in the packet (hardware offload)? */
+#define _NETRXF_csum_blank (1)
+#define NETRXF_csum_blank (1U<<_NETRXF_csum_blank)
+
+/* Packet continues in the next request descriptor. */
+#define _NETRXF_more_data (2)
+#define NETRXF_more_data (1U<<_NETRXF_more_data)
+
+/* Packet to be followed by extra descriptor(s). */
+#define _NETRXF_extra_info (3)
+#define NETRXF_extra_info (1U<<_NETRXF_extra_info)
+
+struct netif_rx_response {
+ uint16_t id;
+ uint16_t offset; /* Offset in page of start of received packet */
+ uint16_t flags; /* NETRXF_* */
+ int16_t status; /* -ve: BLKIF_RSP_* ; +ve: Rx'ed pkt size. */
+};
+typedef struct netif_rx_response netif_rx_response_t;
+
+/*
+ * Generate netif ring structures and types.
+ */
+
+DEFINE_RING_TYPES(netif_tx, struct netif_tx_request, struct netif_tx_response);
+DEFINE_RING_TYPES(netif_rx, struct netif_rx_request, struct netif_rx_response);
+
+#define NETIF_RSP_DROPPED -2
+#define NETIF_RSP_ERROR -1
+#define NETIF_RSP_OKAY 0
+/* No response: used for auxiliary requests (e.g., netif_tx_extra). */
+#define NETIF_RSP_NULL 1
+
+#endif
+
+/*
+ * Local variables:
+ * mode: C
+ * c-set-style: "BSD"
+ * c-basic-offset: 4
+ * tab-width: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
--- /dev/null
+/******************************************************************************
+ * ring.h
+ *
+ * Shared producer-consumer ring macros.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Tim Deegan and Andrew Warfield November 2004.
+ */
+
+#ifndef __XEN_PUBLIC_IO_RING_H__
+#define __XEN_PUBLIC_IO_RING_H__
+
+#include "../xen-compat.h"
+
+#if __XEN_INTERFACE_VERSION__ < 0x00030208
+#define xen_mb() mb()
+#define xen_rmb() rmb()
+#define xen_wmb() wmb()
+#endif
+
+typedef unsigned int RING_IDX;
+
+/* Round a 32-bit unsigned constant down to the nearest power of two. */
+#define __RD2(_x) (((_x) & 0x00000002) ? 0x2 : ((_x) & 0x1))
+#define __RD4(_x) (((_x) & 0x0000000c) ? __RD2((_x)>>2)<<2 : __RD2(_x))
+#define __RD8(_x) (((_x) & 0x000000f0) ? __RD4((_x)>>4)<<4 : __RD4(_x))
+#define __RD16(_x) (((_x) & 0x0000ff00) ? __RD8((_x)>>8)<<8 : __RD8(_x))
+#define __RD32(_x) (((_x) & 0xffff0000) ? __RD16((_x)>>16)<<16 : __RD16(_x))
+
+/*
+ * Calculate size of a shared ring, given the total available space for the
+ * ring and indexes (_sz), and the name tag of the request/response structure.
+ * A ring contains as many entries as will fit, rounded down to the nearest
+ * power of two (so we can mask with (size-1) to loop around).
+ */
+#define __CONST_RING_SIZE(_s, _sz) \
+ (__RD32(((_sz) - offsetof(struct _s##_sring, ring)) / \
+ sizeof(((struct _s##_sring *)0)->ring[0])))
+/*
+ * The same for passing in an actual pointer instead of a name tag.
+ */
+#define __RING_SIZE(_s, _sz) \
+ (__RD32(((_sz) - (LONG_PTR)(_s)->ring + (LONG_PTR)(_s)) / sizeof((_s)->ring[0])))
+
+/*
+ * Macros to make the correct C datatypes for a new kind of ring.
+ *
+ * To make a new ring datatype, you need to have two message structures,
+ * let's say request_t, and response_t already defined.
+ *
+ * In a header where you want the ring datatype declared, you then do:
+ *
+ * DEFINE_RING_TYPES(mytag, request_t, response_t);
+ *
+ * These expand out to give you a set of types, as you can see below.
+ * The most important of these are:
+ *
+ * mytag_sring_t - The shared ring.
+ * mytag_front_ring_t - The 'front' half of the ring.
+ * mytag_back_ring_t - The 'back' half of the ring.
+ *
+ * To initialize a ring in your code you need to know the location and size
+ * of the shared memory area (PAGE_SIZE, for instance). To initialise
+ * the front half:
+ *
+ * mytag_front_ring_t front_ring;
+ * SHARED_RING_INIT((mytag_sring_t *)shared_page);
+ * FRONT_RING_INIT(&front_ring, (mytag_sring_t *)shared_page, PAGE_SIZE);
+ *
+ * Initializing the back follows similarly (note that only the front
+ * initializes the shared ring):
+ *
+ * mytag_back_ring_t back_ring;
+ * BACK_RING_INIT(&back_ring, (mytag_sring_t *)shared_page, PAGE_SIZE);
+ */
+
+#define DEFINE_RING_TYPES(__name, __req_t, __rsp_t) \
+ \
+/* Shared ring entry */ \
+union __name##_sring_entry { \
+ __req_t req; \
+ __rsp_t rsp; \
+}; \
+ \
+/* Shared ring page */ \
+struct __name##_sring { \
+ RING_IDX req_prod, req_event; \
+ RING_IDX rsp_prod, rsp_event; \
+ union { \
+ struct { \
+ uint8_t smartpoll_active; \
+ } netif; \
+ struct { \
+ uint8_t msg; \
+ } tapif_user; \
+ uint8_t pvt_pad[4]; \
+ } private; \
+ uint8_t __pad[44]; \
+ union __name##_sring_entry ring[1]; /* variable-length */ \
+}; \
+ \
+/* "Front" end's private variables */ \
+struct __name##_front_ring { \
+ RING_IDX req_prod_pvt; \
+ RING_IDX rsp_cons; \
+ unsigned int nr_ents; \
+ struct __name##_sring *sring; \
+}; \
+ \
+/* "Back" end's private variables */ \
+struct __name##_back_ring { \
+ RING_IDX rsp_prod_pvt; \
+ RING_IDX req_cons; \
+ unsigned int nr_ents; \
+ struct __name##_sring *sring; \
+}; \
+ \
+/* Syntactic sugar */ \
+typedef struct __name##_sring __name##_sring_t; \
+typedef struct __name##_front_ring __name##_front_ring_t; \
+typedef struct __name##_back_ring __name##_back_ring_t
+
+/*
+ * Macros for manipulating rings.
+ *
+ * FRONT_RING_whatever works on the "front end" of a ring: here
+ * requests are pushed on to the ring and responses taken off it.
+ *
+ * BACK_RING_whatever works on the "back end" of a ring: here
+ * requests are taken off the ring and responses put on.
+ *
+ * N.B. these macros do NO INTERLOCKS OR FLOW CONTROL.
+ * This is OK in 1-for-1 request-response situations where the
+ * requestor (front end) never has more than RING_SIZE()-1
+ * outstanding requests.
+ */
+
+/* Initialising empty rings */
+#define SHARED_RING_INIT(_s) do { \
+ (_s)->req_prod = (_s)->rsp_prod = 0; \
+ (_s)->req_event = (_s)->rsp_event = 1; \
+ (void)memset((_s)->private.pvt_pad, 0, sizeof((_s)->private.pvt_pad)); \
+ (void)memset((_s)->__pad, 0, sizeof((_s)->__pad)); \
+} while(0)
+
+#define FRONT_RING_INIT(_r, _s, __size) do { \
+ (_r)->req_prod_pvt = 0; \
+ (_r)->rsp_cons = 0; \
+ (_r)->nr_ents = __RING_SIZE(_s, __size); \
+ (_r)->sring = (_s); \
+} while (0)
+
+#define BACK_RING_INIT(_r, _s, __size) do { \
+ (_r)->rsp_prod_pvt = 0; \
+ (_r)->req_cons = 0; \
+ (_r)->nr_ents = __RING_SIZE(_s, __size); \
+ (_r)->sring = (_s); \
+} while (0)
+
+/* Initialize to existing shared indexes -- for recovery */
+#define FRONT_RING_ATTACH(_r, _s, __size) do { \
+ (_r)->sring = (_s); \
+ (_r)->req_prod_pvt = (_s)->req_prod; \
+ (_r)->rsp_cons = (_s)->rsp_prod; \
+ (_r)->nr_ents = __RING_SIZE(_s, __size); \
+} while (0)
+
+#define BACK_RING_ATTACH(_r, _s, __size) do { \
+ (_r)->sring = (_s); \
+ (_r)->rsp_prod_pvt = (_s)->rsp_prod; \
+ (_r)->req_cons = (_s)->req_prod; \
+ (_r)->nr_ents = __RING_SIZE(_s, __size); \
+} while (0)
+
+/* How big is this ring? */
+#define RING_SIZE(_r) \
+ ((_r)->nr_ents)
+
+/* Number of free requests (for use on front side only). */
+#define RING_FREE_REQUESTS(_r) \
+ (RING_SIZE(_r) - ((_r)->req_prod_pvt - (_r)->rsp_cons))
+
+/* Test if there is an empty slot available on the front ring.
+ * (This is only meaningful from the front. )
+ */
+#define RING_FULL(_r) \
+ (RING_FREE_REQUESTS(_r) == 0)
+
+/* Test if there are outstanding messages to be processed on a ring. */
+#define RING_HAS_UNCONSUMED_RESPONSES(_r) \
+ ((_r)->sring->rsp_prod - (_r)->rsp_cons)
+
+#ifdef __GNUC__
+#define RING_HAS_UNCONSUMED_REQUESTS(_r) ({ \
+ unsigned int req = (_r)->sring->req_prod - (_r)->req_cons; \
+ unsigned int rsp = RING_SIZE(_r) - \
+ ((_r)->req_cons - (_r)->rsp_prod_pvt); \
+ req < rsp ? req : rsp; \
+})
+#else
+/* Same as above, but without the nice GCC ({ ... }) syntax. */
+#define RING_HAS_UNCONSUMED_REQUESTS(_r) \
+ ((((_r)->sring->req_prod - (_r)->req_cons) < \
+ (RING_SIZE(_r) - ((_r)->req_cons - (_r)->rsp_prod_pvt))) ? \
+ ((_r)->sring->req_prod - (_r)->req_cons) : \
+ (RING_SIZE(_r) - ((_r)->req_cons - (_r)->rsp_prod_pvt)))
+#endif
+
+/* Direct access to individual ring elements, by index. */
+#define RING_GET_REQUEST(_r, _idx) \
+ (&((_r)->sring->ring[((_idx) & (RING_SIZE(_r) - 1))].req))
+
+#define RING_GET_RESPONSE(_r, _idx) \
+ (&((_r)->sring->ring[((_idx) & (RING_SIZE(_r) - 1))].rsp))
+
+/* Loop termination condition: Would the specified index overflow the ring? */
+#define RING_REQUEST_CONS_OVERFLOW(_r, _cons) \
+ (((_cons) - (_r)->rsp_prod_pvt) >= RING_SIZE(_r))
+
+#define RING_PUSH_REQUESTS(_r) do { \
+ xen_wmb(); /* back sees requests /before/ updated producer index */ \
+ (_r)->sring->req_prod = (_r)->req_prod_pvt; \
+} while (0)
+
+#define RING_PUSH_RESPONSES(_r) do { \
+ xen_wmb(); /* front sees resps /before/ updated producer index */ \
+ (_r)->sring->rsp_prod = (_r)->rsp_prod_pvt; \
+} while (0)
+
+/*
+ * Notification hold-off (req_event and rsp_event):
+ *
+ * When queueing requests or responses on a shared ring, it may not always be
+ * necessary to notify the remote end. For example, if requests are in flight
+ * in a backend, the front may be able to queue further requests without
+ * notifying the back (if the back checks for new requests when it queues
+ * responses).
+ *
+ * When enqueuing requests or responses:
+ *
+ * Use RING_PUSH_{REQUESTS,RESPONSES}_AND_CHECK_NOTIFY(). The second argument
+ * is a boolean return value. True indicates that the receiver requires an
+ * asynchronous notification.
+ *
+ * After dequeuing requests or responses (before sleeping the connection):
+ *
+ * Use RING_FINAL_CHECK_FOR_REQUESTS() or RING_FINAL_CHECK_FOR_RESPONSES().
+ * The second argument is a boolean return value. True indicates that there
+ * are pending messages on the ring (i.e., the connection should not be put
+ * to sleep).
+ *
+ * These macros will set the req_event/rsp_event field to trigger a
+ * notification on the very next message that is enqueued. If you want to
+ * create batches of work (i.e., only receive a notification after several
+ * messages have been enqueued) then you will need to create a customised
+ * version of the FINAL_CHECK macro in your own code, which sets the event
+ * field appropriately.
+ */
+
+#define RING_PUSH_REQUESTS_AND_CHECK_NOTIFY(_r, _notify) do { \
+ RING_IDX __old = (_r)->sring->req_prod; \
+ RING_IDX __new = (_r)->req_prod_pvt; \
+ xen_wmb(); /* back sees requests /before/ updated producer index */ \
+ (_r)->sring->req_prod = __new; \
+ xen_mb(); /* back sees new requests /before/ we check req_event */ \
+ (_notify) = ((RING_IDX)(__new - (_r)->sring->req_event) < \
+ (RING_IDX)(__new - __old)); \
+} while (0)
+
+#define RING_PUSH_RESPONSES_AND_CHECK_NOTIFY(_r, _notify) do { \
+ RING_IDX __old = (_r)->sring->rsp_prod; \
+ RING_IDX __new = (_r)->rsp_prod_pvt; \
+ xen_wmb(); /* front sees resps /before/ updated producer index */ \
+ (_r)->sring->rsp_prod = __new; \
+ xen_mb(); /* front sees new resps /before/ we check rsp_event */ \
+ (_notify) = ((RING_IDX)(__new - (_r)->sring->rsp_event) < \
+ (RING_IDX)(__new - __old)); \
+} while (0)
+
+#define RING_FINAL_CHECK_FOR_REQUESTS(_r, _work_to_do) do { \
+ (_work_to_do) = RING_HAS_UNCONSUMED_REQUESTS(_r); \
+ if (_work_to_do) break; \
+ (_r)->sring->req_event = (_r)->req_cons + 1; \
+ xen_mb(); \
+ (_work_to_do) = RING_HAS_UNCONSUMED_REQUESTS(_r); \
+} while (0)
+
+#define RING_FINAL_CHECK_FOR_RESPONSES(_r, _work_to_do) do { \
+ (_work_to_do) = RING_HAS_UNCONSUMED_RESPONSES(_r); \
+ if (_work_to_do) break; \
+ (_r)->sring->rsp_event = (_r)->rsp_cons + 1; \
+ xen_mb(); \
+ (_work_to_do) = RING_HAS_UNCONSUMED_RESPONSES(_r); \
+} while (0)
+
+#endif /* __XEN_PUBLIC_IO_RING_H__ */
+
+/*
+ * Local variables:
+ * mode: C
+ * c-set-style: "BSD"
+ * c-basic-offset: 4
+ * tab-width: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
--- /dev/null
+/*****************************************************************************
+ * xenbus.h
+ *
+ * Xenbus protocol details.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Copyright (C) 2005 XenSource Ltd.
+ */
+
+#ifndef _XEN_PUBLIC_IO_XENBUS_H
+#define _XEN_PUBLIC_IO_XENBUS_H
+
+/*
+ * The state of either end of the Xenbus, i.e. the current communication
+ * status of initialisation across the bus. States here imply nothing about
+ * the state of the connection between the driver and the kernel's device
+ * layers.
+ */
+enum xenbus_state {
+ XenbusStateUnknown = 0,
+
+ XenbusStateInitialising = 1,
+
+ /*
+ * InitWait: Finished early initialisation but waiting for information
+ * from the peer or hotplug scripts.
+ */
+ XenbusStateInitWait = 2,
+
+ /*
+ * Initialised: Waiting for a connection from the peer.
+ */
+ XenbusStateInitialised = 3,
+
+ XenbusStateConnected = 4,
+
+ /*
+ * Closing: The device is being closed due to an error or an unplug event.
+ */
+ XenbusStateClosing = 5,
+
+ XenbusStateClosed = 6,
+
+ /*
+ * Reconfiguring: The device is being reconfigured.
+ */
+ XenbusStateReconfiguring = 7,
+
+ XenbusStateReconfigured = 8
+};
+typedef enum xenbus_state XenbusState;
+
+#endif /* _XEN_PUBLIC_IO_XENBUS_H */
+
+/*
+ * Local variables:
+ * mode: C
+ * c-set-style: "BSD"
+ * c-basic-offset: 4
+ * tab-width: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
--- /dev/null
+/******************************************************************************
+ * xen-compat.h
+ *
+ * Guest OS interface to Xen. Compatibility layer.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Copyright (c) 2006, Christian Limpach
+ */
+
+#ifndef __XEN_PUBLIC_XEN_COMPAT_H__
+#define __XEN_PUBLIC_XEN_COMPAT_H__
+
+#define __XEN_LATEST_INTERFACE_VERSION__ 0x0003020a
+
+#if defined(__XEN__) || defined(__XEN_TOOLS__)
+/* Xen is built with matching headers and implements the latest interface. */
+#define __XEN_INTERFACE_VERSION__ __XEN_LATEST_INTERFACE_VERSION__
+#elif !defined(__XEN_INTERFACE_VERSION__)
+/* Guests which do not specify a version get the legacy interface. */
+#define __XEN_INTERFACE_VERSION__ 0x00000000
+#endif
+
+#if __XEN_INTERFACE_VERSION__ > __XEN_LATEST_INTERFACE_VERSION__
+#error "These header files do not support the requested interface version."
+#endif
+
+#endif /* __XEN_PUBLIC_XEN_COMPAT_H__ */
--- /dev/null
+/******************************************************************************
+ * xen.h
+ *
+ * Guest OS interface to Xen.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Copyright (c) 2004, K A Fraser
+ */
+
+#ifndef __XEN_PUBLIC_XEN_H__
+#define __XEN_PUBLIC_XEN_H__
+
+#include "xen-compat.h"
+
+#if defined(__i386__) || defined(__x86_64__)
+#include "arch-x86/xen.h"
+#elif defined(__ia64__)
+#include "arch-ia64.h"
+#else
+#error "Unsupported architecture"
+#endif
+
+#ifndef __ASSEMBLY__
+/* Guest handles for primitive C types. */
+DEFINE_XEN_GUEST_HANDLE(char);
+__DEFINE_XEN_GUEST_HANDLE(uchar, unsigned char);
+DEFINE_XEN_GUEST_HANDLE(int);
+__DEFINE_XEN_GUEST_HANDLE(uint, unsigned int);
+DEFINE_XEN_GUEST_HANDLE(LONG_PTR);
+__DEFINE_XEN_GUEST_HANDLE(ulong, ULONG_PTR);
+DEFINE_XEN_GUEST_HANDLE(void);
+
+DEFINE_XEN_GUEST_HANDLE(uint64_t);
+DEFINE_XEN_GUEST_HANDLE(xen_pfn_t);
+#endif
+
+/*
+ * HYPERCALLS
+ */
+
+#define __HYPERVISOR_set_trap_table 0
+#define __HYPERVISOR_mmu_update 1
+#define __HYPERVISOR_set_gdt 2
+#define __HYPERVISOR_stack_switch 3
+#define __HYPERVISOR_set_callbacks 4
+#define __HYPERVISOR_fpu_taskswitch 5
+#define __HYPERVISOR_sched_op_compat 6 /* compat since 0x00030101 */
+#define __HYPERVISOR_platform_op 7
+#define __HYPERVISOR_set_debugreg 8
+#define __HYPERVISOR_get_debugreg 9
+#define __HYPERVISOR_update_descriptor 10
+#define __HYPERVISOR_memory_op 12
+#define __HYPERVISOR_multicall 13
+#define __HYPERVISOR_update_va_mapping 14
+#define __HYPERVISOR_set_timer_op 15
+#define __HYPERVISOR_event_channel_op_compat 16 /* compat since 0x00030202 */
+#define __HYPERVISOR_xen_version 17
+#define __HYPERVISOR_console_io 18
+#define __HYPERVISOR_physdev_op_compat 19 /* compat since 0x00030202 */
+#define __HYPERVISOR_grant_table_op 20
+#define __HYPERVISOR_vm_assist 21
+#define __HYPERVISOR_update_va_mapping_otherdomain 22
+#define __HYPERVISOR_iret 23 /* x86 only */
+#define __HYPERVISOR_vcpu_op 24
+#define __HYPERVISOR_set_segment_base 25 /* x86/64 only */
+#define __HYPERVISOR_mmuext_op 26
+#define __HYPERVISOR_xsm_op 27
+#define __HYPERVISOR_nmi_op 28
+#define __HYPERVISOR_sched_op 29
+#define __HYPERVISOR_callback_op 30
+#define __HYPERVISOR_xenoprof_op 31
+#define __HYPERVISOR_event_channel_op 32
+#define __HYPERVISOR_physdev_op 33
+#define __HYPERVISOR_hvm_op 34
+#define __HYPERVISOR_sysctl 35
+#define __HYPERVISOR_domctl 36
+#define __HYPERVISOR_kexec_op 37
+#define __HYPERVISOR_tmem_op 38
+
+/* Architecture-specific hypercall definitions. */
+#define __HYPERVISOR_arch_0 48
+#define __HYPERVISOR_arch_1 49
+#define __HYPERVISOR_arch_2 50
+#define __HYPERVISOR_arch_3 51
+#define __HYPERVISOR_arch_4 52
+#define __HYPERVISOR_arch_5 53
+#define __HYPERVISOR_arch_6 54
+#define __HYPERVISOR_arch_7 55
+
+/*
+ * HYPERCALL COMPATIBILITY.
+ */
+
+/* New sched_op hypercall introduced in 0x00030101. */
+#if __XEN_INTERFACE_VERSION__ < 0x00030101
+#undef __HYPERVISOR_sched_op
+#define __HYPERVISOR_sched_op __HYPERVISOR_sched_op_compat
+#endif
+
+/* New event-channel and physdev hypercalls introduced in 0x00030202. */
+#if __XEN_INTERFACE_VERSION__ < 0x00030202
+#undef __HYPERVISOR_event_channel_op
+#define __HYPERVISOR_event_channel_op __HYPERVISOR_event_channel_op_compat
+#undef __HYPERVISOR_physdev_op
+#define __HYPERVISOR_physdev_op __HYPERVISOR_physdev_op_compat
+#endif
+
+/* New platform_op hypercall introduced in 0x00030204. */
+#if __XEN_INTERFACE_VERSION__ < 0x00030204
+#define __HYPERVISOR_dom0_op __HYPERVISOR_platform_op
+#endif
+
+/*
+ * VIRTUAL INTERRUPTS
+ *
+ * Virtual interrupts that a guest OS may receive from Xen.
+ *
+ * In the side comments, 'V.' denotes a per-VCPU VIRQ while 'G.' denotes a
+ * global VIRQ. The former can be bound once per VCPU and cannot be re-bound.
+ * The latter can be allocated only once per guest: they must initially be
+ * allocated to VCPU0 but can subsequently be re-bound.
+ */
+#define VIRQ_TIMER 0 /* V. Timebase update, and/or requested timeout. */
+#define VIRQ_DEBUG 1 /* V. Request guest to dump debug info. */
+#define VIRQ_CONSOLE 2 /* G. (DOM0) Bytes received on emergency console. */
+#define VIRQ_DOM_EXC 3 /* G. (DOM0) Exceptional event for some domain. */
+#define VIRQ_TBUF 4 /* G. (DOM0) Trace buffer has records available. */
+#define VIRQ_DEBUGGER 6 /* G. (DOM0) A domain has paused for debugging. */
+#define VIRQ_XENOPROF 7 /* V. XenOprofile interrupt: new sample available */
+#define VIRQ_CON_RING 8 /* G. (DOM0) Bytes received on console */
+#define VIRQ_PCPU_STATE 9 /* G. (DOM0) PCPU state changed */
+#define VIRQ_MEM_EVENT 10 /* G. (DOM0) A memory event has occured */
+
+/* Architecture-specific VIRQ definitions. */
+#define VIRQ_ARCH_0 16
+#define VIRQ_ARCH_1 17
+#define VIRQ_ARCH_2 18
+#define VIRQ_ARCH_3 19
+#define VIRQ_ARCH_4 20
+#define VIRQ_ARCH_5 21
+#define VIRQ_ARCH_6 22
+#define VIRQ_ARCH_7 23
+
+#define NR_VIRQS 24
+
+/*
+ * HYPERVISOR_mmu_update(reqs, count, pdone, foreigndom)
+ *
+ * @reqs is an array of mmu_update_t structures ((ptr, val) pairs).
+ * @count is the length of the above array.
+ * @pdone is an output parameter indicating number of completed operations
+ * @foreigndom[15:0]: FD, the expected owner of data pages referenced in this
+ * hypercall invocation. Can be DOMID_SELF.
+ * @foreigndom[31:16]: PFD, the expected owner of pagetable pages referenced
+ * in this hypercall invocation. The value of this field
+ * (x) encodes the PFD as follows:
+ * x == 0 => PFD == DOMID_SELF
+ * x != 0 => PFD == x - 1
+ *
+ * Sub-commands: ptr[1:0] specifies the appropriate MMU_* command.
+ * -------------
+ * ptr[1:0] == MMU_NORMAL_PT_UPDATE:
+ * Updates an entry in a page table belonging to PFD. If updating an L1 table,
+ * and the new table entry is valid/present, the mapped frame must belong to
+ * FD. If attempting to map an I/O page then the caller assumes the privilege
+ * of the FD.
+ * FD == DOMID_IO: Permit /only/ I/O mappings, at the priv level of the caller.
+ * FD == DOMID_XEN: Map restricted areas of Xen's heap space.
+ * ptr[:2] -- Machine address of the page-table entry to modify.
+ * val -- Value to write.
+ *
+ * ptr[1:0] == MMU_MACHPHYS_UPDATE:
+ * Updates an entry in the machine->pseudo-physical mapping table.
+ * ptr[:2] -- Machine address within the frame whose mapping to modify.
+ * The frame must belong to the FD, if one is specified.
+ * val -- Value to write into the mapping entry.
+ *
+ * ptr[1:0] == MMU_PT_UPDATE_PRESERVE_AD:
+ * As MMU_NORMAL_PT_UPDATE above, but A/D bits currently in the PTE are ORed
+ * with those in @val.
+ */
+#define MMU_NORMAL_PT_UPDATE 0 /* checked '*ptr = val'. ptr is MA. */
+#define MMU_MACHPHYS_UPDATE 1 /* ptr = MA of frame to modify entry for */
+#define MMU_PT_UPDATE_PRESERVE_AD 2 /* atomically: *ptr = val | (*ptr&(A|D)) */
+
+/*
+ * MMU EXTENDED OPERATIONS
+ *
+ * HYPERVISOR_mmuext_op() accepts a list of mmuext_op structures.
+ * A foreigndom (FD) can be specified (or DOMID_SELF for none).
+ * Where the FD has some effect, it is described below.
+ *
+ * cmd: MMUEXT_(UN)PIN_*_TABLE
+ * mfn: Machine frame number to be (un)pinned as a p.t. page.
+ * The frame must belong to the FD, if one is specified.
+ *
+ * cmd: MMUEXT_NEW_BASEPTR
+ * mfn: Machine frame number of new page-table base to install in MMU.
+ *
+ * cmd: MMUEXT_NEW_USER_BASEPTR [x86/64 only]
+ * mfn: Machine frame number of new page-table base to install in MMU
+ * when in user space.
+ *
+ * cmd: MMUEXT_TLB_FLUSH_LOCAL
+ * No additional arguments. Flushes local TLB.
+ *
+ * cmd: MMUEXT_INVLPG_LOCAL
+ * linear_addr: Linear address to be flushed from the local TLB.
+ *
+ * cmd: MMUEXT_TLB_FLUSH_MULTI
+ * vcpumask: Pointer to bitmap of VCPUs to be flushed.
+ *
+ * cmd: MMUEXT_INVLPG_MULTI
+ * linear_addr: Linear address to be flushed.
+ * vcpumask: Pointer to bitmap of VCPUs to be flushed.
+ *
+ * cmd: MMUEXT_TLB_FLUSH_ALL
+ * No additional arguments. Flushes all VCPUs' TLBs.
+ *
+ * cmd: MMUEXT_INVLPG_ALL
+ * linear_addr: Linear address to be flushed from all VCPUs' TLBs.
+ *
+ * cmd: MMUEXT_FLUSH_CACHE
+ * No additional arguments. Writes back and flushes cache contents.
+ *
+ * cmd: MMUEXT_FLUSH_CACHE_GLOBAL
+ * No additional arguments. Writes back and flushes cache contents
+ * on all CPUs in the system.
+ *
+ * cmd: MMUEXT_SET_LDT
+ * linear_addr: Linear address of LDT base (NB. must be page-aligned).
+ * nr_ents: Number of entries in LDT.
+ *
+ * cmd: MMUEXT_CLEAR_PAGE
+ * mfn: Machine frame number to be cleared.
+ *
+ * cmd: MMUEXT_COPY_PAGE
+ * mfn: Machine frame number of the destination page.
+ * src_mfn: Machine frame number of the source page.
+ *
+ * cmd: MMUEXT_[UN]MARK_SUPER
+ * mfn: Machine frame number of head of superpage to be [un]marked.
+ */
+#define MMUEXT_PIN_L1_TABLE 0
+#define MMUEXT_PIN_L2_TABLE 1
+#define MMUEXT_PIN_L3_TABLE 2
+#define MMUEXT_PIN_L4_TABLE 3
+#define MMUEXT_UNPIN_TABLE 4
+#define MMUEXT_NEW_BASEPTR 5
+#define MMUEXT_TLB_FLUSH_LOCAL 6
+#define MMUEXT_INVLPG_LOCAL 7
+#define MMUEXT_TLB_FLUSH_MULTI 8
+#define MMUEXT_INVLPG_MULTI 9
+#define MMUEXT_TLB_FLUSH_ALL 10
+#define MMUEXT_INVLPG_ALL 11
+#define MMUEXT_FLUSH_CACHE 12
+#define MMUEXT_SET_LDT 13
+#define MMUEXT_NEW_USER_BASEPTR 15
+#define MMUEXT_CLEAR_PAGE 16
+#define MMUEXT_COPY_PAGE 17
+#define MMUEXT_FLUSH_CACHE_GLOBAL 18
+#define MMUEXT_MARK_SUPER 19
+#define MMUEXT_UNMARK_SUPER 20
+
+#ifndef __ASSEMBLY__
+struct mmuext_op {
+ unsigned int cmd;
+ union {
+ /* [UN]PIN_TABLE, NEW_BASEPTR, NEW_USER_BASEPTR
+ * CLEAR_PAGE, COPY_PAGE, [UN]MARK_SUPER */
+ xen_pfn_t mfn;
+ /* INVLPG_LOCAL, INVLPG_ALL, SET_LDT */
+ ULONG_PTR linear_addr;
+ } arg1;
+ union {
+ /* SET_LDT */
+ unsigned int nr_ents;
+ /* TLB_FLUSH_MULTI, INVLPG_MULTI */
+#if __XEN_INTERFACE_VERSION__ >= 0x00030205
+ XEN_GUEST_HANDLE(const_void) vcpumask;
+#else
+ const void *vcpumask;
+#endif
+ /* COPY_PAGE */
+ xen_pfn_t src_mfn;
+ } arg2;
+};
+typedef struct mmuext_op mmuext_op_t;
+DEFINE_XEN_GUEST_HANDLE(mmuext_op_t);
+#endif
+
+/* These are passed as 'flags' to update_va_mapping. They can be ORed. */
+/* When specifying UVMF_MULTI, also OR in a pointer to a CPU bitmap. */
+/* UVMF_LOCAL is merely UVMF_MULTI with a NULL bitmap pointer. */
+#define UVMF_NONE (0UL<<0) /* No flushing at all. */
+#define UVMF_TLB_FLUSH (1UL<<0) /* Flush entire TLB(s). */
+#define UVMF_INVLPG (2UL<<0) /* Flush only one entry. */
+#define UVMF_FLUSHTYPE_MASK (3UL<<0)
+#define UVMF_MULTI (0UL<<2) /* Flush subset of TLBs. */
+#define UVMF_LOCAL (0UL<<2) /* Flush local TLB. */
+#define UVMF_ALL (1UL<<2) /* Flush all TLBs. */
+
+/*
+ * Commands to HYPERVISOR_console_io().
+ */
+#define CONSOLEIO_write 0
+#define CONSOLEIO_read 1
+
+/*
+ * Commands to HYPERVISOR_vm_assist().
+ */
+#define VMASST_CMD_enable 0
+#define VMASST_CMD_disable 1
+
+/* x86/32 guests: simulate full 4GB segment limits. */
+#define VMASST_TYPE_4gb_segments 0
+
+/* x86/32 guests: trap (vector 15) whenever above vmassist is used. */
+#define VMASST_TYPE_4gb_segments_notify 1
+
+/*
+ * x86 guests: support writes to bottom-level PTEs.
+ * NB1. Page-directory entries cannot be written.
+ * NB2. Guest must continue to remove all writable mappings of PTEs.
+ */
+#define VMASST_TYPE_writable_pagetables 2
+
+/* x86/PAE guests: support PDPTs above 4GB. */
+#define VMASST_TYPE_pae_extended_cr3 3
+
+#define MAX_VMASST_TYPE 3
+
+#ifndef __ASSEMBLY__
+
+typedef uint16_t domid_t;
+
+/* Domain ids >= DOMID_FIRST_RESERVED cannot be used for ordinary domains. */
+#define DOMID_FIRST_RESERVED (0x7FF0U)
+
+/* DOMID_SELF is used in certain contexts to refer to oneself. */
+#define DOMID_SELF (0x7FF0U)
+
+/*
+ * DOMID_IO is used to restrict page-table updates to mapping I/O memory.
+ * Although no Foreign Domain need be specified to map I/O pages, DOMID_IO
+ * is useful to ensure that no mappings to the OS's own heap are accidentally
+ * installed. (e.g., in Linux this could cause havoc as reference counts
+ * aren't adjusted on the I/O-mapping code path).
+ * This only makes sense in MMUEXT_SET_FOREIGNDOM, but in that context can
+ * be specified by any calling domain.
+ */
+#define DOMID_IO (0x7FF1U)
+
+/*
+ * DOMID_XEN is used to allow privileged domains to map restricted parts of
+ * Xen's heap space (e.g., the machine_to_phys table).
+ * This only makes sense in MMUEXT_SET_FOREIGNDOM, and is only permitted if
+ * the caller is privileged.
+ */
+#define DOMID_XEN (0x7FF2U)
+
+/*
+ * DOMID_COW is used as the owner of sharable pages */
+#define DOMID_COW (0x7FF3U)
+
+/* DOMID_INVALID is used to identify pages with unknown owner. */
+#define DOMID_INVALID (0x7FF4U)
+
+/* Idle domain. */
+#define DOMID_IDLE (0x7FFFU)
+
+/*
+ * Send an array of these to HYPERVISOR_mmu_update().
+ * NB. The fields are natural pointer/address size for this architecture.
+ */
+struct mmu_update {
+ uint64_t ptr; /* Machine address of PTE. */
+ uint64_t val; /* New contents of PTE. */
+};
+typedef struct mmu_update mmu_update_t;
+DEFINE_XEN_GUEST_HANDLE(mmu_update_t);
+
+/*
+ * Send an array of these to HYPERVISOR_multicall().
+ * NB. The fields are natural register size for this architecture.
+ */
+struct multicall_entry {
+ ULONG_PTR op, result;
+ ULONG_PTR args[6];
+};
+typedef struct multicall_entry multicall_entry_t;
+DEFINE_XEN_GUEST_HANDLE(multicall_entry_t);
+
+/*
+ * Event channel endpoints per domain:
+ * 1024 if a LONG_PTR is 32 bits; 4096 if a LONG_PTR is 64 bits.
+ */
+#define NR_EVENT_CHANNELS (sizeof(ULONG_PTR) * sizeof(ULONG_PTR) * 64)
+
+struct vcpu_time_info {
+ /*
+ * Updates to the following values are preceded and followed by an
+ * increment of 'version'. The guest can therefore detect updates by
+ * looking for changes to 'version'. If the least-significant bit of
+ * the version number is set then an update is in progress and the guest
+ * must wait to read a consistent set of values.
+ * The correct way to interact with the version number is similar to
+ * Linux's seqlock: see the implementations of read_seqbegin/read_seqretry.
+ */
+ uint32_t version;
+ uint32_t pad0;
+ uint64_t tsc_timestamp; /* TSC at last update of time vals. */
+ uint64_t system_time; /* Time, in nanosecs, since boot. */
+ /*
+ * Current system time:
+ * system_time +
+ * ((((tsc - tsc_timestamp) << tsc_shift) * tsc_to_system_mul) >> 32)
+ * CPU frequency (Hz):
+ * ((10^9 << 32) / tsc_to_system_mul) >> tsc_shift
+ */
+ uint32_t tsc_to_system_mul;
+ int8_t tsc_shift;
+ int8_t pad1[3];
+}; /* 32 bytes */
+typedef struct vcpu_time_info vcpu_time_info_t;
+
+struct vcpu_info {
+ /*
+ * 'evtchn_upcall_pending' is written non-zero by Xen to indicate
+ * a pending notification for a particular VCPU. It is then cleared
+ * by the guest OS /before/ checking for pending work, thus avoiding
+ * a set-and-check race. Note that the mask is only accessed by Xen
+ * on the CPU that is currently hosting the VCPU. This means that the
+ * pending and mask flags can be updated by the guest without special
+ * synchronisation (i.e., no need for the x86 LOCK prefix).
+ * This may seem suboptimal because if the pending flag is set by
+ * a different CPU then an IPI may be scheduled even when the mask
+ * is set. However, note:
+ * 1. The task of 'interrupt holdoff' is covered by the per-event-
+ * channel mask bits. A 'noisy' event that is continually being
+ * triggered can be masked at source at this very precise
+ * granularity.
+ * 2. The main purpose of the per-VCPU mask is therefore to restrict
+ * reentrant execution: whether for concurrency control, or to
+ * prevent unbounded stack usage. Whatever the purpose, we expect
+ * that the mask will be asserted only for short periods at a time,
+ * and so the likelihood of a 'spurious' IPI is suitably small.
+ * The mask is read before making an event upcall to the guest: a
+ * non-zero mask therefore guarantees that the VCPU will not receive
+ * an upcall activation. The mask is cleared when the VCPU requests
+ * to block: this avoids wakeup-waiting races.
+ */
+ uint8_t evtchn_upcall_pending;
+ uint8_t evtchn_upcall_mask;
+ ULONG_PTR evtchn_pending_sel;
+ struct arch_vcpu_info arch;
+ struct vcpu_time_info time;
+}; /* 64 bytes (x86) */
+#ifndef __XEN__
+typedef struct vcpu_info vcpu_info_t;
+#endif
+
+/*
+ * Xen/kernel shared data -- pointer provided in start_info.
+ *
+ * This structure is defined to be both smaller than a page, and the
+ * only data on the shared page, but may vary in actual size even within
+ * compatible Xen versions; guests should not rely on the size
+ * of this structure remaining constant.
+ */
+struct shared_info {
+ struct vcpu_info vcpu_info[XEN_LEGACY_MAX_VCPUS];
+
+ /*
+ * A domain can create "event channels" on which it can send and receive
+ * asynchronous event notifications. There are three classes of event that
+ * are delivered by this mechanism:
+ * 1. Bi-directional inter- and intra-domain connections. Domains must
+ * arrange out-of-band to set up a connection (usually by allocating
+ * an unbound 'listener' port and avertising that via a storage service
+ * such as xenstore).
+ * 2. Physical interrupts. A domain with suitable hardware-access
+ * privileges can bind an event-channel port to a physical interrupt
+ * source.
+ * 3. Virtual interrupts ('events'). A domain can bind an event-channel
+ * port to a virtual interrupt source, such as the virtual-timer
+ * device or the emergency console.
+ *
+ * Event channels are addressed by a "port index". Each channel is
+ * associated with two bits of information:
+ * 1. PENDING -- notifies the domain that there is a pending notification
+ * to be processed. This bit is cleared by the guest.
+ * 2. MASK -- if this bit is clear then a 0->1 transition of PENDING
+ * will cause an asynchronous upcall to be scheduled. This bit is only
+ * updated by the guest. It is read-only within Xen. If a channel
+ * becomes pending while the channel is masked then the 'edge' is lost
+ * (i.e., when the channel is unmasked, the guest must manually handle
+ * pending notifications as no upcall will be scheduled by Xen).
+ *
+ * To expedite scanning of pending notifications, any 0->1 pending
+ * transition on an unmasked channel causes a corresponding bit in a
+ * per-vcpu selector word to be set. Each bit in the selector covers a
+ * 'C LONG_PTR' in the PENDING bitfield array.
+ */
+ ULONG_PTR evtchn_pending[sizeof(ULONG_PTR) * 8];
+ ULONG_PTR evtchn_mask[sizeof(ULONG_PTR) * 8];
+
+ /*
+ * Wallclock time: updated only by control software. Guests should base
+ * their gettimeofday() syscall on this wallclock-base value.
+ */
+ uint32_t wc_version; /* Version counter: see vcpu_time_info_t. */
+ uint32_t wc_sec; /* Secs 00:00:00 UTC, Jan 1, 1970. */
+ uint32_t wc_nsec; /* Nsecs 00:00:00 UTC, Jan 1, 1970. */
+
+ struct arch_shared_info arch;
+
+};
+#ifndef __XEN__
+typedef struct shared_info shared_info_t;
+#endif
+
+/*
+ * Start-of-day memory layout:
+ * 1. The domain is started within contiguous virtual-memory region.
+ * 2. The contiguous region ends on an aligned 4MB boundary.
+ * 3. This the order of bootstrap elements in the initial virtual region:
+ * a. relocated kernel image
+ * b. initial ram disk [mod_start, mod_len]
+ * c. list of allocated page frames [mfn_list, nr_pages]
+ * (unless relocated due to XEN_ELFNOTE_INIT_P2M)
+ * d. start_info_t structure [register ESI (x86)]
+ * e. bootstrap page tables [pt_base, CR3 (x86)]
+ * f. bootstrap stack [register ESP (x86)]
+ * 4. Bootstrap elements are packed together, but each is 4kB-aligned.
+ * 5. The initial ram disk may be omitted.
+ * 6. The list of page frames forms a contiguous 'pseudo-physical' memory
+ * layout for the domain. In particular, the bootstrap virtual-memory
+ * region is a 1:1 mapping to the first section of the pseudo-physical map.
+ * 7. All bootstrap elements are mapped read-writable for the guest OS. The
+ * only exception is the bootstrap page table, which is mapped read-only.
+ * 8. There is guaranteed to be at least 512kB padding after the final
+ * bootstrap element. If necessary, the bootstrap virtual region is
+ * extended by an extra 4MB to ensure this.
+ */
+
+#define MAX_GUEST_CMDLINE 1024
+struct start_info {
+ /* THE FOLLOWING ARE FILLED IN BOTH ON INITIAL BOOT AND ON RESUME. */
+ char magic[32]; /* "xen-<version>-<platform>". */
+ ULONG_PTR nr_pages; /* Total pages allocated to this domain. */
+ ULONG_PTR shared_info; /* MACHINE address of shared info struct. */
+ uint32_t flags; /* SIF_xxx flags. */
+ xen_pfn_t store_mfn; /* MACHINE page number of shared page. */
+ uint32_t store_evtchn; /* Event channel for store communication. */
+ union {
+ struct {
+ xen_pfn_t mfn; /* MACHINE page number of console page. */
+ uint32_t evtchn; /* Event channel for console page. */
+ } domU;
+ struct {
+ uint32_t info_off; /* Offset of console_info struct. */
+ uint32_t info_size; /* Size of console_info struct from start.*/
+ } dom0;
+ } console;
+ /* THE FOLLOWING ARE ONLY FILLED IN ON INITIAL BOOT (NOT RESUME). */
+ ULONG_PTR pt_base; /* VIRTUAL address of page directory. */
+ ULONG_PTR nr_pt_frames; /* Number of bootstrap p.t. frames. */
+ ULONG_PTR mfn_list; /* VIRTUAL address of page-frame list. */
+ ULONG_PTR mod_start; /* VIRTUAL address of pre-loaded module */
+ /* (PFN of pre-loaded module if */
+ /* SIF_MOD_START_PFN set in flags). */
+ ULONG_PTR mod_len; /* Size (bytes) of pre-loaded module. */
+ int8_t cmd_line[MAX_GUEST_CMDLINE];
+ /* The pfn range here covers both page table and p->m table frames. */
+ ULONG_PTR first_p2m_pfn;/* 1st pfn forming initial P->M table. */
+ ULONG_PTR nr_p2m_frames;/* # of pfns forming initial P->M table. */
+};
+typedef struct start_info start_info_t;
+
+/* New console union for dom0 introduced in 0x00030203. */
+#if __XEN_INTERFACE_VERSION__ < 0x00030203
+#define console_mfn console.domU.mfn
+#define console_evtchn console.domU.evtchn
+#endif
+
+/* These flags are passed in the 'flags' field of start_info_t. */
+#define SIF_PRIVILEGED (1<<0) /* Is the domain privileged? */
+#define SIF_INITDOMAIN (1<<1) /* Is this the initial control domain? */
+#define SIF_MULTIBOOT_MOD (1<<2) /* Is mod_start a multiboot module? */
+#define SIF_MOD_START_PFN (1<<3) /* Is mod_start a PFN? */
+#define SIF_PM_MASK (0xFF<<8) /* reserve 1 byte for xen-pm options */
+
+/*
+ * A multiboot module is a package containing modules very similar to a
+ * multiboot module array. The only differences are:
+ * - the array of module descriptors is by convention simply at the beginning
+ * of the multiboot module,
+ * - addresses in the module descriptors are based on the beginning of the
+ * multiboot module,
+ * - the number of modules is determined by a termination descriptor that has
+ * mod_start == 0.
+ *
+ * This permits to both build it statically and reference it in a configuration
+ * file, and let the PV guest easily rebase the addresses to virtual addresses
+ * and at the same time count the number of modules.
+ */
+struct xen_multiboot_mod_list
+{
+ /* Address of first byte of the module */
+ uint32_t mod_start;
+ /* Address of last byte of the module (inclusive) */
+ uint32_t mod_end;
+ /* Address of zero-terminated command line */
+ uint32_t cmdline;
+ /* Unused, must be zero */
+ uint32_t pad;
+};
+
+typedef struct dom0_vga_console_info {
+ uint8_t video_type; /* DOM0_VGA_CONSOLE_??? */
+#define XEN_VGATYPE_TEXT_MODE_3 0x03
+#define XEN_VGATYPE_VESA_LFB 0x23
+
+ union {
+ struct {
+ /* Font height, in pixels. */
+ uint16_t font_height;
+ /* Cursor location (column, row). */
+ uint16_t cursor_x, cursor_y;
+ /* Number of rows and columns (dimensions in characters). */
+ uint16_t rows, columns;
+ } text_mode_3;
+
+ struct {
+ /* Width and height, in pixels. */
+ uint16_t width, height;
+ /* Bytes per scan line. */
+ uint16_t bytes_per_line;
+ /* Bits per pixel. */
+ uint16_t bits_per_pixel;
+ /* LFB physical address, and size (in units of 64kB). */
+ uint32_t lfb_base;
+ uint32_t lfb_size;
+ /* RGB mask offsets and sizes, as defined by VBE 1.2+ */
+ uint8_t red_pos, red_size;
+ uint8_t green_pos, green_size;
+ uint8_t blue_pos, blue_size;
+ uint8_t rsvd_pos, rsvd_size;
+#if __XEN_INTERFACE_VERSION__ >= 0x00030206
+ /* VESA capabilities (offset 0xa, VESA command 0x4f00). */
+ uint32_t gbl_caps;
+ /* Mode attributes (offset 0x0, VESA command 0x4f01). */
+ uint16_t mode_attrs;
+#endif
+ } vesa_lfb;
+ } u;
+} dom0_vga_console_info_t;
+#define xen_vga_console_info dom0_vga_console_info
+#define xen_vga_console_info_t dom0_vga_console_info_t
+
+typedef uint8_t xen_domain_handle_t[16];
+
+/* Turn a plain number into a C ULONG_PTR constant. */
+#define __mk_unsigned_long(x) x ## UL
+#define mk_unsigned_long(x) __mk_unsigned_long(x)
+
+__DEFINE_XEN_GUEST_HANDLE(uint8, uint8_t);
+__DEFINE_XEN_GUEST_HANDLE(uint16, uint16_t);
+__DEFINE_XEN_GUEST_HANDLE(uint32, uint32_t);
+__DEFINE_XEN_GUEST_HANDLE(uint64, uint64_t);
+
+#else /* __ASSEMBLY__ */
+
+/* In assembly code we cannot use C numeric constant suffixes. */
+#define mk_unsigned_long(x) x
+
+#endif /* !__ASSEMBLY__ */
+
+/* Default definitions for macros used by domctl/sysctl. */
+#if defined(__XEN__) || defined(__XEN_TOOLS__)
+
+#ifndef uint64_aligned_t
+#define uint64_aligned_t uint64_t
+#endif
+#ifndef XEN_GUEST_HANDLE_64
+#define XEN_GUEST_HANDLE_64(name) XEN_GUEST_HANDLE(name)
+#endif
+
+#ifndef __ASSEMBLY__
+struct xenctl_cpumap {
+ XEN_GUEST_HANDLE_64(uint8) bitmap;
+ uint32_t nr_cpus;
+};
+#endif
+
+#endif /* defined(__XEN__) || defined(__XEN_TOOLS__) */
+
+#endif /* __XEN_PUBLIC_XEN_H__ */
+
+/*
+ * Local variables:
+ * mode: C
+ * c-set-style: "BSD"
+ * c-basic-offset: 4
+ * tab-width: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
--- /dev/null
+#!python -u
+
+import os, sys
+import subprocess
+from pprint import pprint
+
+def callfnout(cmd):
+ print(cmd)
+
+ sub = subprocess.Popen(cmd, stdout=subprocess.PIPE)
+ output = sub.communicate()[0]
+ ret = sub.returncode
+
+ if ret != 0:
+ raise(Exception("Error %d in : %s" % (ret, cmd)))
+
+ return output.decode('utf-8')
+
+def regenerate_kdfiles(filename, arch, pkg, source):
+ cwd = os.getcwd()
+ file = open(filename, 'w')
+ os.chdir(pkg + '/' + arch)
+ drivers = callfnout(['ls','*.sys']).split()
+ pprint(drivers)
+ for driver in drivers:
+ file.write("map\n")
+ file.write('\SystemRoot\System32\drivers\\' + driver + '\n')
+ file.write(source + '\\' + pkg + '\\' + arch + '\\' + driver + '\n')
+ file.write('\n')
+ os.chdir(cwd)
+ file.close()
+
+if __name__ == '__main__':
+ pkg = 'xenvif'
+ source = os.getcwd()
+ regenerate_kdfiles('kdfiles32.txt', 'x86', pkg, source)
+ regenerate_kdfiles('kdfiles64.txt', 'x64', pkg, source)
--- /dev/null
+call "%VS%\VC\vcvarsall.bat" x86
+msbuild.exe /m:4 /p:Configuration="%CONFIGURATION%" /p:Platform="%PLATFORM%" /t:"%TARGET%" /p:SignMode="ProductionSign" %SOLUTION%.sln
+if errorlevel 1 goto error
+exit 0
+
+:error
+exit 1
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Windows Developer Preview Debug|Win32">
+ <Configuration>Windows Developer Preview Debug</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Windows Developer Preview Release|Win32">
+ <Configuration>Windows Developer Preview Release</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Windows 7 Debug|Win32">
+ <Configuration>Windows 7 Debug</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Windows 7 Release|Win32">
+ <Configuration>Windows 7 Release</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Windows Vista Debug|Win32">
+ <Configuration>Windows Vista Debug</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Windows Vista Release|Win32">
+ <Configuration>Windows Vista Release</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Windows Developer Preview Debug|x64">
+ <Configuration>Windows Developer Preview Debug</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Windows Developer Preview Release|x64">
+ <Configuration>Windows Developer Preview Release</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Windows 7 Debug|x64">
+ <Configuration>Windows 7 Debug</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Windows 7 Release|x64">
+ <Configuration>Windows 7 Release</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Windows Vista Debug|x64">
+ <Configuration>Windows Vista Debug</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Windows Vista Release|x64">
+ <Configuration>Windows Vista Release</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <ProjectGuid>{73768CC9-DB26-4297-9EC8-1042F815EB15}</ProjectGuid>
+ <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
+ <MinimumVisualStudioVersion>11.0</MinimumVisualStudioVersion>
+ </PropertyGroup>
+ <PropertyGroup Label="PropertySheets">
+ <PlatformToolset>WindowsKernelModeDriver8.0</PlatformToolset>
+ <ConfigurationType>Utility</ConfigurationType>
+ <DriverType>Package</DriverType>
+ <Configuration>Windows Developer Preview Debug</Configuration>
+ <DisableFastUpToDateCheck>true</DisableFastUpToDateCheck>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Windows Developer Preview Debug|Win32'" Label="Configuration">
+ <TargetVersion>Windows8</TargetVersion>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Windows Developer Preview Release|Win32'" Label="Configuration">
+ <TargetVersion>Windows8</TargetVersion>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Windows 7 Debug|Win32'" Label="Configuration">
+ <TargetVersion>Windows7</TargetVersion>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Windows 7 Release|Win32'" Label="Configuration">
+ <TargetVersion>Windows7</TargetVersion>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Windows Vista Debug|Win32'" Label="Configuration">
+ <TargetVersion>Vista</TargetVersion>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Windows Vista Release|Win32'" Label="Configuration">
+ <TargetVersion>Vista</TargetVersion>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Windows Developer Preview Debug|x64'" Label="Configuration">
+ <TargetVersion>Windows8</TargetVersion>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Windows Developer Preview Release|x64'" Label="Configuration">
+ <TargetVersion>Windows8</TargetVersion>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Windows 7 Debug|x64'" Label="Configuration">
+ <TargetVersion>Windows7</TargetVersion>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Windows 7 Release|x64'" Label="Configuration">
+ <TargetVersion>Windows7</TargetVersion>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Windows Vista Debug|x64'" Label="Configuration">
+ <TargetVersion>Vista</TargetVersion>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Windows Vista Release|x64'" Label="Configuration">
+ <TargetVersion>Vista</TargetVersion>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup>
+ <EnableInf2cat>true</EnableInf2cat>
+ <Inf2CatWindowsVersionList Condition="'$(Platform)'=='x64'">Vista_$(DDKPlatform);7_$(DDKPlatform);Server2008_$(DDKPlatform);Server2008R2_$(DDKPlatform)</Inf2CatWindowsVersionList>
+ <Inf2CatWindowsVersionList Condition="'$(Platform)'=='Win32'">Vista_$(DDKPlatform);7_$(DDKPlatform);Server2008_$(DDKPlatform)</Inf2CatWindowsVersionList>
+ </PropertyGroup>
+ <PropertyGroup>
+ <DebuggerFlavor>DbgengKernelDebugger</DebuggerFlavor>
+ <EnableDeployment>False</EnableDeployment>
+ <ImportToStore>False</ImportToStore>
+ <InstallMode>None</InstallMode>
+ <HardwareIdString />
+ <CommandLine />
+ <ScriptPath />
+ <DeployFiles />
+ <ScriptName />
+ <ScriptDeviceQuery>%PathToInf%</ScriptDeviceQuery>
+ <EnableVerifier>False</EnableVerifier>
+ <AllDrivers>False</AllDrivers>
+ <VerifyProjectOutput>True</VerifyProjectOutput>
+ <VerifyDrivers />
+ <VerifyFlags>133563</VerifyFlags>
+ <PackageDir>..\..\xenvif\$(DDKPlatform)</PackageDir>
+ </PropertyGroup>
+ <ItemGroup>
+ <ProjectReference Include="..\xenvif\xenvif.vcxproj">
+ <Project>{C3F96D4C-E441-47F7-A44C-D2D0543C1D18}</Project>
+ </ProjectReference>
+ <ProjectReference Include="..\xenvif_coinst\xenvif_coinst.vcxproj">
+ <Project>{2BFAC7E6-3420-47A5-A092-BDC5C9D78A44}</Project>
+ </ProjectReference>
+ </ItemGroup>
+ <ItemGroup>
+ <FilesToPackage Include="$(KIT)\Redist\DIFx\dpinst\EngMui\x86\dpinst.exe" Condition="'$(Platform)'=='Win32'" />
+ <FilesToPackage Include="$(KIT)\Redist\DIFx\dpinst\EngMui\x64\dpinst.exe" Condition="'$(Platform)'=='x64'" />
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project>
\ No newline at end of file
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <PropertyGroup>
+ <SignMode>ProductionSign</SignMode>
+ <ProductionCertificate>CN="Citrix Systems, Inc.", OU=Digital ID Class 3 - Microsoft Software Validation v2, O="Citrix Systems, Inc.", L=Santa Clara, S=California, C=US | 9505C081954CC0A3E8B904458ACACD72EABD98B5</ProductionCertificate>
+ <TimeStampServer>http://timestamp.verisign.com/scripts/timstamp.dll</TimeStampServer>
+ </PropertyGroup>
+</Project>
\ No newline at end of file
--- /dev/null
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio 11
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "xenvif", "xenvif\xenvif.vcxproj", "{C3F96D4C-E441-47F7-A44C-D2D0543C1D18}"
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "package", "package\package.vcxproj", "{73768CC9-DB26-4297-9EC8-1042F815EB15}"
+ ProjectSection(ProjectDependencies) = postProject
+ {2BFAC7E6-3420-47A5-A092-BDC5C9D78A44} = {2BFAC7E6-3420-47A5-A092-BDC5C9D78A44}
+ EndProjectSection
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "xenvif_coinst", "xenvif_coinst\xenvif_coinst.vcxproj", "{2BFAC7E6-3420-47A5-A092-BDC5C9D78A44}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Windows 7 Debug|Win32 = Windows 7 Debug|Win32
+ Windows 7 Debug|x64 = Windows 7 Debug|x64
+ Windows 7 Release|Win32 = Windows 7 Release|Win32
+ Windows 7 Release|x64 = Windows 7 Release|x64
+ Windows Developer Preview Debug|Win32 = Windows Developer Preview Debug|Win32
+ Windows Developer Preview Debug|x64 = Windows Developer Preview Debug|x64
+ Windows Developer Preview Release|Win32 = Windows Developer Preview Release|Win32
+ Windows Developer Preview Release|x64 = Windows Developer Preview Release|x64
+ Windows Vista Debug|Win32 = Windows Vista Debug|Win32
+ Windows Vista Debug|x64 = Windows Vista Debug|x64
+ Windows Vista Release|Win32 = Windows Vista Release|Win32
+ Windows Vista Release|x64 = Windows Vista Release|x64
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {2BFAC7E6-3420-47A5-A092-BDC5C9D78A44}.Windows 7 Debug|Win32.ActiveCfg = Windows 7 Debug|Win32
+ {2BFAC7E6-3420-47A5-A092-BDC5C9D78A44}.Windows 7 Debug|Win32.Build.0 = Windows 7 Debug|Win32
+ {2BFAC7E6-3420-47A5-A092-BDC5C9D78A44}.Windows 7 Debug|Win32.Deploy.0 = Windows 7 Debug|Win32
+ {2BFAC7E6-3420-47A5-A092-BDC5C9D78A44}.Windows 7 Debug|x64.ActiveCfg = Windows 7 Debug|x64
+ {2BFAC7E6-3420-47A5-A092-BDC5C9D78A44}.Windows 7 Debug|x64.Build.0 = Windows 7 Debug|x64
+ {2BFAC7E6-3420-47A5-A092-BDC5C9D78A44}.Windows 7 Release|Win32.ActiveCfg = Windows 7 Release|Win32
+ {2BFAC7E6-3420-47A5-A092-BDC5C9D78A44}.Windows 7 Release|Win32.Build.0 = Windows 7 Release|Win32
+ {2BFAC7E6-3420-47A5-A092-BDC5C9D78A44}.Windows 7 Release|Win32.Deploy.0 = Windows 7 Release|Win32
+ {2BFAC7E6-3420-47A5-A092-BDC5C9D78A44}.Windows 7 Release|x64.ActiveCfg = Windows 7 Release|x64
+ {2BFAC7E6-3420-47A5-A092-BDC5C9D78A44}.Windows 7 Release|x64.Build.0 = Windows 7 Release|x64
+ {2BFAC7E6-3420-47A5-A092-BDC5C9D78A44}.Windows Developer Preview Debug|Win32.ActiveCfg = Windows Developer Preview Debug|Win32
+ {2BFAC7E6-3420-47A5-A092-BDC5C9D78A44}.Windows Developer Preview Debug|Win32.Build.0 = Windows Developer Preview Debug|Win32
+ {2BFAC7E6-3420-47A5-A092-BDC5C9D78A44}.Windows Developer Preview Debug|Win32.Deploy.0 = Windows Developer Preview Debug|Win32
+ {2BFAC7E6-3420-47A5-A092-BDC5C9D78A44}.Windows Developer Preview Debug|x64.ActiveCfg = Windows Developer Preview Debug|x64
+ {2BFAC7E6-3420-47A5-A092-BDC5C9D78A44}.Windows Developer Preview Debug|x64.Build.0 = Windows Developer Preview Debug|x64
+ {2BFAC7E6-3420-47A5-A092-BDC5C9D78A44}.Windows Developer Preview Release|Win32.ActiveCfg = Windows Developer Preview Release|Win32
+ {2BFAC7E6-3420-47A5-A092-BDC5C9D78A44}.Windows Developer Preview Release|Win32.Build.0 = Windows Developer Preview Release|Win32
+ {2BFAC7E6-3420-47A5-A092-BDC5C9D78A44}.Windows Developer Preview Release|Win32.Deploy.0 = Windows Developer Preview Release|Win32
+ {2BFAC7E6-3420-47A5-A092-BDC5C9D78A44}.Windows Developer Preview Release|x64.ActiveCfg = Windows Developer Preview Release|x64
+ {2BFAC7E6-3420-47A5-A092-BDC5C9D78A44}.Windows Developer Preview Release|x64.Build.0 = Windows Developer Preview Release|x64
+ {2BFAC7E6-3420-47A5-A092-BDC5C9D78A44}.Windows Vista Debug|Win32.ActiveCfg = Windows Vista Debug|Win32
+ {2BFAC7E6-3420-47A5-A092-BDC5C9D78A44}.Windows Vista Debug|Win32.Build.0 = Windows Vista Debug|Win32
+ {2BFAC7E6-3420-47A5-A092-BDC5C9D78A44}.Windows Vista Debug|Win32.Deploy.0 = Windows Vista Debug|Win32
+ {2BFAC7E6-3420-47A5-A092-BDC5C9D78A44}.Windows Vista Debug|x64.ActiveCfg = Windows Vista Debug|x64
+ {2BFAC7E6-3420-47A5-A092-BDC5C9D78A44}.Windows Vista Debug|x64.Build.0 = Windows Vista Debug|x64
+ {2BFAC7E6-3420-47A5-A092-BDC5C9D78A44}.Windows Vista Release|Win32.ActiveCfg = Windows Vista Release|Win32
+ {2BFAC7E6-3420-47A5-A092-BDC5C9D78A44}.Windows Vista Release|Win32.Build.0 = Windows Vista Release|Win32
+ {2BFAC7E6-3420-47A5-A092-BDC5C9D78A44}.Windows Vista Release|Win32.Deploy.0 = Windows Vista Release|Win32
+ {2BFAC7E6-3420-47A5-A092-BDC5C9D78A44}.Windows Vista Release|x64.ActiveCfg = Windows Vista Release|x64
+ {2BFAC7E6-3420-47A5-A092-BDC5C9D78A44}.Windows Vista Release|x64.Build.0 = Windows Vista Release|x64
+ {73768CC9-DB26-4297-9EC8-1042F815EB15}.Windows 7 Debug|Win32.ActiveCfg = Windows 7 Debug|Win32
+ {73768CC9-DB26-4297-9EC8-1042F815EB15}.Windows 7 Debug|Win32.Build.0 = Windows 7 Debug|Win32
+ {73768CC9-DB26-4297-9EC8-1042F815EB15}.Windows 7 Debug|Win32.Deploy.0 = Windows 7 Debug|Win32
+ {73768CC9-DB26-4297-9EC8-1042F815EB15}.Windows 7 Debug|x64.ActiveCfg = Windows 7 Debug|x64
+ {73768CC9-DB26-4297-9EC8-1042F815EB15}.Windows 7 Debug|x64.Build.0 = Windows 7 Debug|x64
+ {73768CC9-DB26-4297-9EC8-1042F815EB15}.Windows 7 Debug|x64.Deploy.0 = Windows 7 Debug|x64
+ {73768CC9-DB26-4297-9EC8-1042F815EB15}.Windows 7 Release|Win32.ActiveCfg = Windows 7 Release|Win32
+ {73768CC9-DB26-4297-9EC8-1042F815EB15}.Windows 7 Release|Win32.Build.0 = Windows 7 Release|Win32
+ {73768CC9-DB26-4297-9EC8-1042F815EB15}.Windows 7 Release|Win32.Deploy.0 = Windows 7 Release|Win32
+ {73768CC9-DB26-4297-9EC8-1042F815EB15}.Windows 7 Release|x64.ActiveCfg = Windows 7 Release|x64
+ {73768CC9-DB26-4297-9EC8-1042F815EB15}.Windows 7 Release|x64.Build.0 = Windows 7 Release|x64
+ {73768CC9-DB26-4297-9EC8-1042F815EB15}.Windows 7 Release|x64.Deploy.0 = Windows 7 Release|x64
+ {73768CC9-DB26-4297-9EC8-1042F815EB15}.Windows Developer Preview Debug|Win32.ActiveCfg = Windows Developer Preview Debug|Win32
+ {73768CC9-DB26-4297-9EC8-1042F815EB15}.Windows Developer Preview Debug|Win32.Build.0 = Windows Developer Preview Debug|Win32
+ {73768CC9-DB26-4297-9EC8-1042F815EB15}.Windows Developer Preview Debug|Win32.Deploy.0 = Windows Developer Preview Debug|Win32
+ {73768CC9-DB26-4297-9EC8-1042F815EB15}.Windows Developer Preview Debug|x64.ActiveCfg = Windows Developer Preview Debug|x64
+ {73768CC9-DB26-4297-9EC8-1042F815EB15}.Windows Developer Preview Debug|x64.Build.0 = Windows Developer Preview Debug|x64
+ {73768CC9-DB26-4297-9EC8-1042F815EB15}.Windows Developer Preview Debug|x64.Deploy.0 = Windows Developer Preview Debug|x64
+ {73768CC9-DB26-4297-9EC8-1042F815EB15}.Windows Developer Preview Release|Win32.ActiveCfg = Windows Developer Preview Release|Win32
+ {73768CC9-DB26-4297-9EC8-1042F815EB15}.Windows Developer Preview Release|Win32.Build.0 = Windows Developer Preview Release|Win32
+ {73768CC9-DB26-4297-9EC8-1042F815EB15}.Windows Developer Preview Release|Win32.Deploy.0 = Windows Developer Preview Release|Win32
+ {73768CC9-DB26-4297-9EC8-1042F815EB15}.Windows Developer Preview Release|x64.ActiveCfg = Windows Developer Preview Release|x64
+ {73768CC9-DB26-4297-9EC8-1042F815EB15}.Windows Developer Preview Release|x64.Build.0 = Windows Developer Preview Release|x64
+ {73768CC9-DB26-4297-9EC8-1042F815EB15}.Windows Developer Preview Release|x64.Deploy.0 = Windows Developer Preview Release|x64
+ {73768CC9-DB26-4297-9EC8-1042F815EB15}.Windows Vista Debug|Win32.ActiveCfg = Windows Vista Debug|Win32
+ {73768CC9-DB26-4297-9EC8-1042F815EB15}.Windows Vista Debug|Win32.Build.0 = Windows Vista Debug|Win32
+ {73768CC9-DB26-4297-9EC8-1042F815EB15}.Windows Vista Debug|Win32.Deploy.0 = Windows Vista Debug|Win32
+ {73768CC9-DB26-4297-9EC8-1042F815EB15}.Windows Vista Debug|x64.ActiveCfg = Windows Vista Debug|x64
+ {73768CC9-DB26-4297-9EC8-1042F815EB15}.Windows Vista Debug|x64.Build.0 = Windows Vista Debug|x64
+ {73768CC9-DB26-4297-9EC8-1042F815EB15}.Windows Vista Debug|x64.Deploy.0 = Windows Vista Debug|x64
+ {73768CC9-DB26-4297-9EC8-1042F815EB15}.Windows Vista Release|Win32.ActiveCfg = Windows Vista Release|Win32
+ {73768CC9-DB26-4297-9EC8-1042F815EB15}.Windows Vista Release|Win32.Build.0 = Windows Vista Release|Win32
+ {73768CC9-DB26-4297-9EC8-1042F815EB15}.Windows Vista Release|Win32.Deploy.0 = Windows Vista Release|Win32
+ {73768CC9-DB26-4297-9EC8-1042F815EB15}.Windows Vista Release|x64.ActiveCfg = Windows Vista Release|x64
+ {73768CC9-DB26-4297-9EC8-1042F815EB15}.Windows Vista Release|x64.Build.0 = Windows Vista Release|x64
+ {73768CC9-DB26-4297-9EC8-1042F815EB15}.Windows Vista Release|x64.Deploy.0 = Windows Vista Release|x64
+ {C3F96D4C-E441-47F7-A44C-D2D0543C1D18}.Windows 7 Debug|Win32.ActiveCfg = Windows 7 Debug|Win32
+ {C3F96D4C-E441-47F7-A44C-D2D0543C1D18}.Windows 7 Debug|Win32.Build.0 = Windows 7 Debug|Win32
+ {C3F96D4C-E441-47F7-A44C-D2D0543C1D18}.Windows 7 Debug|Win32.Deploy.0 = Windows 7 Debug|Win32
+ {C3F96D4C-E441-47F7-A44C-D2D0543C1D18}.Windows 7 Debug|x64.ActiveCfg = Windows 7 Debug|x64
+ {C3F96D4C-E441-47F7-A44C-D2D0543C1D18}.Windows 7 Debug|x64.Build.0 = Windows 7 Debug|x64
+ {C3F96D4C-E441-47F7-A44C-D2D0543C1D18}.Windows 7 Debug|x64.Deploy.0 = Windows 7 Debug|x64
+ {C3F96D4C-E441-47F7-A44C-D2D0543C1D18}.Windows 7 Release|Win32.ActiveCfg = Windows 7 Release|Win32
+ {C3F96D4C-E441-47F7-A44C-D2D0543C1D18}.Windows 7 Release|Win32.Build.0 = Windows 7 Release|Win32
+ {C3F96D4C-E441-47F7-A44C-D2D0543C1D18}.Windows 7 Release|Win32.Deploy.0 = Windows 7 Release|Win32
+ {C3F96D4C-E441-47F7-A44C-D2D0543C1D18}.Windows 7 Release|x64.ActiveCfg = Windows 7 Release|x64
+ {C3F96D4C-E441-47F7-A44C-D2D0543C1D18}.Windows 7 Release|x64.Build.0 = Windows 7 Release|x64
+ {C3F96D4C-E441-47F7-A44C-D2D0543C1D18}.Windows 7 Release|x64.Deploy.0 = Windows 7 Release|x64
+ {C3F96D4C-E441-47F7-A44C-D2D0543C1D18}.Windows Developer Preview Debug|Win32.ActiveCfg = Windows Developer Preview Debug|Win32
+ {C3F96D4C-E441-47F7-A44C-D2D0543C1D18}.Windows Developer Preview Debug|Win32.Build.0 = Windows Developer Preview Debug|Win32
+ {C3F96D4C-E441-47F7-A44C-D2D0543C1D18}.Windows Developer Preview Debug|Win32.Deploy.0 = Windows Developer Preview Debug|Win32
+ {C3F96D4C-E441-47F7-A44C-D2D0543C1D18}.Windows Developer Preview Debug|x64.ActiveCfg = Windows Developer Preview Debug|x64
+ {C3F96D4C-E441-47F7-A44C-D2D0543C1D18}.Windows Developer Preview Debug|x64.Build.0 = Windows Developer Preview Debug|x64
+ {C3F96D4C-E441-47F7-A44C-D2D0543C1D18}.Windows Developer Preview Debug|x64.Deploy.0 = Windows Developer Preview Debug|x64
+ {C3F96D4C-E441-47F7-A44C-D2D0543C1D18}.Windows Developer Preview Release|Win32.ActiveCfg = Windows Developer Preview Release|Win32
+ {C3F96D4C-E441-47F7-A44C-D2D0543C1D18}.Windows Developer Preview Release|Win32.Build.0 = Windows Developer Preview Release|Win32
+ {C3F96D4C-E441-47F7-A44C-D2D0543C1D18}.Windows Developer Preview Release|Win32.Deploy.0 = Windows Developer Preview Release|Win32
+ {C3F96D4C-E441-47F7-A44C-D2D0543C1D18}.Windows Developer Preview Release|x64.ActiveCfg = Windows Developer Preview Release|x64
+ {C3F96D4C-E441-47F7-A44C-D2D0543C1D18}.Windows Developer Preview Release|x64.Build.0 = Windows Developer Preview Release|x64
+ {C3F96D4C-E441-47F7-A44C-D2D0543C1D18}.Windows Developer Preview Release|x64.Deploy.0 = Windows Developer Preview Release|x64
+ {C3F96D4C-E441-47F7-A44C-D2D0543C1D18}.Windows Vista Debug|Win32.ActiveCfg = Windows Vista Debug|Win32
+ {C3F96D4C-E441-47F7-A44C-D2D0543C1D18}.Windows Vista Debug|Win32.Build.0 = Windows Vista Debug|Win32
+ {C3F96D4C-E441-47F7-A44C-D2D0543C1D18}.Windows Vista Debug|Win32.Deploy.0 = Windows Vista Debug|Win32
+ {C3F96D4C-E441-47F7-A44C-D2D0543C1D18}.Windows Vista Debug|x64.ActiveCfg = Windows Vista Debug|x64
+ {C3F96D4C-E441-47F7-A44C-D2D0543C1D18}.Windows Vista Debug|x64.Build.0 = Windows Vista Debug|x64
+ {C3F96D4C-E441-47F7-A44C-D2D0543C1D18}.Windows Vista Debug|x64.Deploy.0 = Windows Vista Debug|x64
+ {C3F96D4C-E441-47F7-A44C-D2D0543C1D18}.Windows Vista Release|Win32.ActiveCfg = Windows Vista Release|Win32
+ {C3F96D4C-E441-47F7-A44C-D2D0543C1D18}.Windows Vista Release|Win32.Build.0 = Windows Vista Release|Win32
+ {C3F96D4C-E441-47F7-A44C-D2D0543C1D18}.Windows Vista Release|Win32.Deploy.0 = Windows Vista Release|Win32
+ {C3F96D4C-E441-47F7-A44C-D2D0543C1D18}.Windows Vista Release|x64.ActiveCfg = Windows Vista Release|x64
+ {C3F96D4C-E441-47F7-A44C-D2D0543C1D18}.Windows Vista Release|x64.Build.0 = Windows Vista Release|x64
+ {C3F96D4C-E441-47F7-A44C-D2D0543C1D18}.Windows Vista Release|x64.Deploy.0 = Windows Vista Release|x64
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+EndGlobal
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Windows Developer Preview Debug|Win32">
+ <Configuration>Windows Developer Preview Debug</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Windows Developer Preview Release|Win32">
+ <Configuration>Windows Developer Preview Release</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Windows 7 Debug|Win32">
+ <Configuration>Windows 7 Debug</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Windows 7 Release|Win32">
+ <Configuration>Windows 7 Release</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Windows Vista Debug|Win32">
+ <Configuration>Windows Vista Debug</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Windows Vista Release|Win32">
+ <Configuration>Windows Vista Release</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Windows Developer Preview Debug|x64">
+ <Configuration>Windows Developer Preview Debug</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Windows Developer Preview Release|x64">
+ <Configuration>Windows Developer Preview Release</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Windows 7 Debug|x64">
+ <Configuration>Windows 7 Debug</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Windows 7 Release|x64">
+ <Configuration>Windows 7 Release</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Windows Vista Debug|x64">
+ <Configuration>Windows Vista Debug</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Windows Vista Release|x64">
+ <Configuration>Windows Vista Release</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <ProjectGuid>{C3F96D4C-E441-47F7-A44C-D2D0543C1D18}</ProjectGuid>
+ <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
+ <MinimumVisualStudioVersion>11.0</MinimumVisualStudioVersion>
+ <ProjectName>xenvif</ProjectName>
+ </PropertyGroup>
+ <PropertyGroup Label="PropertySheets">
+ <PlatformToolset>WindowsKernelModeDriver8.0</PlatformToolset>
+ <ConfigurationType>Driver</ConfigurationType>
+ <DriverType>WDM</DriverType>
+ <Configuration>Windows Developer Preview Debug</Configuration>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Windows Developer Preview Debug|Win32'" Label="Configuration">
+ <TargetVersion>Windows8</TargetVersion>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Windows Developer Preview Release|Win32'" Label="Configuration">
+ <TargetVersion>Windows8</TargetVersion>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Windows 7 Debug|Win32'" Label="Configuration">
+ <TargetVersion>Windows7</TargetVersion>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Windows 7 Release|Win32'" Label="Configuration">
+ <TargetVersion>Windows7</TargetVersion>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Windows Vista Debug|Win32'" Label="Configuration">
+ <TargetVersion>Vista</TargetVersion>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Windows Vista Release|Win32'" Label="Configuration">
+ <TargetVersion>Vista</TargetVersion>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Windows Developer Preview Debug|x64'" Label="Configuration">
+ <TargetVersion>Windows8</TargetVersion>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Windows Developer Preview Release|x64'" Label="Configuration">
+ <TargetVersion>Windows8</TargetVersion>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Windows 7 Debug|x64'" Label="Configuration">
+ <TargetVersion>Windows7</TargetVersion>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Windows 7 Release|x64'" Label="Configuration">
+ <TargetVersion>Windows7</TargetVersion>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Windows Vista Debug|x64'" Label="Configuration">
+ <TargetVersion>Vista</TargetVersion>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Windows Vista Release|x64'" Label="Configuration">
+ <TargetVersion>Vista</TargetVersion>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup>
+ <IncludePath>$(IncludePath)</IncludePath>
+ <RunCodeAnalysis>true</RunCodeAnalysis>
+ <EnableInf2cat>false</EnableInf2cat>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Platform)'=='Win32'">
+ <ClCompile>
+ <AdditionalIncludeDirectories>$(SolutionDir)..\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>__i386__;__MODULE__="XENVIF";POOL_NX_OPTIN=1;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <WarningLevel>EnableAllWarnings</WarningLevel>
+ <DisableSpecificWarnings>4548;4820;4668;4255;6001;6054;28196;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ <MultiProcessorCompilation>true</MultiProcessorCompilation>
+ <EnablePREfast>true</EnablePREfast>
+ </ClCompile>
+ <Link>
+ <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
+ <AdditionalDependencies>$(DDK_LIB_PATH)/libcntpr.lib;$(DDK_LIB_PATH)/netio.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <EnableCOMDATFolding>false</EnableCOMDATFolding>
+ </Link>
+ <ResourceCompile>
+ <AdditionalIncludeDirectories>$(SolutionDir)..\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ </ResourceCompile>
+ <Inf>
+ <SpecifyArchitecture>true</SpecifyArchitecture>
+ <Architecture>x86</Architecture>
+ <SpecifyDriverVerDirectiveVersion>true</SpecifyDriverVerDirectiveVersion>
+ <TimeStamp>$(MAJOR_VERSION).$(MINOR_VERSION).$(MICRO_VERSION).$(BUILD_NUMBER)</TimeStamp>
+ <EnableVerbose>true</EnableVerbose>
+ </Inf>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Platform)'=='x64'">
+ <ClCompile>
+ <AdditionalIncludeDirectories>$(SolutionDir)..\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>__x86_64__;__MODULE__="XENVIF";POOL_NX_OPTIN=1;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <WarningLevel>EnableAllWarnings</WarningLevel>
+ <DisableSpecificWarnings>4548;4820;4668;4255;6001;6054;28196;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ <MultiProcessorCompilation>true</MultiProcessorCompilation>
+ <EnablePREfast>true</EnablePREfast>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>$(DDK_LIB_PATH)/libcntpr.lib;$(DDK_LIB_PATH)/netio.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <EnableCOMDATFolding>false</EnableCOMDATFolding>
+ </Link>
+ <ResourceCompile>
+ <AdditionalIncludeDirectories>$(SolutionDir)..\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ </ResourceCompile>
+ <Inf>
+ <SpecifyArchitecture>true</SpecifyArchitecture>
+ <Architecture>amd64</Architecture>
+ <SpecifyDriverVerDirectiveVersion>true</SpecifyDriverVerDirectiveVersion>
+ <TimeStamp>$(MAJOR_VERSION).$(MINOR_VERSION).$(MICRO_VERSION).$(BUILD_NUMBER)</TimeStamp>
+ <EnableVerbose>true</EnableVerbose>
+ </Inf>
+ </ItemDefinitionGroup>
+ <ItemGroup>
+ <FilesToPackage Include="$(TargetPath)" />
+ <FilesToPackage Include="$(OutDir)$(TargetName).pdb" />
+ <FilesToPackage Include="@(Inf->'%(CopyOutput)')" Condition="'@(Inf)'!=''" />
+ </ItemGroup>
+ <ItemGroup>
+ <ClCompile Include="../../src/xenvif/checksum.c" />
+ <ClCompile Include="../../src/xenvif/driver.c" />
+ <ClCompile Include="../../src/xenvif/fdo.c" />
+ <ClCompile Include="../../src/xenvif/frontend.c" />
+ <ClCompile Include="../../src/xenvif/mac.c" />
+ <ClCompile Include="../../src/xenvif/notifier.c" />
+ <ClCompile Include="../../src/xenvif/parse.c" />
+ <ClCompile Include="../../src/xenvif/pdo.c" />
+ <ClCompile Include="../../src/xenvif/pool.c" />
+ <ClCompile Include="../../src/xenvif/receiver.c" />
+ <ClCompile Include="../../src/xenvif/registry.c" />
+ <ClCompile Include="../../src/xenvif/thread.c" />
+ <ClCompile Include="../../src/xenvif/transmitter.c" />
+ <ClCompile Include="../../src/xenvif/vif.c" />
+ </ItemGroup>
+ <ItemGroup>
+ <ResourceCompile Include="..\..\src\xenvif\xenvif.rc" />
+ </ItemGroup>
+ <ItemGroup>
+ <Inf Include="..\xenvif.inf" />
+ </ItemGroup>
+ <ItemGroup>
+ <None Include="..\package\package.vcxproj" />
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+</Project>
\ No newline at end of file
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <PropertyGroup>
+ <SignMode>ProductionSign</SignMode>
+ <ProductionCertificate>CN="Citrix Systems, Inc.", OU=Digital ID Class 3 - Microsoft Software Validation v2, O="Citrix Systems, Inc.", L=Santa Clara, S=California, C=US | 9505C081954CC0A3E8B904458ACACD72EABD98B5</ProductionCertificate>
+ <TimeStampServer>http://timestamp.verisign.com/scripts/timstamp.dll</TimeStampServer>
+ </PropertyGroup>
+</Project>
\ No newline at end of file
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Windows Developer Preview Debug|Win32">
+ <Configuration>Windows Developer Preview Debug</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Windows Developer Preview Release|Win32">
+ <Configuration>Windows Developer Preview Release</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Windows 7 Debug|Win32">
+ <Configuration>Windows 7 Debug</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Windows 7 Release|Win32">
+ <Configuration>Windows 7 Release</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Windows Vista Debug|Win32">
+ <Configuration>Windows Vista Debug</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Windows Vista Release|Win32">
+ <Configuration>Windows Vista Release</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Windows Developer Preview Debug|x64">
+ <Configuration>Windows Developer Preview Debug</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Windows Developer Preview Release|x64">
+ <Configuration>Windows Developer Preview Release</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Windows 7 Debug|x64">
+ <Configuration>Windows 7 Debug</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Windows 7 Release|x64">
+ <Configuration>Windows 7 Release</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Windows Vista Debug|x64">
+ <Configuration>Windows Vista Debug</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Windows Vista Release|x64">
+ <Configuration>Windows Vista Release</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <ProjectGuid>{2BFAC7E6-3420-47A5-A092-BDC5C9D78A44}</ProjectGuid>
+ <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
+ <MinimumVisualStudioVersion>11.0</MinimumVisualStudioVersion>
+ <ProjectName>xenvif_coinst</ProjectName>
+ </PropertyGroup>
+ <PropertyGroup Label="PropertySheets">
+ <PlatformToolset>WindowsApplicationForDrivers8.0</PlatformToolset>
+ <ConfigurationType>DynamicLibrary</ConfigurationType>
+ <DriverType>WDM</DriverType>
+ <Configuration>Windows Developer Preview Debug</Configuration>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Windows Developer Preview Debug|Win32'" Label="Configuration">
+ <TargetVersion>Windows8</TargetVersion>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Windows Developer Preview Release|Win32'" Label="Configuration">
+ <TargetVersion>Windows8</TargetVersion>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Windows 7 Debug|Win32'" Label="Configuration">
+ <TargetVersion>Windows7</TargetVersion>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Windows 7 Release|Win32'" Label="Configuration">
+ <TargetVersion>Windows7</TargetVersion>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Windows Vista Debug|Win32'" Label="Configuration">
+ <TargetVersion>Vista</TargetVersion>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Windows Vista Release|Win32'" Label="Configuration">
+ <TargetVersion>Vista</TargetVersion>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Windows Developer Preview Debug|x64'" Label="Configuration">
+ <TargetVersion>Windows8</TargetVersion>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Windows Developer Preview Release|x64'" Label="Configuration">
+ <TargetVersion>Windows8</TargetVersion>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Windows 7 Debug|x64'" Label="Configuration">
+ <TargetVersion>Windows7</TargetVersion>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Windows 7 Release|x64'" Label="Configuration">
+ <TargetVersion>Windows7</TargetVersion>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Windows Vista Debug|x64'" Label="Configuration">
+ <TargetVersion>Vista</TargetVersion>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Windows Vista Release|x64'" Label="Configuration">
+ <TargetVersion>Vista</TargetVersion>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup>
+ <DebuggerFlavor>DbgengKernelDebugger</DebuggerFlavor>
+ </PropertyGroup>
+ <PropertyGroup>
+ <IncludePath>$(IncludePath)</IncludePath>
+ <RunCodeAnalysis>true</RunCodeAnalysis>
+ <EnableInf2cat>false</EnableInf2cat>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Platform)'=='Win32'">
+ <ClCompile>
+ <AdditionalIncludeDirectories>$(SolutionDir)..\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>__i386__;__MODULE__="XENVIF_COINST";%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <WarningLevel>EnableAllWarnings</WarningLevel>
+ <DisableSpecificWarnings>4548;4820;4668;4255;6001;6054;28196;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ <MultiProcessorCompilation>true</MultiProcessorCompilation>
+ <EnablePREfast>true</EnablePREfast>
+ <RuntimeLibrary Condition="'$(UseDebugLibraries)'=='true'">MultiThreadedDebug</RuntimeLibrary>
+ <RuntimeLibrary Condition="'$(UseDebugLibraries)'=='false'">MultiThreaded</RuntimeLibrary>
+ </ClCompile>
+ <Link>
+ <ModuleDefinitionFile>../../src/coinst/xenvif_coinst.def</ModuleDefinitionFile>
+ <AdditionalDependencies>setupapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ </Link>
+ <ResourceCompile>
+ <AdditionalIncludeDirectories>$(SolutionDir)..\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ </ResourceCompile>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Platform)'=='x64'">
+ <ClCompile>
+ <AdditionalIncludeDirectories>$(SolutionDir)..\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>__x86_64__;__MODULE__="XENVIF_COINST";%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <WarningLevel>EnableAllWarnings</WarningLevel>
+ <DisableSpecificWarnings>4548;4820;4668;4255;6001;6054;28196;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ <MultiProcessorCompilation>true</MultiProcessorCompilation>
+ <EnablePREfast>true</EnablePREfast>
+ <RuntimeLibrary Condition="'$(UseDebugLibraries)'=='true'">MultiThreadedDebug</RuntimeLibrary>
+ <RuntimeLibrary Condition="'$(UseDebugLibraries)'=='false'">MultiThreaded</RuntimeLibrary>
+ </ClCompile>
+ <Link>
+ <ModuleDefinitionFile>../../src/coinst/xenvif_coinst.def</ModuleDefinitionFile>
+ <AdditionalDependencies>setupapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ </Link>
+ <ResourceCompile>
+ <AdditionalIncludeDirectories>$(SolutionDir)..\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ </ResourceCompile>
+ </ItemDefinitionGroup>
+ <ItemGroup>
+ <FilesToPackage Include="$(TargetPath)" />
+ <FilesToPackage Include="$(OutDir)$(TargetName).pdb" />
+ <FilesToPackage Include="@(Inf->'%(CopyOutput)')" Condition="'@(Inf)'!=''" />
+ </ItemGroup>
+ <ItemGroup>
+ <ClCompile Include="..\..\src\coinst\coinst.c" />
+ </ItemGroup>
+ <ItemGroup>
+ <None Include="..\..\src\coinst\xenvif_coinst.def" />
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+</Project>
\ No newline at end of file
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <PropertyGroup>
+ <SignMode>ProductionSign</SignMode>
+ <ProductionCertificate>CN="Citrix Systems, Inc.", OU=Digital ID Class 3 - Microsoft Software Validation v2, O="Citrix Systems, Inc.", L=Santa Clara, S=California, C=US | 9505C081954CC0A3E8B904458ACACD72EABD98B5</ProductionCertificate>
+ <TimeStampServer>http://timestamp.verisign.com/scripts/timstamp.dll</TimeStampServer>
+ </PropertyGroup>
+</Project>
\ No newline at end of file
--- /dev/null
+/* Copyright (c) Citrix Systems Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms,
+ * with or without modification, are permitted provided
+ * that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer in the documentation and/or other
+ * materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <windows.h>
+#include <setupapi.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <strsafe.h>
+#include <malloc.h>
+
+#include <version.h>
+
+__user_code;
+
+#define MAXIMUM_BUFFER_SIZE 1024
+
+#define SERVICES_KEY "SYSTEM\\CurrentControlSet\\Services"
+
+#define PARAMETERS_KEY(_Driver) \
+ SERVICES_KEY ## "\\" ## #_Driver ## "\\Parameters"
+
+#define STATUS_KEY(_Driver) \
+ SERVICES_KEY ## "\\" ## #_Driver ## "\\Status"
+
+static VOID
+#pragma prefast(suppress:6262) // Function uses '1036' bytes of stack: exceeds /analyze:stacksize'1024'
+__Log(
+ IN const CHAR *Format,
+ IN ...
+ )
+{
+ TCHAR Buffer[MAXIMUM_BUFFER_SIZE];
+ va_list Arguments;
+ size_t Length;
+ SP_LOG_TOKEN LogToken;
+ DWORD Category;
+ DWORD Flags;
+ HRESULT Result;
+
+ va_start(Arguments, Format);
+ Result = StringCchVPrintf(Buffer, MAXIMUM_BUFFER_SIZE, Format, Arguments);
+ va_end(Arguments);
+
+ if (Result != S_OK && Result != STRSAFE_E_INSUFFICIENT_BUFFER)
+ return;
+
+ Result = StringCchLength(Buffer, MAXIMUM_BUFFER_SIZE, &Length);
+ if (Result != S_OK)
+ return;
+
+ LogToken = SetupGetThreadLogToken();
+ Category = TXTLOG_VENDOR;
+ Flags = TXTLOG_DETAILS;
+
+ SetupWriteTextLog(LogToken, Category, Flags, Buffer);
+ Length = __min(MAXIMUM_BUFFER_SIZE - 1, Length + 2);
+
+ __analysis_assume(Length < MAXIMUM_BUFFER_SIZE);
+ __analysis_assume(Length >= 2);
+ Buffer[Length] = '\0';
+ Buffer[Length - 1] = '\n';
+ Buffer[Length - 2] = '\r';
+
+ OutputDebugString(Buffer);
+}
+
+#define Log(_Format, ...) \
+ __Log(__MODULE__ "|" __FUNCTION__ ": " _Format, __VA_ARGS__)
+
+static PTCHAR
+GetErrorMessage(
+ IN DWORD Error
+ )
+{
+ PTCHAR Message;
+ ULONG Index;
+
+ FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
+ FORMAT_MESSAGE_FROM_SYSTEM |
+ FORMAT_MESSAGE_IGNORE_INSERTS,
+ NULL,
+ Error,
+ MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+ (LPTSTR)&Message,
+ 0,
+ NULL);
+
+ for (Index = 0; Message[Index] != '\0'; Index++) {
+ if (Message[Index] == '\r' || Message[Index] == '\n') {
+ Message[Index] = '\0';
+ break;
+ }
+ }
+
+ return Message;
+}
+
+static const CHAR *
+FunctionName(
+ IN DI_FUNCTION Function
+ )
+{
+#define _NAME(_Function) \
+ case DIF_ ## _Function: \
+ return #_Function;
+
+ switch (Function) {
+ _NAME(INSTALLDEVICE);
+ _NAME(REMOVE);
+ _NAME(SELECTDEVICE);
+ _NAME(ASSIGNRESOURCES);
+ _NAME(PROPERTIES);
+ _NAME(FIRSTTIMESETUP);
+ _NAME(FOUNDDEVICE);
+ _NAME(SELECTCLASSDRIVERS);
+ _NAME(VALIDATECLASSDRIVERS);
+ _NAME(INSTALLCLASSDRIVERS);
+ _NAME(CALCDISKSPACE);
+ _NAME(DESTROYPRIVATEDATA);
+ _NAME(VALIDATEDRIVER);
+ _NAME(MOVEDEVICE);
+ _NAME(DETECT);
+ _NAME(INSTALLWIZARD);
+ _NAME(DESTROYWIZARDDATA);
+ _NAME(PROPERTYCHANGE);
+ _NAME(ENABLECLASS);
+ _NAME(DETECTVERIFY);
+ _NAME(INSTALLDEVICEFILES);
+ _NAME(ALLOW_INSTALL);
+ _NAME(SELECTBESTCOMPATDRV);
+ _NAME(REGISTERDEVICE);
+ _NAME(NEWDEVICEWIZARD_PRESELECT);
+ _NAME(NEWDEVICEWIZARD_SELECT);
+ _NAME(NEWDEVICEWIZARD_PREANALYZE);
+ _NAME(NEWDEVICEWIZARD_POSTANALYZE);
+ _NAME(NEWDEVICEWIZARD_FINISHINSTALL);
+ _NAME(INSTALLINTERFACES);
+ _NAME(DETECTCANCEL);
+ _NAME(REGISTER_COINSTALLERS);
+ _NAME(ADDPROPERTYPAGE_ADVANCED);
+ _NAME(ADDPROPERTYPAGE_BASIC);
+ _NAME(TROUBLESHOOTER);
+ _NAME(POWERMESSAGEWAKE);
+ default:
+ break;
+ }
+
+ return "UNKNOWN";
+
+#undef _NAME
+}
+
+static BOOLEAN
+InstallClass(
+ IN PTCHAR Class
+ )
+{
+ HKEY Key;
+ DWORD OldLength;
+ DWORD NewLength;
+ DWORD Type;
+ LONG Error;
+ PTCHAR Classes;
+ ULONG Offset;
+
+ Error = RegCreateKeyEx(HKEY_LOCAL_MACHINE,
+ PARAMETERS_KEY(XENFILT),
+ 0,
+ NULL,
+ REG_OPTION_NON_VOLATILE,
+ KEY_ALL_ACCESS,
+ NULL,
+ &Key,
+ NULL);
+ if (Error != ERROR_SUCCESS) {
+ SetLastError(Error);
+ goto fail1;
+ }
+
+ OldLength = 0;
+ Error = RegQueryValueEx(Key,
+ "UnplugClasses",
+ NULL,
+ &Type,
+ NULL,
+ &OldLength);
+ if (Error != ERROR_SUCCESS) {
+ if (Error != ERROR_FILE_NOT_FOUND) {
+ SetLastError(Error);
+ goto fail2;
+ }
+
+ OldLength = sizeof (TCHAR);
+ Type = REG_MULTI_SZ;
+ }
+
+ if (Type != REG_MULTI_SZ) {
+ SetLastError(ERROR_BAD_FORMAT);
+ goto fail3;
+ }
+
+ NewLength = OldLength + (DWORD)((strlen(Class) + 1) * sizeof (TCHAR));
+
+ Classes = malloc(NewLength);
+ if (Classes == NULL)
+ goto fail4;
+
+ memset(Classes, 0, NewLength);
+
+ Offset = 0;
+ if (OldLength != sizeof (TCHAR)) {
+ Error = RegQueryValueEx(Key,
+ "UnplugClasses",
+ NULL,
+ NULL,
+ (PBYTE)Classes,
+ &OldLength);
+ if (Error != ERROR_SUCCESS) {
+ SetLastError(Error);
+ goto fail5;
+ }
+
+ while (Classes[Offset] != '\0') {
+ ULONG ClassLength;
+
+ ClassLength = (ULONG)strlen(&Classes[Offset]) / sizeof (TCHAR);
+
+ if (_stricmp(&Classes[Offset], Class) == 0) {
+ Log("%s already present", Class);
+ goto done;
+ }
+
+ Offset += ClassLength + 1;
+ }
+ }
+
+ memmove(&Classes[Offset], Class, strlen(Class));
+ Log("added %s", Class);
+
+ Error = RegSetValueEx(Key,
+ "UnplugClasses",
+ 0,
+ Type,
+ (PBYTE)Classes,
+ NewLength);
+ if (Error != ERROR_SUCCESS) {
+ SetLastError(Error);
+ goto fail6;
+ }
+
+done:
+ free(Classes);
+
+ RegCloseKey(Key);
+
+ return TRUE;
+
+fail6:
+fail5:
+ free(Classes);
+
+fail4:
+fail3:
+fail2:
+ RegCloseKey(Key);
+
+fail1:
+ return FALSE;
+}
+
+static BOOLEAN
+RemoveClass(
+ IN PTCHAR Class
+ )
+{
+ HKEY Key;
+ DWORD OldLength;
+ DWORD NewLength;
+ DWORD Type;
+ LONG Error;
+ PTCHAR Classes;
+ ULONG Offset;
+ ULONG ClassLength;
+
+ Error = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
+ PARAMETERS_KEY(XENFILT),
+ 0,
+ KEY_ALL_ACCESS,
+ &Key);
+ if (Error != ERROR_SUCCESS) {
+ SetLastError(Error);
+ goto fail1;
+ }
+
+ OldLength = 0;
+ Error = RegQueryValueEx(Key,
+ "UnplugClasses",
+ NULL,
+ &Type,
+ NULL,
+ &OldLength);
+ if (Error != ERROR_SUCCESS) {
+ if (Error != ERROR_FILE_NOT_FOUND) {
+ SetLastError(Error);
+ goto fail2;
+ }
+
+ goto done;
+ }
+
+ if (Type != REG_MULTI_SZ) {
+ SetLastError(ERROR_BAD_FORMAT);
+ goto fail3;
+ }
+
+ Classes = malloc(OldLength);
+ if (Classes == NULL)
+ goto fail4;
+
+ memset(Classes, 0, OldLength);
+
+ Error = RegQueryValueEx(Key,
+ "UnplugClasses",
+ NULL,
+ NULL,
+ (PBYTE)Classes,
+ &OldLength);
+ if (Error != ERROR_SUCCESS) {
+ SetLastError(Error);
+ goto fail5;
+ }
+
+ Offset = 0;
+ ClassLength = 0;
+ while (Classes[Offset] != '\0') {
+ ClassLength = (ULONG)strlen(&Classes[Offset]) / sizeof (TCHAR);
+
+ if (_stricmp(&Classes[Offset], Class) == 0)
+ goto remove;
+
+ Offset += ClassLength + 1;
+ }
+
+ free(Classes);
+ goto done;
+
+remove:
+ NewLength = OldLength - ((ClassLength + 1) * sizeof (TCHAR));
+
+ memmove(&Classes[Offset],
+ &Classes[Offset + ClassLength + 1],
+ (NewLength - Offset) * sizeof (TCHAR));
+
+ Log("removed %s", Class);
+
+ if (NewLength == 1) {
+ Error = RegDeleteValue(Key,
+ "UnplugClasses");
+ } else {
+ Error = RegSetValueEx(Key,
+ "UnplugClasses",
+ 0,
+ Type,
+ (PBYTE)Classes,
+ NewLength);
+ }
+ if (Error != ERROR_SUCCESS) {
+ SetLastError(Error);
+ goto fail6;
+ }
+
+ free(Classes);
+
+done:
+ RegCloseKey(Key);
+
+ return TRUE;
+
+fail6:
+fail5:
+ free(Classes);
+
+fail4:
+fail3:
+fail2:
+ RegCloseKey(Key);
+
+fail1:
+ return FALSE;
+}
+
+static BOOLEAN
+IsClassEmulated(
+ IN PTCHAR Class,
+ OUT PBOOLEAN Present
+ )
+{
+ HKEY Key;
+ DWORD Length;
+ DWORD Type;
+ LONG Error;
+ PTCHAR Devices;
+ ULONG Count;
+ ULONG Offset;
+
+ Error = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
+ STATUS_KEY(XENFILT),
+ 0,
+ KEY_READ,
+ &Key);
+ if (Error != ERROR_SUCCESS) {
+ if (Error == ERROR_FILE_NOT_FOUND)
+ goto done;
+
+ SetLastError(Error);
+ goto fail1;
+ }
+
+ Length = 0;
+ Error = RegQueryValueEx(Key,
+ Class,
+ NULL,
+ &Type,
+ NULL,
+ &Length);
+ if (Error != ERROR_SUCCESS) {
+ if (Error == ERROR_FILE_NOT_FOUND) {
+ RegCloseKey(Key);
+ goto done;
+ }
+
+ SetLastError(Error);
+ goto fail2;
+ }
+
+ if (Type != REG_MULTI_SZ) {
+ SetLastError(ERROR_BAD_FORMAT);
+ goto fail3;
+ }
+
+ Devices = malloc(Length);
+ if (Devices == NULL)
+ goto fail4;
+
+ memset(Devices, 0, Length);
+
+ Error = RegQueryValueEx(Key,
+ Class,
+ NULL,
+ NULL,
+ (PBYTE)Devices,
+ &Length);
+ if (Error != ERROR_SUCCESS) {
+ SetLastError(Error);
+ goto fail5;
+ }
+
+ Count = 0;
+
+ Offset = 0;
+ while (Devices[Offset] != '\0') {
+ ULONG DeviceLength;
+
+ DeviceLength = (ULONG)strlen(&Devices[Offset]) / sizeof (TCHAR);
+
+ Count++;
+ Offset += DeviceLength + 1;
+ }
+
+ *Present = (Count != 0) ? TRUE : FALSE;
+
+ free(Devices);
+
+ RegCloseKey(Key);
+
+done:
+ return TRUE;
+
+fail5:
+ free(Devices);
+
+fail4:
+fail3:
+fail2:
+ RegCloseKey(Key);
+
+fail1:
+ return FALSE;
+}
+
+static BOOLEAN
+RequestReboot(
+ IN HDEVINFO DeviceInfoSet,
+ IN PSP_DEVINFO_DATA DeviceInfoData
+ )
+{
+ SP_DEVINSTALL_PARAMS DeviceInstallParams;
+
+ DeviceInstallParams.cbSize = sizeof (DeviceInstallParams);
+
+ if (!SetupDiGetDeviceInstallParams(DeviceInfoSet,
+ DeviceInfoData,
+ &DeviceInstallParams))
+ goto fail1;
+
+ DeviceInstallParams.Flags |= DI_NEEDREBOOT;
+
+ Log("Flags = %08x", DeviceInstallParams.Flags);
+
+ if (!SetupDiSetDeviceInstallParams(DeviceInfoSet,
+ DeviceInfoData,
+ &DeviceInstallParams))
+ goto fail2;
+
+ return TRUE;
+
+fail2:
+fail1:
+ return FALSE;
+}
+
+static FORCEINLINE HRESULT
+__DifInstallPreProcess(
+ IN HDEVINFO DeviceInfoSet,
+ IN PSP_DEVINFO_DATA DeviceInfoData,
+ IN PCOINSTALLER_CONTEXT_DATA Context
+ )
+{
+ UNREFERENCED_PARAMETER(DeviceInfoSet);
+ UNREFERENCED_PARAMETER(DeviceInfoData);
+ UNREFERENCED_PARAMETER(Context);
+
+ Log("<===>");
+
+ return ERROR_DI_POSTPROCESSING_REQUIRED;
+}
+
+static FORCEINLINE HRESULT
+__DifInstallPostProcess(
+ IN HDEVINFO DeviceInfoSet,
+ IN PSP_DEVINFO_DATA DeviceInfoData,
+ IN PCOINSTALLER_CONTEXT_DATA Context
+ )
+{
+ HRESULT Error;
+ BOOLEAN Present;
+ BOOLEAN Success;
+
+ Log("====>");
+
+ Error = Context->InstallResult;
+ if (Error != NO_ERROR) {
+ SetLastError(Error);
+ goto fail1;
+ }
+
+ Success = InstallClass("VIF");
+ if (!Success)
+ goto fail2;
+
+ Success = IsClassEmulated("VIF", &Present);
+ if (!Success)
+ goto fail3;
+
+ if (!Present)
+ goto done;
+
+ Success = RequestReboot(DeviceInfoSet, DeviceInfoData);
+ if (!Success)
+ goto fail4;
+
+done:
+ Log("<====");
+
+ return NO_ERROR;
+
+fail4:
+ Log("fail4");
+
+fail3:
+ Log("fail3");
+
+fail2:
+ Log("fail2");
+
+fail1:
+ Error = GetLastError();
+
+ {
+ PTCHAR Message;
+
+ Message = GetErrorMessage(Error);
+ Log("fail1 (%s)", Message);
+ LocalFree(Message);
+ }
+
+ return Error;
+}
+
+static DECLSPEC_NOINLINE HRESULT
+DifInstall(
+ IN HDEVINFO DeviceInfoSet,
+ IN PSP_DEVINFO_DATA DeviceInfoData,
+ IN PCOINSTALLER_CONTEXT_DATA Context
+ )
+{
+ SP_DEVINSTALL_PARAMS DeviceInstallParams;
+ HRESULT Error;
+
+ DeviceInstallParams.cbSize = sizeof (DeviceInstallParams);
+
+ if (!SetupDiGetDeviceInstallParams(DeviceInfoSet,
+ DeviceInfoData,
+ &DeviceInstallParams))
+ goto fail1;
+
+ Log("Flags = %08x", DeviceInstallParams.Flags);
+
+ Error = (!Context->PostProcessing) ?
+ __DifInstallPreProcess(DeviceInfoSet, DeviceInfoData, Context) :
+ __DifInstallPostProcess(DeviceInfoSet, DeviceInfoData, Context);
+
+ return Error;
+
+fail1:
+ Error = GetLastError();
+
+ {
+ PTCHAR Message;
+
+ Message = GetErrorMessage(Error);
+ Log("fail1 (%s)", Message);
+ LocalFree(Message);
+ }
+
+ return Error;
+}
+
+static FORCEINLINE HRESULT
+__DifRemovePreProcess(
+ IN HDEVINFO DeviceInfoSet,
+ IN PSP_DEVINFO_DATA DeviceInfoData,
+ IN PCOINSTALLER_CONTEXT_DATA Context
+ )
+{
+ UNREFERENCED_PARAMETER(DeviceInfoSet);
+ UNREFERENCED_PARAMETER(DeviceInfoData);
+ UNREFERENCED_PARAMETER(Context);
+
+ Log("<===>");
+
+ return ERROR_DI_POSTPROCESSING_REQUIRED;
+}
+
+static FORCEINLINE HRESULT
+__DifRemovePostProcess(
+ IN HDEVINFO DeviceInfoSet,
+ IN PSP_DEVINFO_DATA DeviceInfoData,
+ IN PCOINSTALLER_CONTEXT_DATA Context
+ )
+{
+ HRESULT Error;
+ BOOLEAN Success;
+
+ Log("====>");
+
+ Error = Context->InstallResult;
+ if (Error != NO_ERROR) {
+ SetLastError(Error);
+ goto fail1;
+ }
+
+ Success = RemoveClass("VIF");
+ if (!Success)
+ goto fail2;
+
+ Success = RequestReboot(DeviceInfoSet, DeviceInfoData);
+ if (!Success)
+ goto fail3;
+
+ Log("<====");
+
+ return NO_ERROR;
+
+fail3:
+ Log("fail3");
+
+fail2:
+ Log("fail2");
+
+fail1:
+ Error = GetLastError();
+
+ {
+ PTCHAR Message;
+
+ Message = GetErrorMessage(Error);
+ Log("fail1 (%s)", Message);
+ LocalFree(Message);
+ }
+
+ return Error;
+}
+
+static DECLSPEC_NOINLINE HRESULT
+DifRemove(
+ IN HDEVINFO DeviceInfoSet,
+ IN PSP_DEVINFO_DATA DeviceInfoData,
+ IN PCOINSTALLER_CONTEXT_DATA Context
+ )
+{
+ SP_DEVINSTALL_PARAMS DeviceInstallParams;
+ HRESULT Error;
+
+ DeviceInstallParams.cbSize = sizeof (DeviceInstallParams);
+
+ if (!SetupDiGetDeviceInstallParams(DeviceInfoSet,
+ DeviceInfoData,
+ &DeviceInstallParams))
+ goto fail1;
+
+ Log("Flags = %08x", DeviceInstallParams.Flags);
+
+ Error = (!Context->PostProcessing) ?
+ __DifRemovePreProcess(DeviceInfoSet, DeviceInfoData, Context) :
+ __DifRemovePostProcess(DeviceInfoSet, DeviceInfoData, Context);
+
+ return Error;
+
+fail1:
+ Error = GetLastError();
+
+ {
+ PTCHAR Message;
+
+ Message = GetErrorMessage(Error);
+ Log("fail1 (%s)", Message);
+ LocalFree(Message);
+ }
+
+ return Error;
+}
+
+DWORD CALLBACK
+Entry(
+ IN DI_FUNCTION Function,
+ IN HDEVINFO DeviceInfoSet,
+ IN PSP_DEVINFO_DATA DeviceInfoData,
+ IN PCOINSTALLER_CONTEXT_DATA Context
+ )
+{
+ HRESULT Error;
+
+ Log("%s (%s) ===>",
+ MAJOR_VERSION_STR "." MINOR_VERSION_STR "." MICRO_VERSION_STR "." BUILD_NUMBER_STR,
+ DAY_STR "/" MONTH_STR "/" YEAR_STR);
+
+ switch (Function) {
+ case DIF_INSTALLDEVICE: {
+ SP_DRVINFO_DATA DriverInfoData;
+ BOOLEAN DriverInfoAvailable;
+
+ DriverInfoData.cbSize = sizeof (DriverInfoData);
+ DriverInfoAvailable = SetupDiGetSelectedDriver(DeviceInfoSet,
+ DeviceInfoData,
+ &DriverInfoData) ?
+ TRUE :
+ FALSE;
+
+ // If there is no driver information then the NULL driver is being
+ // installed. Treat this as we would a DIF_REMOVE.
+ Error = (DriverInfoAvailable) ?
+ DifInstall(DeviceInfoSet, DeviceInfoData, Context) :
+ DifRemove(DeviceInfoSet, DeviceInfoData, Context);
+ break;
+ }
+ case DIF_REMOVE:
+ Error = DifRemove(DeviceInfoSet, DeviceInfoData, Context);
+ break;
+ default:
+ if (!Context->PostProcessing) {
+ Log("%s PreProcessing",
+ FunctionName(Function));
+
+ Error = NO_ERROR;
+ } else {
+ Log("%s PostProcessing (%08x)",
+ FunctionName(Function),
+ Context->InstallResult);
+
+ Error = Context->InstallResult;
+ }
+
+ break;
+ }
+
+ Log("%s (%s) <===",
+ MAJOR_VERSION_STR "." MINOR_VERSION_STR "." MICRO_VERSION_STR "." BUILD_NUMBER_STR,
+ DAY_STR "/" MONTH_STR "/" YEAR_STR);
+
+ return (DWORD)Error;
+}
+
+DWORD CALLBACK
+Version(
+ IN HWND Window,
+ IN HINSTANCE Module,
+ IN PTCHAR Buffer,
+ IN INT Reserved
+ )
+{
+ UNREFERENCED_PARAMETER(Window);
+ UNREFERENCED_PARAMETER(Module);
+ UNREFERENCED_PARAMETER(Buffer);
+ UNREFERENCED_PARAMETER(Reserved);
+
+ Log("%s (%s)",
+ MAJOR_VERSION_STR "." MINOR_VERSION_STR "." MICRO_VERSION_STR "." BUILD_NUMBER_STR,
+ DAY_STR "/" MONTH_STR "/" YEAR_STR);
+
+ return NO_ERROR;
+}
+
+static FORCEINLINE const CHAR *
+__ReasonName(
+ IN DWORD Reason
+ )
+{
+#define _NAME(_Reason) \
+ case DLL_ ## _Reason: \
+ return #_Reason;
+
+ switch (Reason) {
+ _NAME(PROCESS_ATTACH);
+ _NAME(PROCESS_DETACH);
+ _NAME(THREAD_ATTACH);
+ _NAME(THREAD_DETACH);
+ default:
+ break;
+ }
+
+ return "UNKNOWN";
+
+#undef _NAME
+}
+
+BOOL WINAPI
+DllMain(
+ IN HINSTANCE Module,
+ IN DWORD Reason,
+ IN PVOID Reserved
+ )
+{
+ UNREFERENCED_PARAMETER(Module);
+ UNREFERENCED_PARAMETER(Reserved);
+
+ Log("%s (%s): %s",
+ MAJOR_VERSION_STR "." MINOR_VERSION_STR "." MICRO_VERSION_STR "." BUILD_NUMBER_STR,
+ DAY_STR "/" MONTH_STR "/" YEAR_STR,
+ __ReasonName(Reason));
+
+ return TRUE;
+}
--- /dev/null
+; 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
--- /dev/null
+; Copyright 2011 Citrix Systems Inc. All rights reserved.
+; Use is subject to license terms.
+;
+
+[Version]
+Signature="$Windows NT$"
+Class=System
+ClassGUID={4d36e97d-e325-11ce-bfc1-08002be10318}
+Provider=%Citrix%
+CatalogFile=xenvif.cat
+DriverVer=01/01/1900,0.0.0.0
+
+[DestinationDirs]
+DefaultDestDir=12
+CoInst_CopyFiles=11
+
+[SourceDisksNames]
+0=%DiskDesc%
+
+[SourceDisksFiles]
+xenvif.sys=0,,
+xenvif_coinst.dll=0,,
+
+[CoInst_CopyFiles]
+xenvif_coinst_@MAJOR_VERSION@_@MINOR_VERSION@_@MICRO_VERSION@_@BUILD_NUMBER@.dll,xenvif_coinst.dll
+
+[Manufacturer]
+%Citrix%=Citrix,NT$ARCH$
+
+[Citrix.NT$ARCH$]
+%DriverDesc%=XenVif_Inst,XENBUS\CLASS_VIF&REV_02
+
+[XenVif_Inst]
+CopyFiles=XenVif_Copyfiles
+
+[XenVif_Copyfiles]
+xenvif.sys
+
+[XenVif_Inst.Services]
+AddService=xenvif,0x02,XenVif_Service,
+
+[XenVif_Service]
+ServiceType=%SERVICE_KERNEL_DRIVER%
+StartType=%SERVICE_DEMAND_START%
+ErrorControl=%SERVICE_ERROR_NORMAL%
+ServiceBinary=%12%\xenvif.sys
+LoadOrderGroup="NDIS"
+AddReg = XenVif_Parameters
+
+[XenVif_Parameters]
+HKR,"Parameters","ReceiverMaximumProtocol",0x00010001,0x00000000
+
+[XenVif_Inst.CoInstallers]
+CopyFiles=CoInst_CopyFiles
+AddReg=CoInst_AddReg
+
+[CoInst_AddReg]
+HKR,,CoInstallers32,0x00010000,"xenvif_coinst_@MAJOR_VERSION@_@MINOR_VERSION@_@MICRO_VERSION@_@BUILD_NUMBER@.dll,Entry"
+
+[Strings]
+
+Citrix="Citrix Systems Inc."
+DiskDesc="Citrix Tools for Virtual Machines"
+DriverDesc="Citrix VIF Class"
+
+SERVICE_BOOT_START=0x0
+SERVICE_SYSTEM_START=0x1
+SERVICE_AUTO_START=0x2
+SERVICE_DEMAND_START=0x3
+SERVICE_DISABLED=0x4
+
+SERVICE_KERNEL_DRIVER=0x1
+SERVICE_ERROR_IGNORE=0x0
+SERVICE_ERROR_NORMAL=0x1
+SERVICE_ERROR_SEVERE=0x2
+SERVICE_ERROR_CRITICAL=0x3
--- /dev/null
+/* Copyright (c) Citrix Systems Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms,
+ * with or without modification, are permitted provided
+ * that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer in the documentation and/or other
+ * materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef _XENVIF_ASSERT_H
+#define _XENVIF_ASSERT_H
+
+#include <ntddk.h>
+
+#include "log.h"
+
+static FORCEINLINE VOID
+__BugCheck(
+ IN ULONG Code,
+ IN ULONG_PTR Parameter1,
+ IN ULONG_PTR Parameter2,
+ IN ULONG_PTR Parameter3,
+ IN ULONG_PTR Parameter4
+ )
+{
+#pragma prefast(suppress:28159)
+ KeBugCheckEx(Code,
+ Parameter1,
+ Parameter2,
+ Parameter3,
+ Parameter4);
+}
+
+#define ASSERTION_FAILURE 0x0000DEAD
+
+#define BUG(_TEXT) \
+ do { \
+ const CHAR *_Text = (_TEXT); \
+ const CHAR *_File = __FILE__; \
+ ULONG _Line = __LINE__; \
+ \
+ Error("BUG: " _TEXT "\n"); \
+ __BugCheck(ASSERTION_FAILURE, \
+ (ULONG_PTR)_Text, \
+ (ULONG_PTR)_File, \
+ (ULONG_PTR)_Line, \
+ 0); \
+ } while (FALSE)
+
+#define BUG_ON(_EXP) \
+ if (_EXP) BUG(#_EXP)
+
+#if DBG
+
+#define __NT_ASSERT(_EXP) \
+ ((!(_EXP)) ? \
+ (Error("ASSERTION FAILED: " #_EXP "\n"), \
+ __annotation(L"Debug", L"AssertFail", L#_EXP), \
+ DbgRaiseAssertionFailure(), FALSE) : \
+ TRUE)
+
+#define __ASSERT(_EXP) __NT_ASSERT(_EXP)
+
+#else // DBG
+
+#define __ASSERT(_EXP) BUG_ON(!(_EXP))
+
+#endif // DBG
+
+#undef ASSERT
+
+#define ASSERT(_EXP) \
+ do { \
+ __ASSERT(_EXP); \
+ __analysis_assume(_EXP); \
+ } while (FALSE)
+
+#define ASSERT3U(_X, _OP, _Y) \
+ do { \
+ ULONGLONG _Lval = (ULONGLONG)(_X); \
+ ULONGLONG _Rval = (ULONGLONG)(_Y); \
+ if (!(_Lval _OP _Rval)) { \
+ Error("%s = %llu\n", #_X, _Lval); \
+ Error("%s = %llu\n", #_Y, _Rval); \
+ ASSERT(_X _OP _Y); \
+ } \
+ } while (FALSE)
+
+#define ASSERT3S(_X, _OP, _Y) \
+ do { \
+ LONGLONG _Lval = (LONGLONG)(_X); \
+ LONGLONG _Rval = (LONGLONG)(_Y); \
+ if (!(_Lval _OP _Rval)) { \
+ Error("%s = %lld\n", #_X, _Lval); \
+ Error("%s = %lld\n", #_Y, _Rval); \
+ ASSERT(_X _OP _Y); \
+ } \
+ } while (FALSE)
+
+#define ASSERT3P(_X, _OP, _Y) \
+ do { \
+ PVOID _Lval = (PVOID)(_X); \
+ PVOID _Rval = (PVOID)(_Y); \
+ if (!(_Lval _OP _Rval)) { \
+ Error("%s = %p\n", #_X, _Lval); \
+ Error("%s = %p\n", #_Y, _Rval); \
+ ASSERT(_X _OP _Y); \
+ } \
+ } while (FALSE)
+
+#ifndef TEST_MEMORY
+#define TEST_MEMORY DBG
+#endif
+
+#if TEST_MEMORY
+
+static __inline BOOLEAN
+_IsZeroMemory(
+ IN const PCHAR Caller,
+ IN const PCHAR Name,
+ IN PVOID Buffer,
+ IN ULONG Length
+ )
+{
+ ULONG Offset;
+
+ Offset = 0;
+ while (Offset < Length) {
+ if (*((PUCHAR)Buffer + Offset) != 0) {
+ Error("%s: non-zero byte in %s (0x%p+0x%x)\n", Caller, Name, Buffer, Offset);
+ return FALSE;
+ }
+ Offset++;
+ }
+
+ return TRUE;
+}
+
+#define IsZeroMemory(_Buffer, _Length) \
+ _IsZeroMemory(__FUNCTION__, #_Buffer, (_Buffer), (_Length))
+
+#else // TEST_MEMORY
+
+#define IsZeroMemory(_Buffer, _Length) TRUE
+
+#endif // TEST_MEMORY
+
+#define IMPLY(_X, _Y) (!(_X) || (_Y))
+#define EQUIV(_X, _Y) (IMPLY((_X), (_Y)) && IMPLY((_Y), (_X)))
+
+#endif // _XENVIF_ASSERT_H
+
--- /dev/null
+/* Copyright (c) Citrix Systems Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms,
+ * with or without modification, are permitted provided
+ * that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer in the documentation and/or other
+ * materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <ntddk.h>
+#include <ntstrsafe.h>
+#include <stdlib.h>
+#include <util.h>
+#include <ethernet.h>
+#include <tcpip.h>
+#include <vif_interface.h>
+
+#include "checksum.h"
+#include "log.h"
+#include "assert.h"
+
+static FORCEINLINE VOID
+__AccumulateChecksum(
+ IN OUT PULONG Accumulator,
+ IN PVOID MappedSystemVa,
+ IN ULONG ByteCount
+ )
+{
+ PUSHORT Word;
+
+ Word = (PUSHORT)MappedSystemVa;
+ while (ByteCount > 1) {
+ *Accumulator += *Word++;
+ ByteCount -= 2;
+ }
+
+ if (ByteCount != 0) {
+ UCHAR Extra[2];
+
+ Extra[0] = *((PUCHAR)Word);
+ Extra[1] = 0;
+
+ Word = (PUSHORT)Extra;
+ *Accumulator += *Word;
+ }
+}
+
+VOID
+AccumulateChecksum(
+ IN OUT PULONG Accumulator,
+ IN PVOID MappedSystemVa,
+ IN ULONG ByteCount
+ )
+{
+ __AccumulateChecksum(Accumulator, MappedSystemVa, ByteCount);
+}
+
+static FORCEINLINE USHORT
+__FoldChecksum(
+ IN ULONG Accumulator,
+ IN BOOLEAN Invert
+ )
+{
+ Accumulator = (Accumulator & 0xFFFF) + (Accumulator >> 16);
+ Accumulator += Accumulator >> 16;
+
+ return (USHORT)((Invert) ? ~Accumulator : Accumulator);
+}
+
+USHORT
+FoldChecksum(
+ IN ULONG Accumulator,
+ IN BOOLEAN Invert
+ )
+{
+ return __FoldChecksum(Accumulator, Invert);
+}
+
+static FORCEINLINE USHORT
+__ChecksumIpVersion4PseudoHeader(
+ IN PIPV4_ADDRESS SourceAddress,
+ IN PIPV4_ADDRESS DestinationAddress,
+ IN USHORT Length,
+ IN UCHAR Protocol
+ )
+{
+ IPV4_PSEUDO_HEADER Header;
+ ULONG Accumulator;
+
+ RtlZeroMemory(&Header, sizeof (IPV4_PSEUDO_HEADER));
+
+ Header.SourceAddress = *SourceAddress;
+ Header.DestinationAddress = *DestinationAddress;
+ Header.Length = HTONS(Length);
+ Header.Protocol = Protocol;
+
+ Accumulator = 0;
+ __AccumulateChecksum(&Accumulator, (PUCHAR)&Header, sizeof (IPV4_PSEUDO_HEADER));
+
+ return __FoldChecksum(Accumulator, FALSE);
+}
+
+USHORT
+ChecksumIpVersion4PseudoHeader(
+ IN PIPV4_ADDRESS SourceAddress,
+ IN PIPV4_ADDRESS DestinationAddress,
+ IN USHORT Length,
+ IN UCHAR Protocol
+ )
+{
+ return __ChecksumIpVersion4PseudoHeader(SourceAddress,
+ DestinationAddress,
+ Length,
+ Protocol);
+}
+
+static FORCEINLINE USHORT
+__ChecksumIpVersion6PseudoHeader(
+ IN PIPV6_ADDRESS SourceAddress,
+ IN PIPV6_ADDRESS DestinationAddress,
+ IN USHORT Length,
+ IN UCHAR Protocol
+ )
+{
+ IPV6_PSEUDO_HEADER Header;
+ ULONG Accumulator;
+
+ RtlZeroMemory(&Header, sizeof (IPV6_PSEUDO_HEADER));
+
+ Header.SourceAddress = *SourceAddress;
+ Header.DestinationAddress = *DestinationAddress;
+ Header.Length = HTONS(Length);
+ Header.NextHeader = Protocol;
+
+ Accumulator = 0;
+ __AccumulateChecksum(&Accumulator, (PUCHAR)&Header, sizeof (IPV6_PSEUDO_HEADER));
+
+ return __FoldChecksum(Accumulator, FALSE);
+}
+
+USHORT
+ChecksumIpVersion6PseudoHeader(
+ IN PIPV6_ADDRESS SourceAddress,
+ IN PIPV6_ADDRESS DestinationAddress,
+ IN USHORT Length,
+ IN UCHAR Protocol
+ )
+{
+ return __ChecksumIpVersion6PseudoHeader(SourceAddress,
+ DestinationAddress,
+ Length,
+ Protocol);
+}
+
+USHORT
+ChecksumPseudoHeader(
+ IN PUCHAR StartVa,
+ IN PXENVIF_PACKET_INFO Info
+ )
+{
+ PIP_HEADER Header;
+ UCHAR Protocol;
+ USHORT Checksum;
+
+ ASSERT(Info->IpHeader.Length != 0);
+ Header = (PIP_HEADER)(StartVa + Info->IpHeader.Offset);
+
+ if (Info->TcpHeader.Length != 0) {
+ Protocol = IPPROTO_TCP;
+ } else {
+ ASSERT(Info->UdpHeader.Length != 0);
+ Protocol = IPPROTO_UDP;
+ }
+
+ if (Header->Version == 4) {
+ USHORT Length;
+
+ Length = NTOHS(Header->Version4.PacketLength) -
+ sizeof (IPV4_HEADER) -
+ (USHORT)Info->IpOptions.Length;
+
+ Checksum = __ChecksumIpVersion4PseudoHeader(&Header->Version4.SourceAddress,
+ &Header->Version4.DestinationAddress,
+ Length,
+ Protocol);
+ } else {
+ USHORT Length;
+
+ ASSERT3U(Header->Version, ==, 6);
+
+ Length = NTOHS(Header->Version6.PayloadLength) -
+ (USHORT)Info->IpOptions.Length;
+
+ Checksum = __ChecksumIpVersion6PseudoHeader(&Header->Version6.SourceAddress,
+ &Header->Version6.DestinationAddress,
+ Length,
+ Protocol);
+ }
+
+ return Checksum;
+}
+
+USHORT
+ChecksumIpVersion4Header(
+ IN PUCHAR StartVa,
+ IN PXENVIF_PACKET_INFO Info
+ )
+{
+ ULONG Accumulator;
+ PIPV4_HEADER Header;
+ USHORT Saved;
+
+ ASSERT(Info->IpHeader.Length != 0);
+ Header = (PIPV4_HEADER)(StartVa + Info->IpHeader.Offset);
+
+ ASSERT3U(Header->Version, ==, 4);
+
+ Saved = Header->Checksum;
+ Header->Checksum = 0;
+
+ Accumulator = 0;
+ __AccumulateChecksum(&Accumulator,
+ StartVa + Info->IpHeader.Offset,
+ Info->IpHeader.Length);
+
+ Header->Checksum = Saved;
+
+ if (Info->IpOptions.Length != 0)
+ __AccumulateChecksum(&Accumulator,
+ StartVa + Info->IpOptions.Offset,
+ Info->IpOptions.Length);
+
+ return __FoldChecksum(Accumulator, TRUE);
+}
+
+USHORT
+ChecksumTcpPacket(
+ IN PUCHAR StartVa,
+ IN PXENVIF_PACKET_INFO Info,
+ IN USHORT PseudoHeaderChecksum,
+ IN PXENVIF_PACKET_PAYLOAD Payload
+ )
+{
+ ULONG Accumulator;
+ PIP_HEADER IpHeader;
+ PTCP_HEADER TcpHeader;
+ USHORT Saved;
+ PMDL Mdl;
+ ULONG Offset;
+ ULONG Length;
+
+ ASSERT(Info->IpHeader.Length != 0);
+ IpHeader = (PIP_HEADER)(StartVa + Info->IpHeader.Offset);
+
+ ASSERT(Info->TcpHeader.Length != 0);
+ TcpHeader = (PTCP_HEADER)(StartVa + Info->TcpHeader.Offset);
+
+ Saved = TcpHeader->Checksum;
+ TcpHeader->Checksum = 0;
+
+ Accumulator = PseudoHeaderChecksum;
+ __AccumulateChecksum(&Accumulator,
+ StartVa + Info->TcpHeader.Offset,
+ Info->TcpHeader.Length);
+
+ TcpHeader->Checksum = Saved;
+
+ if (Info->TcpOptions.Length != 0)
+ __AccumulateChecksum(&Accumulator,
+ StartVa + Info->TcpOptions.Offset,
+ Info->TcpOptions.Length);
+
+ Mdl = Payload->Mdl;
+ Offset = Payload->Offset;
+
+ if (IpHeader->Version == 4) {
+ PIPV4_HEADER Version4 = &IpHeader->Version4;
+
+ Length = NTOHS(Version4->PacketLength) -
+ sizeof (IPV4_HEADER) -
+ (USHORT)Info->IpOptions.Length;
+ } else {
+ PIPV6_HEADER Version6 = &IpHeader->Version6;
+
+ Length = NTOHS(Version6->PayloadLength) -
+ (USHORT)Info->IpOptions.Length;
+ }
+
+ Length -= Info->TcpHeader.Length;
+ Length -= Info->TcpOptions.Length;
+ Length = __min(Length, Payload->Length);
+
+ while (Length != 0) {
+ PUCHAR MappedSystemVa;
+ ULONG ByteCount;
+
+ ASSERT(Mdl != NULL);
+
+ MappedSystemVa = MmGetSystemAddressForMdlSafe(Mdl, NormalPagePriority);
+ MappedSystemVa += Offset;
+
+ ByteCount = Mdl->ByteCount;
+ ASSERT3U(Offset, <=, ByteCount);
+ ByteCount -= Offset;
+ ByteCount = __min(ByteCount, Length);
+
+ __AccumulateChecksum(&Accumulator, MappedSystemVa, ByteCount);
+
+ Length -= ByteCount;
+
+ Mdl = Mdl->Next;
+ Offset = 0;
+ }
+
+ return __FoldChecksum(Accumulator, TRUE);
+}
+
+USHORT
+ChecksumUdpPacket(
+ IN PUCHAR StartVa,
+ IN PXENVIF_PACKET_INFO Info,
+ IN USHORT PseudoHeaderChecksum,
+ IN PXENVIF_PACKET_PAYLOAD Payload
+ )
+{
+ ULONG Accumulator;
+ PIP_HEADER IpHeader;
+ PUDP_HEADER UdpHeader;
+ USHORT Saved;
+ PMDL Mdl;
+ ULONG Offset;
+ ULONG Length;
+
+ ASSERT(Info->IpHeader.Length != 0);
+ IpHeader = (PIP_HEADER)(StartVa + Info->IpHeader.Offset);
+
+ ASSERT(Info->UdpHeader.Length != 0);
+ UdpHeader = (PUDP_HEADER)(StartVa + Info->UdpHeader.Offset);
+
+ Saved = UdpHeader->Checksum;
+ UdpHeader->Checksum = 0;
+
+ Accumulator = PseudoHeaderChecksum;
+ __AccumulateChecksum(&Accumulator,
+ StartVa + Info->UdpHeader.Offset,
+ Info->UdpHeader.Length);
+
+ UdpHeader->Checksum = Saved;
+
+ Mdl = Payload->Mdl;
+ Offset = Payload->Offset;
+
+ if (IpHeader->Version == 4) {
+ PIPV4_HEADER Version4 = &IpHeader->Version4;
+
+ Length = NTOHS(Version4->PacketLength) -
+ sizeof (IPV4_HEADER) -
+ (USHORT)Info->IpOptions.Length;
+ } else {
+ PIPV6_HEADER Version6 = &IpHeader->Version6;
+
+ Length = NTOHS(Version6->PayloadLength) -
+ (USHORT)Info->IpOptions.Length;
+ }
+
+ Length -= Info->UdpHeader.Length;
+ Length = __min(Length, Payload->Length);
+
+ while (Length != 0) {
+ PUCHAR MappedSystemVa;
+ ULONG ByteCount;
+
+ ASSERT(Mdl != NULL);
+
+ MappedSystemVa = MmGetSystemAddressForMdlSafe(Mdl, NormalPagePriority);
+ MappedSystemVa += Offset;
+
+ ByteCount = Mdl->ByteCount;
+ ASSERT3U(Offset, <=, ByteCount);
+ ByteCount -= Offset;
+ ByteCount = __min(ByteCount, Length);
+
+ __AccumulateChecksum(&Accumulator, MappedSystemVa, ByteCount);
+
+ Length -= ByteCount;
+
+ Mdl = Mdl->Next;
+ Offset = 0;
+ }
+
+ return __FoldChecksum(Accumulator, TRUE);
+}
--- /dev/null
+/* Copyright (c) Citrix Systems Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms,
+ * with or without modification, are permitted provided
+ * that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer in the documentation and/or other
+ * materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef _XENVIF_CHECKSUM_H
+#define _XENVIF_CHECKSUM_H
+
+#include "parse.h"
+
+extern VOID
+AccumulateChecksum(
+ IN OUT PULONG Accumulator,
+ IN PVOID MappedSystemVa,
+ IN ULONG ByteCount
+ );
+
+extern USHORT
+FoldChecksum(
+ IN ULONG Accumulator,
+ IN BOOLEAN Invert
+ );
+
+extern USHORT
+ChecksumIpVersion4Header(
+ IN PUCHAR StartVa,
+ IN PXENVIF_PACKET_INFO Info
+ );
+
+extern USHORT
+ChecksumIpVersion4PseudoHeader(
+ IN PIPV4_ADDRESS SourceAddress,
+ IN PIPV4_ADDRESS DestinationAddress,
+ IN USHORT Length,
+ IN UCHAR Protocol
+ );
+
+extern USHORT
+ChecksumIpVersion6PseudoHeader(
+ IN PIPV6_ADDRESS SourceAddress,
+ IN PIPV6_ADDRESS DestinationAddress,
+ IN USHORT Length,
+ IN UCHAR Protocol
+ );
+
+extern USHORT
+ChecksumPseudoHeader(
+ IN PUCHAR StartVa,
+ IN PXENVIF_PACKET_INFO Info
+ );
+
+extern USHORT
+ChecksumTcpPacket(
+ IN PUCHAR StartVa,
+ IN PXENVIF_PACKET_INFO Info,
+ IN USHORT PseudoHeaderChecksum,
+ IN PXENVIF_PACKET_PAYLOAD Payload
+ );
+
+extern USHORT
+ChecksumUdpPacket(
+ IN PUCHAR StartVa,
+ IN PXENVIF_PACKET_INFO Info,
+ IN USHORT PseudoHeaderChecksum,
+ IN PXENVIF_PACKET_PAYLOAD Payload
+ );
+
+#endif // _XENVIF_CHECKSUM_H
--- /dev/null
+/* Copyright (c) Citrix Systems Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms,
+ * with or without modification, are permitted provided
+ * that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer in the documentation and/or other
+ * materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <ntddk.h>
+#include <util.h>
+#include <version.h>
+
+#include "registry.h"
+#include "fdo.h"
+#include "pdo.h"
+#include "receiver.h"
+#include "driver.h"
+#include "log.h"
+#include "assert.h"
+
+extern PULONG InitSafeBootMode;
+
+PDRIVER_OBJECT DriverObject;
+
+XENVIF_PARAMETERS DriverParameters;
+
+DRIVER_UNLOAD DriverUnload;
+
+VOID
+DriverUnload(
+ IN PDRIVER_OBJECT _DriverObject
+ )
+{
+ ASSERT3P(_DriverObject, ==, DriverObject);
+
+ Trace("====>\n");
+
+ if (*InitSafeBootMode > 0)
+ goto done;
+
+ RegistryFreeSzValue(DriverParameters.UnsupportedDevices);
+
+ RegistryTeardown();
+
+done:
+ DriverObject = NULL;
+
+ Trace("<====\n");
+}
+
+DRIVER_ADD_DEVICE AddDevice;
+
+NTSTATUS
+AddDevice(
+ IN PDRIVER_OBJECT _DriverObject,
+ IN PDEVICE_OBJECT DeviceObject
+ )
+{
+ NTSTATUS status;
+
+ ASSERT3P(_DriverObject, ==, DriverObject);
+
+ status = FdoCreate(DeviceObject);
+ if (!NT_SUCCESS(status))
+ goto fail1;
+
+ // prefast stupidity
+ ASSERT(!(DeviceObject->Flags & DO_DEVICE_INITIALIZING));
+ DeviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
+
+ return STATUS_SUCCESS;
+
+fail1:
+ Error("fail1 (%08x)\n", status);
+
+ return status;
+}
+
+DRIVER_DISPATCH Dispatch;
+
+NTSTATUS
+Dispatch(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp
+ )
+{
+ PXENVIF_DX Dx;
+ NTSTATUS status;
+
+ Dx = (PXENVIF_DX)DeviceObject->DeviceExtension;
+ ASSERT3P(Dx->DeviceObject, ==, DeviceObject);
+
+ if (Dx->DevicePnpState == Deleted) {
+ status = STATUS_NO_SUCH_DEVICE;
+
+ Irp->IoStatus.Status = status;
+ IoCompleteRequest(Irp, IO_NO_INCREMENT);
+ goto done;
+ }
+
+ status = STATUS_NOT_SUPPORTED;
+ switch (Dx->Type) {
+ case PHYSICAL_DEVICE_OBJECT: {
+ PXENVIF_PDO Pdo = Dx->Pdo;
+
+ status = PdoDispatch(Pdo, Irp);
+ break;
+ }
+ case FUNCTION_DEVICE_OBJECT: {
+ PXENVIF_FDO Fdo = Dx->Fdo;
+
+ status = FdoDispatch(Fdo, Irp);
+ break;
+ }
+ default:
+ ASSERT(FALSE);
+ break;
+ }
+
+done:
+ return status;
+}
+
+DRIVER_INITIALIZE DriverEntry;
+
+NTSTATUS
+DriverEntry(
+ IN PDRIVER_OBJECT _DriverObject,
+ IN PUNICODE_STRING RegistryPath
+ )
+{
+ HANDLE Key;
+ ULONG Index;
+ NTSTATUS status;
+
+ ASSERT3P(DriverObject, ==, NULL);
+
+ ExInitializeDriverRuntime(DrvRtPoolNxOptIn);
+
+ Trace("====>\n");
+
+ Info("%s (%s)\n",
+ MAJOR_VERSION_STR "." MINOR_VERSION_STR "." MICRO_VERSION_STR "." BUILD_NUMBER_STR,
+ DAY_STR "/" MONTH_STR "/" YEAR_STR);
+
+ DriverObject = _DriverObject;
+ DriverObject->DriverUnload = DriverUnload;
+
+ if (*InitSafeBootMode > 0)
+ goto done;
+
+ status = RegistryInitialize(RegistryPath);
+ if (!NT_SUCCESS(status))
+ goto fail1;
+
+ status = RegistryOpenSubKey("Parameters", KEY_READ, &Key);
+ if (NT_SUCCESS(status)) {
+ status = RegistryQuerySzValue(Key,
+ "UnsupportedDevices",
+ &DriverParameters.UnsupportedDevices);
+ if (!NT_SUCCESS(status))
+ DriverParameters.UnsupportedDevices = NULL;
+
+ status = RegistryQueryDwordValue(Key,
+ "ReceiverMaximumProtocol",
+ &DriverParameters.ReceiverMaximumProtocol);
+ if (!NT_SUCCESS(status))
+ DriverParameters.ReceiverMaximumProtocol = 0;
+
+ status = RegistryQueryDwordValue(Key,
+ "ReceiverCalculateChecksums",
+ &DriverParameters.ReceiverCalculateChecksums);
+ if (!NT_SUCCESS(status))
+ DriverParameters.ReceiverCalculateChecksums = 0;
+
+ status = RegistryQueryDwordValue(Key,
+ "ReceiverAllowGsoPackets",
+ &DriverParameters.ReceiverAllowGsoPackets);
+ if (!NT_SUCCESS(status))
+ DriverParameters.ReceiverAllowGsoPackets = 1;
+
+ status = RegistryQueryDwordValue(Key,
+ "ReceiverIpAlignOffset",
+ &DriverParameters.ReceiverIpAlignOffset);
+ if (!NT_SUCCESS(status))
+ DriverParameters.ReceiverIpAlignOffset = 0;
+
+ status = RegistryQueryDwordValue(Key,
+ "CreatePDOs",
+ &DriverParameters.CreatePDOs);
+ if (!NT_SUCCESS(status))
+ DriverParameters.CreatePDOs = 1;
+
+ RegistryCloseKey(Key);
+ }
+
+ DriverObject->DriverExtension->AddDevice = AddDevice;
+
+ for (Index = 0; Index <= IRP_MJ_MAXIMUM_FUNCTION; Index++) {
+#pragma prefast(suppress:28169) // No __drv_dispatchType annotation
+#pragma prefast(suppress:28168) // No matching __drv_dispatchType annotation for IRP_MJ_CREATE
+ DriverObject->MajorFunction[Index] = Dispatch;
+ }
+
+done:
+ Trace("<====\n");
+
+ return STATUS_SUCCESS;
+
+fail1:
+ Error("fail1 (%08x)\n", status);
+
+ return status;
+}
--- /dev/null
+/* Copyright (c) Citrix Systems Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms,
+ * with or without modification, are permitted provided
+ * that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer in the documentation and/or other
+ * materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef _XENVIF_DRIVER_H
+#define _XENVIF_DRIVER_H
+
+typedef struct _XENVIF_PDO XENVIF_PDO, *PXENVIF_PDO;
+typedef struct _XENVIF_FDO XENVIF_FDO, *PXENVIF_FDO;
+
+#include "fdo.h"
+#include "pdo.h"
+
+extern PDRIVER_OBJECT DriverObject;
+
+typedef struct _XENVIF_PARAMETERS {
+ PANSI_STRING UnsupportedDevices;
+ ULONG ReceiverMaximumProtocol;
+ ULONG ReceiverCalculateChecksums;
+ ULONG ReceiverAllowGsoPackets;
+ ULONG ReceiverIpAlignOffset;
+ ULONG CreatePDOs;
+} XENVIF_PARAMETERS, *PXENVIF_PARAMETERS;
+
+extern XENVIF_PARAMETERS DriverParameters;
+
+#define MAX_DEVICE_ID_LEN 200
+
+#pragma warning(push)
+#pragma warning(disable:4201) // nonstandard extension used : nameless struct/union
+
+typedef struct _XENVIF_DX {
+ PDEVICE_OBJECT DeviceObject;
+ DEVICE_OBJECT_TYPE Type;
+
+ DEVICE_PNP_STATE DevicePnpState;
+ DEVICE_PNP_STATE PreviousDevicePnpState;
+
+ SYSTEM_POWER_STATE SystemPowerState;
+ DEVICE_POWER_STATE DevicePowerState;
+
+ CHAR Name[MAX_DEVICE_ID_LEN];
+
+ LIST_ENTRY ListEntry;
+
+ union {
+ PXENVIF_FDO Fdo;
+ PXENVIF_PDO Pdo;
+ };
+} XENVIF_DX, *PXENVIF_DX;
+
+#pragma warning(pop)
+
+#endif // _XENVIF_DRIVER_H
--- /dev/null
+/* Copyright (c) Citrix Systems Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms,
+ * with or without modification, are permitted provided
+ * that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer in the documentation and/or other
+ * materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#define INITGUID 1
+
+#include <ntddk.h>
+#include <wdmguid.h>
+#include <ntstrsafe.h>
+#include <stdlib.h>
+#include <util.h>
+
+#include <evtchn_interface.h>
+#include <debug_interface.h>
+#include <store_interface.h>
+#include <gnttab_interface.h>
+#include <suspend_interface.h>
+#include <emulated_interface.h>
+
+#include "driver.h"
+#include "registry.h"
+#include "fdo.h"
+#include "pdo.h"
+#include "thread.h"
+#include "mutex.h"
+#include "frontend.h"
+#include "names.h"
+#include "log.h"
+#include "assert.h"
+
+#define FDO_POOL 'ODF'
+
+#define MAXNAMELEN 128
+
+typedef enum _FDO_RESOURCE_TYPE {
+ MEMORY_RESOURCE = 0,
+ INTERRUPT_RESOURCE,
+ RESOURCE_COUNT
+} FDO_RESOURCE_TYPE, *PFDO_RESOURCE_TYPE;
+
+typedef struct _FDO_RESOURCE {
+ CM_PARTIAL_RESOURCE_DESCRIPTOR Raw;
+ CM_PARTIAL_RESOURCE_DESCRIPTOR Translated;
+} FDO_RESOURCE, *PFDO_RESOURCE;
+
+struct _XENVIF_FDO {
+ PXENVIF_DX Dx;
+ PDEVICE_OBJECT LowerDeviceObject;
+ PDEVICE_OBJECT PhysicalDeviceObject;
+ DEVICE_CAPABILITIES LowerDeviceCapabilities;
+ ULONG Usage[DeviceUsageTypeDumpFile + 1];
+ BOOLEAN NotDisableable;
+
+ PXENVIF_THREAD SystemPowerThread;
+ PIRP SystemPowerIrp;
+ PXENVIF_THREAD DevicePowerThread;
+ PIRP DevicePowerIrp;
+
+ PXENVIF_THREAD ScanThread;
+ KEVENT ScanEvent;
+ PXENBUS_STORE_WATCH ScanWatch;
+ XENVIF_MUTEX Mutex;
+ ULONG References;
+
+ FDO_RESOURCE Resource[RESOURCE_COUNT];
+
+ PXENBUS_EVTCHN_INTERFACE EvtchnInterface;
+ PXENBUS_DEBUG_INTERFACE DebugInterface;
+ PXENBUS_STORE_INTERFACE StoreInterface;
+ PXENBUS_GNTTAB_INTERFACE GnttabInterface;
+ PXENBUS_SUSPEND_INTERFACE SuspendInterface;
+ PXENFILT_EMULATED_INTERFACE EmulatedInterface;
+
+ PXENBUS_SUSPEND_CALLBACK SuspendCallbackLate;
+};
+
+static FORCEINLINE PVOID
+__FdoAllocate(
+ IN ULONG Length
+ )
+{
+ return __AllocateNonPagedPoolWithTag(Length, FDO_POOL);
+}
+
+static FORCEINLINE VOID
+__FdoFree(
+ IN PVOID Buffer
+ )
+{
+ __FreePoolWithTag(Buffer, FDO_POOL);
+}
+
+static FORCEINLINE VOID
+__FdoSetDevicePnpState(
+ IN PXENVIF_FDO Fdo,
+ IN DEVICE_PNP_STATE State
+ )
+{
+ PXENVIF_DX Dx = Fdo->Dx;
+
+ // We can never transition out of the deleted state
+ ASSERT(Dx->DevicePnpState != Deleted || State == Deleted);
+
+ Dx->PreviousDevicePnpState = Dx->DevicePnpState;
+ Dx->DevicePnpState = State;
+}
+
+static FORCEINLINE VOID
+__FdoRestoreDevicePnpState(
+ IN PXENVIF_FDO Fdo,
+ IN DEVICE_PNP_STATE State
+ )
+{
+ PXENVIF_DX Dx = Fdo->Dx;
+
+ if (Dx->DevicePnpState == State)
+ Dx->DevicePnpState = Dx->PreviousDevicePnpState;
+}
+
+static FORCEINLINE DEVICE_PNP_STATE
+__FdoGetDevicePnpState(
+ IN PXENVIF_FDO Fdo
+ )
+{
+ PXENVIF_DX Dx = Fdo->Dx;
+
+ return Dx->DevicePnpState;
+}
+
+static FORCEINLINE VOID
+__FdoSetDevicePowerState(
+ IN PXENVIF_FDO Fdo,
+ IN DEVICE_POWER_STATE State
+ )
+{
+ PXENVIF_DX Dx = Fdo->Dx;
+
+ Dx->DevicePowerState = State;
+}
+
+static FORCEINLINE DEVICE_POWER_STATE
+__FdoGetDevicePowerState(
+ IN PXENVIF_FDO Fdo
+ )
+{
+ PXENVIF_DX Dx = Fdo->Dx;
+
+ return Dx->DevicePowerState;
+}
+
+static FORCEINLINE VOID
+__FdoSetSystemPowerState(
+ IN PXENVIF_FDO Fdo,
+ IN SYSTEM_POWER_STATE State
+ )
+{
+ PXENVIF_DX Dx = Fdo->Dx;
+
+ Dx->SystemPowerState = State;
+}
+
+static FORCEINLINE SYSTEM_POWER_STATE
+__FdoGetSystemPowerState(
+ IN PXENVIF_FDO Fdo
+ )
+{
+ PXENVIF_DX Dx = Fdo->Dx;
+
+ return Dx->SystemPowerState;
+}
+
+static FORCEINLINE PDEVICE_OBJECT
+__FdoGetPhysicalDeviceObject(
+ IN PXENVIF_FDO Fdo
+ )
+{
+ return Fdo->PhysicalDeviceObject;
+}
+
+PDEVICE_OBJECT
+FdoGetPhysicalDeviceObject(
+ IN PXENVIF_FDO Fdo
+ )
+{
+ return __FdoGetPhysicalDeviceObject(Fdo);
+}
+
+static FORCEINLINE NTSTATUS
+__FdoSetName(
+ IN PXENVIF_FDO Fdo,
+ IN PWCHAR Name
+ )
+{
+ PXENVIF_DX Dx = Fdo->Dx;
+ UNICODE_STRING Unicode;
+ ANSI_STRING Ansi;
+ ULONG Index;
+ NTSTATUS status;
+
+ RtlInitUnicodeString(&Unicode, Name);
+
+ Ansi.Buffer = Dx->Name;
+ Ansi.MaximumLength = sizeof (Dx->Name);
+ Ansi.Length = 0;
+
+ status = RtlUnicodeStringToAnsiString(&Ansi, &Unicode, FALSE);
+ if (!NT_SUCCESS(status))
+ goto fail1;
+
+ for (Index = 0; Dx->Name[Index] != '\0'; Index++) {
+ if (!isalnum((UCHAR)Dx->Name[Index]))
+ Dx->Name[Index] = '_';
+ }
+
+ return STATUS_SUCCESS;
+
+fail1:
+ Error("fail1 (%08x)\n", status);
+
+ return status;
+}
+
+static FORCEINLINE PCHAR
+__FdoGetName(
+ IN PXENVIF_FDO Fdo
+ )
+{
+ PXENVIF_DX Dx = Fdo->Dx;
+
+ return Dx->Name;
+}
+
+PCHAR
+FdoGetName(
+ IN PXENVIF_FDO Fdo
+ )
+{
+ return __FdoGetName(Fdo);
+}
+
+__drv_functionClass(IO_COMPLETION_ROUTINE)
+__drv_sameIRQL
+static NTSTATUS
+__FdoDelegateIrp(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp,
+ IN PVOID Context
+ )
+{
+ PKEVENT Event = Context;
+
+ UNREFERENCED_PARAMETER(DeviceObject);
+ UNREFERENCED_PARAMETER(Irp);
+
+ KeSetEvent(Event, IO_NO_INCREMENT, FALSE);
+
+ return STATUS_MORE_PROCESSING_REQUIRED;
+}
+
+NTSTATUS
+FdoDelegateIrp(
+ IN PXENVIF_FDO Fdo,
+ IN PIRP Irp
+ )
+{
+ PDEVICE_OBJECT DeviceObject;
+ PIO_STACK_LOCATION StackLocation;
+ PIRP SubIrp;
+ KEVENT Event;
+ PIO_STACK_LOCATION SubStackLocation;
+ NTSTATUS status;
+
+ ASSERT3U(KeGetCurrentIrql(), ==, PASSIVE_LEVEL);
+
+ StackLocation = IoGetCurrentIrpStackLocation(Irp);
+
+ // Find the top of the FDO stack and hold a reference
+ DeviceObject = IoGetAttachedDeviceReference(Fdo->Dx->DeviceObject);
+
+ // Get a new IRP for the FDO stack
+ SubIrp = IoAllocateIrp(DeviceObject->StackSize, FALSE);
+
+ status = STATUS_NO_MEMORY;
+ if (SubIrp == NULL)
+ goto done;
+
+ // Copy in the information from the original IRP
+ SubStackLocation = IoGetNextIrpStackLocation(SubIrp);
+
+ KeInitializeEvent(&Event, NotificationEvent, FALSE);
+
+ RtlCopyMemory(SubStackLocation, StackLocation,
+ FIELD_OFFSET(IO_STACK_LOCATION, CompletionRoutine));
+ SubStackLocation->Control = 0;
+
+ IoSetCompletionRoutine(SubIrp,
+ __FdoDelegateIrp,
+ &Event,
+ TRUE,
+ TRUE,
+ TRUE);
+
+ // Default completion status
+ SubIrp->IoStatus.Status = Irp->IoStatus.Status;
+
+ status = IoCallDriver(DeviceObject, SubIrp);
+ if (status == STATUS_PENDING) {
+ (VOID) KeWaitForSingleObject(&Event,
+ Executive,
+ KernelMode,
+ FALSE,
+ NULL);
+ status = SubIrp->IoStatus.Status;
+ } else {
+ ASSERT3U(status, ==, SubIrp->IoStatus.Status);
+ }
+
+ IoFreeIrp(SubIrp);
+
+done:
+ ObDereferenceObject(DeviceObject);
+
+ return status;
+}
+
+__drv_functionClass(IO_COMPLETION_ROUTINE)
+__drv_sameIRQL
+static NTSTATUS
+__FdoForwardIrpSynchronously(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp,
+ IN PVOID Context
+ )
+{
+ PKEVENT Event = Context;
+
+ UNREFERENCED_PARAMETER(DeviceObject);
+ UNREFERENCED_PARAMETER(Irp);
+
+ KeSetEvent(Event, IO_NO_INCREMENT, FALSE);
+
+ return STATUS_MORE_PROCESSING_REQUIRED;
+}
+
+static NTSTATUS
+FdoForwardIrpSynchronously(
+ IN PXENVIF_FDO Fdo,
+ IN PIRP Irp
+ )
+{
+ KEVENT Event;
+ NTSTATUS status;
+
+ ASSERT3U(KeGetCurrentIrql(), ==, PASSIVE_LEVEL);
+
+ KeInitializeEvent(&Event, NotificationEvent, FALSE);
+
+ IoCopyCurrentIrpStackLocationToNext(Irp);
+ IoSetCompletionRoutine(Irp,
+ __FdoForwardIrpSynchronously,
+ &Event,
+ TRUE,
+ TRUE,
+ TRUE);
+
+ status = IoCallDriver(Fdo->LowerDeviceObject, Irp);
+ if (status == STATUS_PENDING) {
+ (VOID) KeWaitForSingleObject(&Event,
+ Executive,
+ KernelMode,
+ FALSE,
+ NULL);
+ status = Irp->IoStatus.Status;
+ } else {
+ ASSERT3U(status, ==, Irp->IoStatus.Status);
+ }
+
+ Trace("%08x\n", status);
+
+ return status;
+}
+
+VOID
+FdoAddPhysicalDeviceObject(
+ IN PXENVIF_FDO Fdo,
+ IN PXENVIF_PDO Pdo
+ )
+{
+ PDEVICE_OBJECT DeviceObject;
+ PXENVIF_DX Dx;
+
+ DeviceObject = PdoGetDeviceObject(Pdo);
+ Dx = (PXENVIF_DX)DeviceObject->DeviceExtension;
+ ASSERT3U(Dx->Type, ==, PHYSICAL_DEVICE_OBJECT);
+
+ InsertTailList(&Fdo->Dx->ListEntry, &Dx->ListEntry);
+ ASSERT3U(Fdo->References, !=, 0);
+ Fdo->References++;
+
+ PdoResume(Pdo);
+}
+
+VOID
+FdoRemovePhysicalDeviceObject(
+ IN PXENVIF_FDO Fdo,
+ IN PXENVIF_PDO Pdo
+ )
+{
+ PDEVICE_OBJECT DeviceObject;
+ PXENVIF_DX Dx;
+
+ DeviceObject = PdoGetDeviceObject(Pdo);
+ Dx = (PXENVIF_DX)DeviceObject->DeviceExtension;
+ ASSERT3U(Dx->Type, ==, PHYSICAL_DEVICE_OBJECT);
+
+ PdoSuspend(Pdo);
+
+ RemoveEntryList(&Dx->ListEntry);
+ ASSERT3U(Fdo->References, !=, 0);
+ --Fdo->References;
+
+ if (Fdo->ScanThread)
+ ThreadWake(Fdo->ScanThread);
+}
+
+static FORCEINLINE VOID
+__FdoAcquireMutex(
+ IN PXENVIF_FDO Fdo
+ )
+{
+ AcquireMutex(&Fdo->Mutex);
+}
+
+VOID
+FdoAcquireMutex(
+ IN PXENVIF_FDO Fdo
+ )
+{
+ __FdoAcquireMutex(Fdo);
+}
+
+static FORCEINLINE VOID
+__FdoReleaseMutex(
+ IN PXENVIF_FDO Fdo
+ )
+{
+ ReleaseMutex(&Fdo->Mutex);
+}
+
+VOID
+FdoReleaseMutex(
+ IN PXENVIF_FDO Fdo
+ )
+{
+ __FdoReleaseMutex(Fdo);
+
+ if (Fdo->References == 0)
+ FdoDestroy(Fdo);
+}
+
+static FORCEINLINE BOOLEAN
+__FdoEnumerate(
+ IN PXENVIF_FDO Fdo,
+ IN PANSI_STRING Devices
+ )
+{
+ BOOLEAN NeedInvalidate;
+ PLIST_ENTRY ListEntry;
+ ULONG Index;
+
+ Trace("====>\n");
+
+ NeedInvalidate = FALSE;
+
+ if (DriverParameters.CreatePDOs == 0)
+ goto done;
+
+ __FdoAcquireMutex(Fdo);
+
+ ListEntry = Fdo->Dx->ListEntry.Flink;
+ while (ListEntry != &Fdo->Dx->ListEntry) {
+ PLIST_ENTRY Next = ListEntry->Flink;
+ PXENVIF_DX Dx = CONTAINING_RECORD(ListEntry, XENVIF_DX, ListEntry);
+ PCHAR Name = Dx->Name;
+ PXENVIF_PDO Pdo = Dx->Pdo;
+ BOOLEAN Missing;
+
+ Missing = TRUE;
+
+ // If the PDO exists in the device list
+ // from xenstore then we don't want to remove it.
+
+ for (Index = 0; Devices[Index].Buffer != NULL; Index++) {
+ PANSI_STRING Device = &Devices[Index];
+
+ if (strcmp(Name, Device->Buffer) == 0) {
+ Missing = FALSE;
+ Device->Length = 0; // avoid duplication
+ break;
+ }
+ }
+
+ if (Missing &&
+ !PdoIsMissing(Pdo) &&
+ PdoGetDevicePnpState(Pdo) != Deleted) {
+ PdoSetMissing(Pdo, "device disappeared");
+
+ // If the PDO has not yet been enumerated then we can go ahead
+ // and mark it as deleted, otherwise we need to notify PnP manager and
+ // wait for the REMOVE_DEVICE IRP.
+ if (PdoGetDevicePnpState(Pdo) == Present) {
+ PdoSetDevicePnpState(Pdo, Deleted);
+ PdoDestroy(Pdo);
+ } else {
+ NeedInvalidate = TRUE;
+ }
+ }
+
+ ListEntry = Next;
+ }
+
+ // Check the device list from xenstore against the unsupported list
+ for (Index = 0; Devices[Index].Buffer != NULL; Index++) {
+ PANSI_STRING Device = &Devices[Index];
+ ULONG Entry;
+ BOOLEAN Supported;
+
+ Supported = TRUE;
+
+ for (Entry = 0;
+ DriverParameters.UnsupportedDevices != NULL && DriverParameters.UnsupportedDevices[Entry].Buffer != NULL;
+ Entry++) {
+ if (strncmp(Device->Buffer,
+ DriverParameters.UnsupportedDevices[Entry].Buffer,
+ Device->Length) == 0) {
+ Supported = FALSE;
+ break;
+ }
+ }
+
+ if (!Supported)
+ Device->Length = 0; // avoid creation
+ }
+
+ // Walk the device list and create PDOs for any new devices
+
+ for (Index = 0; Devices[Index].Buffer != NULL; Index++) {
+ PANSI_STRING Device = &Devices[Index];
+
+ if (Device->Length != 0) {
+ NTSTATUS status;
+
+ status = PdoCreate(Fdo, Device);
+ if (NT_SUCCESS(status))
+ NeedInvalidate = TRUE;
+ }
+ }
+
+ __FdoReleaseMutex(Fdo);
+
+done:
+ Trace("<====\n");
+ return NeedInvalidate;
+}
+
+static FORCEINLINE PANSI_STRING
+__FdoMultiSzToUpcaseAnsi(
+ IN PCHAR Buffer
+ )
+{
+ PANSI_STRING Ansi;
+ LONG Index;
+ LONG Count;
+ NTSTATUS status;
+
+ Index = 0;
+ Count = 0;
+ for (;;) {
+ if (Buffer[Index] == '\0') {
+ Count++;
+ Index++;
+
+ // Check for double NUL
+ if (Buffer[Index] == '\0')
+ break;
+ } else {
+ Buffer[Index] = (CHAR)toupper(Buffer[Index]);
+ Index++;
+ }
+ }
+
+ Ansi = __FdoAllocate(sizeof (ANSI_STRING) * (Count + 1));
+
+ status = STATUS_NO_MEMORY;
+ if (Ansi == NULL)
+ goto fail1;
+
+ for (Index = 0; Index < Count; Index++) {
+ ULONG Length;
+
+ Length = (ULONG)strlen(Buffer);
+ Ansi[Index].MaximumLength = (USHORT)(Length + 1);
+ Ansi[Index].Buffer = __FdoAllocate(Ansi[Index].MaximumLength);
+
+ status = STATUS_NO_MEMORY;
+ if (Ansi[Index].Buffer == NULL)
+ goto fail2;
+
+ RtlCopyMemory(Ansi[Index].Buffer, Buffer, Length);
+ Ansi[Index].Length = (USHORT)Length;
+
+ Buffer += Length + 1;
+ }
+
+ return Ansi;
+
+fail2:
+ Error("fail2\n");
+
+ while (--Index >= 0)
+ __FdoFree(Ansi[Index].Buffer);
+
+ __FdoFree(Ansi);
+
+fail1:
+ Error("fail1 (%08x)\n", status);
+
+ return NULL;
+}
+
+static FORCEINLINE VOID
+__FdoFreeAnsi(
+ IN PANSI_STRING Ansi
+ )
+{
+ ULONG Index;
+
+ for (Index = 0; Ansi[Index].Buffer != NULL; Index++)
+ __FdoFree(Ansi[Index].Buffer);
+
+ __FdoFree(Ansi);
+}
+
+static NTSTATUS
+FdoScan(
+ PXENVIF_THREAD Self,
+ PVOID Context
+ )
+{
+ PXENVIF_FDO Fdo = Context;
+ BOOLEAN NeedInvalidate;
+ PKEVENT Event;
+
+ Trace("====>\n");
+
+ NeedInvalidate = FALSE;
+
+ Event = ThreadGetEvent(Self);
+
+ for (;;) {
+ PCHAR Buffer;
+ PANSI_STRING Devices;
+ NTSTATUS status;
+
+ Trace("waiting...\n");
+
+ (VOID) KeWaitForSingleObject(Event,
+ Executive,
+ KernelMode,
+ FALSE,
+ NULL);
+ KeClearEvent(Event);
+
+ if (ThreadIsAlerted(Self))
+ break;
+
+ STORE(Acquire, Fdo->StoreInterface);
+
+ if (__FdoGetDevicePnpState(Fdo) != Started)
+ goto loop;
+
+ status = STORE(Directory,
+ Fdo->StoreInterface,
+ NULL,
+ "device",
+ "vif",
+ &Buffer);
+ if (!NT_SUCCESS(status))
+ goto loop;
+
+ Devices = __FdoMultiSzToUpcaseAnsi(Buffer);
+
+ STORE(Free,
+ Fdo->StoreInterface,
+ Buffer);
+
+ if (Devices != NULL) {
+ NeedInvalidate = __FdoEnumerate(Fdo, Devices);
+ __FdoFreeAnsi(Devices);
+ }
+
+ if (NeedInvalidate) {
+ NeedInvalidate = FALSE;
+ IoInvalidateDeviceRelations(__FdoGetPhysicalDeviceObject(Fdo),
+ BusRelations);
+ }
+
+loop:
+ STORE(Release, Fdo->StoreInterface);
+
+ KeSetEvent(&Fdo->ScanEvent, IO_NO_INCREMENT, FALSE);
+ }
+
+ KeSetEvent(&Fdo->ScanEvent, IO_NO_INCREMENT, FALSE);
+
+ Trace("<====\n");
+ return STATUS_SUCCESS;
+}
+
+static DECLSPEC_NOINLINE VOID
+FdoParseResources(
+ IN PXENVIF_FDO Fdo,
+ IN PCM_RESOURCE_LIST RawResourceList,
+ IN PCM_RESOURCE_LIST TranslatedResourceList
+ )
+{
+ PCM_PARTIAL_RESOURCE_LIST RawPartialList;
+ PCM_PARTIAL_RESOURCE_LIST TranslatedPartialList;
+ ULONG Index;
+
+ ASSERT3U(RawResourceList->Count, ==, 1);
+ RawPartialList = &RawResourceList->List[0].PartialResourceList;
+
+ ASSERT3U(RawPartialList->Version, ==, 1);
+ ASSERT3U(RawPartialList->Revision, ==, 1);
+
+ ASSERT3U(TranslatedResourceList->Count, ==, 1);
+ TranslatedPartialList = &TranslatedResourceList->List[0].PartialResourceList;
+
+ ASSERT3U(TranslatedPartialList->Version, ==, 1);
+ ASSERT3U(TranslatedPartialList->Revision, ==, 1);
+
+ for (Index = 0; Index < TranslatedPartialList->Count; Index++) {
+ PCM_PARTIAL_RESOURCE_DESCRIPTOR RawPartialDescriptor;
+ PCM_PARTIAL_RESOURCE_DESCRIPTOR TranslatedPartialDescriptor;
+
+ RawPartialDescriptor = &RawPartialList->PartialDescriptors[Index];
+ TranslatedPartialDescriptor = &TranslatedPartialList->PartialDescriptors[Index];
+
+ switch (TranslatedPartialDescriptor->Type) {
+ case CmResourceTypeMemory:
+ Fdo->Resource[MEMORY_RESOURCE].Raw = *RawPartialDescriptor;
+ Fdo->Resource[MEMORY_RESOURCE].Translated = *TranslatedPartialDescriptor;
+ break;
+
+ case CmResourceTypeInterrupt:
+ Fdo->Resource[INTERRUPT_RESOURCE].Raw = *RawPartialDescriptor;
+ Fdo->Resource[INTERRUPT_RESOURCE].Translated = *TranslatedPartialDescriptor;
+ break;
+
+ default:
+ break;
+ }
+ }
+}
+
+PXENBUS_EVTCHN_INTERFACE
+FdoGetEvtchnInterface(
+ IN PXENVIF_FDO Fdo
+ )
+{
+ return Fdo->EvtchnInterface;
+}
+
+PXENBUS_DEBUG_INTERFACE
+FdoGetDebugInterface(
+ IN PXENVIF_FDO Fdo
+ )
+{
+ return Fdo->DebugInterface;
+}
+
+PXENBUS_STORE_INTERFACE
+FdoGetStoreInterface(
+ IN PXENVIF_FDO Fdo
+ )
+{
+ return Fdo->StoreInterface;
+}
+
+PXENBUS_GNTTAB_INTERFACE
+FdoGetGnttabInterface(
+ IN PXENVIF_FDO Fdo
+ )
+{
+ return Fdo->GnttabInterface;
+}
+
+PXENBUS_SUSPEND_INTERFACE
+FdoGetSuspendInterface(
+ IN PXENVIF_FDO Fdo
+ )
+{
+ return Fdo->SuspendInterface;
+}
+
+static FORCEINLINE PXENFILT_EMULATED_INTERFACE
+__FdoGetEmulatedInterface(
+ IN PXENVIF_FDO Fdo
+ )
+{
+ return Fdo->EmulatedInterface;
+}
+
+PXENFILT_EMULATED_INTERFACE
+FdoGetEmulatedInterface(
+ IN PXENVIF_FDO Fdo
+ )
+{
+ return __FdoGetEmulatedInterface(Fdo);
+}
+
+static FORCEINLINE NTSTATUS
+__FdoD3ToD0(
+ IN PXENVIF_FDO Fdo
+ )
+{
+ POWER_STATE PowerState;
+ NTSTATUS status;
+
+ Trace("====>\n");
+
+ ASSERT3U(KeGetCurrentIrql(), ==, DISPATCH_LEVEL);
+ ASSERT3U(__FdoGetDevicePowerState(Fdo), ==, PowerDeviceD3);
+
+ __FdoSetDevicePowerState(Fdo, PowerDeviceD0);
+
+ STORE(Acquire, Fdo->StoreInterface);
+
+ status = STORE(Watch,
+ Fdo->StoreInterface,
+ "device",
+ "vif",
+ ThreadGetEvent(Fdo->ScanThread),
+ &Fdo->ScanWatch);
+ if (!NT_SUCCESS(status))
+ goto fail1;
+
+ PowerState.DeviceState = PowerDeviceD0;
+ PoSetPowerState(Fdo->Dx->DeviceObject,
+ DevicePowerState,
+ PowerState);
+
+ Trace("<====\n");
+
+ return STATUS_SUCCESS;
+
+fail1:
+ Error("fail1 (%08x)\n", status);
+
+ STORE(Release, Fdo->StoreInterface);
+
+ __FdoSetDevicePowerState(Fdo, PowerDeviceD3);
+
+ return status;
+}
+
+static FORCEINLINE VOID
+__FdoD0ToD3(
+ IN PXENVIF_FDO Fdo
+ )
+{
+ POWER_STATE PowerState;
+
+ Trace("====>\n");
+
+ ASSERT3U(KeGetCurrentIrql(), ==, DISPATCH_LEVEL);
+ ASSERT3U(__FdoGetDevicePowerState(Fdo), ==, PowerDeviceD0);
+
+ PowerState.DeviceState = PowerDeviceD3;
+ PoSetPowerState(Fdo->Dx->DeviceObject,
+ DevicePowerState,
+ PowerState);
+
+ __FdoSetDevicePowerState(Fdo, PowerDeviceD3);
+
+ (VOID) STORE(Unwatch,
+ Fdo->StoreInterface,
+ Fdo->ScanWatch);
+ Fdo->ScanWatch = NULL;
+
+ STORE(Release, Fdo->StoreInterface);
+
+ Trace("<====\n");
+}
+
+static DECLSPEC_NOINLINE VOID
+FdoSuspendCallbackLate(
+ IN PVOID Argument
+ )
+{
+ PXENVIF_FDO Fdo = Argument;
+ NTSTATUS status;
+
+ __FdoD0ToD3(Fdo);
+
+ status = __FdoD3ToD0(Fdo);
+ ASSERT(NT_SUCCESS(status));
+}
+
+static DECLSPEC_NOINLINE NTSTATUS
+FdoD3ToD0(
+ IN PXENVIF_FDO Fdo
+ )
+{
+ KIRQL Irql;
+ PLIST_ENTRY ListEntry;
+ NTSTATUS status;
+
+ ASSERT3U(KeGetCurrentIrql(), ==, PASSIVE_LEVEL);
+
+ KeRaiseIrql(DISPATCH_LEVEL, &Irql);
+
+ status = __FdoD3ToD0(Fdo);
+ if (!NT_SUCCESS(status))
+ goto fail1;
+
+ SUSPEND(Acquire, Fdo->SuspendInterface);
+
+ status = SUSPEND(Register,
+ Fdo->SuspendInterface,
+ SUSPEND_CALLBACK_LATE,
+ FdoSuspendCallbackLate,
+ Fdo,
+ &Fdo->SuspendCallbackLate);
+ if (!NT_SUCCESS(status))
+ goto fail2;
+
+ KeLowerIrql(Irql);
+
+ __FdoAcquireMutex(Fdo);
+
+ for (ListEntry = Fdo->Dx->ListEntry.Flink;
+ ListEntry != &Fdo->Dx->ListEntry;
+ ListEntry = ListEntry->Flink) {
+ PXENVIF_DX Dx = CONTAINING_RECORD(ListEntry, XENVIF_DX, ListEntry);
+ PXENVIF_PDO Pdo = Dx->Pdo;
+
+ ASSERT3U(Dx->Type, ==, PHYSICAL_DEVICE_OBJECT);
+
+ PdoResume(Pdo);
+ }
+
+ __FdoReleaseMutex(Fdo);
+
+ return STATUS_SUCCESS;
+
+fail2:
+ Error("fail2\n");
+
+ SUSPEND(Release, Fdo->SuspendInterface);
+
+ __FdoD0ToD3(Fdo);
+
+fail1:
+ Error("fail1 (%08x)\n", status);
+
+ KeLowerIrql(Irql);
+
+ return status;
+}
+
+static DECLSPEC_NOINLINE VOID
+FdoD0ToD3(
+ IN PXENVIF_FDO Fdo
+ )
+{
+ PLIST_ENTRY ListEntry;
+ KIRQL Irql;
+
+ ASSERT3U(KeGetCurrentIrql(), ==, PASSIVE_LEVEL);
+
+ __FdoAcquireMutex(Fdo);
+
+ for (ListEntry = Fdo->Dx->ListEntry.Flink;
+ ListEntry != &Fdo->Dx->ListEntry;
+ ListEntry = ListEntry->Flink) {
+ PXENVIF_DX Dx = CONTAINING_RECORD(ListEntry, XENVIF_DX, ListEntry);
+ PXENVIF_PDO Pdo = Dx->Pdo;
+
+ ASSERT3U(Dx->Type, ==, PHYSICAL_DEVICE_OBJECT);
+
+ if (PdoGetDevicePnpState(Pdo) == Deleted ||
+ PdoIsMissing(Pdo))
+ continue;
+
+ PdoSuspend(Pdo);
+ }
+
+ __FdoReleaseMutex(Fdo);
+
+ KeRaiseIrql(DISPATCH_LEVEL, &Irql);
+
+ SUSPEND(Deregister,
+ Fdo->SuspendInterface,
+ Fdo->SuspendCallbackLate);
+ Fdo->SuspendCallbackLate = NULL;
+
+ SUSPEND(Release, Fdo->SuspendInterface);
+
+ __FdoD0ToD3(Fdo);
+
+ KeLowerIrql(Irql);
+}
+
+static DECLSPEC_NOINLINE VOID
+FdoS4ToS3(
+ IN PXENVIF_FDO Fdo
+ )
+{
+ ASSERT3U(KeGetCurrentIrql(), ==, PASSIVE_LEVEL);
+ ASSERT3U(__FdoGetSystemPowerState(Fdo), ==, PowerSystemHibernate);
+
+ __FdoSetSystemPowerState(Fdo, PowerSystemSleeping3);
+}
+
+static DECLSPEC_NOINLINE VOID
+FdoS3ToS4(
+ IN PXENVIF_FDO Fdo
+ )
+{
+ ASSERT3U(KeGetCurrentIrql(), ==, PASSIVE_LEVEL);
+ ASSERT3U(__FdoGetSystemPowerState(Fdo), ==, PowerSystemSleeping3);
+
+ __FdoSetSystemPowerState(Fdo, PowerSystemHibernate);
+}
+
+static DECLSPEC_NOINLINE NTSTATUS
+FdoStartDevice(
+ IN PXENVIF_FDO Fdo,
+ IN PIRP Irp
+ )
+{
+ PIO_STACK_LOCATION StackLocation;
+ NTSTATUS status;
+
+ status = FdoForwardIrpSynchronously(Fdo, Irp);
+ if (!NT_SUCCESS(status))
+ goto fail1;
+
+ StackLocation = IoGetCurrentIrpStackLocation(Irp);
+
+ FdoParseResources(Fdo,
+ StackLocation->Parameters.StartDevice.AllocatedResources,
+ StackLocation->Parameters.StartDevice.AllocatedResourcesTranslated);
+
+ KeInitializeEvent(&Fdo->ScanEvent, NotificationEvent, FALSE);
+
+ status = ThreadCreate(FdoScan, Fdo, &Fdo->ScanThread);
+ if (!NT_SUCCESS(status))
+ goto fail2;
+
+ __FdoSetSystemPowerState(Fdo, PowerSystemHibernate);
+ FdoS4ToS3(Fdo);
+ __FdoSetSystemPowerState(Fdo, PowerSystemWorking);
+
+ status = FdoD3ToD0(Fdo);
+ if (!NT_SUCCESS(status))
+ goto fail3;
+
+ __FdoSetDevicePnpState(Fdo, Started);
+ ThreadWake(Fdo->ScanThread);
+
+ status = Irp->IoStatus.Status;
+ IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+ return status;
+
+fail3:
+ Error("fail3\n");
+
+ __FdoSetSystemPowerState(Fdo, PowerSystemSleeping3);
+ FdoS3ToS4(Fdo);
+ __FdoSetSystemPowerState(Fdo, PowerSystemShutdown);
+
+ ThreadAlert(Fdo->ScanThread);
+ ThreadJoin(Fdo->ScanThread);
+ Fdo->ScanThread = NULL;
+
+fail2:
+ Error("fail2\n");
+
+ RtlZeroMemory(&Fdo->ScanEvent, sizeof (KEVENT));
+
+ RtlZeroMemory(&Fdo->Resource, sizeof (FDO_RESOURCE) * RESOURCE_COUNT);
+
+fail1:
+ Error("fail1 (%08x)\n", status);
+
+ Irp->IoStatus.Status = status;
+ IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+ return status;
+}
+
+static DECLSPEC_NOINLINE NTSTATUS
+FdoQueryStopDevice(
+ IN PXENVIF_FDO Fdo,
+ IN PIRP Irp
+ )
+{
+ NTSTATUS status;
+
+ __FdoSetDevicePnpState(Fdo, StopPending);
+ Irp->IoStatus.Status = STATUS_SUCCESS;
+
+ IoSkipCurrentIrpStackLocation(Irp);
+ status = IoCallDriver(Fdo->LowerDeviceObject, Irp);
+
+ return status;
+}
+
+static DECLSPEC_NOINLINE NTSTATUS
+FdoCancelStopDevice(
+ IN PXENVIF_FDO Fdo,
+ IN PIRP Irp
+ )
+{
+ NTSTATUS status;
+
+ Irp->IoStatus.Status = STATUS_SUCCESS;
+
+ __FdoRestoreDevicePnpState(Fdo, StopPending);
+
+ IoSkipCurrentIrpStackLocation(Irp);
+ status = IoCallDriver(Fdo->LowerDeviceObject, Irp);
+
+ return status;
+}
+
+static DECLSPEC_NOINLINE NTSTATUS
+FdoStopDevice(
+ IN PXENVIF_FDO Fdo,
+ IN PIRP Irp
+ )
+{
+ NTSTATUS status;
+
+ FdoD0ToD3(Fdo);
+
+ __FdoSetSystemPowerState(Fdo, PowerSystemSleeping3);
+ FdoS3ToS4(Fdo);
+ __FdoSetSystemPowerState(Fdo, PowerSystemShutdown);
+
+ ThreadAlert(Fdo->ScanThread);
+ ThreadJoin(Fdo->ScanThread);
+ Fdo->ScanThread = NULL;
+
+ RtlZeroMemory(&Fdo->ScanEvent, sizeof (KEVENT));
+
+ RtlZeroMemory(&Fdo->Resource, sizeof (FDO_RESOURCE) * RESOURCE_COUNT);
+
+ __FdoSetDevicePnpState(Fdo, Stopped);
+ Irp->IoStatus.Status = STATUS_SUCCESS;
+
+ IoSkipCurrentIrpStackLocation(Irp);
+ status = IoCallDriver(Fdo->LowerDeviceObject, Irp);
+
+ return status;
+}
+
+static DECLSPEC_NOINLINE NTSTATUS
+FdoQueryRemoveDevice(
+ IN PXENVIF_FDO Fdo,
+ IN PIRP Irp
+ )
+{
+ NTSTATUS status;
+
+ __FdoSetDevicePnpState(Fdo, RemovePending);
+ Irp->IoStatus.Status = STATUS_SUCCESS;
+
+ IoSkipCurrentIrpStackLocation(Irp);
+ status = IoCallDriver(Fdo->LowerDeviceObject, Irp);
+
+ return status;
+}
+
+static DECLSPEC_NOINLINE NTSTATUS
+FdoCancelRemoveDevice(
+ IN PXENVIF_FDO Fdo,
+ IN PIRP Irp
+ )
+{
+ NTSTATUS status;
+
+ __FdoRestoreDevicePnpState(Fdo, RemovePending);
+
+ Irp->IoStatus.Status = STATUS_SUCCESS;
+
+ IoSkipCurrentIrpStackLocation(Irp);
+ status = IoCallDriver(Fdo->LowerDeviceObject, Irp);
+
+ return status;
+}
+
+static DECLSPEC_NOINLINE NTSTATUS
+FdoSurpriseRemoval(
+ IN PXENVIF_FDO Fdo,
+ IN PIRP Irp
+ )
+{
+ PLIST_ENTRY ListEntry;
+ NTSTATUS status;
+
+ __FdoSetDevicePnpState(Fdo, SurpriseRemovePending);
+
+ __FdoAcquireMutex(Fdo);
+
+ for (ListEntry = Fdo->Dx->ListEntry.Flink;
+ ListEntry != &Fdo->Dx->ListEntry;
+ ListEntry = ListEntry->Flink) {
+ PXENVIF_DX Dx = CONTAINING_RECORD(ListEntry, XENVIF_DX, ListEntry);
+ PXENVIF_PDO Pdo = Dx->Pdo;
+
+ ASSERT3U(Dx->Type, ==, PHYSICAL_DEVICE_OBJECT);
+
+ if (!PdoIsMissing(Pdo))
+ PdoSetMissing(Pdo, "FDO surprise removed");
+ }
+
+ __FdoReleaseMutex(Fdo);
+
+ Irp->IoStatus.Status = STATUS_SUCCESS;
+
+ IoSkipCurrentIrpStackLocation(Irp);
+ status = IoCallDriver(Fdo->LowerDeviceObject, Irp);
+
+ return status;
+}
+
+static DECLSPEC_NOINLINE NTSTATUS
+FdoRemoveDevice(
+ IN PXENVIF_FDO Fdo,
+ IN PIRP Irp
+ )
+{
+ PLIST_ENTRY ListEntry;
+ NTSTATUS status;
+
+ ASSERT3U(KeGetCurrentIrql(), ==, PASSIVE_LEVEL);
+
+ if (__FdoGetDevicePowerState(Fdo) != PowerDeviceD0)
+ goto done;
+
+ KeClearEvent(&Fdo->ScanEvent);
+ ThreadWake(Fdo->ScanThread);
+
+ Trace("waiting for scan thread\n");
+
+ (VOID) KeWaitForSingleObject(&Fdo->ScanEvent,
+ Executive,
+ KernelMode,
+ FALSE,
+ NULL);
+
+ __FdoAcquireMutex(Fdo);
+
+ ListEntry = Fdo->Dx->ListEntry.Flink;
+ while (ListEntry != &Fdo->Dx->ListEntry) {
+ PLIST_ENTRY Flink = ListEntry->Flink;
+ PXENVIF_DX Dx = CONTAINING_RECORD(ListEntry, XENVIF_DX, ListEntry);
+ PXENVIF_PDO Pdo = Dx->Pdo;
+
+ ASSERT3U(Dx->Type, ==, PHYSICAL_DEVICE_OBJECT);
+
+ if (!PdoIsMissing(Pdo))
+ PdoSetMissing(Pdo, "FDO removed");
+
+ if (PdoGetDevicePnpState(Pdo) != SurpriseRemovePending)
+ PdoSetDevicePnpState(Pdo, Deleted);
+
+ if (PdoGetDevicePnpState(Pdo) == Deleted)
+ PdoDestroy(Pdo);
+
+ ListEntry = Flink;
+ }
+
+ __FdoReleaseMutex(Fdo);
+
+ FdoD0ToD3(Fdo);
+
+ __FdoSetSystemPowerState(Fdo, PowerSystemSleeping3);
+ FdoS3ToS4(Fdo);
+ __FdoSetSystemPowerState(Fdo, PowerSystemShutdown);
+
+ ThreadAlert(Fdo->ScanThread);
+ ThreadJoin(Fdo->ScanThread);
+ Fdo->ScanThread = NULL;
+
+ RtlZeroMemory(&Fdo->ScanEvent, sizeof (KEVENT));
+
+ RtlZeroMemory(&Fdo->Resource, sizeof (FDO_RESOURCE) * RESOURCE_COUNT);
+
+done:
+ __FdoSetDevicePnpState(Fdo, Deleted);
+
+ Irp->IoStatus.Status = STATUS_SUCCESS;
+
+ IoSkipCurrentIrpStackLocation(Irp);
+ status = IoCallDriver(Fdo->LowerDeviceObject, Irp);
+
+ __FdoAcquireMutex(Fdo);
+ ASSERT3U(Fdo->References, !=, 0);
+ --Fdo->References;
+ __FdoReleaseMutex(Fdo);
+
+ if (Fdo->References == 0)
+ FdoDestroy(Fdo);
+
+ return status;
+}
+
+static DECLSPEC_NOINLINE NTSTATUS
+FdoQueryDeviceRelations(
+ IN PXENVIF_FDO Fdo,
+ IN PIRP Irp
+ )
+{
+ PIO_STACK_LOCATION StackLocation;
+ ULONG Size;
+ PDEVICE_RELATIONS Relations;
+ ULONG Count;
+ PLIST_ENTRY ListEntry;
+ NTSTATUS status;
+
+ ASSERT3U(KeGetCurrentIrql(), ==, PASSIVE_LEVEL);
+
+ StackLocation = IoGetCurrentIrpStackLocation(Irp);
+
+ status = Irp->IoStatus.Status;
+
+ if (StackLocation->Parameters.QueryDeviceRelations.Type != BusRelations) {
+ IoSkipCurrentIrpStackLocation(Irp);
+ status = IoCallDriver(Fdo->LowerDeviceObject, Irp);
+
+ goto done;
+ }
+
+ (VOID) KeWaitForSingleObject(&Fdo->ScanEvent,
+ Executive,
+ KernelMode,
+ FALSE,
+ NULL);
+
+ __FdoAcquireMutex(Fdo);
+
+ Count = 0;
+ for (ListEntry = Fdo->Dx->ListEntry.Flink;
+ ListEntry != &Fdo->Dx->ListEntry;
+ ListEntry = ListEntry->Flink)
+ Count++;
+
+ Size = FIELD_OFFSET(DEVICE_RELATIONS, Objects) + (sizeof (DEVICE_OBJECT) * __min(Count, 1));
+
+ Relations = ExAllocatePoolWithTag(PagedPool, Size, 'FIV');
+
+ status = STATUS_NO_MEMORY;
+ if (Relations == NULL)
+ goto fail1;
+
+ RtlZeroMemory(Relations, Size);
+
+ for (ListEntry = Fdo->Dx->ListEntry.Flink;
+ ListEntry != &Fdo->Dx->ListEntry;
+ ListEntry = ListEntry->Flink) {
+ PXENVIF_DX Dx = CONTAINING_RECORD(ListEntry, XENVIF_DX, ListEntry);
+ PXENVIF_PDO Pdo = Dx->Pdo;
+
+ ASSERT3U(Dx->Type, ==, PHYSICAL_DEVICE_OBJECT);
+
+ if (PdoGetDevicePnpState(Pdo) == Deleted &&
+ !PdoIsMissing(Pdo))
+ PdoSetMissing(Pdo, "surprise remove");
+
+ if (PdoIsMissing(Pdo))
+ continue;
+
+ if (PdoGetDevicePnpState(Pdo) == Present)
+ PdoSetDevicePnpState(Pdo, Enumerated);
+
+ ObReferenceObject(Dx->DeviceObject);
+ Relations->Objects[Relations->Count++] = Dx->DeviceObject;
+ }
+
+ ASSERT3U(Relations->Count, <=, Count);
+
+ Trace("%d PDO(s)\n", Relations->Count);
+
+ __FdoReleaseMutex(Fdo);
+
+ Irp->IoStatus.Information = (ULONG_PTR)Relations;
+ Irp->IoStatus.Status = STATUS_SUCCESS;
+
+ status = FdoForwardIrpSynchronously(Fdo, Irp);
+ if (!NT_SUCCESS(status))
+ goto fail2;
+
+ IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+ __FdoAcquireMutex(Fdo);
+
+ for (ListEntry = Fdo->Dx->ListEntry.Flink;
+ ListEntry != &Fdo->Dx->ListEntry;
+ ListEntry = ListEntry->Flink) {
+ PXENVIF_DX Dx = CONTAINING_RECORD(ListEntry, XENVIF_DX, ListEntry);
+ PXENVIF_PDO Pdo = Dx->Pdo;
+
+ ASSERT3U(Dx->Type, ==, PHYSICAL_DEVICE_OBJECT);
+
+ if (PdoGetDevicePnpState(Pdo) == Deleted &&
+ PdoIsMissing(Pdo))
+ PdoDestroy(Pdo);
+ }
+
+ __FdoReleaseMutex(Fdo);
+
+done:
+ return status;
+
+fail2:
+ Error("fail2\n");
+
+ __FdoAcquireMutex(Fdo);
+
+fail1:
+ Error("fail1 (%08x)\n", status);
+
+ __FdoReleaseMutex(Fdo);
+
+ Irp->IoStatus.Status = status;
+ IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+ return status;
+}
+
+static DECLSPEC_NOINLINE NTSTATUS
+FdoQueryCapabilities(
+ IN PXENVIF_FDO Fdo,
+ IN PIRP Irp
+ )
+{
+ PIO_STACK_LOCATION StackLocation;
+ PDEVICE_CAPABILITIES Capabilities;
+ SYSTEM_POWER_STATE SystemPowerState;
+ NTSTATUS status;
+
+ status = FdoForwardIrpSynchronously(Fdo, Irp);
+ if (!NT_SUCCESS(status))
+ goto fail1;
+
+ StackLocation = IoGetCurrentIrpStackLocation(Irp);
+ Capabilities = StackLocation->Parameters.DeviceCapabilities.Capabilities;
+
+ Fdo->LowerDeviceCapabilities = *Capabilities;
+
+ for (SystemPowerState = 0; SystemPowerState < PowerSystemMaximum; SystemPowerState++) {
+ DEVICE_POWER_STATE DevicePowerState;
+
+ DevicePowerState = Fdo->LowerDeviceCapabilities.DeviceState[SystemPowerState];
+ Trace("%s -> %s\n",
+ PowerSystemStateName(SystemPowerState),
+ PowerDeviceStateName(DevicePowerState));
+ }
+
+ status = Irp->IoStatus.Status;
+ IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+ return status;
+
+fail1:
+ Error("fail1 (%08x)\n", status);
+
+ Irp->IoStatus.Status = status;
+ IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+ return status;
+}
+
+static DECLSPEC_NOINLINE NTSTATUS
+FdoDeviceUsageNotification(
+ IN PXENVIF_FDO Fdo,
+ IN PIRP Irp
+ )
+{
+ PIO_STACK_LOCATION StackLocation;
+ DEVICE_USAGE_NOTIFICATION_TYPE Type;
+ BOOLEAN InPath;
+ BOOLEAN NotDisableable;
+ NTSTATUS status;
+
+ StackLocation = IoGetCurrentIrpStackLocation(Irp);
+ Type = StackLocation->Parameters.UsageNotification.Type;
+ InPath = StackLocation->Parameters.UsageNotification.InPath;
+
+ if (InPath) {
+ Trace("%s: ADDING %s\n",
+ __FdoGetName(Fdo),
+ DeviceUsageTypeName(Type));
+ Fdo->Usage[Type]++;
+ } else {
+ ASSERT(Fdo->Usage[Type] != 0);
+
+ Trace("%s: REMOVING %s\n",
+ __FdoGetName(Fdo),
+ DeviceUsageTypeName(Type));
+ --Fdo->Usage[Type];
+ }
+
+ status = FdoForwardIrpSynchronously(Fdo, Irp);
+ if (!NT_SUCCESS(status))
+ goto fail1;
+
+ NotDisableable = FALSE;
+ for (Type = 0; Type <= DeviceUsageTypeDumpFile; Type++) {
+ if (Fdo->Usage[Type] != 0) {
+ NotDisableable = TRUE;
+ break;
+ }
+ }
+
+ IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+ if (Fdo->NotDisableable != NotDisableable) {
+ Fdo->NotDisableable = NotDisableable;
+
+ IoInvalidateDeviceState(__FdoGetPhysicalDeviceObject(Fdo));
+ }
+
+ return status;
+
+fail1:
+ Error("fail1 (%08x)\n", status);
+
+ IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+ return status;
+}
+
+static DECLSPEC_NOINLINE NTSTATUS
+FdoQueryPnpDeviceState(
+ IN PXENVIF_FDO Fdo,
+ IN PIRP Irp
+ )
+{
+ ULONG_PTR State;
+ NTSTATUS status;
+
+ if (Irp->IoStatus.Status == STATUS_SUCCESS)
+ State = Irp->IoStatus.Information;
+ else if (Irp->IoStatus.Status == STATUS_NOT_SUPPORTED)
+ State = 0;
+ else
+ goto done;
+
+ if (Fdo->NotDisableable) {
+ Info("%s: not disableable\n", __FdoGetName(Fdo));
+ State |= PNP_DEVICE_NOT_DISABLEABLE;
+ }
+
+ Irp->IoStatus.Information = State;
+ Irp->IoStatus.Status = STATUS_SUCCESS;
+
+done:
+ IoSkipCurrentIrpStackLocation(Irp);
+ status = IoCallDriver(Fdo->LowerDeviceObject, Irp);
+
+ return status;
+}
+
+static DECLSPEC_NOINLINE NTSTATUS
+FdoDispatchPnp(
+ IN PXENVIF_FDO Fdo,
+ IN PIRP Irp
+ )
+{
+ PIO_STACK_LOCATION StackLocation;
+ UCHAR MinorFunction;
+ NTSTATUS status;
+
+ StackLocation = IoGetCurrentIrpStackLocation(Irp);
+ MinorFunction = StackLocation->MinorFunction;
+
+ Trace("====> (%02x:%s)\n",
+ MinorFunction,
+ PnpMinorFunctionName(MinorFunction));
+
+ switch (StackLocation->MinorFunction) {
+ case IRP_MN_START_DEVICE:
+ status = FdoStartDevice(Fdo, Irp);
+ break;
+
+ case IRP_MN_QUERY_STOP_DEVICE:
+ status = FdoQueryStopDevice(Fdo, Irp);
+ break;
+
+ case IRP_MN_CANCEL_STOP_DEVICE:
+ status = FdoCancelStopDevice(Fdo, Irp);
+ break;
+
+ case IRP_MN_STOP_DEVICE:
+ status = FdoStopDevice(Fdo, Irp);
+ break;
+
+ case IRP_MN_QUERY_REMOVE_DEVICE:
+ status = FdoQueryRemoveDevice(Fdo, Irp);
+ break;
+
+ case IRP_MN_SURPRISE_REMOVAL:
+ status = FdoSurpriseRemoval(Fdo, Irp);
+ break;
+
+ case IRP_MN_REMOVE_DEVICE:
+ status = FdoRemoveDevice(Fdo, Irp);
+ break;
+
+ case IRP_MN_CANCEL_REMOVE_DEVICE:
+ status = FdoCancelRemoveDevice(Fdo, Irp);
+ break;
+
+ case IRP_MN_QUERY_DEVICE_RELATIONS:
+ status = FdoQueryDeviceRelations(Fdo, Irp);
+ break;
+
+ case IRP_MN_QUERY_CAPABILITIES:
+ status = FdoQueryCapabilities(Fdo, Irp);
+ break;
+
+ case IRP_MN_DEVICE_USAGE_NOTIFICATION:
+ status = FdoDeviceUsageNotification(Fdo, Irp);
+ break;
+
+ case IRP_MN_QUERY_PNP_DEVICE_STATE:
+ status = FdoQueryPnpDeviceState(Fdo, Irp);
+ break;
+
+ default:
+ IoSkipCurrentIrpStackLocation(Irp);
+ status = IoCallDriver(Fdo->LowerDeviceObject, Irp);
+ break;
+ }
+
+ Trace("<==== (%02x:%s)(%08x)\n",
+ MinorFunction,
+ PnpMinorFunctionName(MinorFunction),
+ status);
+
+ return status;
+}
+
+static FORCEINLINE NTSTATUS
+__FdoSetDevicePowerUp(
+ IN PXENVIF_FDO Fdo,
+ IN PIRP Irp
+ )
+{
+ PIO_STACK_LOCATION StackLocation;
+ DEVICE_POWER_STATE DeviceState;
+ NTSTATUS status;
+
+ Trace("====>\n");
+
+ StackLocation = IoGetCurrentIrpStackLocation(Irp);
+ DeviceState = StackLocation->Parameters.Power.State.DeviceState;
+
+ ASSERT3U(DeviceState, <, __FdoGetDevicePowerState(Fdo));
+
+ status = FdoForwardIrpSynchronously(Fdo, Irp);
+ if (!NT_SUCCESS(status))
+ goto done;
+
+ Info("%s -> %s\n",
+ PowerDeviceStateName(__FdoGetDevicePowerState(Fdo)),
+ PowerDeviceStateName(DeviceState));
+
+ ASSERT3U(DeviceState, ==, PowerDeviceD0);
+ status = FdoD3ToD0(Fdo);
+ ASSERT(NT_SUCCESS(status));
+
+done:
+ Irp->IoStatus.Status = status;
+ IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+ Trace("<==== (%08x)\n", status);
+ return status;
+}
+
+static FORCEINLINE NTSTATUS
+__FdoSetDevicePowerDown(
+ IN PXENVIF_FDO Fdo,
+ IN PIRP Irp
+ )
+{
+ PIO_STACK_LOCATION StackLocation;
+ DEVICE_POWER_STATE DeviceState;
+ NTSTATUS status;
+
+ StackLocation = IoGetCurrentIrpStackLocation(Irp);
+ DeviceState = StackLocation->Parameters.Power.State.DeviceState;
+
+ ASSERT3U(DeviceState, >, __FdoGetDevicePowerState(Fdo));
+
+ Info("%s -> %s\n",
+ PowerDeviceStateName(__FdoGetDevicePowerState(Fdo)),
+ PowerDeviceStateName(DeviceState));
+
+ ASSERT3U(DeviceState, ==, PowerDeviceD3);
+
+ if (__FdoGetDevicePowerState(Fdo) == PowerDeviceD0)
+ FdoD0ToD3(Fdo);
+
+ IoSkipCurrentIrpStackLocation(Irp);
+ status = IoCallDriver(Fdo->LowerDeviceObject, Irp);
+
+ return status;
+}
+
+static FORCEINLINE NTSTATUS
+__FdoSetDevicePower(
+ IN PXENVIF_FDO Fdo,
+ IN PIRP Irp
+ )
+{
+ PIO_STACK_LOCATION StackLocation;
+ DEVICE_POWER_STATE DeviceState;
+ POWER_ACTION PowerAction;
+ NTSTATUS status;
+
+ StackLocation = IoGetCurrentIrpStackLocation(Irp);
+ DeviceState = StackLocation->Parameters.Power.State.DeviceState;
+ PowerAction = StackLocation->Parameters.Power.ShutdownType;
+
+ Trace("====> (%s:%s)\n",
+ PowerDeviceStateName(DeviceState),
+ PowerActionName(PowerAction));
+
+ ASSERT3U(PowerAction, <, PowerActionShutdown);
+
+ if (DeviceState == __FdoGetDevicePowerState(Fdo)) {
+ IoSkipCurrentIrpStackLocation(Irp);
+ status = IoCallDriver(Fdo->LowerDeviceObject, Irp);
+
+ goto done;
+ }
+
+ status = (DeviceState < __FdoGetDevicePowerState(Fdo)) ?
+ __FdoSetDevicePowerUp(Fdo, Irp) :
+ __FdoSetDevicePowerDown(Fdo, Irp);
+
+done:
+ Trace("<==== (%s:%s)(%08x)\n",
+ PowerDeviceStateName(DeviceState),
+ PowerActionName(PowerAction),
+ status);
+ return status;
+}
+
+__drv_functionClass(REQUEST_POWER_COMPLETE)
+__drv_sameIRQL
+VOID
+__FdoRequestSetDevicePower(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN UCHAR MinorFunction,
+ IN POWER_STATE PowerState,
+ IN PVOID Context,
+ IN PIO_STATUS_BLOCK IoStatus
+ )
+{
+ PKEVENT Event = Context;
+
+ UNREFERENCED_PARAMETER(DeviceObject);
+ UNREFERENCED_PARAMETER(MinorFunction);
+ UNREFERENCED_PARAMETER(PowerState);
+
+ ASSERT(NT_SUCCESS(IoStatus->Status));
+
+ KeSetEvent(Event, IO_NO_INCREMENT, FALSE);
+}
+
+static VOID
+FdoRequestSetDevicePower(
+ IN PXENVIF_FDO Fdo,
+ IN DEVICE_POWER_STATE DeviceState
+ )
+{
+ POWER_STATE PowerState;
+ KEVENT Event;
+ NTSTATUS status;
+
+ Trace("%s\n", PowerDeviceStateName(DeviceState));
+
+ ASSERT3U(KeGetCurrentIrql(), ==, PASSIVE_LEVEL);
+
+ PowerState.DeviceState = DeviceState;
+ KeInitializeEvent(&Event, NotificationEvent, FALSE);
+
+ status = PoRequestPowerIrp(Fdo->LowerDeviceObject,
+ IRP_MN_SET_POWER,
+ PowerState,
+ __FdoRequestSetDevicePower,
+ &Event,
+ NULL);
+ ASSERT(NT_SUCCESS(status));
+
+ (VOID) KeWaitForSingleObject(&Event,
+ Executive,
+ KernelMode,
+ FALSE,
+ NULL);
+}
+
+static FORCEINLINE NTSTATUS
+__FdoSetSystemPowerUp(
+ IN PXENVIF_FDO Fdo,
+ IN PIRP Irp
+ )
+{
+
+ PIO_STACK_LOCATION StackLocation;
+ SYSTEM_POWER_STATE SystemState;
+ DEVICE_POWER_STATE DeviceState;
+ NTSTATUS status;
+
+ StackLocation = IoGetCurrentIrpStackLocation(Irp);
+ SystemState = StackLocation->Parameters.Power.State.SystemState;
+
+ ASSERT3U(SystemState, <, __FdoGetSystemPowerState(Fdo));
+
+ status = FdoForwardIrpSynchronously(Fdo, Irp);
+ if (!NT_SUCCESS(status))
+ goto done;
+
+ if (SystemState < PowerSystemHibernate &&
+ __FdoGetSystemPowerState(Fdo) >= PowerSystemHibernate)
+ __FdoSetSystemPowerState(Fdo, PowerSystemHibernate);
+ FdoS4ToS3(Fdo);
+
+ Info("%s -> %s\n",
+ PowerSystemStateName(__FdoGetSystemPowerState(Fdo)),
+ PowerSystemStateName(SystemState));
+
+ __FdoSetSystemPowerState(Fdo, SystemState);
+
+ DeviceState = Fdo->LowerDeviceCapabilities.DeviceState[SystemState];
+ FdoRequestSetDevicePower(Fdo, DeviceState);
+
+done:
+ Irp->IoStatus.Status = status;
+ IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+ return status;
+}
+
+static FORCEINLINE NTSTATUS
+__FdoSetSystemPowerDown(
+ IN PXENVIF_FDO Fdo,
+ IN PIRP Irp
+ )
+{
+ PIO_STACK_LOCATION StackLocation;
+ SYSTEM_POWER_STATE SystemState;
+ DEVICE_POWER_STATE DeviceState;
+ NTSTATUS status;
+
+ StackLocation = IoGetCurrentIrpStackLocation(Irp);
+ SystemState = StackLocation->Parameters.Power.State.SystemState;
+
+ ASSERT3U(SystemState, >, __FdoGetSystemPowerState(Fdo));
+
+ DeviceState = Fdo->LowerDeviceCapabilities.DeviceState[SystemState];
+
+ FdoRequestSetDevicePower(Fdo, DeviceState);
+
+ Info("%s -> %s\n",
+ PowerSystemStateName(__FdoGetSystemPowerState(Fdo)),
+ PowerSystemStateName(SystemState));
+
+ if (SystemState >= PowerSystemHibernate &&
+ __FdoGetSystemPowerState(Fdo) < PowerSystemHibernate) {
+ __FdoSetSystemPowerState(Fdo, PowerSystemSleeping3);
+ FdoS3ToS4(Fdo);
+ }
+
+ __FdoSetSystemPowerState(Fdo, SystemState);
+
+ IoSkipCurrentIrpStackLocation(Irp);
+ status = IoCallDriver(Fdo->LowerDeviceObject, Irp);
+
+ return status;
+}
+
+static FORCEINLINE NTSTATUS
+__FdoSetSystemPower(
+ IN PXENVIF_FDO Fdo,
+ IN PIRP Irp
+ )
+{
+ PIO_STACK_LOCATION StackLocation;
+ SYSTEM_POWER_STATE SystemState;
+ POWER_ACTION PowerAction;
+ NTSTATUS status;
+
+ StackLocation = IoGetCurrentIrpStackLocation(Irp);
+ SystemState = StackLocation->Parameters.Power.State.SystemState;
+ PowerAction = StackLocation->Parameters.Power.ShutdownType;
+
+ Trace("====> (%s:%s)\n",
+ PowerSystemStateName(SystemState),
+ PowerActionName(PowerAction));
+
+ ASSERT3U(PowerAction, <, PowerActionShutdown);
+
+ if (SystemState == __FdoGetSystemPowerState(Fdo)) {
+ IoSkipCurrentIrpStackLocation(Irp);
+ status = IoCallDriver(Fdo->LowerDeviceObject, Irp);
+
+ goto done;
+ }
+
+ status = (SystemState < __FdoGetSystemPowerState(Fdo)) ?
+ __FdoSetSystemPowerUp(Fdo, Irp) :
+ __FdoSetSystemPowerDown(Fdo, Irp);
+
+done:
+ Trace("<==== (%s:%s)(%08x)\n",
+ PowerSystemStateName(SystemState),
+ PowerActionName(PowerAction),
+ status);
+ return status;
+}
+
+static FORCEINLINE NTSTATUS
+__FdoQueryDevicePowerUp(
+ IN PXENVIF_FDO Fdo,
+ IN PIRP Irp
+ )
+{
+ PIO_STACK_LOCATION StackLocation;
+ DEVICE_POWER_STATE DeviceState;
+ NTSTATUS status;
+
+ StackLocation = IoGetCurrentIrpStackLocation(Irp);
+ DeviceState = StackLocation->Parameters.Power.State.DeviceState;
+
+ ASSERT3U(DeviceState, <, __FdoGetDevicePowerState(Fdo));
+
+ status = FdoForwardIrpSynchronously(Fdo, Irp);
+
+ Irp->IoStatus.Status = status;
+ IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+ return status;
+}
+
+static FORCEINLINE NTSTATUS
+__FdoQueryDevicePowerDown(
+ IN PXENVIF_FDO Fdo,
+ IN PIRP Irp
+ )
+{
+ PIO_STACK_LOCATION StackLocation;
+ DEVICE_POWER_STATE DeviceState;
+ NTSTATUS status;
+
+ StackLocation = IoGetCurrentIrpStackLocation(Irp);
+ DeviceState = StackLocation->Parameters.Power.State.DeviceState;
+
+ ASSERT3U(DeviceState, >, __FdoGetDevicePowerState(Fdo));
+
+ IoSkipCurrentIrpStackLocation(Irp);
+ status = IoCallDriver(Fdo->LowerDeviceObject, Irp);
+
+ return status;
+}
+
+static FORCEINLINE NTSTATUS
+__FdoQueryDevicePower(
+ IN PXENVIF_FDO Fdo,
+ IN PIRP Irp
+ )
+{
+ PIO_STACK_LOCATION StackLocation;
+ DEVICE_POWER_STATE DeviceState;
+ POWER_ACTION PowerAction;
+ NTSTATUS status;
+
+ StackLocation = IoGetCurrentIrpStackLocation(Irp);
+ DeviceState = StackLocation->Parameters.Power.State.DeviceState;
+ PowerAction = StackLocation->Parameters.Power.ShutdownType;
+
+ Trace("====> (%s:%s)\n",
+ PowerDeviceStateName(DeviceState),
+ PowerActionName(PowerAction));
+
+ ASSERT3U(PowerAction, <, PowerActionShutdown);
+
+ if (DeviceState == __FdoGetDevicePowerState(Fdo)) {
+ IoSkipCurrentIrpStackLocation(Irp);
+ status = IoCallDriver(Fdo->LowerDeviceObject, Irp);
+
+ goto done;
+ }
+
+ status = (DeviceState < __FdoGetDevicePowerState(Fdo)) ?
+ __FdoQueryDevicePowerUp(Fdo, Irp) :
+ __FdoQueryDevicePowerDown(Fdo, Irp);
+
+done:
+ Trace("<==== (%s:%s)(%08x)\n",
+ PowerDeviceStateName(DeviceState),
+ PowerActionName(PowerAction),
+ status);
+ return status;
+}
+
+__drv_functionClass(REQUEST_POWER_COMPLETE)
+__drv_sameIRQL
+VOID
+__FdoRequestQueryDevicePower(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN UCHAR MinorFunction,
+ IN POWER_STATE PowerState,
+ IN PVOID Context,
+ IN PIO_STATUS_BLOCK IoStatus
+ )
+{
+ PKEVENT Event = Context;
+
+ UNREFERENCED_PARAMETER(DeviceObject);
+ UNREFERENCED_PARAMETER(MinorFunction);
+ UNREFERENCED_PARAMETER(PowerState);
+
+ ASSERT(NT_SUCCESS(IoStatus->Status));
+
+ KeSetEvent(Event, IO_NO_INCREMENT, FALSE);
+}
+
+static VOID
+FdoRequestQueryDevicePower(
+ IN PXENVIF_FDO Fdo,
+ IN DEVICE_POWER_STATE DeviceState
+ )
+{
+ POWER_STATE PowerState;
+ KEVENT Event;
+ NTSTATUS status;
+
+ Trace("%s\n", PowerDeviceStateName(DeviceState));
+
+ ASSERT3U(KeGetCurrentIrql(), ==, PASSIVE_LEVEL);
+
+ PowerState.DeviceState = DeviceState;
+ KeInitializeEvent(&Event, NotificationEvent, FALSE);
+
+ status = PoRequestPowerIrp(Fdo->LowerDeviceObject,
+ IRP_MN_QUERY_POWER,
+ PowerState,
+ __FdoRequestQueryDevicePower,
+ &Event,
+ NULL);
+ ASSERT(NT_SUCCESS(status));
+
+ (VOID) KeWaitForSingleObject(&Event,
+ Executive,
+ KernelMode,
+ FALSE,
+ NULL);
+}
+
+static FORCEINLINE NTSTATUS
+__FdoQuerySystemPowerUp(
+ IN PXENVIF_FDO Fdo,
+ IN PIRP Irp
+ )
+{
+
+ PIO_STACK_LOCATION StackLocation;
+ SYSTEM_POWER_STATE SystemState;
+ DEVICE_POWER_STATE DeviceState;
+ NTSTATUS status;
+
+ StackLocation = IoGetCurrentIrpStackLocation(Irp);
+ SystemState = StackLocation->Parameters.Power.State.SystemState;
+
+ ASSERT3U(SystemState, <, __FdoGetSystemPowerState(Fdo));
+
+ status = FdoForwardIrpSynchronously(Fdo, Irp);
+ if (!NT_SUCCESS(status))
+ goto done;
+
+ DeviceState = Fdo->LowerDeviceCapabilities.DeviceState[SystemState];
+
+ FdoRequestQueryDevicePower(Fdo, DeviceState);
+
+done:
+ Irp->IoStatus.Status = status;
+ IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+ return status;
+}
+
+static FORCEINLINE NTSTATUS
+__FdoQuerySystemPowerDown(
+ IN PXENVIF_FDO Fdo,
+ IN PIRP Irp
+ )
+{
+ PIO_STACK_LOCATION StackLocation;
+ SYSTEM_POWER_STATE SystemState;
+ DEVICE_POWER_STATE DeviceState;
+ NTSTATUS status;
+
+ StackLocation = IoGetCurrentIrpStackLocation(Irp);
+ SystemState = StackLocation->Parameters.Power.State.SystemState;
+
+ ASSERT3U(SystemState, >, __FdoGetSystemPowerState(Fdo));
+
+ DeviceState = Fdo->LowerDeviceCapabilities.DeviceState[SystemState];
+
+ FdoRequestQueryDevicePower(Fdo, DeviceState);
+
+ IoSkipCurrentIrpStackLocation(Irp);
+ status = IoCallDriver(Fdo->LowerDeviceObject, Irp);
+
+ return status;
+}
+
+static FORCEINLINE NTSTATUS
+__FdoQuerySystemPower(
+ IN PXENVIF_FDO Fdo,
+ IN PIRP Irp
+ )
+{
+ PIO_STACK_LOCATION StackLocation;
+ SYSTEM_POWER_STATE SystemState;
+ POWER_ACTION PowerAction;
+ NTSTATUS status;
+
+ StackLocation = IoGetCurrentIrpStackLocation(Irp);
+ SystemState = StackLocation->Parameters.Power.State.SystemState;
+ PowerAction = StackLocation->Parameters.Power.ShutdownType;
+
+ Trace("====> (%s:%s)\n",
+ PowerSystemStateName(SystemState),
+ PowerActionName(PowerAction));
+
+ ASSERT3U(PowerAction, <, PowerActionShutdown);
+
+ if (SystemState == __FdoGetSystemPowerState(Fdo)) {
+ IoSkipCurrentIrpStackLocation(Irp);
+ status = IoCallDriver(Fdo->LowerDeviceObject, Irp);
+
+ goto done;
+ }
+
+ status = (SystemState < __FdoGetSystemPowerState(Fdo)) ?
+ __FdoQuerySystemPowerUp(Fdo, Irp) :
+ __FdoQuerySystemPowerDown(Fdo, Irp);
+
+done:
+ Trace("<==== (%s:%s)(%08x)\n",
+ PowerSystemStateName(SystemState),
+ PowerActionName(PowerAction),
+ status);
+
+ return status;
+}
+
+static NTSTATUS
+FdoDevicePower(
+ IN PXENVIF_THREAD Self,
+ IN PVOID Context
+ )
+{
+ PXENVIF_FDO Fdo = Context;
+ PKEVENT Event;
+
+ Event = ThreadGetEvent(Self);
+
+ for (;;) {
+ PIRP Irp;
+ PIO_STACK_LOCATION StackLocation;
+ UCHAR MinorFunction;
+
+ if (Fdo->DevicePowerIrp == NULL) {
+ (VOID) KeWaitForSingleObject(Event,
+ Executive,
+ KernelMode,
+ FALSE,
+ NULL);
+ KeClearEvent(Event);
+ }
+
+ if (ThreadIsAlerted(Self))
+ break;
+
+ Irp = Fdo->DevicePowerIrp;
+
+ if (Irp == NULL)
+ continue;
+
+ Fdo->DevicePowerIrp = NULL;
+ KeMemoryBarrier();
+
+ StackLocation = IoGetCurrentIrpStackLocation(Irp);
+ MinorFunction = StackLocation->MinorFunction;
+
+ switch (StackLocation->MinorFunction) {
+ case IRP_MN_SET_POWER:
+ (VOID) __FdoSetDevicePower(Fdo, Irp);
+ break;
+
+ case IRP_MN_QUERY_POWER:
+ (VOID) __FdoQueryDevicePower(Fdo, Irp);
+ break;
+
+ default:
+ ASSERT(FALSE);
+ break;
+ }
+ }
+
+ return STATUS_SUCCESS;
+}
+
+static NTSTATUS
+FdoSystemPower(
+ IN PXENVIF_THREAD Self,
+ IN PVOID Context
+ )
+{
+ PXENVIF_FDO Fdo = Context;
+ PKEVENT Event;
+
+ Event = ThreadGetEvent(Self);
+
+ for (;;) {
+ PIRP Irp;
+ PIO_STACK_LOCATION StackLocation;
+ UCHAR MinorFunction;
+
+ if (Fdo->SystemPowerIrp == NULL) {
+ (VOID) KeWaitForSingleObject(Event,
+ Executive,
+ KernelMode,
+ FALSE,
+ NULL);
+ KeClearEvent(Event);
+ }
+
+ if (ThreadIsAlerted(Self))
+ break;
+
+ Irp = Fdo->SystemPowerIrp;
+
+ if (Irp == NULL)
+ continue;
+
+ Fdo->SystemPowerIrp = NULL;
+ KeMemoryBarrier();
+
+ StackLocation = IoGetCurrentIrpStackLocation(Irp);
+ MinorFunction = StackLocation->MinorFunction;
+
+ switch (StackLocation->MinorFunction) {
+ case IRP_MN_SET_POWER:
+ (VOID) __FdoSetSystemPower(Fdo, Irp);
+ break;
+
+ case IRP_MN_QUERY_POWER:
+ (VOID) __FdoQuerySystemPower(Fdo, Irp);
+ break;
+
+ default:
+ ASSERT(FALSE);
+ break;
+ }
+ }
+
+ return STATUS_SUCCESS;
+}
+
+static DECLSPEC_NOINLINE NTSTATUS
+FdoDispatchPower(
+ IN PXENVIF_FDO Fdo,
+ IN PIRP Irp
+ )
+{
+ PIO_STACK_LOCATION StackLocation;
+ UCHAR MinorFunction;
+ POWER_STATE_TYPE PowerType;
+ POWER_ACTION PowerAction;
+ NTSTATUS status;
+
+ StackLocation = IoGetCurrentIrpStackLocation(Irp);
+ MinorFunction = StackLocation->MinorFunction;
+
+ if (MinorFunction != IRP_MN_QUERY_POWER &&
+ MinorFunction != IRP_MN_SET_POWER) {
+ IoSkipCurrentIrpStackLocation(Irp);
+ status = IoCallDriver(Fdo->LowerDeviceObject, Irp);
+
+ goto done;
+ }
+
+ PowerType = StackLocation->Parameters.Power.Type;
+ PowerAction = StackLocation->Parameters.Power.ShutdownType;
+
+ if (PowerAction >= PowerActionShutdown) {
+ IoSkipCurrentIrpStackLocation(Irp);
+ status = IoCallDriver(Fdo->LowerDeviceObject, Irp);
+
+ goto done;
+ }
+
+ switch (PowerType) {
+ case DevicePowerState:
+ IoMarkIrpPending(Irp);
+
+ ASSERT3P(Fdo->DevicePowerIrp, ==, NULL);
+ Fdo->DevicePowerIrp = Irp;
+ KeMemoryBarrier();
+
+ ThreadWake(Fdo->DevicePowerThread);
+
+ status = STATUS_PENDING;
+ break;
+
+ case SystemPowerState:
+ IoMarkIrpPending(Irp);
+
+ ASSERT3P(Fdo->SystemPowerIrp, ==, NULL);
+ Fdo->SystemPowerIrp = Irp;
+ KeMemoryBarrier();
+
+ ThreadWake(Fdo->SystemPowerThread);
+
+ status = STATUS_PENDING;
+ break;
+
+ default:
+ IoSkipCurrentIrpStackLocation(Irp);
+ status = IoCallDriver(Fdo->LowerDeviceObject, Irp);
+ break;
+ }
+
+done:
+ return status;
+}
+
+static DECLSPEC_NOINLINE NTSTATUS
+FdoDispatchDefault(
+ IN PXENVIF_FDO Fdo,
+ IN PIRP Irp
+ )
+{
+ NTSTATUS status;
+
+ IoSkipCurrentIrpStackLocation(Irp);
+ status = IoCallDriver(Fdo->LowerDeviceObject, Irp);
+
+ return status;
+}
+
+NTSTATUS
+FdoDispatch(
+ IN PXENVIF_FDO Fdo,
+ IN PIRP Irp
+ )
+{
+ PIO_STACK_LOCATION StackLocation;
+ NTSTATUS status;
+
+ StackLocation = IoGetCurrentIrpStackLocation(Irp);
+
+ switch (StackLocation->MajorFunction) {
+ case IRP_MJ_PNP:
+ status = FdoDispatchPnp(Fdo, Irp);
+ break;
+
+ case IRP_MJ_POWER:
+ status = FdoDispatchPower(Fdo, Irp);
+ break;
+
+ default:
+ status = FdoDispatchDefault(Fdo, Irp);
+ break;
+ }
+
+ return status;
+}
+
+static NTSTATUS
+FdoQueryEvtchnInterface(
+ IN PXENVIF_FDO Fdo
+ )
+{
+ KEVENT Event;
+ IO_STATUS_BLOCK StatusBlock;
+ PIRP Irp;
+ PIO_STACK_LOCATION StackLocation;
+ INTERFACE Interface;
+ NTSTATUS status;
+
+ ASSERT3U(KeGetCurrentIrql(), ==, PASSIVE_LEVEL);
+
+ KeInitializeEvent(&Event, NotificationEvent, FALSE);
+ RtlZeroMemory(&StatusBlock, sizeof(IO_STATUS_BLOCK));
+
+ Irp = IoBuildSynchronousFsdRequest(IRP_MJ_PNP,
+ Fdo->LowerDeviceObject,
+ NULL,
+ 0,
+ NULL,
+ &Event,
+ &StatusBlock);
+
+ status = STATUS_UNSUCCESSFUL;
+ if (Irp == NULL)
+ goto fail1;
+
+ StackLocation = IoGetNextIrpStackLocation(Irp);
+ StackLocation->MinorFunction = IRP_MN_QUERY_INTERFACE;
+
+ StackLocation->Parameters.QueryInterface.InterfaceType = &GUID_EVTCHN_INTERFACE;
+ StackLocation->Parameters.QueryInterface.Size = sizeof (INTERFACE);
+ StackLocation->Parameters.QueryInterface.Version = EVTCHN_INTERFACE_VERSION;
+ StackLocation->Parameters.QueryInterface.Interface = &Interface;
+
+ Irp->IoStatus.Status = STATUS_NOT_SUPPORTED;
+
+ status = IoCallDriver(Fdo->LowerDeviceObject, Irp);
+ if (status == STATUS_PENDING) {
+ (VOID) KeWaitForSingleObject(&Event,
+ Executive,
+ KernelMode,
+ FALSE,
+ NULL);
+ status = StatusBlock.Status;
+ }
+
+ if (!NT_SUCCESS(status))
+ goto fail2;
+
+ status = STATUS_INVALID_PARAMETER;
+ if (Interface.Version != EVTCHN_INTERFACE_VERSION)
+ goto fail3;
+
+ Fdo->EvtchnInterface = Interface.Context;
+
+ return STATUS_SUCCESS;
+
+fail3:
+ Error("fail3\n");
+
+fail2:
+ Error("fail2\n");
+
+fail1:
+ Error("fail1 (%08x)\n", status);
+
+ return status;
+}
+
+static NTSTATUS
+FdoQueryDebugInterface(
+ IN PXENVIF_FDO Fdo
+ )
+{
+ KEVENT Event;
+ IO_STATUS_BLOCK StatusBlock;
+ PIRP Irp;
+ PIO_STACK_LOCATION StackLocation;
+ INTERFACE Interface;
+ NTSTATUS status;
+
+ ASSERT3U(KeGetCurrentIrql(), ==, PASSIVE_LEVEL);
+
+ KeInitializeEvent(&Event, NotificationEvent, FALSE);
+ RtlZeroMemory(&StatusBlock, sizeof(IO_STATUS_BLOCK));
+
+ Irp = IoBuildSynchronousFsdRequest(IRP_MJ_PNP,
+ Fdo->LowerDeviceObject,
+ NULL,
+ 0,
+ NULL,
+ &Event,
+ &StatusBlock);
+
+ status = STATUS_UNSUCCESSFUL;
+ if (Irp == NULL)
+ goto fail1;
+
+ StackLocation = IoGetNextIrpStackLocation(Irp);
+ StackLocation->MinorFunction = IRP_MN_QUERY_INTERFACE;
+
+ StackLocation->Parameters.QueryInterface.InterfaceType = &GUID_DEBUG_INTERFACE;
+ StackLocation->Parameters.QueryInterface.Size = sizeof (INTERFACE);
+ StackLocation->Parameters.QueryInterface.Version = DEBUG_INTERFACE_VERSION;
+ StackLocation->Parameters.QueryInterface.Interface = &Interface;
+
+ Irp->IoStatus.Status = STATUS_NOT_SUPPORTED;
+
+ status = IoCallDriver(Fdo->LowerDeviceObject, Irp);
+ if (status == STATUS_PENDING) {
+ (VOID) KeWaitForSingleObject(&Event,
+ Executive,
+ KernelMode,
+ FALSE,
+ NULL);
+ status = StatusBlock.Status;
+ }
+
+ if (!NT_SUCCESS(status))
+ goto fail2;
+
+ status = STATUS_INVALID_PARAMETER;
+ if (Interface.Version != DEBUG_INTERFACE_VERSION)
+ goto fail3;
+
+ Fdo->DebugInterface = Interface.Context;
+
+ return STATUS_SUCCESS;
+
+fail3:
+ Error("fail3\n");
+
+fail2:
+ Error("fail2\n");
+
+fail1:
+ Error("fail1 (%08x)\n", status);
+
+ return status;
+}
+
+static NTSTATUS
+FdoQueryStoreInterface(
+ IN PXENVIF_FDO Fdo
+ )
+{
+ KEVENT Event;
+ IO_STATUS_BLOCK StatusBlock;
+ PIRP Irp;
+ PIO_STACK_LOCATION StackLocation;
+ INTERFACE Interface;
+ NTSTATUS status;
+
+ ASSERT3U(KeGetCurrentIrql(), ==, PASSIVE_LEVEL);
+
+ KeInitializeEvent(&Event, NotificationEvent, FALSE);
+ RtlZeroMemory(&StatusBlock, sizeof(IO_STATUS_BLOCK));
+
+ Irp = IoBuildSynchronousFsdRequest(IRP_MJ_PNP,
+ Fdo->LowerDeviceObject,
+ NULL,
+ 0,
+ NULL,
+ &Event,
+ &StatusBlock);
+
+ status = STATUS_UNSUCCESSFUL;
+ if (Irp == NULL)
+ goto fail1;
+
+ StackLocation = IoGetNextIrpStackLocation(Irp);
+ StackLocation->MinorFunction = IRP_MN_QUERY_INTERFACE;
+
+ StackLocation->Parameters.QueryInterface.InterfaceType = &GUID_STORE_INTERFACE;
+ StackLocation->Parameters.QueryInterface.Size = sizeof (INTERFACE);
+ StackLocation->Parameters.QueryInterface.Version = STORE_INTERFACE_VERSION;
+ StackLocation->Parameters.QueryInterface.Interface = &Interface;
+
+ Irp->IoStatus.Status = STATUS_NOT_SUPPORTED;
+
+ status = IoCallDriver(Fdo->LowerDeviceObject, Irp);
+ if (status == STATUS_PENDING) {
+ (VOID) KeWaitForSingleObject(&Event,
+ Executive,
+ KernelMode,
+ FALSE,
+ NULL);
+ status = StatusBlock.Status;
+ }
+
+ if (!NT_SUCCESS(status))
+ goto fail2;
+
+ status = STATUS_INVALID_PARAMETER;
+ if (Interface.Version != STORE_INTERFACE_VERSION)
+ goto fail3;
+
+ Fdo->StoreInterface = Interface.Context;
+
+ return STATUS_SUCCESS;
+
+fail3:
+ Error("fail3\n");
+
+fail2:
+ Error("fail2\n");
+
+fail1:
+ Error("fail1 (%08x)\n", status);
+
+ return status;
+}
+
+static NTSTATUS
+FdoQueryGnttabInterface(
+ IN PXENVIF_FDO Fdo
+ )
+{
+ KEVENT Event;
+ IO_STATUS_BLOCK StatusBlock;
+ PIRP Irp;
+ PIO_STACK_LOCATION StackLocation;
+ INTERFACE Interface;
+ NTSTATUS status;
+
+ ASSERT3U(KeGetCurrentIrql(), ==, PASSIVE_LEVEL);
+
+ KeInitializeEvent(&Event, NotificationEvent, FALSE);
+ RtlZeroMemory(&StatusBlock, sizeof(IO_STATUS_BLOCK));
+
+ Irp = IoBuildSynchronousFsdRequest(IRP_MJ_PNP,
+ Fdo->LowerDeviceObject,
+ NULL,
+ 0,
+ NULL,
+ &Event,
+ &StatusBlock);
+
+ status = STATUS_UNSUCCESSFUL;
+ if (Irp == NULL)
+ goto fail1;
+
+ StackLocation = IoGetNextIrpStackLocation(Irp);
+ StackLocation->MinorFunction = IRP_MN_QUERY_INTERFACE;
+
+ StackLocation->Parameters.QueryInterface.InterfaceType = &GUID_GNTTAB_INTERFACE;
+ StackLocation->Parameters.QueryInterface.Size = sizeof (INTERFACE);
+ StackLocation->Parameters.QueryInterface.Version = GNTTAB_INTERFACE_VERSION;
+ StackLocation->Parameters.QueryInterface.Interface = &Interface;
+
+ Irp->IoStatus.Status = STATUS_NOT_SUPPORTED;
+
+ status = IoCallDriver(Fdo->LowerDeviceObject, Irp);
+ if (status == STATUS_PENDING) {
+ (VOID) KeWaitForSingleObject(&Event,
+ Executive,
+ KernelMode,
+ FALSE,
+ NULL);
+ status = StatusBlock.Status;
+ }
+
+ if (!NT_SUCCESS(status))
+ goto fail2;
+
+ status = STATUS_INVALID_PARAMETER;
+ if (Interface.Version != GNTTAB_INTERFACE_VERSION)
+ goto fail3;
+
+ Fdo->GnttabInterface = Interface.Context;
+
+ return STATUS_SUCCESS;
+
+fail3:
+ Error("fail3\n");
+
+fail2:
+ Error("fail2\n");
+
+fail1:
+ Error("fail1 (%08x)\n", status);
+
+ return status;
+}
+
+static NTSTATUS
+FdoQuerySuspendInterface(
+ IN PXENVIF_FDO Fdo
+ )
+{
+ KEVENT Event;
+ IO_STATUS_BLOCK StatusBlock;
+ PIRP Irp;
+ PIO_STACK_LOCATION StackLocation;
+ INTERFACE Interface;
+ NTSTATUS status;
+
+ ASSERT3U(KeGetCurrentIrql(), ==, PASSIVE_LEVEL);
+
+ KeInitializeEvent(&Event, NotificationEvent, FALSE);
+ RtlZeroMemory(&StatusBlock, sizeof(IO_STATUS_BLOCK));
+
+ Irp = IoBuildSynchronousFsdRequest(IRP_MJ_PNP,
+ Fdo->LowerDeviceObject,
+ NULL,
+ 0,
+ NULL,
+ &Event,
+ &StatusBlock);
+
+ status = STATUS_UNSUCCESSFUL;
+ if (Irp == NULL)
+ goto fail1;
+
+ StackLocation = IoGetNextIrpStackLocation(Irp);
+ StackLocation->MinorFunction = IRP_MN_QUERY_INTERFACE;
+
+ StackLocation->Parameters.QueryInterface.InterfaceType = &GUID_SUSPEND_INTERFACE;
+ StackLocation->Parameters.QueryInterface.Size = sizeof (INTERFACE);
+ StackLocation->Parameters.QueryInterface.Version = SUSPEND_INTERFACE_VERSION;
+ StackLocation->Parameters.QueryInterface.Interface = &Interface;
+
+ Irp->IoStatus.Status = STATUS_NOT_SUPPORTED;
+
+ status = IoCallDriver(Fdo->LowerDeviceObject, Irp);
+ if (status == STATUS_PENDING) {
+ (VOID) KeWaitForSingleObject(&Event,
+ Executive,
+ KernelMode,
+ FALSE,
+ NULL);
+ status = StatusBlock.Status;
+ }
+
+ if (!NT_SUCCESS(status))
+ goto fail2;
+
+ status = STATUS_INVALID_PARAMETER;
+ if (Interface.Version != SUSPEND_INTERFACE_VERSION)
+ goto fail3;
+
+ Fdo->SuspendInterface = Interface.Context;
+
+ return STATUS_SUCCESS;
+
+fail3:
+ Error("fail3\n");
+
+fail2:
+ Error("fail2\n");
+
+fail1:
+ Error("fail1 (%08x)\n", status);
+
+ return status;
+}
+
+static NTSTATUS
+FdoQueryEmulatedInterface(
+ IN PXENVIF_FDO Fdo
+ )
+{
+ KEVENT Event;
+ IO_STATUS_BLOCK StatusBlock;
+ PIRP Irp;
+ PIO_STACK_LOCATION StackLocation;
+ INTERFACE Interface;
+ NTSTATUS status;
+
+ ASSERT3U(KeGetCurrentIrql(), ==, PASSIVE_LEVEL);
+
+ KeInitializeEvent(&Event, NotificationEvent, FALSE);
+ RtlZeroMemory(&StatusBlock, sizeof(IO_STATUS_BLOCK));
+
+ Irp = IoBuildSynchronousFsdRequest(IRP_MJ_PNP,
+ Fdo->LowerDeviceObject,
+ NULL,
+ 0,
+ NULL,
+ &Event,
+ &StatusBlock);
+
+ status = STATUS_UNSUCCESSFUL;
+ if (Irp == NULL)
+ goto fail1;
+
+ StackLocation = IoGetNextIrpStackLocation(Irp);
+ StackLocation->MinorFunction = IRP_MN_QUERY_INTERFACE;
+
+ StackLocation->Parameters.QueryInterface.InterfaceType = &GUID_EMULATED_INTERFACE;
+ StackLocation->Parameters.QueryInterface.Size = sizeof (INTERFACE);
+ StackLocation->Parameters.QueryInterface.Version = EMULATED_INTERFACE_VERSION;
+ StackLocation->Parameters.QueryInterface.Interface = &Interface;
+
+ Irp->IoStatus.Status = STATUS_NOT_SUPPORTED;
+
+ status = IoCallDriver(Fdo->LowerDeviceObject, Irp);
+ if (status == STATUS_PENDING) {
+ (VOID) KeWaitForSingleObject(&Event,
+ Executive,
+ KernelMode,
+ FALSE,
+ NULL);
+ status = StatusBlock.Status;
+ }
+
+ if (!NT_SUCCESS(status))
+ goto fail2;
+
+ status = STATUS_INVALID_PARAMETER;
+ if (Interface.Version != EMULATED_INTERFACE_VERSION)
+ goto fail3;
+
+ Fdo->EmulatedInterface = Interface.Context;
+
+ return STATUS_SUCCESS;
+
+fail3:
+ Error("fail3\n");
+
+fail2:
+ Error("fail2\n");
+
+fail1:
+ Error("fail1 (%08x)\n", status);
+
+ return status;
+}
+
+NTSTATUS
+FdoCreate(
+ IN PDEVICE_OBJECT PhysicalDeviceObject
+ )
+{
+ PDEVICE_OBJECT FunctionDeviceObject;
+ PXENVIF_DX Dx;
+ PXENVIF_FDO Fdo;
+ WCHAR Name[MAXNAMELEN * sizeof (WCHAR)];
+ ULONG Size;
+ NTSTATUS status;
+
+#pragma prefast(suppress:28197) // Possibly leaking memory 'FunctionDeviceObject'
+ status = IoCreateDevice(DriverObject,
+ sizeof (XENVIF_DX),
+ NULL,
+ FILE_DEVICE_BUS_EXTENDER,
+ FILE_DEVICE_SECURE_OPEN,
+ FALSE,
+ &FunctionDeviceObject);
+ if (!NT_SUCCESS(status))
+ goto fail1;
+
+ Dx = (PXENVIF_DX)FunctionDeviceObject->DeviceExtension;
+ RtlZeroMemory(Dx, sizeof (XENVIF_DX));
+
+ Dx->Type = FUNCTION_DEVICE_OBJECT;
+ Dx->DeviceObject = FunctionDeviceObject;
+ Dx->DevicePnpState = Added;
+ Dx->SystemPowerState = PowerSystemShutdown;
+ Dx->DevicePowerState = PowerDeviceD3;
+
+ Fdo = __FdoAllocate(sizeof (XENVIF_FDO));
+
+ status = STATUS_NO_MEMORY;
+ if (Fdo == NULL)
+ goto fail2;
+
+ Fdo->Dx = Dx;
+ Fdo->PhysicalDeviceObject = PhysicalDeviceObject;
+ Fdo->LowerDeviceObject = IoAttachDeviceToDeviceStack(FunctionDeviceObject,
+ PhysicalDeviceObject);
+
+ status = ThreadCreate(FdoSystemPower, Fdo, &Fdo->SystemPowerThread);
+ if (!NT_SUCCESS(status))
+ goto fail3;
+
+ status = ThreadCreate(FdoDevicePower, Fdo, &Fdo->DevicePowerThread);
+ if (!NT_SUCCESS(status))
+ goto fail4;
+
+ status = IoGetDeviceProperty(PhysicalDeviceObject,
+ DevicePropertyLocationInformation,
+ sizeof (Name),
+ Name,
+ &Size);
+ if (!NT_SUCCESS(status))
+ goto fail5;
+
+ status = __FdoSetName(Fdo, Name);
+ if (!NT_SUCCESS(status))
+ goto fail6;
+
+ status = FdoQueryEvtchnInterface(Fdo);
+ if (!NT_SUCCESS(status))
+ goto fail7;
+
+ status = FdoQueryDebugInterface(Fdo);
+ if (!NT_SUCCESS(status))
+ goto fail8;
+
+ status = FdoQueryStoreInterface(Fdo);
+ if (!NT_SUCCESS(status))
+ goto fail9;
+
+ status = FdoQueryGnttabInterface(Fdo);
+ if (!NT_SUCCESS(status))
+ goto fail10;
+
+ status = FdoQuerySuspendInterface(Fdo);
+ if (!NT_SUCCESS(status))
+ goto fail11;
+
+ status = FdoQueryEmulatedInterface(Fdo);
+ ASSERT(IMPLY(!NT_SUCCESS(status),
+ __FdoGetEmulatedInterface(Fdo) == NULL));
+
+ InitializeMutex(&Fdo->Mutex);
+ InitializeListHead(&Dx->ListEntry);
+ Fdo->References = 1;
+
+ Info("%p (%s)\n",
+ FunctionDeviceObject,
+ __FdoGetName(Fdo));
+
+ Dx->Fdo = Fdo;
+ FunctionDeviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
+
+ return STATUS_SUCCESS;
+
+fail11:
+ Error("fail11\n");
+
+ Fdo->GnttabInterface = NULL;
+
+fail10:
+ Error("fail10\n");
+
+ Fdo->StoreInterface = NULL;
+
+fail9:
+ Error("fail9\n");
+
+ Fdo->DebugInterface = NULL;
+
+fail8:
+ Error("fail8\n");
+
+ Fdo->EvtchnInterface = NULL;
+
+fail7:
+ Error("fail7\n");
+
+fail6:
+ Error("fail6\n");
+
+fail5:
+ Error("fail5\n");
+
+ ThreadAlert(Fdo->DevicePowerThread);
+ ThreadJoin(Fdo->DevicePowerThread);
+ Fdo->DevicePowerThread = NULL;
+
+fail4:
+ Error("fail4\n");
+
+ ThreadAlert(Fdo->SystemPowerThread);
+ ThreadJoin(Fdo->SystemPowerThread);
+ Fdo->SystemPowerThread = NULL;
+
+fail3:
+ Error("fail3\n");
+
+#pragma prefast(suppress:28183) // Fdo->LowerDeviceObject could be NULL
+ IoDetachDevice(Fdo->LowerDeviceObject);
+
+ Fdo->PhysicalDeviceObject = NULL;
+ Fdo->LowerDeviceObject = NULL;
+ Fdo->Dx = NULL;
+
+ ASSERT(IsZeroMemory(Fdo, sizeof (XENVIF_FDO)));
+ __FdoFree(Fdo);
+
+fail2:
+ Error("fail2\n");
+
+ IoDeleteDevice(FunctionDeviceObject);
+
+fail1:
+ Error("fail1 (%08x)\n", status);
+
+ return status;
+}
+
+VOID
+FdoDestroy(
+ IN PXENVIF_FDO Fdo
+ )
+{
+ PXENVIF_DX Dx = Fdo->Dx;
+ PDEVICE_OBJECT FunctionDeviceObject = Dx->DeviceObject;
+
+ ASSERT(IsListEmpty(&Dx->ListEntry));
+ ASSERT3U(Fdo->References, ==, 0);
+ ASSERT3U(__FdoGetDevicePnpState(Fdo), ==, Deleted);
+
+ Fdo->NotDisableable = FALSE;
+
+ Info("%p (%s)\n",
+ FunctionDeviceObject,
+ __FdoGetName(Fdo));
+
+ Dx->Fdo = NULL;
+
+ RtlZeroMemory(&Fdo->Mutex, sizeof (XENVIF_MUTEX));
+
+ Fdo->EmulatedInterface = NULL;
+ Fdo->SuspendInterface = NULL;
+ Fdo->GnttabInterface = NULL;
+ Fdo->StoreInterface = NULL;
+ Fdo->DebugInterface = NULL;
+ Fdo->EvtchnInterface = NULL;
+
+ ThreadAlert(Fdo->DevicePowerThread);
+ ThreadJoin(Fdo->DevicePowerThread);
+ Fdo->DevicePowerThread = NULL;
+
+ ThreadAlert(Fdo->SystemPowerThread);
+ ThreadJoin(Fdo->SystemPowerThread);
+ Fdo->SystemPowerThread = NULL;
+
+ IoDetachDevice(Fdo->LowerDeviceObject);
+
+ RtlZeroMemory(&Fdo->LowerDeviceCapabilities, sizeof (DEVICE_CAPABILITIES));
+ Fdo->LowerDeviceObject = NULL;
+ Fdo->PhysicalDeviceObject = NULL;
+ Fdo->Dx = NULL;
+
+ ASSERT(IsZeroMemory(Fdo, sizeof (XENVIF_FDO)));
+ __FdoFree(Fdo);
+
+ IoDeleteDevice(FunctionDeviceObject);
+}
+
+
--- /dev/null
+/* Copyright (c) Citrix Systems Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms,
+ * with or without modification, are permitted provided
+ * that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer in the documentation and/or other
+ * materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef _XENVIF_FDO_H
+#define _XENVIF_FDO_H
+
+#include <ntddk.h>
+#include <evtchn_interface.h>
+#include <debug_interface.h>
+#include <store_interface.h>
+#include <gnttab_interface.h>
+#include <suspend_interface.h>
+#include <emulated_interface.h>
+
+#include "driver.h"
+#include "types.h"
+
+extern PCHAR
+FdoGetName(
+ IN PXENVIF_FDO Fdo
+ );
+
+extern NTSTATUS
+FdoCreate(
+ IN PDEVICE_OBJECT PhysicalDeviceObject
+ );
+
+extern VOID
+FdoDestroy(
+ IN PXENVIF_FDO Fdo
+ );
+
+extern VOID
+FdoAddPhysicalDeviceObject(
+ IN PXENVIF_FDO Fdo,
+ IN PXENVIF_PDO Pdo
+ );
+
+extern VOID
+FdoRemovePhysicalDeviceObject(
+ IN PXENVIF_FDO Fdo,
+ IN PXENVIF_PDO Pdo
+ );
+
+extern VOID
+FdoAcquireMutex(
+ IN PXENVIF_FDO Fdo
+ );
+
+extern VOID
+FdoReleaseMutex(
+ IN PXENVIF_FDO Fdo
+ );
+
+extern PDEVICE_OBJECT
+FdoGetPhysicalDeviceObject(
+ IN PXENVIF_FDO Fdo
+ );
+
+extern VOID
+FdoReap(
+ IN PXENVIF_FDO Fdo
+ );
+
+extern NTSTATUS
+FdoDelegateIrp(
+ IN PXENVIF_FDO Fdo,
+ IN PIRP Irp
+ );
+
+extern PXENBUS_EVTCHN_INTERFACE
+FdoGetEvtchnInterface(
+ IN PXENVIF_FDO Fdo
+ );
+
+extern PXENBUS_DEBUG_INTERFACE
+FdoGetDebugInterface(
+ IN PXENVIF_FDO Fdo
+ );
+
+extern PXENBUS_STORE_INTERFACE
+FdoGetStoreInterface(
+ IN PXENVIF_FDO Fdo
+ );
+
+extern PXENBUS_GNTTAB_INTERFACE
+FdoGetGnttabInterface(
+ IN PXENVIF_FDO Fdo
+ );
+
+extern PXENBUS_SUSPEND_INTERFACE
+FdoGetSuspendInterface(
+ IN PXENVIF_FDO Fdo
+ );
+
+extern PXENFILT_EMULATED_INTERFACE
+FdoGetEmulatedInterface(
+ IN PXENVIF_FDO Fdo
+ );
+
+extern NTSTATUS
+FdoDispatch(
+ IN PXENVIF_FDO Fdo,
+ IN PIRP Irp
+ );
+
+#endif // _XENVIF_FDO_H
--- /dev/null
+/* Copyright (c) Citrix Systems Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms,
+ * with or without modification, are permitted provided
+ * that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer in the documentation and/or other
+ * materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <ntddk.h>
+#include <ntstrsafe.h>
+#include <stdlib.h>
+#include <netioapi.h>
+#include <util.h>
+#include <xen.h>
+#include <store_interface.h>
+#include <gnttab_interface.h>
+#include <vif_interface.h>
+
+#include "driver.h"
+#include "registry.h"
+#include "fdo.h"
+#include "pdo.h"
+#include "thread.h"
+#include "frontend.h"
+#include "names.h"
+#include "notifier.h"
+#include "mac.h"
+#include "tcpip.h"
+#include "receiver.h"
+#include "transmitter.h"
+#include "log.h"
+#include "assert.h"
+
+struct _XENVIF_FRONTEND {
+ PXENVIF_PDO Pdo;
+ PCHAR Path;
+ PCHAR Prefix;
+ XENVIF_FRONTEND_STATE State;
+ KSPIN_LOCK Lock;
+ PXENVIF_THREAD MibThread;
+ PXENVIF_THREAD EjectThread;
+ BOOLEAN EjectRequested;
+ PXENBUS_STORE_WATCH Watch;
+ PCHAR BackendPath;
+ USHORT BackendDomain;
+ PXENVIF_NOTIFIER Notifier;
+ PXENVIF_MAC Mac;
+ PXENVIF_RECEIVER Receiver;
+ PXENVIF_TRANSMITTER Transmitter;
+
+ PXENBUS_SUSPEND_INTERFACE SuspendInterface;
+ PXENBUS_STORE_INTERFACE StoreInterface;
+ PXENBUS_DEBUG_INTERFACE DebugInterface;
+
+ PXENBUS_SUSPEND_CALLBACK SuspendCallbackLate;
+ PXENBUS_DEBUG_CALLBACK DebugCallback;
+ HANDLE Handle;
+};
+
+static const PCHAR
+FrontendStateName(
+ IN XENVIF_FRONTEND_STATE State
+ )
+{
+#define _STATE_NAME(_State) \
+ case FRONTEND_ ## _State: \
+ return #_State;
+
+ switch (State) {
+ _STATE_NAME(CLOSED);
+ _STATE_NAME(PREPARED);
+ _STATE_NAME(CONNECTED);
+ _STATE_NAME(ENABLED);
+ default:
+ break;
+ }
+
+ return "INVALID";
+
+#undef _STATE_NAME
+}
+
+#define FRONTEND_POOL 'NORF'
+
+static FORCEINLINE PVOID
+__FrontendAllocate(
+ IN ULONG Length
+ )
+{
+ return __AllocateNonPagedPoolWithTag(Length, FRONTEND_POOL);
+}
+
+static FORCEINLINE VOID
+__FrontendFree(
+ IN PVOID Buffer
+ )
+{
+ __FreePoolWithTag(Buffer, FRONTEND_POOL);
+}
+
+static FORCEINLINE PXENVIF_PDO
+__FrontendGetPdo(
+ IN PXENVIF_FRONTEND Frontend
+ )
+{
+ return Frontend->Pdo;
+}
+
+static FORCEINLINE PXENBUS_EVTCHN_INTERFACE
+__FrontendGetEvtchnInterface(
+ IN PXENVIF_FRONTEND Frontend
+ )
+{
+ return PdoGetEvtchnInterface(__FrontendGetPdo(Frontend));
+}
+
+PXENBUS_EVTCHN_INTERFACE
+FrontendGetEvtchnInterface(
+ IN PXENVIF_FRONTEND Frontend
+ )
+{
+ return __FrontendGetEvtchnInterface(Frontend);
+}
+
+static FORCEINLINE PXENBUS_DEBUG_INTERFACE
+__FrontendGetDebugInterface(
+ IN PXENVIF_FRONTEND Frontend
+ )
+{
+ return PdoGetDebugInterface(__FrontendGetPdo(Frontend));
+}
+
+PXENBUS_DEBUG_INTERFACE
+FrontendGetDebugInterface(
+ IN PXENVIF_FRONTEND Frontend
+ )
+{
+ return __FrontendGetDebugInterface(Frontend);
+}
+
+static FORCEINLINE PXENBUS_SUSPEND_INTERFACE
+__FrontendGetSuspendInterface(
+ IN PXENVIF_FRONTEND Frontend
+ )
+{
+ return PdoGetSuspendInterface(__FrontendGetPdo(Frontend));
+}
+
+PXENBUS_SUSPEND_INTERFACE
+FrontendGetSuspendInterface(
+ IN PXENVIF_FRONTEND Frontend
+ )
+{
+ return __FrontendGetSuspendInterface(Frontend);
+}
+
+static FORCEINLINE PXENBUS_STORE_INTERFACE
+__FrontendGetStoreInterface(
+ IN PXENVIF_FRONTEND Frontend
+ )
+{
+ return PdoGetStoreInterface(__FrontendGetPdo(Frontend));
+}
+
+PXENBUS_STORE_INTERFACE
+FrontendGetStoreInterface(
+ IN PXENVIF_FRONTEND Frontend
+ )
+{
+ return __FrontendGetStoreInterface(Frontend);
+}
+
+static FORCEINLINE PXENBUS_GNTTAB_INTERFACE
+__FrontendGetGnttabInterface(
+ IN PXENVIF_FRONTEND Frontend
+ )
+{
+ return PdoGetGnttabInterface(__FrontendGetPdo(Frontend));
+}
+
+PXENBUS_GNTTAB_INTERFACE
+FrontendGetGnttabInterface(
+ IN PXENVIF_FRONTEND Frontend
+ )
+{
+ return __FrontendGetGnttabInterface(Frontend);
+}
+
+static FORCEINLINE PXENVIF_VIF_INTERFACE
+__FrontendGetVifInterface(
+ IN PXENVIF_FRONTEND Frontend
+ )
+{
+ return PdoGetVifInterface(__FrontendGetPdo(Frontend));
+}
+
+PXENVIF_VIF_INTERFACE
+FrontendGetVifInterface(
+ IN PXENVIF_FRONTEND Frontend
+ )
+{
+ return __FrontendGetVifInterface(Frontend);
+}
+
+static FORCEINLINE PCHAR
+__FrontendGetPath(
+ IN PXENVIF_FRONTEND Frontend
+ )
+{
+ return Frontend->Path;
+}
+
+PCHAR
+FrontendGetPath(
+ IN PXENVIF_FRONTEND Frontend
+ )
+{
+ return __FrontendGetPath(Frontend);
+}
+
+static FORCEINLINE PCHAR
+__FrontendGetPrefix(
+ IN PXENVIF_FRONTEND Frontend
+ )
+{
+ return Frontend->Prefix;
+}
+
+PCHAR
+FrontendGetPrefix(
+ IN PXENVIF_FRONTEND Frontend
+ )
+{
+ return __FrontendGetPrefix(Frontend);
+}
+
+static FORCEINLINE PCHAR
+__FrontendGetBackendPath(
+ IN PXENVIF_FRONTEND Frontend
+ )
+{
+ return Frontend->BackendPath;
+}
+
+PCHAR
+FrontendGetBackendPath(
+ IN PXENVIF_FRONTEND Frontend
+ )
+{
+ return __FrontendGetBackendPath(Frontend);
+}
+
+static FORCEINLINE USHORT
+__FrontendGetBackendDomain(
+ IN PXENVIF_FRONTEND Frontend
+ )
+{
+ return Frontend->BackendDomain;
+}
+
+USHORT
+FrontendGetBackendDomain(
+ IN PXENVIF_FRONTEND Frontend
+ )
+{
+ return __FrontendGetBackendDomain(Frontend);
+}
+
+static FORCEINLINE PXENVIF_NOTIFIER
+__FrontendGetNotifier(
+ IN PXENVIF_FRONTEND Frontend
+ )
+{
+ return Frontend->Notifier;
+}
+
+PXENVIF_NOTIFIER
+FrontendGetNotifier(
+ IN PXENVIF_FRONTEND Frontend
+ )
+{
+ return __FrontendGetNotifier(Frontend);
+}
+
+static FORCEINLINE PXENVIF_MAC
+__FrontendGetMac(
+ IN PXENVIF_FRONTEND Frontend
+ )
+{
+ return Frontend->Mac;
+}
+
+PXENVIF_MAC
+FrontendGetMac(
+ IN PXENVIF_FRONTEND Frontend
+ )
+{
+ return __FrontendGetMac(Frontend);
+}
+
+static FORCEINLINE PXENVIF_RECEIVER
+__FrontendGetReceiver(
+ IN PXENVIF_FRONTEND Frontend
+ )
+{
+ return Frontend->Receiver;
+}
+
+PXENVIF_RECEIVER
+FrontendGetReceiver(
+ IN PXENVIF_FRONTEND Frontend
+ )
+{
+ return __FrontendGetReceiver(Frontend);
+}
+
+static FORCEINLINE PXENVIF_TRANSMITTER
+__FrontendGetTransmitter(
+ IN PXENVIF_FRONTEND Frontend
+ )
+{
+ return Frontend->Transmitter;
+}
+
+PXENVIF_TRANSMITTER
+FrontendGetTransmitter(
+ IN PXENVIF_FRONTEND Frontend
+ )
+{
+ return __FrontendGetTransmitter(Frontend);
+}
+
+static FORCEINLINE ULONG
+__FrontendGetLuidIndex(
+ IN PXENVIF_FRONTEND Frontend
+ )
+{
+ return PdoGetLuidIndex(__FrontendGetPdo(Frontend));
+}
+
+static FORCEINLINE PCHAR
+__FrontendGetAddress(
+ IN PXENVIF_FRONTEND Frontend
+ )
+{
+ return PdoGetAddress(__FrontendGetPdo(Frontend));
+}
+
+static DECLSPEC_NOINLINE NTSTATUS
+FrontendEject(
+ IN PXENVIF_THREAD Self,
+ IN PVOID Context
+ )
+{
+ PXENVIF_FRONTEND Frontend = Context;
+ PKEVENT Event;
+
+ Trace("%s: ====>\n", __FrontendGetPath(Frontend));
+
+ Event = ThreadGetEvent(Self);
+
+ for (;;) {
+ KIRQL Irql;
+ BOOLEAN Online;
+ XenbusState State;
+ ULONG Attempt;
+ NTSTATUS status;
+
+ KeWaitForSingleObject(Event,
+ Executive,
+ KernelMode,
+ FALSE,
+ NULL);
+ KeClearEvent(Event);
+
+ if (ThreadIsAlerted(Self))
+ break;
+
+ KeAcquireSpinLock(&Frontend->Lock, &Irql);
+
+ if (Frontend->State == FRONTEND_CLOSED) {
+ KeReleaseSpinLock(&Frontend->Lock, Irql);
+ continue;
+ }
+
+ STORE(Acquire, Frontend->StoreInterface);
+
+ Online = TRUE;
+ State = XenbusStateUnknown;
+
+ Attempt = 0;
+ for (;;) {
+ PXENBUS_STORE_TRANSACTION Transaction;
+ PCHAR Buffer;
+
+ status = STATUS_UNSUCCESSFUL;
+ if (__FrontendGetBackendPath(Frontend) == NULL)
+ break;
+
+ status = STORE(TransactionStart,
+ Frontend->StoreInterface,
+ &Transaction);
+ if (!NT_SUCCESS(status))
+ break;
+
+ status = STORE(Read,
+ Frontend->StoreInterface,
+ Transaction,
+ __FrontendGetBackendPath(Frontend),
+ "online",
+ &Buffer);
+ if (!NT_SUCCESS(status))
+ goto abort;
+
+ Online = (BOOLEAN)strtol(Buffer, NULL, 2);
+
+ STORE(Free,
+ Frontend->StoreInterface,
+ Buffer);
+
+ status = STORE(Read,
+ Frontend->StoreInterface,
+ Transaction,
+ __FrontendGetBackendPath(Frontend),
+ "state",
+ &Buffer);
+ if (!NT_SUCCESS(status))
+ goto abort;
+
+ State = (XenbusState)strtol(Buffer, NULL, 10);
+
+ STORE(Free,
+ Frontend->StoreInterface,
+ Buffer);
+
+ status = STORE(TransactionEnd,
+ Frontend->StoreInterface,
+ Transaction,
+ TRUE);
+ if (status != STATUS_RETRY || ++Attempt > 10)
+ break;
+
+ continue;
+
+abort:
+ (VOID) STORE(TransactionEnd,
+ Frontend->StoreInterface,
+ Transaction,
+ FALSE);
+ break;
+ }
+
+ if (!NT_SUCCESS(status)) {
+ Online = TRUE;
+ State = XenbusStateUnknown;
+ }
+
+ if (!Online && State == XenbusStateClosing) {
+ Info("%s: requesting device eject\n", __FrontendGetPath(Frontend));
+
+ PdoRequestEject(Frontend->Pdo);
+ Frontend->EjectRequested = TRUE;
+ }
+
+ STORE(Release, Frontend->StoreInterface);
+
+ KeReleaseSpinLock(&Frontend->Lock, Irql);
+ }
+
+ Trace("%s: <====\n", __FrontendGetPath(Frontend));
+
+ return STATUS_SUCCESS;
+}
+
+static FORCEINLINE NTSTATUS
+__FrontendGetAddressTable(
+ IN PXENVIF_FRONTEND Frontend,
+ OUT PSOCKADDR_INET *AddressTable,
+ OUT PULONG AddressCount
+ )
+{
+ ULONG LuidIndex;
+ ULONG Index;
+ PMIB_UNICASTIPADDRESS_TABLE MibTable;
+ ULONG Count;
+ NTSTATUS status;
+
+ LuidIndex = __FrontendGetLuidIndex(Frontend);
+
+ status = GetUnicastIpAddressTable(AF_UNSPEC, &MibTable);
+ if (!NT_SUCCESS(status))
+ goto fail1;
+
+ *AddressCount = 0;
+ for (Index = 0; Index < MibTable->NumEntries; Index++) {
+ PMIB_UNICASTIPADDRESS_ROW Row = &MibTable->Table[Index];
+
+ if (Row->InterfaceLuid.Info.IfType != IF_TYPE_ETHERNET_CSMACD)
+ continue;
+
+ if (Row->InterfaceLuid.Info.NetLuidIndex != LuidIndex)
+ continue;
+
+ if (Row->Address.si_family != AF_INET &&
+ Row->Address.si_family != AF_INET6)
+ continue;
+
+ (*AddressCount)++;
+ }
+
+ *AddressTable = NULL;
+
+ if (*AddressCount == 0)
+ goto done;
+
+ *AddressTable = __FrontendAllocate(sizeof (SOCKADDR_INET) * *AddressCount);
+
+ status = STATUS_NO_MEMORY;
+ if (*AddressTable == NULL)
+ goto fail2;
+
+ Count = 0;
+
+ for (Index = 0; Index < MibTable->NumEntries; Index++) {
+ PMIB_UNICASTIPADDRESS_ROW Row = &MibTable->Table[Index];
+
+ if (Row->InterfaceLuid.Info.IfType != IF_TYPE_ETHERNET_CSMACD)
+ continue;
+
+ if (Row->InterfaceLuid.Info.NetLuidIndex != LuidIndex)
+ continue;
+
+ switch (Row->Address.si_family) {
+ case AF_INET:
+ case AF_INET6:
+ (*AddressTable)[Count++] = Row->Address;
+ break;
+ default:
+ break;
+ }
+ }
+ ASSERT3U(Count, ==, *AddressCount);
+
+done:
+ FreeMibTable(MibTable);
+
+ return STATUS_SUCCESS;
+
+fail2:
+ Error("fail2\n");
+
+ FreeMibTable(MibTable);
+
+fail1:
+ Error("fail1 (%08x)\n", status);
+
+ return status;
+}
+
+static DECLSPEC_NOINLINE NTSTATUS
+FrontendMib(
+ IN PXENVIF_THREAD Self,
+ IN PVOID Context
+ )
+{
+ PXENVIF_FRONTEND Frontend = Context;
+ PKEVENT Event;
+
+ Trace("====>\n");
+
+ Event = ThreadGetEvent(Self);
+
+ for (;;) {
+ PSOCKADDR_INET Table;
+ ULONG Count;
+ NTSTATUS status;
+
+ (VOID) KeWaitForSingleObject(Event,
+ Executive,
+ KernelMode,
+ FALSE,
+ NULL);
+ KeClearEvent(Event);
+
+ if (ThreadIsAlerted(Self))
+ break;
+
+ status = __FrontendGetAddressTable(Frontend,
+ &Table,
+ &Count);
+ if (!NT_SUCCESS(status))
+ continue;
+
+ TransmitterUpdateAddressTable(__FrontendGetTransmitter(Frontend),
+ Table,
+ Count);
+
+ if (Count != 0)
+ __FrontendFree(Table);
+ }
+
+ Trace("<====\n");
+
+ return STATUS_SUCCESS;
+}
+
+static VOID
+FrontendIpAddressChange(
+ IN PVOID Context,
+ IN PMIB_UNICASTIPADDRESS_ROW _Row OPTIONAL,
+ IN MIB_NOTIFICATION_TYPE NotificationType
+ )
+{
+ PXENVIF_FRONTEND Frontend = Context;
+
+ UNREFERENCED_PARAMETER(_Row);
+ UNREFERENCED_PARAMETER(NotificationType);
+
+ ThreadWake(Frontend->MibThread);
+}
+
+#define TIME_US(_us) ((_us) * 10)
+#define TIME_MS(_ms) (TIME_US((_ms) * 1000))
+#define TIME_S(_s) (TIME_MS((_s) * 1000))
+#define TIME_RELATIVE(_t) (-(_t))
+
+static FORCEINLINE NTSTATUS
+__FrontendWaitForStateChange(
+ IN PXENVIF_FRONTEND Frontend,
+ IN PCHAR Path,
+ IN XenbusState *State
+ )
+{
+ KEVENT Event;
+ PXENBUS_STORE_WATCH Watch;
+ LARGE_INTEGER Start;
+ ULONGLONG TimeDelta;
+ LARGE_INTEGER Timeout;
+ XenbusState Old = *State;
+ NTSTATUS status;
+
+ Trace("%s: ====> (%s)\n", Path, XenbusStateName(*State));
+
+ KeInitializeEvent(&Event, NotificationEvent, FALSE);
+
+ status = STORE(Watch,
+ Frontend->StoreInterface,
+ Path,
+ "state",
+ &Event,
+ &Watch);
+ if (!NT_SUCCESS(status))
+ goto fail1;
+
+ KeQuerySystemTime(&Start);
+ TimeDelta = 0;
+
+ Timeout.QuadPart = 0;
+
+ while (*State == Old && TimeDelta < 120000) {
+ ULONG Attempt;
+ PCHAR Buffer;
+ LARGE_INTEGER Now;
+
+ Attempt = 0;
+ while (++Attempt < 1000) {
+ status = KeWaitForSingleObject(&Event,
+ Executive,
+ KernelMode,
+ FALSE,
+ &Timeout);
+ if (status != STATUS_TIMEOUT)
+ break;
+
+ // We are waiting for a watch event at DISPATCH_LEVEL so
+ // it is our responsibility to poll the store ring.
+ STORE(Poll,
+ Frontend->StoreInterface);
+
+ KeStallExecutionProcessor(1000); // 1ms
+ }
+
+ KeClearEvent(&Event);
+
+ status = STORE(Read,
+ Frontend->StoreInterface,
+ NULL,
+ Path,
+ "state",
+ &Buffer);
+ if (!NT_SUCCESS(status))
+ goto fail2;
+
+ *State = (XenbusState)strtol(Buffer, NULL, 10);
+
+ STORE(Free,
+ Frontend->StoreInterface,
+ Buffer);
+
+ KeQuerySystemTime(&Now);
+
+ TimeDelta = (Now.QuadPart - Start.QuadPart) / 10000ull;
+ }
+
+ status = STATUS_UNSUCCESSFUL;
+ if (*State == Old)
+ goto fail3;
+
+ (VOID) STORE(Unwatch,
+ Frontend->StoreInterface,
+ Watch);
+
+ Trace("%s: <==== (%s)\n", Path, XenbusStateName(*State));
+
+ return STATUS_SUCCESS;
+
+fail3:
+ Error("fail3\n");
+
+fail2:
+ Error("fail2\n");
+
+ (VOID) STORE(Unwatch,
+ Frontend->StoreInterface,
+ Watch);
+
+fail1:
+ Error("fail1 (%08x)\n", status);
+
+ return status;
+}
+
+static FORCEINLINE NTSTATUS
+__FrontendClose(
+ IN PXENVIF_FRONTEND Frontend
+ )
+{
+ PCHAR Path;
+ XenbusState State;
+ NTSTATUS status;
+
+ Trace("====>\n");
+
+ ASSERT(Frontend->Watch != NULL);
+ (VOID) STORE(Unwatch,
+ Frontend->StoreInterface,
+ Frontend->Watch);
+ Frontend->Watch = NULL;
+
+ // Release cached information about the backend
+ ASSERT(Frontend->BackendPath != NULL);
+ STORE(Free,
+ Frontend->StoreInterface,
+ Frontend->BackendPath);
+ Frontend->BackendPath = NULL;
+
+ Frontend->BackendDomain = DOMID_INVALID;
+
+ status = STORE(Read,
+ Frontend->StoreInterface,
+ NULL,
+ __FrontendGetPath(Frontend),
+ "backend",
+ &Path);
+ if (!NT_SUCCESS(status))
+ goto fail1;
+
+ State = XenbusStateInitialising;
+ status = __FrontendWaitForStateChange(Frontend, Path, &State);
+ if (!NT_SUCCESS(status))
+ goto fail2;
+
+ while (State != XenbusStateClosing &&
+ State != XenbusStateClosed &&
+ State != XenbusStateUnknown) {
+ (VOID) STORE(Printf,
+ Frontend->StoreInterface,
+ NULL,
+ __FrontendGetPath(Frontend),
+ "state",
+ "%u",
+ XenbusStateClosing);
+ status = __FrontendWaitForStateChange(Frontend, Path, &State);
+ if (!NT_SUCCESS(status))
+ goto fail2;
+ }
+
+ while (State != XenbusStateClosed &&
+ State != XenbusStateUnknown) {
+ (VOID) STORE(Printf,
+ Frontend->StoreInterface,
+ NULL,
+ __FrontendGetPath(Frontend),
+ "state",
+ "%u",
+ XenbusStateClosed);
+ status = __FrontendWaitForStateChange(Frontend, Path, &State);
+ if (!NT_SUCCESS(status))
+ goto fail3;
+ }
+
+ STORE(Free,
+ Frontend->StoreInterface,
+ Path);
+
+ STORE(Release, Frontend->StoreInterface);
+ Frontend->StoreInterface = NULL;
+
+ Trace("<====\n");
+ return STATUS_SUCCESS;
+
+fail3:
+ Error("fail3\n");
+
+fail2:
+ Error("fail2\n");
+
+ STORE(Free,
+ Frontend->StoreInterface,
+ Path);
+
+fail1:
+ Error("fail1 (%08x)\n", status);
+
+ return status;
+}
+
+static FORCEINLINE NTSTATUS
+__FrontendPrepare(
+ IN PXENVIF_FRONTEND Frontend
+ )
+{
+ PCHAR Path;
+ XenbusState State;
+ PCHAR Buffer;
+ NTSTATUS status;
+
+ Trace("====>\n");
+
+ Frontend->StoreInterface = __FrontendGetStoreInterface(Frontend);
+
+ STORE(Acquire, Frontend->StoreInterface);
+
+ status = STORE(Read,
+ Frontend->StoreInterface,
+ NULL,
+ __FrontendGetPath(Frontend),
+ "backend",
+ &Path);
+ if (!NT_SUCCESS(status))
+ goto fail1;
+
+ State = XenbusStateUnknown;
+ status = __FrontendWaitForStateChange(Frontend, Path, &State);
+ if (!NT_SUCCESS(status))
+ goto fail2;
+
+ while (State != XenbusStateClosed &&
+ State != XenbusStateInitialising &&
+ State != XenbusStateInitWait) {
+ status = __FrontendWaitForStateChange(Frontend, Path, &State);
+ if (!NT_SUCCESS(status))
+ goto fail3;
+ }
+
+ status = STORE(Printf,
+ Frontend->StoreInterface,
+ NULL,
+ __FrontendGetPath(Frontend),
+ "state",
+ "%u",
+ XenbusStateInitialising);
+ if (!NT_SUCCESS(status))
+ goto fail4;
+
+ while (State == XenbusStateClosed ||
+ State == XenbusStateInitialising) {
+ status = __FrontendWaitForStateChange(Frontend, Path, &State);
+ if (!NT_SUCCESS(status))
+ goto fail5;
+ }
+
+ status = STATUS_UNSUCCESSFUL;
+ if (State != XenbusStateInitWait)
+ goto fail6;
+
+ Frontend->BackendPath = Path;
+
+ status = STORE(Read,
+ Frontend->StoreInterface,
+ NULL,
+ __FrontendGetPath(Frontend),
+ "backend-id",
+ &Buffer);
+ if (!NT_SUCCESS(status)) {
+ Frontend->BackendDomain = 0;
+ } else {
+ Frontend->BackendDomain = (USHORT)strtol(Buffer, NULL, 10);
+
+ STORE(Free,
+ Frontend->StoreInterface,
+ Buffer);
+ }
+
+ status = STORE(Watch,
+ Frontend->StoreInterface,
+ __FrontendGetBackendPath(Frontend),
+ "online",
+ ThreadGetEvent(Frontend->EjectThread),
+ &Frontend->Watch);
+ if (!NT_SUCCESS(status))
+ goto fail7;
+
+ Trace("<====\n");
+ return STATUS_SUCCESS;
+
+fail7:
+ Error("fail7\n");
+
+ Frontend->BackendDomain = DOMID_INVALID;
+ Frontend->BackendPath = NULL;
+
+fail6:
+ Error("fail6\n");
+
+fail5:
+ Error("fail5\n");
+
+fail4:
+ Error("fail4\n");
+
+fail3:
+ Error("fail3\n");
+
+fail2:
+ Error("fail2\n");
+
+ STORE(Free,
+ Frontend->StoreInterface,
+ Path);
+
+fail1:
+ Error("fail1 (%08x)\n", status);
+
+ STORE(Release, Frontend->StoreInterface);
+ Frontend->StoreInterface = NULL;
+
+ Trace("<====\n");
+ return status;
+}
+
+static VOID
+FrontendDebugCallback(
+ IN PVOID Argument,
+ IN BOOLEAN Crashing
+ )
+{
+ PXENVIF_FRONTEND Frontend = Argument;
+
+ UNREFERENCED_PARAMETER(Crashing);
+
+ DEBUG(Printf,
+ Frontend->DebugInterface,
+ Frontend->DebugCallback,
+ "PATH: %s\n",
+ __FrontendGetPath(Frontend));
+}
+
+static FORCEINLINE NTSTATUS
+__FrontendConnect(
+ IN PXENVIF_FRONTEND Frontend
+ )
+{
+ PCHAR Path = __FrontendGetBackendPath(Frontend);
+ XenbusState State;
+ ULONG Attempt;
+ NTSTATUS status;
+
+ Trace("====>\n");
+
+ Frontend->DebugInterface = __FrontendGetDebugInterface(Frontend);
+
+ DEBUG(Acquire, Frontend->DebugInterface);
+
+ status = DEBUG(Register,
+ Frontend->DebugInterface,
+ __MODULE__ "|FRONTEND",
+ FrontendDebugCallback,
+ Frontend,
+ &Frontend->DebugCallback);
+ if (!NT_SUCCESS(status))
+ goto fail1;
+
+ status = MacConnect(__FrontendGetMac(Frontend));
+ if (!NT_SUCCESS(status))
+ goto fail2;
+
+ status = ReceiverConnect(__FrontendGetReceiver(Frontend));
+ if (!NT_SUCCESS(status))
+ goto fail3;
+
+ status = TransmitterConnect(__FrontendGetTransmitter(Frontend));
+ if (!NT_SUCCESS(status))
+ goto fail4;
+
+ status = NotifierConnect(__FrontendGetNotifier(Frontend));
+ if (!NT_SUCCESS(status))
+ goto fail5;
+
+ Attempt = 0;
+ do {
+ PXENBUS_STORE_TRANSACTION Transaction;
+
+ status = STORE(TransactionStart,
+ Frontend->StoreInterface,
+ &Transaction);
+ if (!NT_SUCCESS(status))
+ break;
+
+ status = NotifierStoreWrite(__FrontendGetNotifier(Frontend),
+ Transaction);
+ if (!NT_SUCCESS(status))
+ goto abort;
+
+ status = ReceiverStoreWrite(__FrontendGetReceiver(Frontend),
+ Transaction);
+ if (!NT_SUCCESS(status))
+ goto abort;
+
+ status = TransmitterStoreWrite(__FrontendGetTransmitter(Frontend),
+ Transaction);
+ if (!NT_SUCCESS(status))
+ goto abort;
+
+ status = STORE(TransactionEnd,
+ Frontend->StoreInterface,
+ Transaction,
+ TRUE);
+ if (status != STATUS_RETRY || ++Attempt > 10)
+ break;
+
+ continue;
+
+abort:
+ (VOID) STORE(TransactionEnd,
+ Frontend->StoreInterface,
+ Transaction,
+ FALSE);
+ break;
+ } while (status == STATUS_RETRY);
+
+ if (!NT_SUCCESS(status))
+ goto fail6;
+
+ status = STORE(Printf,
+ Frontend->StoreInterface,
+ NULL,
+ __FrontendGetPath(Frontend),
+ "state",
+ "%u",
+ XenbusStateConnected);
+ if (!NT_SUCCESS(status))
+ goto fail7;
+
+ State = XenbusStateInitWait;
+ status = __FrontendWaitForStateChange(Frontend, Path, &State);
+ if (!NT_SUCCESS(status))
+ goto fail8;
+
+ status = STATUS_UNSUCCESSFUL;
+ if (State != XenbusStateConnected)
+ goto fail9;
+
+ Trace("<====\n");
+ return STATUS_SUCCESS;
+
+fail9:
+ Error("fail9\n");
+
+fail8:
+ Error("fail8\n");
+
+fail7:
+ Error("fail7\n");
+
+fail6:
+ Error("fail7\n");
+
+ NotifierDisconnect(__FrontendGetNotifier(Frontend));
+
+fail5:
+ Error("fail5\n");
+
+ TransmitterDisconnect(__FrontendGetTransmitter(Frontend));
+
+fail4:
+ Error("fail4\n");
+
+ ReceiverDisconnect(__FrontendGetReceiver(Frontend));
+
+fail3:
+ Error("fail3\n");
+
+ MacDisconnect(__FrontendGetMac(Frontend));
+
+fail2:
+ Error("fail2\n");
+
+ DEBUG(Deregister,
+ Frontend->DebugInterface,
+ Frontend->DebugCallback);
+ Frontend->DebugCallback = NULL;
+
+fail1:
+ Error("fail1 (%08x)\n", status);
+
+ DEBUG(Release, Frontend->DebugInterface);
+ Frontend->DebugInterface = NULL;
+
+ Trace("<====\n");
+ return status;
+}
+
+static FORCEINLINE VOID
+__FrontendDisconnect(
+ IN PXENVIF_FRONTEND Frontend
+ )
+{
+ Trace("====>\n");
+
+ NotifierDisconnect(__FrontendGetNotifier(Frontend));
+ TransmitterDisconnect(__FrontendGetTransmitter(Frontend));
+ ReceiverDisconnect(__FrontendGetReceiver(Frontend));
+ MacDisconnect(__FrontendGetMac(Frontend));
+
+ DEBUG(Deregister,
+ Frontend->DebugInterface,
+ Frontend->DebugCallback);
+ Frontend->DebugCallback = NULL;
+
+ DEBUG(Release, Frontend->DebugInterface);
+ Frontend->DebugInterface = NULL;
+
+ Trace("<====\n");
+}
+
+static FORCEINLINE NTSTATUS
+__FrontendEnable(
+ IN PXENVIF_FRONTEND Frontend
+ )
+{
+ NTSTATUS status;
+
+ Trace("====>\n");
+
+ status = MacEnable(__FrontendGetMac(Frontend));
+ if (!NT_SUCCESS(status))
+ goto fail1;
+
+ status = ReceiverEnable(__FrontendGetReceiver(Frontend));
+ if (!NT_SUCCESS(status))
+ goto fail2;
+
+ status = TransmitterEnable(__FrontendGetTransmitter(Frontend));
+ if (!NT_SUCCESS(status))
+ goto fail3;
+
+ status = NotifierEnable(__FrontendGetNotifier(Frontend));
+ if (!NT_SUCCESS(status))
+ goto fail4;
+
+ Trace("<====\n");
+ return STATUS_SUCCESS;
+
+fail4:
+ Error("fail4\n");
+
+ TransmitterDisable(__FrontendGetTransmitter(Frontend));
+
+fail3:
+ Error("fail3\n");
+
+ ReceiverDisable(__FrontendGetReceiver(Frontend));
+
+fail2:
+ Error("fail2\n");
+
+ MacDisable(__FrontendGetMac(Frontend));
+
+fail1:
+ Error("fail1 (%08x)\n", status);
+
+ return status;
+}
+
+static FORCEINLINE VOID
+__FrontendDisable(
+ IN PXENVIF_FRONTEND Frontend
+ )
+{
+ Trace("====>\n");
+
+ NotifierDisable(__FrontendGetNotifier(Frontend));
+ TransmitterDisable(__FrontendGetTransmitter(Frontend));
+ ReceiverDisable(__FrontendGetReceiver(Frontend));
+ MacDisable(__FrontendGetMac(Frontend));
+
+ Trace("<====\n");
+}
+
+NTSTATUS
+FrontendSetState(
+ IN PXENVIF_FRONTEND Frontend,
+ IN XENVIF_FRONTEND_STATE State
+ )
+{
+ BOOLEAN Failed;
+ KIRQL Irql;
+
+ KeAcquireSpinLock(&Frontend->Lock, &Irql);
+
+ Trace("%s: ====> '%s' -> '%s'\n",
+ __FrontendGetPath(Frontend),
+ FrontendStateName(Frontend->State),
+ FrontendStateName(State));
+
+ Failed = FALSE;
+ while (Frontend->State != State && !Failed) {
+ NTSTATUS status;
+
+ switch (Frontend->State) {
+ case FRONTEND_CLOSED:
+ switch (State) {
+ case FRONTEND_PREPARED:
+ case FRONTEND_CONNECTED:
+ case FRONTEND_ENABLED:
+ status = __FrontendPrepare(Frontend);
+ if (NT_SUCCESS(status)) {
+ Frontend->State = FRONTEND_PREPARED;
+ } else {
+ Failed = TRUE;
+ }
+ break;
+
+ default:
+ ASSERT(FALSE);
+ break;
+ }
+ break;
+
+ case FRONTEND_PREPARED:
+ switch (State) {
+ case FRONTEND_CONNECTED:
+ case FRONTEND_ENABLED:
+ status = __FrontendConnect(Frontend);
+ if (NT_SUCCESS(status)) {
+ Frontend->State = FRONTEND_CONNECTED;
+ } else {
+ status = __FrontendClose(Frontend);
+ if (NT_SUCCESS(status))
+ Frontend->State = FRONTEND_CLOSED;
+ else
+ Frontend->State = FRONTEND_STATE_INVALID;
+
+ Failed = TRUE;
+ }
+ break;
+
+ case FRONTEND_CLOSED:
+ status = __FrontendClose(Frontend);
+ if (NT_SUCCESS(status)) {
+ Frontend->State = FRONTEND_CLOSED;
+ } else {
+ Frontend->State = FRONTEND_STATE_INVALID;
+ Failed = TRUE;
+ }
+
+ break;
+
+ default:
+ ASSERT(FALSE);
+ break;
+ }
+ break;
+
+ case FRONTEND_CONNECTED:
+ switch (State) {
+ case FRONTEND_ENABLED:
+ status = __FrontendEnable(Frontend);
+ if (NT_SUCCESS(status)) {
+ Frontend->State = FRONTEND_ENABLED;
+ } else {
+ status = __FrontendClose(Frontend);
+ if (NT_SUCCESS(status))
+ Frontend->State = FRONTEND_CLOSED;
+ else
+ Frontend->State = FRONTEND_STATE_INVALID;
+
+ Failed = TRUE;
+ }
+ break;
+
+ case FRONTEND_PREPARED:
+ case FRONTEND_CLOSED:
+ status = __FrontendClose(Frontend);
+ if (NT_SUCCESS(status)) {
+ Frontend->State = FRONTEND_CLOSED;
+ } else {
+ Frontend->State = FRONTEND_STATE_INVALID;
+ Failed = TRUE;
+ }
+
+ __FrontendDisconnect(Frontend);
+
+ break;
+
+ default:
+ ASSERT(FALSE);
+ break;
+ }
+ break;
+
+ case FRONTEND_ENABLED:
+ switch (State) {
+ case FRONTEND_CONNECTED:
+ case FRONTEND_PREPARED:
+ case FRONTEND_CLOSED:
+ __FrontendDisable(Frontend);
+ Frontend->State = FRONTEND_CONNECTED;
+ break;
+
+ default:
+ ASSERT(FALSE);
+ break;
+ }
+ break;
+
+ case FRONTEND_STATE_INVALID:
+ Failed = TRUE;
+ break;
+
+ default:
+ ASSERT(FALSE);
+ break;
+ }
+
+ Trace("%s in state '%s'\n",
+ __FrontendGetPath(Frontend),
+ FrontendStateName(Frontend->State));
+ }
+
+ KeReleaseSpinLock(&Frontend->Lock, Irql);
+
+ Trace("%s: <=====\n", __FrontendGetPath(Frontend));
+
+ return (!Failed) ? STATUS_SUCCESS : STATUS_UNSUCCESSFUL;
+}
+
+PCHAR
+FrontendGetAddress(
+ IN PXENVIF_FRONTEND Frontend
+ )
+{
+ return __FrontendGetAddress(Frontend);
+}
+
+static FORCEINLINE NTSTATUS
+__FrontendResume(
+ IN PXENVIF_FRONTEND Frontend
+ )
+{
+ NTSTATUS status;
+
+ ASSERT3U(KeGetCurrentIrql(), ==, DISPATCH_LEVEL);
+
+ status = FrontendSetState(Frontend, FRONTEND_PREPARED);
+ if (!NT_SUCCESS(status))
+ goto fail1;
+
+ status = FrontendSetState(Frontend, FRONTEND_CLOSED);
+ if (!NT_SUCCESS(status))
+ goto fail2;
+
+ return STATUS_SUCCESS;
+
+fail2:
+ Error("fail2\n");
+
+ STORE(Release, Frontend->StoreInterface);
+ Frontend->StoreInterface = NULL;
+
+ Frontend->State = FRONTEND_CLOSED;
+
+fail1:
+ Error("fail1 (%08x)\n", status);
+
+ return status;
+}
+
+static FORCEINLINE VOID
+__FrontendSuspend(
+ IN PXENVIF_FRONTEND Frontend
+ )
+{
+ UNREFERENCED_PARAMETER(Frontend);
+
+ ASSERT3U(KeGetCurrentIrql(), ==, DISPATCH_LEVEL);
+}
+
+static DECLSPEC_NOINLINE VOID
+FrontendSuspendCallbackLate(
+ IN PVOID Argument
+ )
+{
+ PXENVIF_FRONTEND Frontend = Argument;
+ NTSTATUS status;
+
+ __FrontendSuspend(Frontend);
+
+ status = __FrontendResume(Frontend);
+ ASSERT(NT_SUCCESS(status));
+}
+
+NTSTATUS
+FrontendResume(
+ IN PXENVIF_FRONTEND Frontend
+ )
+{
+ KIRQL Irql;
+ NTSTATUS status;
+
+ Trace("====>\n");
+
+ KeRaiseIrql(DISPATCH_LEVEL, &Irql);
+ status = __FrontendResume(Frontend);
+ KeLowerIrql(Irql);
+
+ if (!NT_SUCCESS(status))
+ goto fail1;
+
+ Frontend->SuspendInterface = __FrontendGetSuspendInterface(Frontend);
+
+ SUSPEND(Acquire, Frontend->SuspendInterface);
+
+ status = SUSPEND(Register,
+ Frontend->SuspendInterface,
+ SUSPEND_CALLBACK_LATE,
+ FrontendSuspendCallbackLate,
+ Frontend,
+ &Frontend->SuspendCallbackLate);
+ if (!NT_SUCCESS(status))
+ goto fail2;
+
+ Trace("<====\n");
+
+ return STATUS_SUCCESS;
+
+fail2:
+ Error("fail2\n");
+
+ SUSPEND(Release, Frontend->SuspendInterface);
+ Frontend->SuspendInterface = NULL;
+
+ KeRaiseIrql(DISPATCH_LEVEL, &Irql);
+ __FrontendSuspend(Frontend);
+ KeLowerIrql(Irql);
+
+fail1:
+ Error("fail1 (%08x)\n", status);
+
+ return status;
+}
+
+VOID
+FrontendSuspend(
+ IN PXENVIF_FRONTEND Frontend
+ )
+{
+ KIRQL Irql;
+
+ Trace("====>\n");
+
+ ASSERT3U(KeGetCurrentIrql(), ==, PASSIVE_LEVEL);
+
+ // FrontendResume() may have failed
+ if (Frontend->SuspendInterface == NULL)
+ goto done;
+
+ SUSPEND(Deregister,
+ Frontend->SuspendInterface,
+ Frontend->SuspendCallbackLate);
+ Frontend->SuspendCallbackLate = NULL;
+
+ SUSPEND(Release, Frontend->SuspendInterface);
+ Frontend->SuspendInterface = NULL;
+
+ KeRaiseIrql(DISPATCH_LEVEL, &Irql);
+ __FrontendSuspend(Frontend);
+ KeLowerIrql(Irql);
+
+done:
+ Trace("<====\n");
+}
+
+NTSTATUS
+FrontendInitialize(
+ IN PXENVIF_PDO Pdo,
+ OUT PXENVIF_FRONTEND *Frontend
+ )
+{
+ PCHAR Name;
+ ULONG Length;
+ PCHAR Path;
+ PCHAR Prefix;
+ NTSTATUS status;
+
+ Trace("====>\n");
+
+ Name = PdoGetName(Pdo);
+
+ Length = sizeof ("devices/vif/") + (ULONG)strlen(Name);
+ Path = __FrontendAllocate(Length);
+
+ status = STATUS_NO_MEMORY;
+ if (Path == NULL)
+ goto fail1;
+
+ status = RtlStringCbPrintfA(Path,
+ Length,
+ "device/vif/%s",
+ Name);
+ if (!NT_SUCCESS(status))
+ goto fail2;
+
+ Length = sizeof ("attr/eth") + (ULONG)strlen(Name);
+ Prefix = __FrontendAllocate(Length);
+
+ status = STATUS_NO_MEMORY;
+ if (Prefix == NULL)
+ goto fail3;
+
+ status = RtlStringCbPrintfA(Prefix,
+ Length,
+ "attr/eth%s",
+ Name);
+ if (!NT_SUCCESS(status))
+ goto fail4;
+
+ *Frontend = __FrontendAllocate(sizeof (XENVIF_FRONTEND));
+
+ status = STATUS_NO_MEMORY;
+ if (*Frontend == NULL)
+ goto fail5;
+
+ (*Frontend)->Pdo = Pdo;
+ (*Frontend)->Path = Path;
+ (*Frontend)->Prefix = Prefix;
+ (*Frontend)->BackendDomain = DOMID_INVALID;
+
+ KeInitializeSpinLock(&(*Frontend)->Lock);
+
+ (*Frontend)->State = FRONTEND_CLOSED;
+
+ status = ThreadCreate(FrontendEject, *Frontend, &(*Frontend)->EjectThread);
+ if (!NT_SUCCESS(status))
+ goto fail6;
+
+ status = MacInitialize(*Frontend, &(*Frontend)->Mac);
+ if (!NT_SUCCESS(status))
+ goto fail7;
+
+ status = NotifierInitialize(*Frontend, &(*Frontend)->Notifier);
+ if (!NT_SUCCESS(status))
+ goto fail8;
+
+ status = ReceiverInitialize(*Frontend, 1, &(*Frontend)->Receiver);
+ if (!NT_SUCCESS(status))
+ goto fail9;
+
+ status = TransmitterInitialize(*Frontend, 1, &(*Frontend)->Transmitter);
+ if (!NT_SUCCESS(status))
+ goto fail10;
+
+ status = ThreadCreate(FrontendMib, *Frontend, &(*Frontend)->MibThread);
+ if (!NT_SUCCESS(status))
+ goto fail11;
+
+ status = NotifyUnicastIpAddressChange(AF_UNSPEC,
+ FrontendIpAddressChange,
+ *Frontend,
+ TRUE,
+ &(*Frontend)->Handle);
+ if (!NT_SUCCESS(status))
+ goto fail12;
+
+ Trace("<====\n");
+
+ return STATUS_SUCCESS;
+
+fail12:
+ Error("fail12\n");
+
+ ThreadAlert((*Frontend)->MibThread);
+ ThreadJoin((*Frontend)->MibThread);
+ (*Frontend)->MibThread = NULL;
+
+fail11:
+ Error("fail11\n");
+
+ TransmitterTeardown(__FrontendGetTransmitter(*Frontend));
+ (*Frontend)->Transmitter = NULL;
+
+fail10:
+ Error("fail10\n");
+
+ ReceiverTeardown(__FrontendGetReceiver(*Frontend));
+ (*Frontend)->Receiver = NULL;
+
+fail9:
+ Error("fail9\n");
+
+ NotifierTeardown(__FrontendGetNotifier(*Frontend));
+ (*Frontend)->Notifier = NULL;
+
+fail8:
+ Error("fail8\n");
+
+ MacTeardown(__FrontendGetMac(*Frontend));
+ (*Frontend)->Mac = NULL;
+
+fail7:
+ Error("fail7\n");
+
+ ThreadAlert((*Frontend)->EjectThread);
+ ThreadJoin((*Frontend)->EjectThread);
+ (*Frontend)->EjectThread = NULL;
+
+fail6:
+ Error("fail6\n");
+
+ (*Frontend)->State = FRONTEND_STATE_INVALID;
+ RtlZeroMemory(&(*Frontend)->Lock, sizeof (KSPIN_LOCK));
+
+ (*Frontend)->BackendDomain = 0;
+ (*Frontend)->Prefix = NULL;
+ (*Frontend)->Path = NULL;
+ (*Frontend)->Pdo = NULL;
+
+ ASSERT(IsZeroMemory(*Frontend, sizeof (XENVIF_FRONTEND)));
+
+ __FrontendFree(*Frontend);
+ *Frontend = NULL;
+
+fail5:
+ Error("fail5\n");
+
+fail4:
+ Error("fail4\n");
+
+ __FrontendFree(Prefix);
+
+fail3:
+ Error("fail3\n");
+
+fail2:
+ Error("fail2\n");
+
+ __FrontendFree(Path);
+
+fail1:
+ Error("fail1 (%08x)\n", status);
+
+ return status;
+}
+
+VOID
+FrontendTeardown(
+ IN PXENVIF_FRONTEND Frontend
+ )
+{
+ Trace("====>\n");
+
+ ASSERT(Frontend->State != FRONTEND_ENABLED);
+ ASSERT(Frontend->State != FRONTEND_CONNECTED);
+
+ if (Frontend->State == FRONTEND_PREPARED) {
+ ASSERT(Frontend->Handle != NULL);
+ CancelMibChangeNotify2(Frontend->Handle);
+ Frontend->Handle = NULL;
+
+ ASSERT(Frontend->Watch != NULL);
+ (VOID) STORE(Unwatch,
+ Frontend->StoreInterface,
+ Frontend->Watch);
+ Frontend->Watch = NULL;
+
+ // Release cached information about the backend
+ ASSERT(Frontend->BackendPath != NULL);
+ STORE(Free,
+ Frontend->StoreInterface,
+ Frontend->BackendPath);
+ Frontend->BackendPath = NULL;
+
+ Frontend->BackendDomain = DOMID_INVALID;
+
+ Frontend->State = FRONTEND_CLOSED;
+ }
+
+ ASSERT3U(Frontend->State, ==, FRONTEND_CLOSED);
+
+ ASSERT(Frontend->Handle != NULL);
+ CancelMibChangeNotify2(Frontend->Handle);
+ Frontend->Handle = NULL;
+
+ ThreadAlert(Frontend->MibThread);
+ ThreadJoin(Frontend->MibThread);
+ Frontend->MibThread = NULL;
+
+ TransmitterTeardown(__FrontendGetTransmitter(Frontend));
+ Frontend->Transmitter = NULL;
+
+ ReceiverTeardown(__FrontendGetReceiver(Frontend));
+ Frontend->Receiver = NULL;
+
+ NotifierTeardown(__FrontendGetNotifier(Frontend));
+ Frontend->Notifier = NULL;
+
+ MacTeardown(__FrontendGetMac(Frontend));
+ Frontend->Mac = NULL;
+
+ ThreadAlert(Frontend->EjectThread);
+ ThreadJoin(Frontend->EjectThread);
+ Frontend->EjectThread = NULL;
+
+ Frontend->State = FRONTEND_STATE_INVALID;
+ RtlZeroMemory(&Frontend->Lock, sizeof (KSPIN_LOCK));
+
+ Frontend->BackendDomain = 0;
+
+ __FrontendFree(Frontend->Prefix);
+ Frontend->Prefix = NULL;
+
+ __FrontendFree(Frontend->Path);
+ Frontend->Path = NULL;
+
+ Frontend->Pdo = NULL;
+
+ ASSERT(IsZeroMemory(Frontend, sizeof (XENVIF_FRONTEND)));
+
+ __FrontendFree(Frontend);
+
+ Trace("<====\n");
+}
+
+VOID
+FrontendRemoveFailed(
+ IN PXENVIF_FRONTEND Frontend
+ )
+{
+ KIRQL Irql;
+ ULONG Length;
+ PCHAR Path;
+ NTSTATUS status;
+
+ KeAcquireSpinLock(&Frontend->Lock, &Irql);
+ if (!Frontend->EjectRequested)
+ goto done;
+
+ Frontend->EjectRequested = FALSE;
+
+ Info("%s: device eject failed\n", __FrontendGetPath(Frontend));
+
+ Length = sizeof ("error/") + (ULONG)strlen(__FrontendGetPath(Frontend));
+ Path = __FrontendAllocate(Length);
+
+ status = STATUS_NO_MEMORY;
+ if (Path == NULL)
+ goto fail1;
+
+ status = RtlStringCbPrintfA(Path,
+ Length,
+ "error/%s",
+ __FrontendGetPath(Frontend));
+ if (!NT_SUCCESS(status))
+ goto fail2;
+
+ (VOID) STORE(Printf,
+ Frontend->StoreInterface,
+ NULL,
+ Path,
+ "error",
+ "UNPLUG FAILED: device is still in use");
+
+ __FrontendFree(Path);
+
+done:
+ KeReleaseSpinLock(&Frontend->Lock, Irql);
+ return;
+
+fail2:
+ Error("fail2\n");
+
+ __FrontendFree(Path);
+
+fail1:
+ Error("fail1 (%08x)\n", status);
+
+ KeReleaseSpinLock(&Frontend->Lock, Irql);
+}
--- /dev/null
+/* Copyright (c) Citrix Systems Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms,
+ * with or without modification, are permitted provided
+ * that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer in the documentation and/or other
+ * materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef _XENVIF_FRONTEND_H
+#define _XENVIF_FRONTEND_H
+
+#include <ntddk.h>
+#include <evtchn_interface.h>
+#include <debug_interface.h>
+#include <store_interface.h>
+#include <gnttab_interface.h>
+#include <vif_interface.h>
+
+#include "pdo.h"
+#include "ethernet.h"
+
+typedef struct _XENVIF_FRONTEND XENVIF_FRONTEND, *PXENVIF_FRONTEND;
+
+typedef enum _XENVIF_FRONTEND_STATE {
+ FRONTEND_STATE_INVALID,
+ FRONTEND_CLOSED,
+ FRONTEND_PREPARED,
+ FRONTEND_CONNECTED,
+ FRONTEND_ENABLED
+} XENVIF_FRONTEND_STATE, *PXENVIF_FRONTEND_STATE;
+
+extern NTSTATUS
+FrontendInitialize(
+ IN PXENVIF_PDO Pdo,
+ OUT PXENVIF_FRONTEND *Frontend
+ );
+
+extern VOID
+FrontendTeardown(
+ IN PXENVIF_FRONTEND Frontend
+ );
+
+extern VOID
+FrontendRemoveFailed(
+ IN PXENVIF_FRONTEND Frontend
+ );
+
+extern NTSTATUS
+FrontendSetState(
+ IN PXENVIF_FRONTEND Frontend,
+ IN XENVIF_FRONTEND_STATE State
+ );
+
+extern NTSTATUS
+FrontendResume(
+ IN PXENVIF_FRONTEND Frontend
+ );
+
+extern VOID
+FrontendSuspend(
+ IN PXENVIF_FRONTEND Frontend
+ );
+
+extern NTSTATUS
+FrontendGetNetworkAddress(
+ IN PXENVIF_FRONTEND Frontend,
+ OUT PCHAR *Buffer
+ );
+
+extern NTSTATUS
+FrontendGetNetLuidIndex(
+ IN PXENVIF_FRONTEND Frontend,
+ OUT PULONG LuidIndex
+ );
+
+extern PCHAR
+FrontendGetPrefix(
+ IN PXENVIF_FRONTEND Frontend
+ );
+
+extern PCHAR
+FrontendGetPath(
+ IN PXENVIF_FRONTEND Frontend
+ );
+
+extern PCHAR
+FrontendGetBackendPath(
+ IN PXENVIF_FRONTEND Frontend
+ );
+
+extern USHORT
+FrontendGetBackendDomain(
+ IN PXENVIF_FRONTEND Frontend
+ );
+
+#include "notifier.h"
+
+extern PXENVIF_NOTIFIER
+FrontendGetNotifier(
+ IN PXENVIF_FRONTEND Frontend
+ );
+
+#include "mac.h"
+
+extern PXENVIF_MAC
+FrontendGetMac(
+ IN PXENVIF_FRONTEND Frontend
+ );
+
+#include "receiver.h"
+
+extern PXENVIF_RECEIVER
+FrontendGetReceiver(
+ IN PXENVIF_FRONTEND Frontend
+ );
+
+#include "transmitter.h"
+
+extern PXENVIF_TRANSMITTER
+FrontendGetTransmitter(
+ IN PXENVIF_FRONTEND Frontend
+ );
+
+extern PXENBUS_EVTCHN_INTERFACE
+FrontendGetEvtchnInterface(
+ IN PXENVIF_FRONTEND Frontend
+ );
+
+extern PXENBUS_DEBUG_INTERFACE
+FrontendGetDebugInterface(
+ IN PXENVIF_FRONTEND Frontend
+ );
+
+extern PXENBUS_SUSPEND_INTERFACE
+FrontendGetSuspendInterface(
+ IN PXENVIF_FRONTEND Frontend
+ );
+
+extern PXENBUS_STORE_INTERFACE
+FrontendGetStoreInterface(
+ IN PXENVIF_FRONTEND Frontend
+ );
+
+extern PXENBUS_GNTTAB_INTERFACE
+FrontendGetGnttabInterface(
+ IN PXENVIF_FRONTEND Frontend
+ );
+
+extern PXENVIF_VIF_INTERFACE
+FrontendGetVifInterface(
+ IN PXENVIF_FRONTEND Frontend
+ );
+
+extern PCHAR
+FrontendGetAddress(
+ IN PXENVIF_FRONTEND Frontend
+ );
+
+#endif // _XENVIF_FRONTEND_H
--- /dev/null
+/* Copyright (c) Citrix Systems Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms,
+ * with or without modification, are permitted provided
+ * that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer in the documentation and/or other
+ * materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef _XENVIF_LOG_H
+#define _XENVIF_LOG_H
+
+#include <ntddk.h>
+#include <stdarg.h>
+
+#pragma warning(disable:4127) // conditional expression is constant
+
+static __inline VOID
+__Error(
+ IN const CHAR *Prefix,
+ IN const CHAR *Format,
+ ...
+ )
+{
+ va_list Arguments;
+
+ va_start(Arguments, Format);
+
+#pragma prefast(suppress:6001) // Using uninitialized memory
+ vDbgPrintExWithPrefix(Prefix,
+ DPFLTR_IHVDRIVER_ID,
+ DPFLTR_ERROR_LEVEL,
+ Format,
+ Arguments);
+ va_end(Arguments);
+}
+
+#define Error(...) \
+ __Error(__MODULE__ "|" __FUNCTION__ ": ", __VA_ARGS__)
+
+static __inline VOID
+__Warning(
+ IN const CHAR *Prefix,
+ IN const CHAR *Format,
+ ...
+ )
+{
+ va_list Arguments;
+
+ va_start(Arguments, Format);
+
+#pragma prefast(suppress:6001) // Using uninitialized memory
+ vDbgPrintExWithPrefix(Prefix,
+ DPFLTR_IHVDRIVER_ID,
+ DPFLTR_WARNING_LEVEL,
+ Format,
+ Arguments);
+ va_end(Arguments);
+}
+
+#define Warning(...) \
+ __Warning(__MODULE__ "|" __FUNCTION__ ": ", __VA_ARGS__)
+
+#if DBG
+static __inline VOID
+__Trace(
+ IN const CHAR *Prefix,
+ IN const CHAR *Format,
+ ...
+ )
+{
+ va_list Arguments;
+
+ va_start(Arguments, Format);
+
+#pragma prefast(suppress:6001) // Using uninitialized memory
+ vDbgPrintExWithPrefix(Prefix,
+ DPFLTR_IHVDRIVER_ID,
+ DPFLTR_TRACE_LEVEL,
+ Format,
+ Arguments);
+ va_end(Arguments);
+}
+
+#define Trace(...) \
+ __Trace(__MODULE__ "|" __FUNCTION__ ": ", __VA_ARGS__)
+#else // DBG
+#define Trace(...) (VOID)(__VA_ARGS__)
+#endif // DBG
+
+static __inline VOID
+__Info(
+ IN const CHAR *Prefix,
+ IN const CHAR *Format,
+ ...
+ )
+{
+ va_list Arguments;
+
+ va_start(Arguments, Format);
+
+#pragma prefast(suppress:6001) // Using uninitialized memory
+ vDbgPrintExWithPrefix(Prefix,
+ DPFLTR_IHVDRIVER_ID,
+ DPFLTR_INFO_LEVEL,
+ Format,
+ Arguments);
+ va_end(Arguments);
+}
+
+#define Info(...) \
+ __Info(__MODULE__ "|" __FUNCTION__ ": ", __VA_ARGS__)
+
+#endif // _XENVIF_LOG_H
--- /dev/null
+/* Copyright (c) Citrix Systems Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms,
+ * with or without modification, are permitted provided
+ * that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer in the documentation and/or other
+ * materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <ntddk.h>
+#include <ntstrsafe.h>
+#include <stdlib.h>
+#include <util.h>
+#include <ethernet.h>
+#include <store_interface.h>
+
+#include "pdo.h"
+#include "frontend.h"
+#include "mac.h"
+#include "thread.h"
+#include "log.h"
+#include "assert.h"
+
+struct _XENVIF_MAC {
+ PXENVIF_FRONTEND Frontend;
+ KSPIN_LOCK Lock;
+ BOOLEAN Connected;
+ BOOLEAN Enabled;
+ KEVENT Event;
+ ULONG MaximumFrameSize;
+ ETHERNET_ADDRESS PermanentAddress;
+ ETHERNET_ADDRESS CurrentAddress;
+ ETHERNET_ADDRESS BroadcastAddress;
+ ETHERNET_ADDRESS MulticastAddress[MAXIMUM_MULTICAST_ADDRESS_COUNT];
+ ULONG MulticastAddressCount;
+ XENVIF_MAC_FILTER_LEVEL FilterLevel[ETHERNET_ADDRESS_TYPE_COUNT];
+
+ PXENBUS_STORE_INTERFACE StoreInterface;
+ PXENBUS_DEBUG_INTERFACE DebugInterface;
+
+ PXENBUS_DEBUG_CALLBACK DebugCallback;
+ PXENBUS_STORE_WATCH Watch;
+};
+
+#define MAC_POOL 'CAM'
+
+static FORCEINLINE PVOID
+__MacAllocate(
+ IN ULONG Length
+ )
+{
+ return __AllocateNonPagedPoolWithTag(Length, MAC_POOL);
+}
+
+static FORCEINLINE VOID
+__MacFree(
+ IN PVOID Buffer
+ )
+{
+ __FreePoolWithTag(Buffer, MAC_POOL);
+}
+
+static FORCEINLINE NTSTATUS
+__MacParseNetworkAddress(
+ IN PCHAR Buffer,
+ OUT PETHERNET_ADDRESS Address
+ )
+{
+ ULONG Length;
+ NTSTATUS status;
+
+ Length = 0;
+ for (;;) {
+ CHAR Character;
+ UCHAR Byte;
+
+ Character = *Buffer++;
+ if (Character == '\0')
+ break;
+
+ if (Character >= '0' && Character <= '9')
+ Byte = Character - '0';
+ else if (Character >= 'A' && Character <= 'F')
+ Byte = 0x0A + Character - 'A';
+ else if (Character >= 'a' && Character <= 'f')
+ Byte = 0x0A + Character - 'a';
+ else
+ break;
+
+ Byte <<= 4;
+
+ Character = *Buffer++;
+ if (Character == '\0')
+ break;
+
+ if (Character >= '0' && Character <= '9')
+ Byte += Character - '0';
+ else if (Character >= 'A' && Character <= 'F')
+ Byte += 0x0A + Character - 'A';
+ else if (Character >= 'a' && Character <= 'f')
+ Byte += 0x0A + Character - 'a';
+ else
+ break;
+
+ Address->Byte[Length++] = Byte;
+
+ // Skip over any separator
+ if (*Buffer == ':' || *Buffer == '-')
+ Buffer++;
+ }
+
+ status = STATUS_INVALID_PARAMETER;
+ if (Length != ETHERNET_ADDRESS_LENGTH)
+ goto fail1;
+
+ return STATUS_SUCCESS;
+
+fail1:
+ Error("fail1 (%08x)\n", status);
+
+ return status;
+}
+
+static FORCEINLINE VOID
+__MacSetPermanentAddress(
+ IN PXENVIF_MAC Mac,
+ IN PETHERNET_ADDRESS Address
+ )
+{
+ PXENVIF_FRONTEND Frontend;
+
+ ASSERT(!(Address->Byte[0] & 0x01));
+
+ Frontend = Mac->Frontend;
+
+ Mac->PermanentAddress = *Address;
+
+ STORE(Printf,
+ Mac->StoreInterface,
+ NULL,
+ FrontendGetPrefix(Frontend),
+ "mac/unicast/permanent",
+ "%02x:%02x:%02x:%02x:%02x:%02x",
+ Mac->PermanentAddress.Byte[0],
+ Mac->PermanentAddress.Byte[1],
+ Mac->PermanentAddress.Byte[2],
+ Mac->PermanentAddress.Byte[3],
+ Mac->PermanentAddress.Byte[4],
+ Mac->PermanentAddress.Byte[5]);
+}
+
+static FORCEINLINE PETHERNET_ADDRESS
+__MacGetPermanentAddress(
+ IN PXENVIF_MAC Mac
+ )
+{
+ return &Mac->PermanentAddress;
+}
+
+PETHERNET_ADDRESS
+MacGetPermanentAddress(
+ IN PXENVIF_MAC Mac
+ )
+{
+ return __MacGetPermanentAddress(Mac);
+}
+
+static FORCEINLINE VOID
+__MacSetCurrentAddress(
+ IN PXENVIF_MAC Mac,
+ IN PETHERNET_ADDRESS Address
+ )
+{
+ PXENVIF_FRONTEND Frontend;
+
+ ASSERT(!(Address->Byte[0] & 0x01));
+
+ Frontend = Mac->Frontend;
+
+ Mac->CurrentAddress = *Address;
+
+ STORE(Printf,
+ Mac->StoreInterface,
+ NULL,
+ FrontendGetPrefix(Frontend),
+ "mac/unicast/current",
+ "%02x:%02x:%02x:%02x:%02x:%02x",
+ Mac->CurrentAddress.Byte[0],
+ Mac->CurrentAddress.Byte[1],
+ Mac->CurrentAddress.Byte[2],
+ Mac->CurrentAddress.Byte[3],
+ Mac->CurrentAddress.Byte[4],
+ Mac->CurrentAddress.Byte[5]);
+}
+
+static FORCEINLINE PETHERNET_ADDRESS
+__MacGetCurrentAddress(
+ IN PXENVIF_MAC Mac
+ )
+{
+ return &Mac->CurrentAddress;
+}
+
+PETHERNET_ADDRESS
+MacGetCurrentAddress(
+ IN PXENVIF_MAC Mac
+ )
+{
+ return __MacGetCurrentAddress(Mac);
+}
+
+static VOID
+MacDebugCallback(
+ IN PVOID Argument,
+ IN BOOLEAN Crashing
+ )
+{
+ PXENVIF_MAC Mac = Argument;
+ ULONG Index;
+
+ UNREFERENCED_PARAMETER(Crashing);
+
+ DEBUG(Printf,
+ Mac->DebugInterface,
+ Mac->DebugCallback,
+ "MaximumFrameSize = %u\n",
+ Mac->MaximumFrameSize);
+
+ DEBUG(Printf,
+ Mac->DebugInterface,
+ Mac->DebugCallback,
+ "PermanentAddress = %02x:%02x:%02x:%02x:%02x:%02x\n",
+ Mac->PermanentAddress.Byte[0],
+ Mac->PermanentAddress.Byte[1],
+ Mac->PermanentAddress.Byte[2],
+ Mac->PermanentAddress.Byte[3],
+ Mac->PermanentAddress.Byte[4],
+ Mac->PermanentAddress.Byte[5]);
+
+ DEBUG(Printf,
+ Mac->DebugInterface,
+ Mac->DebugCallback,
+ "CurrentAddress = %02x:%02x:%02x:%02x:%02x:%02x\n",
+ Mac->CurrentAddress.Byte[0],
+ Mac->CurrentAddress.Byte[1],
+ Mac->CurrentAddress.Byte[2],
+ Mac->CurrentAddress.Byte[3],
+ Mac->CurrentAddress.Byte[4],
+ Mac->CurrentAddress.Byte[5]);
+
+ for (Index = 0; Index < Mac->MulticastAddressCount; Index++)
+ DEBUG(Printf,
+ Mac->DebugInterface,
+ Mac->DebugCallback,
+ "MulticastAddress[%u] = %02x:%02x:%02x:%02x:%02x:%02x\n",
+ Index,
+ Mac->MulticastAddress[Index].Byte[0],
+ Mac->MulticastAddress[Index].Byte[1],
+ Mac->MulticastAddress[Index].Byte[2],
+ Mac->MulticastAddress[Index].Byte[3],
+ Mac->MulticastAddress[Index].Byte[4],
+ Mac->MulticastAddress[Index].Byte[5]);
+
+ DEBUG(Printf,
+ Mac->DebugInterface,
+ Mac->DebugCallback,
+ "FilterLevel[ETHERNET_ADDRESS_UNICAST] = %s\n",
+ (Mac->FilterLevel[ETHERNET_ADDRESS_UNICAST] == MAC_FILTER_ALL) ? "All" :
+ (Mac->FilterLevel[ETHERNET_ADDRESS_UNICAST] == MAC_FILTER_MATCHING) ? "Matching" :
+ "None");
+
+ DEBUG(Printf,
+ Mac->DebugInterface,
+ Mac->DebugCallback,
+ "FilterLevel[ETHERNET_ADDRESS_MULTICAST] = %s\n",
+ (Mac->FilterLevel[ETHERNET_ADDRESS_MULTICAST] == MAC_FILTER_ALL) ? "All" :
+ (Mac->FilterLevel[ETHERNET_ADDRESS_MULTICAST] == MAC_FILTER_MATCHING) ? "Matching" :
+ "None");
+
+ DEBUG(Printf,
+ Mac->DebugInterface,
+ Mac->DebugCallback,
+ "FilterLevel[ETHERNET_ADDRESS_BROADCAST] = %s\n",
+ (Mac->FilterLevel[ETHERNET_ADDRESS_BROADCAST] == MAC_FILTER_ALL) ? "All" :
+ (Mac->FilterLevel[ETHERNET_ADDRESS_BROADCAST] == MAC_FILTER_MATCHING) ? "Matching" :
+ "None");
+}
+
+NTSTATUS
+MacInitialize(
+ IN PXENVIF_FRONTEND Frontend,
+ OUT PXENVIF_MAC *Mac
+ )
+{
+ NTSTATUS status;
+
+ *Mac = __MacAllocate(sizeof (XENVIF_MAC));
+
+ status = STATUS_NO_MEMORY;
+ if (*Mac == NULL)
+ goto fail1;
+
+ (*Mac)->Frontend = Frontend;
+
+ KeInitializeSpinLock(&(*Mac)->Lock);
+ KeInitializeEvent(&(*Mac)->Event, NotificationEvent, FALSE);
+
+ return STATUS_SUCCESS;
+
+fail1:
+ Error("fail1 (%08x)\n");
+
+ return status;
+}
+
+NTSTATUS
+MacConnect(
+ IN PXENVIF_MAC Mac
+ )
+{
+ PXENVIF_FRONTEND Frontend;
+ PCHAR Buffer;
+ ULONG64 Mtu;
+ ETHERNET_ADDRESS Address;
+ NTSTATUS status;
+
+ Frontend = Mac->Frontend;
+
+ Mac->StoreInterface = FrontendGetStoreInterface(Frontend);
+
+ STORE(Acquire, Mac->StoreInterface);
+
+ status = STORE(Read,
+ Mac->StoreInterface,
+ NULL,
+ FrontendGetPath(Frontend),
+ "mtu",
+ &Buffer);
+ if (!NT_SUCCESS(status)) {
+ Mtu = ETHERNET_MTU;
+ } else {
+ Mtu = strtol(Buffer, NULL, 10);
+
+ STORE(Free,
+ Mac->StoreInterface,
+ Buffer);
+
+ Buffer = NULL;
+ }
+
+ status = STATUS_INVALID_PARAMETER;
+ if (Mtu < ETHERNET_MIN)
+ goto fail1;
+
+ Mac->MaximumFrameSize = (ULONG)Mtu + sizeof (ETHERNET_UNTAGGED_HEADER);
+
+ status = STORE(Read,
+ Mac->StoreInterface,
+ NULL,
+ FrontendGetPath(Frontend),
+ "mac",
+ &Buffer);
+ if (!NT_SUCCESS(status))
+ goto fail2;
+
+ status = __MacParseNetworkAddress(Buffer, &Address);
+ if (!NT_SUCCESS(status))
+ goto fail3;
+
+ STORE(Free,
+ Mac->StoreInterface,
+ Buffer);
+
+ Buffer = NULL;
+
+ __MacSetPermanentAddress(Mac, &Address);
+
+ Buffer = FrontendGetAddress(Frontend);
+ if (Buffer != NULL) {
+ status = __MacParseNetworkAddress(Buffer, &Address);
+ if (!NT_SUCCESS(status))
+ goto fail4;
+
+ if (Address.Byte[0] & 0x01)
+ Address = *__MacGetPermanentAddress(Mac);
+ } else {
+ Address = *__MacGetPermanentAddress(Mac);
+ }
+
+ __MacSetCurrentAddress(Mac, &Address);
+
+ RtlFillMemory(Mac->BroadcastAddress.Byte, ETHERNET_ADDRESS_LENGTH, 0xFF);
+
+ Mac->DebugInterface = FrontendGetDebugInterface(Frontend);
+
+ DEBUG(Acquire, Mac->DebugInterface);
+
+ status = DEBUG(Register,
+ Mac->DebugInterface,
+ __MODULE__ "|MAC",
+ MacDebugCallback,
+ Mac,
+ &Mac->DebugCallback);
+ if (!NT_SUCCESS(status))
+ goto fail5;
+
+ ASSERT(!Mac->Connected);
+ Mac->Connected = TRUE;
+
+ return STATUS_SUCCESS;
+
+fail5:
+ Error("fail5\n");
+
+ DEBUG(Release, Mac->DebugInterface);
+ Mac->DebugInterface = NULL;
+
+ RtlZeroMemory(Mac->BroadcastAddress.Byte, ETHERNET_ADDRESS_LENGTH);
+ RtlZeroMemory(Mac->CurrentAddress.Byte, ETHERNET_ADDRESS_LENGTH);
+
+fail4:
+ Error("fail4\n");
+
+ Buffer = NULL;
+
+ RtlZeroMemory(Mac->PermanentAddress.Byte, ETHERNET_ADDRESS_LENGTH);
+
+fail3:
+ Error("fail3\n");
+
+ if (Buffer != NULL) {
+ STORE(Free,
+ Mac->StoreInterface,
+ Buffer);
+
+ Buffer = NULL;
+ }
+
+fail2:
+ Error("fail2\n");
+
+ Mac->MaximumFrameSize = 0;
+
+fail1:
+ Error("fail1 (%08x)\n");
+
+ STORE(Release, Mac->StoreInterface);
+ Mac->StoreInterface = 0;
+
+ return status;
+}
+
+NTSTATUS
+MacEnable(
+ IN PXENVIF_MAC Mac
+ )
+{
+ PXENVIF_FRONTEND Frontend;
+ NTSTATUS status;
+
+ Frontend = Mac->Frontend;
+
+ ASSERT3U(KeGetCurrentIrql(), ==, DISPATCH_LEVEL);
+ KeAcquireSpinLockAtDpcLevel(&Mac->Lock);
+
+ status = STORE(Watch,
+ Mac->StoreInterface,
+ FrontendGetPath(Frontend),
+ "disconnect",
+ &Mac->Event,
+ &Mac->Watch);
+ if (!NT_SUCCESS(status))
+ goto fail1;
+
+ ASSERT(!Mac->Enabled);
+ Mac->Enabled = TRUE;
+
+ KeReleaseSpinLockFromDpcLevel(&Mac->Lock);
+
+ return STATUS_SUCCESS;
+
+fail1:
+ Error("fail1 (%08x)\n");
+
+ KeReleaseSpinLockFromDpcLevel(&Mac->Lock);
+
+ return status;
+}
+
+VOID
+MacDisable(
+ IN PXENVIF_MAC Mac
+ )
+{
+ ASSERT3U(KeGetCurrentIrql(), ==, DISPATCH_LEVEL);
+ KeAcquireSpinLockAtDpcLevel(&Mac->Lock);
+
+ ASSERT(Mac->Enabled);
+ Mac->Enabled = FALSE;
+
+ (VOID) STORE(Unwatch,
+ Mac->StoreInterface,
+ Mac->Watch);
+ Mac->Watch = NULL;
+
+ KeReleaseSpinLockFromDpcLevel(&Mac->Lock);
+}
+
+VOID
+MacDisconnect(
+ IN PXENVIF_MAC Mac
+ )
+{
+ ASSERT(Mac->Connected);
+ Mac->Connected = FALSE;
+
+ DEBUG(Deregister,
+ Mac->DebugInterface,
+ Mac->DebugCallback);
+ Mac->DebugCallback = NULL;
+
+ DEBUG(Release, Mac->DebugInterface);
+ Mac->DebugInterface = NULL;
+
+ RtlZeroMemory(Mac->BroadcastAddress.Byte, ETHERNET_ADDRESS_LENGTH);
+ RtlZeroMemory(Mac->CurrentAddress.Byte, ETHERNET_ADDRESS_LENGTH);
+ RtlZeroMemory(Mac->PermanentAddress.Byte, ETHERNET_ADDRESS_LENGTH);
+
+ Mac->MaximumFrameSize = 0;
+
+ STORE(Release, Mac->StoreInterface);
+ Mac->StoreInterface = 0;
+}
+
+VOID
+MacTeardown(
+ IN PXENVIF_MAC Mac
+ )
+{
+ RtlZeroMemory(Mac->MulticastAddress,
+ MAXIMUM_MULTICAST_ADDRESS_COUNT * sizeof (ETHERNET_ADDRESS));
+ Mac->MulticastAddressCount = 0;
+
+ RtlZeroMemory(&Mac->FilterLevel,
+ ETHERNET_ADDRESS_TYPE_COUNT * sizeof (XENVIF_MAC_FILTER_LEVEL));
+
+ RtlZeroMemory(&Mac->Event, sizeof (KEVENT));
+ RtlZeroMemory(&Mac->Lock, sizeof (KSPIN_LOCK));
+
+ Mac->Frontend = NULL;
+
+ ASSERT(IsZeroMemory(Mac, sizeof (XENVIF_MAC)));
+ __MacFree(Mac);
+}
+
+ULONG
+MacGetLinkSpeed(
+ IN PXENVIF_MAC Mac
+ )
+{
+ PCHAR Buffer;
+ ULONG Speed;
+ NTSTATUS status;
+
+ status = STORE(Read,
+ Mac->StoreInterface,
+ NULL,
+ FrontendGetPath(Mac->Frontend),
+ "speed",
+ &Buffer);
+ if (!NT_SUCCESS(status)) {
+ Speed = 1;
+ } else {
+ Speed = (ULONG)strtol(Buffer, NULL, 10);
+
+ STORE(Free,
+ Mac->StoreInterface,
+ Buffer);
+ }
+
+ return Speed;
+}
+
+PKEVENT
+MacGetEvent(
+ IN PXENVIF_MAC Mac
+ )
+{
+ return &Mac->Event;
+}
+
+BOOLEAN
+MacGetLinkState(
+ IN PXENVIF_MAC Mac
+ )
+{
+ PCHAR Buffer;
+ BOOLEAN Disconnect;
+ NTSTATUS status;
+
+ status = STORE(Read,
+ Mac->StoreInterface,
+ NULL,
+ FrontendGetPath(Mac->Frontend),
+ "disconnect",
+ &Buffer);
+ if (!NT_SUCCESS(status)) {
+ Disconnect = FALSE;
+ } else {
+ Disconnect = (BOOLEAN)strtol(Buffer, NULL, 2);
+
+ STORE(Free,
+ Mac->StoreInterface,
+ Buffer);
+ }
+
+ return !Disconnect;
+}
+
+ULONG
+MacGetMaximumFrameSize(
+ IN PXENVIF_MAC Mac
+ )
+{
+ return Mac->MaximumFrameSize;
+}
+
+NTSTATUS
+MacSetCurrentAddress(
+ IN PXENVIF_MAC Mac,
+ IN PETHERNET_ADDRESS Address
+ )
+{
+ PXENVIF_FRONTEND Frontend;
+ KIRQL Irql;
+ NTSTATUS status;
+
+ Frontend = Mac->Frontend;
+
+ KeAcquireSpinLock(&Mac->Lock, &Irql);
+
+ status = STATUS_INVALID_PARAMETER;
+ if (Address->Byte[0] & 0x01)
+ goto fail1;
+
+ __MacSetCurrentAddress(Mac, Address);
+
+ KeReleaseSpinLock(&Mac->Lock, Irql);
+
+ return STATUS_SUCCESS;
+
+fail1:
+ Error("fail1 (%08x)\n", status);
+
+ KeReleaseSpinLock(&Mac->Lock, Irql);
+
+ return status;
+}
+
+NTSTATUS
+MacSetMulticastAddresses(
+ IN PXENVIF_MAC Mac,
+ IN ETHERNET_ADDRESS Address[],
+ IN ULONG Count
+ )
+{
+ KIRQL Irql;
+ ULONG Index;
+ NTSTATUS status;
+
+ KeAcquireSpinLock(&Mac->Lock, &Irql);
+
+ status = STATUS_BUFFER_OVERFLOW;
+ if (Count > MAXIMUM_MULTICAST_ADDRESS_COUNT)
+ goto fail1;
+
+ for (Index = 0; Index < Count; Index++) {
+ if (!(Address[Index].Byte[0] & 0x01))
+ goto fail2;
+ }
+
+ for (Index = 0; Index < Count; Index++)
+ Mac->MulticastAddress[Index] = Address[Index];
+
+ Mac->MulticastAddressCount = Count;
+
+ KeReleaseSpinLock(&Mac->Lock, Irql);
+
+ return STATUS_SUCCESS;
+
+fail2:
+ Error("fail2\n");
+
+fail1:
+ Error("fail1 (%08x)\n", status);
+
+ KeReleaseSpinLock(&Mac->Lock, Irql);
+
+ return status;
+}
+
+PETHERNET_ADDRESS
+MacGetMulticastAddresses(
+ IN PXENVIF_MAC Mac,
+ IN OUT PULONG Count
+ )
+{
+ *Count = Mac->MulticastAddressCount;
+
+ return Mac->MulticastAddress;
+}
+
+PETHERNET_ADDRESS
+MacGetBroadcastAddress(
+ IN PXENVIF_MAC Mac
+ )
+{
+ return &Mac->BroadcastAddress;
+}
+
+NTSTATUS
+MacSetFilterLevel(
+ IN PXENVIF_MAC Mac,
+ IN ETHERNET_ADDRESS_TYPE Type,
+ IN XENVIF_MAC_FILTER_LEVEL Level
+ )
+{
+ KIRQL Irql;
+ NTSTATUS status;
+
+ ASSERT3U(Type, <, ETHERNET_ADDRESS_TYPE_COUNT);
+
+ KeAcquireSpinLock(&Mac->Lock, &Irql);
+
+ status = STATUS_INVALID_PARAMETER;
+ if (Level > MAC_FILTER_ALL || Level < MAC_FILTER_NONE)
+ goto fail1;
+
+ Mac->FilterLevel[Type] = Level;
+ KeReleaseSpinLock(&Mac->Lock, Irql);
+
+ return STATUS_SUCCESS;
+
+fail1:
+ Error("fail1 (%08x)\n", status);
+
+ KeReleaseSpinLock(&Mac->Lock, Irql);
+
+ return status;
+}
+
+XENVIF_MAC_FILTER_LEVEL
+MacGetFilterLevel(
+ IN PXENVIF_MAC Mac,
+ IN ETHERNET_ADDRESS_TYPE Type
+ )
+{
+ ASSERT3U(Type, <, ETHERNET_ADDRESS_TYPE_COUNT);
+
+ return Mac->FilterLevel[Type];
+}
+
+BOOLEAN
+MacApplyFilters(
+ IN PXENVIF_MAC Mac,
+ IN PETHERNET_ADDRESS DestinationAddress
+ )
+{
+ ETHERNET_ADDRESS_TYPE Type;
+ BOOLEAN Allow;
+
+ Type = GET_ETHERNET_ADDRESS_TYPE(DestinationAddress);
+ Allow = FALSE;
+
+ ASSERT3U(KeGetCurrentIrql(), ==, DISPATCH_LEVEL);
+ KeAcquireSpinLockAtDpcLevel(&Mac->Lock);
+
+ switch (Type) {
+ case ETHERNET_ADDRESS_UNICAST:
+ switch (Mac->FilterLevel[ETHERNET_ADDRESS_UNICAST]) {
+ case MAC_FILTER_NONE:
+ break;
+
+ case MAC_FILTER_MATCHING:
+ if (RtlEqualMemory(&Mac->CurrentAddress,
+ DestinationAddress,
+ ETHERNET_ADDRESS_LENGTH))
+ Allow = TRUE;
+
+ break;
+
+ case MAC_FILTER_ALL:
+ Allow = TRUE;
+ break;
+
+ default:
+ ASSERT(FALSE);
+ break;
+ }
+ break;
+
+ case ETHERNET_ADDRESS_MULTICAST:
+ switch (Mac->FilterLevel[ETHERNET_ADDRESS_MULTICAST]) {
+ case MAC_FILTER_NONE:
+ break;
+
+ case MAC_FILTER_MATCHING: {
+ ULONG Index;
+
+ for (Index = 0; Index < Mac->MulticastAddressCount; Index++) {
+ if (RtlEqualMemory(&Mac->MulticastAddress[Index],
+ DestinationAddress,
+ ETHERNET_ADDRESS_LENGTH))
+ Allow = TRUE;
+ }
+ break;
+ }
+
+ case MAC_FILTER_ALL:
+ Allow = TRUE;
+ break;
+
+ default:
+ ASSERT(FALSE);
+ break;
+ }
+ break;
+
+ case ETHERNET_ADDRESS_BROADCAST:
+ switch (Mac->FilterLevel[ETHERNET_ADDRESS_BROADCAST]) {
+ case MAC_FILTER_NONE:
+ break;
+
+ case MAC_FILTER_MATCHING:
+ case MAC_FILTER_ALL:
+ Allow = TRUE;
+ break;
+
+ default:
+ ASSERT(FALSE);
+ break;
+ }
+ break;
+
+ default:
+ ASSERT(FALSE);
+ break;
+ }
+
+ KeReleaseSpinLockFromDpcLevel(&Mac->Lock);
+
+ return Allow;
+}
--- /dev/null
+/* Copyright (c) Citrix Systems Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms,
+ * with or without modification, are permitted provided
+ * that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer in the documentation and/or other
+ * materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef _XENVIF_MAC_H
+#define _XENVIF_MAC_H
+
+#include <ntddk.h>
+#include <ethernet.h>
+#include <vif_interface.h>
+
+#include "frontend.h"
+
+typedef struct _XENVIF_MAC XENVIF_MAC, *PXENVIF_MAC;
+
+extern NTSTATUS
+MacInitialize(
+ IN PXENVIF_FRONTEND Frontend,
+ OUT PXENVIF_MAC *Mac
+ );
+
+extern NTSTATUS
+MacConnect(
+ IN PXENVIF_MAC Mac
+ );
+
+extern NTSTATUS
+MacEnable(
+ IN PXENVIF_MAC Mac
+ );
+
+extern VOID
+MacDisable(
+ IN PXENVIF_MAC Mac
+ );
+
+extern VOID
+MacDisconnect(
+ IN PXENVIF_MAC Mac
+ );
+
+extern VOID
+MacTeardown(
+ IN PXENVIF_MAC Mac
+ );
+
+extern PKEVENT
+MacGetEvent(
+ IN PXENVIF_MAC Mac
+ );
+
+extern ULONG
+MacGetLinkSpeed(
+ IN PXENVIF_MAC Mac
+ );
+
+extern BOOLEAN
+MacGetLinkState(
+ IN PXENVIF_MAC Mac
+ );
+
+extern ULONG
+MacGetMaximumFrameSize(
+ IN PXENVIF_MAC Mac
+ );
+
+extern PETHERNET_ADDRESS
+MacGetPermanentAddress(
+ IN PXENVIF_MAC Mac
+ );
+
+extern PETHERNET_ADDRESS
+MacGetCurrentAddress(
+ IN PXENVIF_MAC Mac
+ );
+
+extern NTSTATUS
+MacSetCurrentAddress(
+ IN PXENVIF_MAC Mac,
+ IN PETHERNET_ADDRESS Address
+ );
+
+extern PETHERNET_ADDRESS
+MacGetMulticastAddresses(
+ IN PXENVIF_MAC Mac,
+ OUT PULONG Count
+ );
+
+extern NTSTATUS
+MacSetMulticastAddresses(
+ IN PXENVIF_MAC Mac,
+ IN ETHERNET_ADDRESS Address[],
+ IN ULONG Count
+ );
+
+extern PETHERNET_ADDRESS
+MacGetBroadcastAddress(
+ IN PXENVIF_MAC Mac
+ );
+
+extern NTSTATUS
+MacSetFilterLevel(
+ IN PXENVIF_MAC Mac,
+ IN ETHERNET_ADDRESS_TYPE Type,
+ IN XENVIF_MAC_FILTER_LEVEL Level
+ );
+
+extern XENVIF_MAC_FILTER_LEVEL
+MacGetFilterLevel(
+ IN PXENVIF_MAC Mac,
+ IN ETHERNET_ADDRESS_TYPE Type
+ );
+
+extern BOOLEAN
+MacApplyFilters(
+ IN PXENVIF_MAC Mac,
+ IN PETHERNET_ADDRESS DestinationAddress
+ );
+
+#endif // _XENVIF_MAC_H
--- /dev/null
+/* Copyright (c) Citrix Systems Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms,
+ * with or without modification, are permitted provided
+ * that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer in the documentation and/or other
+ * materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef _XENVIF_MRSW_H
+#define _XENVIF_MRSW_H
+
+#include <ntddk.h>
+
+#include "assert.h"
+
+#pragma warning(disable:4127) // conditional expression is constant
+
+typedef struct _XENVIF_MRSW_HOLDER {
+ PKTHREAD Thread;
+ LONG Level;
+} XENVIF_MRSW_HOLDER, *PXENVIF_MRSW_HOLDER;
+
+typedef struct _XENVIF_MRSW_LOCK {
+ volatile LONG64 Mask;
+ XENVIF_MRSW_HOLDER Holder[64];
+ KEVENT Event;
+} XENVIF_MRSW_LOCK, *PXENVIF_MRSW_LOCK;
+
+C_ASSERT(RTL_FIELD_SIZE(XENVIF_MRSW_LOCK, Holder) == RTL_FIELD_SIZE(XENVIF_MRSW_LOCK, Mask) * 8 * sizeof (XENVIF_MRSW_HOLDER));
+
+#define XENVIF_MRSW_EXCLUSIVE_SLOT 0
+
+static FORCEINLINE VOID
+InitializeMrswLock(
+ IN PXENVIF_MRSW_LOCK Lock
+ )
+{
+ LONG Slot;
+
+ RtlZeroMemory(Lock, sizeof (XENVIF_MRSW_LOCK));
+
+ for (Slot = 0; Slot < sizeof (Lock->Mask) * 8; Slot++)
+ Lock->Holder[Slot].Level = -1;
+
+ KeInitializeEvent(&Lock->Event, NotificationEvent, FALSE);
+}
+
+static FORCEINLINE BOOLEAN
+__ClaimExclusive(
+ IN PXENVIF_MRSW_LOCK Lock
+ )
+{
+ LONG64 Old;
+ LONG64 New;
+
+ Old = 0;
+ New = 1ll << XENVIF_MRSW_EXCLUSIVE_SLOT;
+
+ return (InterlockedCompareExchange64(&Lock->Mask, New, Old) == Old) ? TRUE : FALSE;
+}
+
+static FORCEINLINE KIRQL
+__drv_maxIRQL(APC_LEVEL)
+__drv_raisesIRQL(DISPATCH_LEVEL)
+__drv_savesIRQL
+__AcquireMrswLockExclusive(
+ IN PXENVIF_MRSW_LOCK Lock
+ )
+{
+ KIRQL Irql;
+ LONG Slot;
+ PKTHREAD Self;
+ PXENVIF_MRSW_HOLDER Holder;
+
+ ASSERT3U(KeGetCurrentIrql(), <, DISPATCH_LEVEL);
+ KeRaiseIrql(DISPATCH_LEVEL, &Irql);
+
+ Self = KeGetCurrentThread();
+
+ // Make sure we do not already hold the lock
+ for (Slot = 0; Slot < sizeof (Lock->Mask) * 8; Slot++)
+ ASSERT(Lock->Holder[Slot].Thread != Self);
+
+ for (;;) {
+ if (__ClaimExclusive(Lock))
+ break;
+
+ KeLowerIrql(Irql);
+
+ (VOID) KeWaitForSingleObject(&Lock->Event,
+ Executive,
+ KernelMode,
+ FALSE,
+ NULL);
+ KeClearEvent(&Lock->Event);
+
+ KeRaiseIrql(DISPATCH_LEVEL, &Irql);
+ }
+
+ Holder = &Lock->Holder[XENVIF_MRSW_EXCLUSIVE_SLOT];
+
+ ASSERT3P(Holder->Thread, ==, NULL);
+ Holder->Thread = Self;
+ Holder->Level = 0;
+
+ return Irql;
+}
+
+#define AcquireMrswLockExclusive(_Lock, _Irql) \
+ do { \
+ *(_Irql) = __AcquireMrswLockExclusive(_Lock); \
+ } while (FALSE)
+
+static FORCEINLINE VOID
+__drv_maxIRQL(DISPATCH_LEVEL)
+__drv_requiresIRQL(DISPATCH_LEVEL)
+ReleaseMrswLockExclusive(
+ IN PXENVIF_MRSW_LOCK Lock,
+ IN __drv_restoresIRQL KIRQL Irql,
+ IN BOOLEAN Shared
+ )
+{
+ LONG Slot;
+ PKTHREAD Self;
+ LONG64 Old;
+ LONG64 New;
+ PXENVIF_MRSW_HOLDER Holder;
+
+ ASSERT3U(KeGetCurrentIrql(), ==, DISPATCH_LEVEL);
+
+ Slot = XENVIF_MRSW_EXCLUSIVE_SLOT + 1; // Choose any slot other than the exclusive slot
+
+ Old = 1ll << XENVIF_MRSW_EXCLUSIVE_SLOT;
+ New = (Shared) ? (1ll << Slot) : 0;
+
+ Old = InterlockedCompareExchange64(&Lock->Mask, New, Old);
+ ASSERT3U(Old, == , 1ll << XENVIF_MRSW_EXCLUSIVE_SLOT);
+
+ Self = KeGetCurrentThread();
+
+ ASSERT3P(Lock->Holder[XENVIF_MRSW_EXCLUSIVE_SLOT].Thread, ==, Self);
+
+ // If we are leaving the lock held shared then we need to transfer
+ // our identity information into the hew slot.
+ if (Shared)
+ Lock->Holder[Slot] = Lock->Holder[XENVIF_MRSW_EXCLUSIVE_SLOT];
+
+ Holder = &Lock->Holder[XENVIF_MRSW_EXCLUSIVE_SLOT];
+
+ Holder->Thread = NULL;
+ Holder->Level = -1;
+
+ KeLowerIrql(Irql);
+}
+
+static FORCEINLINE LONG
+__ClaimShared(
+ IN PXENVIF_MRSW_LOCK Lock
+ )
+{
+ LONG Slot;
+ LONG64 Old;
+ LONG64 New;
+
+ // Make sure the exclusive bit is set so that we don't find it
+ Old = Lock->Mask | (1ll << XENVIF_MRSW_EXCLUSIVE_SLOT);
+
+ Slot = __ffu((ULONG64)Old);
+ ASSERT(Slot >= 0);
+ ASSERT3U(Slot, != , XENVIF_MRSW_EXCLUSIVE_SLOT);
+
+ Old &= ~(1ll << XENVIF_MRSW_EXCLUSIVE_SLOT);
+ New = Old | (1ll << Slot);
+
+ return (InterlockedCompareExchange64(&Lock->Mask, New, Old) == Old) ? Slot : -1;
+}
+
+static FORCEINLINE VOID
+AcquireMrswLockShared(
+ IN PXENVIF_MRSW_LOCK Lock
+ )
+{
+ KIRQL Irql;
+ LONG Level;
+ LONG Slot;
+ PKTHREAD Self;
+ PXENVIF_MRSW_HOLDER Holder;
+
+ ASSERT3U(KeGetCurrentIrql(), <=, DISPATCH_LEVEL);
+ KeRaiseIrql(DISPATCH_LEVEL, &Irql);
+
+ Self = KeGetCurrentThread();
+
+ // Do we already hold the lock? If so, get the nesting level
+ Level = -1;
+ for (Slot = 0; Slot < sizeof (Lock->Mask) * 8; Slot++) {
+ if (Lock->Holder[Slot].Thread == Self && Lock->Holder[Slot].Level > Level)
+ Level = Lock->Holder[Slot].Level;
+ }
+ Level++;
+
+ for (;;) {
+ Slot = __ClaimShared(Lock);
+ if (Slot >= 0)
+ break;
+
+ _mm_pause();
+ }
+
+ Holder = &Lock->Holder[Slot];
+
+ Holder->Thread = Self;
+ Holder->Level = Level;
+
+ KeLowerIrql(Irql);
+}
+
+static FORCEINLINE VOID
+ReleaseMrswLockShared(
+ IN PXENVIF_MRSW_LOCK Lock
+ )
+{
+ KIRQL Irql;
+ PKTHREAD Self;
+ LONG Level;
+ LONG Deepest;
+ LONG Slot;
+ LONG64 Old;
+ LONG64 New;
+ PXENVIF_MRSW_HOLDER Holder;
+
+ ASSERT3U(KeGetCurrentIrql(), <=, DISPATCH_LEVEL);
+ KeRaiseIrql(DISPATCH_LEVEL, &Irql);
+
+ Self = KeGetCurrentThread();
+
+ Level = -1;
+ Deepest = -1;
+ for (Slot = 0; Slot < sizeof (Lock->Mask) * 8; Slot++) {
+ if (Lock->Holder[Slot].Thread == Self && Lock->Holder[Slot].Level > Level) {
+ Level = Lock->Holder[Slot].Level;
+ Deepest = Slot;
+ }
+ }
+ ASSERT(Level >= 0);
+
+ Slot = Deepest;
+ ASSERT3U(Slot, !=, XENVIF_MRSW_EXCLUSIVE_SLOT);
+
+ Holder = &Lock->Holder[Slot];
+
+ Holder->Thread = NULL;
+ Holder->Level = -1;
+
+ do {
+ Old = Lock->Mask;
+ New = Old & ~(1ll << Slot);
+ } while (InterlockedCompareExchange64(&Lock->Mask, New, Old) != Old);
+
+ KeSetEvent(&Lock->Event, IO_NO_INCREMENT, FALSE);
+ KeLowerIrql(Irql);
+}
+
+#endif // _XENVIF_MRSW_H
--- /dev/null
+/* Copyright (c) Citrix Systems Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms,
+ * with or without modification, are permitted provided
+ * that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer in the documentation and/or other
+ * materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef _XENVIF_MUTEX_H
+#define _XENVIF_MUTEX_H
+
+#include <ntddk.h>
+
+#include "assert.h"
+
+typedef struct _XENVIF_MUTEX {
+ PKTHREAD Owner;
+ KEVENT Event;
+} XENVIF_MUTEX, *PXENVIF_MUTEX;
+
+static FORCEINLINE VOID
+InitializeMutex(
+ IN PXENVIF_MUTEX Mutex
+ )
+{
+ RtlZeroMemory(Mutex, sizeof (XENVIF_MUTEX));
+
+ KeInitializeEvent(&Mutex->Event, SynchronizationEvent, TRUE);
+}
+
+static FORCEINLINE VOID
+__drv_maxIRQL(PASSIVE_LEVEL)
+AcquireMutex(
+ IN PXENVIF_MUTEX Mutex
+ )
+{
+ (VOID) KeWaitForSingleObject(&Mutex->Event,
+ Executive,
+ KernelMode,
+ FALSE,
+ NULL);
+
+ ASSERT3P(Mutex->Owner, ==, NULL);
+ Mutex->Owner = KeGetCurrentThread();
+}
+
+static FORCEINLINE VOID
+__drv_maxIRQL(PASSIVE_LEVEL)
+ReleaseMutex(
+ IN PXENVIF_MUTEX Mutex
+ )
+{
+ ASSERT3P(Mutex->Owner, ==, KeGetCurrentThread());
+ Mutex->Owner = NULL;
+
+ KeSetEvent(&Mutex->Event, IO_NO_INCREMENT, FALSE);
+}
+
+#endif // _XENVIF_MUTEX_H
--- /dev/null
+/* Copyright (c) Citrix Systems Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms,
+ * with or without modification, are permitted provided
+ * that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer in the documentation and/or other
+ * materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef _XENVIF_NAMES_H_
+#define _XENVIF_NAMES_H_
+
+#include <ntddk.h>
+#include <xen.h>
+
+#include "types.h"
+
+static FORCEINLINE const CHAR *
+PowerTypeName(
+ IN POWER_STATE_TYPE Type
+ )
+{
+#define _POWER_TYPE_NAME(_Type) \
+ case _Type: \
+ return #_Type;
+
+ switch (Type) {
+ _POWER_TYPE_NAME(SystemPowerState);
+ _POWER_TYPE_NAME(DevicePowerState);
+ default:
+ break;
+ }
+
+ return ("UNKNOWN");
+#undef _POWER_ACTION_NAME
+}
+
+static FORCEINLINE const CHAR *
+PowerSystemStateName(
+ IN SYSTEM_POWER_STATE State
+ )
+{
+#define _POWER_SYSTEM_STATE_NAME(_State) \
+ case PowerSystem ## _State: \
+ return #_State;
+
+ switch (State) {
+ _POWER_SYSTEM_STATE_NAME(Unspecified);
+ _POWER_SYSTEM_STATE_NAME(Working);
+ _POWER_SYSTEM_STATE_NAME(Sleeping1);
+ _POWER_SYSTEM_STATE_NAME(Sleeping2);
+ _POWER_SYSTEM_STATE_NAME(Sleeping3);
+ _POWER_SYSTEM_STATE_NAME(Hibernate);
+ _POWER_SYSTEM_STATE_NAME(Shutdown);
+ _POWER_SYSTEM_STATE_NAME(Maximum);
+ default:
+ break;
+ }
+
+ return ("UNKNOWN");
+#undef _POWER_SYSTEM_STATE_NAME
+}
+
+static FORCEINLINE const CHAR *
+PowerDeviceStateName(
+ IN DEVICE_POWER_STATE State
+ )
+{
+#define _POWER_DEVICE_STATE_NAME(_State) \
+ case PowerDevice ## _State: \
+ return #_State;
+
+ switch (State) {
+ _POWER_DEVICE_STATE_NAME(Unspecified);
+ _POWER_DEVICE_STATE_NAME(D0);
+ _POWER_DEVICE_STATE_NAME(D1);
+ _POWER_DEVICE_STATE_NAME(D2);
+ _POWER_DEVICE_STATE_NAME(D3);
+ _POWER_DEVICE_STATE_NAME(Maximum);
+ default:
+ break;
+ }
+
+ return ("UNKNOWN");
+#undef _POWER_DEVICE_STATE_NAME
+}
+
+static FORCEINLINE const CHAR *
+PowerActionName(
+ IN POWER_ACTION Type
+ )
+{
+#define _POWER_ACTION_NAME(_Type) \
+ case PowerAction ## _Type: \
+ return #_Type;
+
+ switch (Type) {
+ _POWER_ACTION_NAME(None);
+ _POWER_ACTION_NAME(Reserved);
+ _POWER_ACTION_NAME(Sleep);
+ _POWER_ACTION_NAME(Hibernate);
+ _POWER_ACTION_NAME(Shutdown);
+ _POWER_ACTION_NAME(ShutdownReset);
+ _POWER_ACTION_NAME(ShutdownOff);
+ _POWER_ACTION_NAME(WarmEject);
+ default:
+ break;
+ }
+
+ return ("UNKNOWN");
+#undef _POWER_ACTION_NAME
+}
+
+static FORCEINLINE const CHAR *
+PowerMinorFunctionName(
+ IN ULONG MinorFunction
+ )
+{
+#define _POWER_MINOR_FUNCTION_NAME(_Function) \
+ case IRP_MN_ ## _Function: \
+ return #_Function;
+
+ switch (MinorFunction) {
+ _POWER_MINOR_FUNCTION_NAME(WAIT_WAKE);
+ _POWER_MINOR_FUNCTION_NAME(POWER_SEQUENCE);
+ _POWER_MINOR_FUNCTION_NAME(SET_POWER);
+ _POWER_MINOR_FUNCTION_NAME(QUERY_POWER);
+
+ default:
+ return "UNKNOWN";
+ }
+
+#undef _POWER_MINOR_FUNCTION_NAME
+}
+
+static FORCEINLINE const CHAR *
+PnpDeviceStateName(
+ IN DEVICE_PNP_STATE State
+ )
+{
+#define _PNP_DEVICE_STATE_NAME(_State) \
+ case _State: \
+ return #_State;
+
+ switch (State) {
+ _PNP_DEVICE_STATE_NAME(Invalid);
+ _PNP_DEVICE_STATE_NAME(Present);
+ _PNP_DEVICE_STATE_NAME(Enumerated);
+ _PNP_DEVICE_STATE_NAME(Added);
+ _PNP_DEVICE_STATE_NAME(Started);
+ _PNP_DEVICE_STATE_NAME(StopPending);
+ _PNP_DEVICE_STATE_NAME(Stopped);
+ _PNP_DEVICE_STATE_NAME(RemovePending);
+ _PNP_DEVICE_STATE_NAME(SurpriseRemovePending);
+ _PNP_DEVICE_STATE_NAME(Deleted);
+ default:
+ break;
+ }
+
+ return "UNKNOWN";
+
+#undef _STATE_NAME
+}
+
+static FORCEINLINE const CHAR *
+PnpMinorFunctionName(
+ IN ULONG Function
+ )
+{
+#define _PNP_MINOR_FUNCTION_NAME(_Function) \
+ case IRP_MN_ ## _Function: \
+ return #_Function;
+
+ switch (Function) {
+ _PNP_MINOR_FUNCTION_NAME(START_DEVICE);
+ _PNP_MINOR_FUNCTION_NAME(QUERY_REMOVE_DEVICE);
+ _PNP_MINOR_FUNCTION_NAME(REMOVE_DEVICE);
+ _PNP_MINOR_FUNCTION_NAME(CANCEL_REMOVE_DEVICE);
+ _PNP_MINOR_FUNCTION_NAME(STOP_DEVICE);
+ _PNP_MINOR_FUNCTION_NAME(QUERY_STOP_DEVICE);
+ _PNP_MINOR_FUNCTION_NAME(CANCEL_STOP_DEVICE);
+ _PNP_MINOR_FUNCTION_NAME(QUERY_DEVICE_RELATIONS);
+ _PNP_MINOR_FUNCTION_NAME(QUERY_INTERFACE);
+ _PNP_MINOR_FUNCTION_NAME(QUERY_CAPABILITIES);
+ _PNP_MINOR_FUNCTION_NAME(QUERY_RESOURCES);
+ _PNP_MINOR_FUNCTION_NAME(QUERY_RESOURCE_REQUIREMENTS);
+ _PNP_MINOR_FUNCTION_NAME(QUERY_DEVICE_TEXT);
+ _PNP_MINOR_FUNCTION_NAME(FILTER_RESOURCE_REQUIREMENTS);
+ _PNP_MINOR_FUNCTION_NAME(READ_CONFIG);
+ _PNP_MINOR_FUNCTION_NAME(WRITE_CONFIG);
+ _PNP_MINOR_FUNCTION_NAME(EJECT);
+ _PNP_MINOR_FUNCTION_NAME(SET_LOCK);
+ _PNP_MINOR_FUNCTION_NAME(QUERY_ID);
+ _PNP_MINOR_FUNCTION_NAME(QUERY_PNP_DEVICE_STATE);
+ _PNP_MINOR_FUNCTION_NAME(QUERY_BUS_INFORMATION);
+ _PNP_MINOR_FUNCTION_NAME(DEVICE_USAGE_NOTIFICATION);
+ _PNP_MINOR_FUNCTION_NAME(SURPRISE_REMOVAL);
+ _PNP_MINOR_FUNCTION_NAME(QUERY_LEGACY_BUS_INFORMATION);
+ default:
+ break;
+ }
+
+ return "UNKNOWN";
+
+#undef _PNP_MINOR_FUNCTION_NAME
+}
+
+static FORCEINLINE const CHAR *
+PartialResourceDescriptorTypeName(
+ IN UCHAR Type
+ )
+{
+#define _PARTIAL_RESOURCE_DESCRIPTOR_TYPE_NAME(_Type) \
+ case CmResourceType ## _Type: \
+ return #_Type;
+
+ switch (Type) {
+ _PARTIAL_RESOURCE_DESCRIPTOR_TYPE_NAME(Null);
+ _PARTIAL_RESOURCE_DESCRIPTOR_TYPE_NAME(Port);
+ _PARTIAL_RESOURCE_DESCRIPTOR_TYPE_NAME(Interrupt);
+ _PARTIAL_RESOURCE_DESCRIPTOR_TYPE_NAME(Memory);
+ _PARTIAL_RESOURCE_DESCRIPTOR_TYPE_NAME(Dma);
+ _PARTIAL_RESOURCE_DESCRIPTOR_TYPE_NAME(DeviceSpecific);
+ _PARTIAL_RESOURCE_DESCRIPTOR_TYPE_NAME(BusNumber);
+ _PARTIAL_RESOURCE_DESCRIPTOR_TYPE_NAME(MemoryLarge);
+ _PARTIAL_RESOURCE_DESCRIPTOR_TYPE_NAME(ConfigData);
+ _PARTIAL_RESOURCE_DESCRIPTOR_TYPE_NAME(DevicePrivate);
+ default:
+ break;
+ }
+
+ return "UNKNOWN";
+
+#undef _PARTIAL_RESOURCE_DESCRIPTOR_TYPE_NAME
+}
+
+static FORCEINLINE const PCHAR
+XenbusStateName(
+ IN XenbusState State
+ )
+{
+#define _STATE_NAME(_State) \
+ case XenbusState ## _State: \
+ return #_State;
+
+ switch (State) {
+ _STATE_NAME(Unknown);
+ _STATE_NAME(Initialising);
+ _STATE_NAME(InitWait);
+ _STATE_NAME(Initialised);
+ _STATE_NAME(Connected);
+ _STATE_NAME(Closing);
+ _STATE_NAME(Closed);
+ _STATE_NAME(Reconfiguring);
+ _STATE_NAME(Reconfigured);
+ default:
+ break;
+ }
+
+ return "INVALID";
+
+#undef _STATE_NAME
+}
+
+static FORCEINLINE const CHAR *
+DeviceUsageTypeName(
+ IN DEVICE_USAGE_NOTIFICATION_TYPE Type
+ )
+{
+#define _DEVICE_USAGE_TYPE_NAME(_Type) \
+ case DeviceUsageType ## _Type: \
+ return #_Type;
+
+ switch (Type) {
+ _DEVICE_USAGE_TYPE_NAME(Paging);
+ _DEVICE_USAGE_TYPE_NAME(Hibernation);
+ _DEVICE_USAGE_TYPE_NAME(DumpFile);
+ default:
+ break;
+ }
+
+ return "UNKNOWN";
+
+#undef _DEVICE_USAGE_TYPE_NAME
+}
+
+#endif // _XENVIF_NAMES_H_
--- /dev/null
+/* Copyright (c) Citrix Systems Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms,
+ * with or without modification, are permitted provided
+ * that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer in the documentation and/or other
+ * materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <ntddk.h>
+#include <ntstrsafe.h>
+#include <util.h>
+#include <evtchn_interface.h>
+#include <store_interface.h>
+
+#include "pdo.h"
+#include "frontend.h"
+#include "notifier.h"
+#include "receiver.h"
+#include "transmitter.h"
+#include "log.h"
+#include "assert.h"
+
+struct _XENVIF_NOTIFIER {
+ PXENVIF_FRONTEND Frontend;
+ PXENBUS_EVTCHN_DESCRIPTOR Evtchn;
+ KDPC Dpc;
+ ULONG Events;
+ ULONG Dpcs;
+ BOOLEAN Connected;
+ KSPIN_LOCK Lock;
+ BOOLEAN Enabled;
+
+ PXENBUS_EVTCHN_INTERFACE EvtchnInterface;
+ PXENBUS_DEBUG_INTERFACE DebugInterface;
+ PXENBUS_STORE_INTERFACE StoreInterface;
+
+ PXENBUS_DEBUG_CALLBACK DebugCallback;
+};
+
+#define NOTIFIER_POOL 'ITON'
+
+static FORCEINLINE PVOID
+__NotifierAllocate(
+ IN ULONG Length
+ )
+{
+ return __AllocateNonPagedPoolWithTag(Length, NOTIFIER_POOL);
+}
+
+static FORCEINLINE VOID
+__NotifierFree(
+ IN PVOID Buffer
+ )
+{
+ __FreePoolWithTag(Buffer, NOTIFIER_POOL);
+}
+
+#pragma warning(push)
+#pragma warning(disable:6011) // Dereferencing NULL pointer
+
+static FORCEINLINE VOID
+__NotifierNotify(
+ IN PXENVIF_NOTIFIER Notifier
+ )
+{
+ PXENVIF_FRONTEND Frontend;
+
+ Frontend = Notifier->Frontend;
+
+ TransmitterNotify(FrontendGetTransmitter(Frontend));
+ ReceiverNotify(FrontendGetReceiver(Frontend));
+}
+
+static FORCEINLINE BOOLEAN
+__NotifierUnmask(
+ IN PXENVIF_NOTIFIER Notifier
+ )
+{
+ BOOLEAN Pending;
+
+ KeAcquireSpinLockAtDpcLevel(&Notifier->Lock);
+
+ Pending = (Notifier->Connected) ?
+ EVTCHN(Unmask,
+ Notifier->EvtchnInterface,
+ Notifier->Evtchn,
+ FALSE) :
+ FALSE;
+
+ KeReleaseSpinLockFromDpcLevel(&Notifier->Lock);
+
+ return Pending;
+}
+
+
+KDEFERRED_ROUTINE NotifierDpc;
+
+VOID
+NotifierDpc(
+ IN PKDPC Dpc,
+ IN PVOID Context,
+ IN PVOID Argument1,
+ IN PVOID Argument2
+ )
+{
+ PXENVIF_NOTIFIER Notifier = Context;
+ BOOLEAN Pending;
+
+ UNREFERENCED_PARAMETER(Dpc);
+ UNREFERENCED_PARAMETER(Argument1);
+ UNREFERENCED_PARAMETER(Argument2);
+
+ ASSERT(Notifier != NULL);
+
+ do {
+ if (Notifier->Enabled)
+ __NotifierNotify(Notifier);
+
+ Pending = __NotifierUnmask(Notifier);
+ } while (Pending);
+}
+
+#pragma warning(pop)
+
+KSERVICE_ROUTINE NotifierEvtchnCallback;
+
+BOOLEAN
+NotifierEvtchnCallback(
+ IN PKINTERRUPT InterruptObject,
+ IN PVOID Argument
+ )
+{
+ PXENVIF_NOTIFIER Notifier = Argument;
+
+ UNREFERENCED_PARAMETER(InterruptObject);
+
+ ASSERT(Notifier != NULL);
+
+ Notifier->Events++;
+
+ if (KeInsertQueueDpc(&Notifier->Dpc, NULL, NULL))
+ Notifier->Dpcs++;
+
+ return TRUE;
+}
+
+static VOID
+NotifierDebugCallback(
+ IN PVOID Argument,
+ IN BOOLEAN Crashing
+ )
+{
+ PXENVIF_NOTIFIER Notifier = Argument;
+
+ UNREFERENCED_PARAMETER(Crashing);
+
+ DEBUG(Printf,
+ Notifier->DebugInterface,
+ Notifier->DebugCallback,
+ "Events = %lu Dpcs = %lu\n",
+ Notifier->Events,
+ Notifier->Dpcs);
+}
+
+NTSTATUS
+NotifierInitialize(
+ IN PXENVIF_FRONTEND Frontend,
+ OUT PXENVIF_NOTIFIER *Notifier
+ )
+{
+ NTSTATUS status;
+
+ *Notifier = __NotifierAllocate(sizeof (XENVIF_NOTIFIER));
+
+ status = STATUS_NO_MEMORY;
+ if (*Notifier == NULL)
+ goto fail1;
+
+ (*Notifier)->Frontend = Frontend;
+
+ KeInitializeSpinLock(&(*Notifier)->Lock);
+ KeInitializeDpc(&(*Notifier)->Dpc,
+ NotifierDpc,
+ *Notifier);
+
+ return STATUS_SUCCESS;
+
+fail1:
+ Error("fail1 (%08x)\n", status);
+
+ return status;
+}
+
+NTSTATUS
+NotifierConnect(
+ IN PXENVIF_NOTIFIER Notifier
+ )
+{
+ PXENVIF_FRONTEND Frontend = Notifier->Frontend;
+ BOOLEAN Pending;
+ NTSTATUS status;
+
+ ASSERT3U(KeGetCurrentIrql(), ==, DISPATCH_LEVEL);
+ KeAcquireSpinLockAtDpcLevel(&Notifier->Lock);
+
+ ASSERT(!Notifier->Connected);
+
+ Notifier->EvtchnInterface = FrontendGetEvtchnInterface(Frontend);
+
+ EVTCHN(Acquire, Notifier->EvtchnInterface);
+
+ Notifier->Evtchn = EVTCHN(Open,
+ Notifier->EvtchnInterface,
+ EVTCHN_UNBOUND,
+ NotifierEvtchnCallback,
+ Notifier,
+ FrontendGetBackendDomain(Frontend),
+ TRUE);
+
+ status = STATUS_UNSUCCESSFUL;
+ if (Notifier->Evtchn == NULL)
+ goto fail1;
+
+ Pending = EVTCHN(Unmask,
+ Notifier->EvtchnInterface,
+ Notifier->Evtchn,
+ FALSE);
+ if (Pending)
+ EVTCHN(Trigger,
+ Notifier->EvtchnInterface,
+ Notifier->Evtchn);
+
+ Notifier->DebugInterface = FrontendGetDebugInterface(Frontend);
+
+ DEBUG(Acquire, Notifier->DebugInterface);
+
+ status = DEBUG(Register,
+ Notifier->DebugInterface,
+ __MODULE__ "|NOTIFIER",
+ NotifierDebugCallback,
+ Notifier,
+ &Notifier->DebugCallback);
+ if (!NT_SUCCESS(status))
+ goto fail2;
+
+ Notifier->StoreInterface = FrontendGetStoreInterface(Frontend);
+
+ STORE(Acquire, Notifier->StoreInterface);
+
+ Notifier->Connected = TRUE;
+ KeReleaseSpinLockFromDpcLevel(&Notifier->Lock);
+
+ return STATUS_SUCCESS;
+
+fail2:
+ Error("fail2\n");
+
+ DEBUG(Release, Notifier->DebugInterface);
+
+ EVTCHN(Close,
+ Notifier->EvtchnInterface,
+ Notifier->Evtchn);
+ Notifier->Evtchn = NULL;
+
+ Notifier->Events = 0;
+
+fail1:
+ Error("fail1 (%08x)\n", status);
+
+ EVTCHN(Release, Notifier->EvtchnInterface);
+
+ KeReleaseSpinLockFromDpcLevel(&Notifier->Lock);
+
+ return status;
+}
+
+NTSTATUS
+NotifierStoreWrite(
+ IN PXENVIF_NOTIFIER Notifier,
+ IN PXENBUS_STORE_TRANSACTION Transaction
+ )
+{
+ PXENVIF_FRONTEND Frontend = Notifier->Frontend;
+ ULONG Port;
+ NTSTATUS status;
+
+ Port = EVTCHN(Port,
+ Notifier->EvtchnInterface,
+ Notifier->Evtchn);
+
+ status = STORE(Printf,
+ Notifier->StoreInterface,
+ Transaction,
+ FrontendGetPath(Frontend),
+ "event-channel",
+ "%u",
+ Port);
+
+ if (!NT_SUCCESS(status))
+ goto fail1;
+
+ return STATUS_SUCCESS;
+
+fail1:
+ Error("fail1 (%08x)\n", status);
+
+ return status;
+}
+
+NTSTATUS
+NotifierEnable(
+ IN PXENVIF_NOTIFIER Notifier
+ )
+{
+ ASSERT(!Notifier->Enabled);
+ Notifier->Enabled = TRUE;
+
+ if (KeInsertQueueDpc(&Notifier->Dpc, NULL, NULL))
+ Notifier->Dpcs++;
+
+ return STATUS_SUCCESS;
+}
+
+VOID
+NotifierDisable(
+ IN PXENVIF_NOTIFIER Notifier
+ )
+{
+ ASSERT(Notifier->Enabled);
+ Notifier->Enabled = FALSE;
+}
+
+VOID
+NotifierDisconnect(
+ IN PXENVIF_NOTIFIER Notifier
+ )
+{
+ KeAcquireSpinLockAtDpcLevel(&Notifier->Lock);
+
+ ASSERT(Notifier->Connected);
+ Notifier->Connected = FALSE;
+
+ STORE(Release, Notifier->StoreInterface);
+ Notifier->StoreInterface = NULL;
+
+ DEBUG(Deregister,
+ Notifier->DebugInterface,
+ Notifier->DebugCallback);
+ Notifier->DebugCallback = NULL;
+
+ DEBUG(Release, Notifier->DebugInterface);
+ Notifier->DebugInterface = NULL;
+
+ EVTCHN(Close,
+ Notifier->EvtchnInterface,
+ Notifier->Evtchn);
+ Notifier->Evtchn = NULL;
+
+ Notifier->Events = 0;
+
+ EVTCHN(Release, Notifier->EvtchnInterface);
+ Notifier->EvtchnInterface = NULL;
+
+ KeReleaseSpinLockFromDpcLevel(&Notifier->Lock);
+}
+
+VOID
+NotifierTeardown(
+ IN PXENVIF_NOTIFIER Notifier
+ )
+{
+ KeFlushQueuedDpcs();
+
+ Notifier->Dpcs = 0;
+ Notifier->Frontend = NULL;
+
+ RtlZeroMemory(&Notifier->Dpc, sizeof (KDPC));
+ RtlZeroMemory(&Notifier->Lock, sizeof (KSPIN_LOCK));
+
+ ASSERT(IsZeroMemory(Notifier, sizeof (XENVIF_NOTIFIER)));
+
+ __NotifierFree(Notifier);
+}
+
+VOID
+NotifierSend(
+ IN PXENVIF_NOTIFIER Notifier
+ )
+{
+ KIRQL Irql;
+
+ KeAcquireSpinLock(&Notifier->Lock, &Irql);
+
+ if (Notifier->Connected)
+ (VOID) EVTCHN(Send,
+ Notifier->EvtchnInterface,
+ Notifier->Evtchn);
+
+ KeReleaseSpinLock(&Notifier->Lock, Irql);
+}
+
+VOID
+NotifierTrigger(
+ IN PXENVIF_NOTIFIER Notifier
+ )
+{
+ KIRQL Irql;
+
+ KeAcquireSpinLock(&Notifier->Lock, &Irql);
+
+ if (Notifier->Connected)
+ (VOID) EVTCHN(Trigger,
+ Notifier->EvtchnInterface,
+ Notifier->Evtchn);
+
+ KeReleaseSpinLock(&Notifier->Lock, Irql);
+}
--- /dev/null
+/* Copyright (c) Citrix Systems Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms,
+ * with or without modification, are permitted provided
+ * that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer in the documentation and/or other
+ * materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef _XENVIF_NOTIFIER_H
+#define _XENVIF_NOTIFIER_H
+
+#include <ntddk.h>
+#include <store_interface.h>
+
+#include "frontend.h"
+
+typedef struct _XENVIF_NOTIFIER XENVIF_NOTIFIER, *PXENVIF_NOTIFIER;
+
+extern NTSTATUS
+NotifierInitialize(
+ IN PXENVIF_FRONTEND Frontend,
+ OUT PXENVIF_NOTIFIER *Notifier
+ );
+
+extern NTSTATUS
+NotifierConnect(
+ IN PXENVIF_NOTIFIER Notifier
+ );
+
+extern NTSTATUS
+NotifierStoreWrite(
+ IN PXENVIF_NOTIFIER Notifier,
+ IN PXENBUS_STORE_TRANSACTION Transaction
+ );
+
+extern NTSTATUS
+NotifierEnable(
+ IN PXENVIF_NOTIFIER Notifier
+ );
+
+extern VOID
+NotifierDisable(
+ IN PXENVIF_NOTIFIER Notifier
+ );
+
+extern VOID
+NotifierDisconnect(
+ IN PXENVIF_NOTIFIER Notifier
+ );
+
+extern VOID
+NotifierTeardown(
+ IN PXENVIF_NOTIFIER Notifier
+ );
+
+extern VOID
+NotifierSend(
+ IN PXENVIF_NOTIFIER Notifier
+ );
+
+extern VOID
+NotifierTrigger(
+ IN PXENVIF_NOTIFIER Notifier
+ );
+
+#endif // _XENVIF_NOTIFIER_H
--- /dev/null
+/* Copyright (c) Citrix Systems Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms,
+ * with or without modification, are permitted provided
+ * that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer in the documentation and/or other
+ * materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <ntddk.h>
+#include <ntstrsafe.h>
+#include <util.h>
+#include <ethernet.h>
+#include <tcpip.h>
+#include <llc.h>
+#include <ipx.h>
+#include <vif_interface.h>
+
+#include "parse.h"
+#include "log.h"
+#include "assert.h"
+
+static FORCEINLINE NTSTATUS
+__ParseTcpHeader(
+ IN PUCHAR StartVa,
+ IN ULONG Offset,
+ IN BOOLEAN (*Pullup)(PVOID, PUCHAR, PXENVIF_PACKET_PAYLOAD, ULONG),
+ IN PVOID Argument,
+ IN OUT PXENVIF_HEADER_STATISTICS Statistics,
+ IN OUT PXENVIF_PACKET_PAYLOAD Payload,
+ OUT PXENVIF_PACKET_INFO Info
+ )
+{
+ PTCP_HEADER Header;
+
+ Info->TcpHeader.Offset = Offset;
+
+ if (!Pullup(Argument, StartVa + Offset, Payload, sizeof (TCP_HEADER)))
+ goto fail1;
+
+ Header = (PTCP_HEADER)(StartVa + Offset);
+ Offset += sizeof (TCP_HEADER);
+
+ Info->TcpHeader.Length = Offset - Info->TcpHeader.Offset;
+ Statistics->Tcp++;
+
+ // Check for malformned header
+ if (TCP_HEADER_LENGTH(Header) < Info->TcpHeader.Length)
+ goto fail2;
+
+ if (TCP_HEADER_LENGTH(Header) > Info->TcpHeader.Length) {
+ ULONG Extra;
+
+ Info->TcpOptions.Offset = Offset;
+
+ Extra = TCP_HEADER_LENGTH(Header) - Info->TcpHeader.Length;
+
+ if (!Pullup(Argument, StartVa + Offset, Payload, Extra))
+ goto fail3;
+
+ Offset += Extra;
+
+ Info->TcpOptions.Length = Offset - Info->TcpOptions.Offset;
+ Statistics->TcpOptions++;
+ }
+
+ Info->Length += Info->TcpHeader.Length + Info->TcpOptions.Length;
+
+ return STATUS_SUCCESS;
+
+fail3:
+ Info->TcpOptions.Offset = 0;
+
+fail2:
+ Info->TcpHeader.Length = 0;
+
+fail1:
+ Info->TcpHeader.Offset = 0;
+
+ return STATUS_UNSUCCESSFUL;
+}
+
+static FORCEINLINE NTSTATUS
+__ParseUdpHeader(
+ IN PUCHAR StartVa,
+ IN ULONG Offset,
+ IN BOOLEAN (*Pullup)(PVOID, PUCHAR, PXENVIF_PACKET_PAYLOAD, ULONG),
+ IN PVOID Argument,
+ IN OUT PXENVIF_HEADER_STATISTICS Statistics,
+ IN OUT PXENVIF_PACKET_PAYLOAD Payload,
+ OUT PXENVIF_PACKET_INFO Info
+ )
+{
+ Info->UdpHeader.Offset = Offset;
+
+ if (!Pullup(Argument, StartVa + Offset, Payload, sizeof (UDP_HEADER)))
+ goto fail1;
+
+ Offset += sizeof (UDP_HEADER);
+
+ Info->UdpHeader.Length = Offset - Info->UdpHeader.Offset;
+ Statistics->Udp++;
+
+ Info->Length += Info->UdpHeader.Length;
+
+ return STATUS_SUCCESS;
+
+fail1:
+ Info->UdpHeader.Offset = 0;
+
+ return STATUS_UNSUCCESSFUL;
+}
+
+static FORCEINLINE NTSTATUS
+__ParseIpVersion4Header(
+ IN PUCHAR StartVa,
+ IN ULONG Offset,
+ IN BOOLEAN (*Pullup)(PVOID, PUCHAR, PXENVIF_PACKET_PAYLOAD, ULONG),
+ IN PVOID Argument,
+ IN OUT PXENVIF_HEADER_STATISTICS Statistics,
+ IN OUT PXENVIF_PACKET_PAYLOAD Payload,
+ OUT PXENVIF_PACKET_INFO Info
+ )
+{
+ PIPV4_HEADER Header;
+ USHORT PacketLength;
+ USHORT FragmentOffsetAndFlags;
+ NTSTATUS status;
+
+ Info->IpHeader.Offset = Offset;
+
+ if (!Pullup(Argument, StartVa + Offset, Payload, sizeof (IPV4_HEADER)))
+ goto fail1;
+
+ Header = (PIPV4_HEADER)(StartVa + Offset);
+ Offset += sizeof (IPV4_HEADER);
+
+ Info->IpHeader.Length = Offset - Info->IpHeader.Offset;
+ Statistics->IpVersion4++;
+
+ if (Header->Version != 4)
+ goto fail2;
+
+ PacketLength = NTOHS(Header->PacketLength);
+ if (PacketLength > Info->IpHeader.Length + Payload->Length)
+ goto fail3;
+
+ if (IPV4_HEADER_LENGTH(Header) < Info->IpHeader.Length)
+ goto fail4;
+
+ if (IPV4_HEADER_LENGTH(Header) > Info->IpHeader.Length) {
+ ULONG Extra;
+
+ Info->IpOptions.Offset = Offset;
+
+ Extra = IPV4_HEADER_LENGTH(Header) - Info->IpHeader.Length;
+
+ if (!Pullup(Argument, StartVa + Offset, Payload, Extra))
+ goto fail5;
+
+ Offset += Extra;
+
+ Info->IpOptions.Length = Offset - Info->IpOptions.Offset;
+ Statistics->IpOptions++;
+ }
+
+ Info->Length += Info->IpHeader.Length + Info->IpOptions.Length;
+
+ FragmentOffsetAndFlags = NTOHS(Header->FragmentOffsetAndFlags);
+
+ status = STATUS_SUCCESS;
+ if (IPV4_IS_A_FRAGMENT(FragmentOffsetAndFlags))
+ goto done;
+
+ switch (Header->Protocol) {
+ case IPPROTO_TCP:
+ status = __ParseTcpHeader(StartVa, Offset, Pullup, Argument, Statistics, Payload, Info);
+ break;
+
+ case IPPROTO_UDP:
+ status = __ParseUdpHeader(StartVa, Offset, Pullup, Argument, Statistics, Payload, Info);
+ break;
+
+ default:
+ status = STATUS_SUCCESS;
+ break;
+ }
+
+done:
+ return status;
+
+fail5:
+ Info->IpOptions.Offset = 0;
+
+fail4:
+fail3:
+fail2:
+ Info->IpHeader.Length = 0;
+
+fail1:
+ Info->IpHeader.Offset = 0;
+
+ return STATUS_UNSUCCESSFUL;
+}
+
+static FORCEINLINE NTSTATUS
+__ParseIpVersion6Header(
+ IN PUCHAR StartVa,
+ IN ULONG Offset,
+ IN BOOLEAN (*Pullup)(PVOID, PUCHAR, PXENVIF_PACKET_PAYLOAD, ULONG),
+ IN PVOID Argument,
+ IN OUT PXENVIF_HEADER_STATISTICS Statistics,
+ IN OUT PXENVIF_PACKET_PAYLOAD Payload,
+ OUT PXENVIF_PACKET_INFO Info
+ )
+{
+ PIPV6_HEADER Header;
+ USHORT PayloadLength;
+ BOOLEAN IsAFragment;
+ UCHAR NextHeader;
+ ULONG Count;
+ BOOLEAN Finished;
+ NTSTATUS status;
+
+ Info->IpHeader.Offset = Offset;
+
+ if (!Pullup(Argument, StartVa + Offset, Payload, sizeof (IPV6_HEADER)))
+ goto fail1;
+
+ Header = (PIPV6_HEADER)(StartVa + Offset);
+ Offset += sizeof (IPV6_HEADER);
+
+ Info->IpHeader.Length = Offset - Info->IpHeader.Offset;
+ Statistics->IpVersion6++;
+
+ if (Header->Version != 6)
+ goto fail2;
+
+ PayloadLength = NTOHS(Header->PayloadLength);
+ if (PayloadLength > Payload->Length)
+ goto fail3;
+
+ Info->IpOptions.Offset = Offset;
+
+ IsAFragment = FALSE;
+
+ NextHeader = Header->NextHeader;
+ Count = 0;
+ Finished = FALSE;
+
+ while (!Finished) {
+ Count++;
+ ASSERT3U(Count, <, 100);
+
+ switch (NextHeader) {
+ case IPPROTO_FRAGMENT: {
+ PIPV6_FRAGMENT_HEADER Fragment;
+ USHORT FragmentOffsetAndFlags;
+
+ if (!Pullup(Argument, StartVa + Offset, Payload, sizeof (IPV6_FRAGMENT_HEADER)))
+ goto fail4;
+
+ Fragment = (PIPV6_FRAGMENT_HEADER)(StartVa + Offset);
+ Offset += sizeof (IPV6_FRAGMENT_HEADER);
+
+ FragmentOffsetAndFlags = NTOHS(Fragment->OffsetAndFlags);
+ IsAFragment = IPV6_IS_A_FRAGMENT(FragmentOffsetAndFlags) ? TRUE : FALSE;
+
+ NextHeader = Fragment->NextHeader;
+ break;
+ }
+ case IPPROTO_HOP_OPTIONS:
+ case IPPROTO_DST_OPTIONS:
+ case IPPROTO_ROUTING: {
+ PIPV6_OPTION_HEADER Option;
+ ULONG Extra;
+
+ if (!Pullup(Argument, StartVa + Offset, Payload, sizeof (IPV6_OPTION_HEADER)))
+ goto fail4;
+
+ Option = (PIPV6_OPTION_HEADER)(StartVa + Offset);
+ Offset += sizeof (IPV6_OPTION_HEADER);
+
+ Extra = ((Option->Length + 1) * 8) - sizeof (IPV6_OPTION_HEADER);
+
+ if (!Pullup(Argument, StartVa + Offset, Payload, Extra))
+ goto fail5;
+
+ Offset += Extra;
+
+ NextHeader = Option->NextHeader;
+ break;
+ }
+ default:
+ Finished = TRUE;
+ break;
+ }
+ }
+
+ Info->IpOptions.Length = (ULONG)(Offset - Info->IpOptions.Offset);
+ if (Info->IpOptions.Length == 0)
+ Info->IpOptions.Offset = 0;
+ else
+ Statistics->IpOptions++;
+
+ Info->Length += Info->IpHeader.Length + Info->IpOptions.Length;
+
+ status = STATUS_SUCCESS;
+ if (IsAFragment)
+ goto done;
+
+ switch (NextHeader) {
+ case IPPROTO_TCP:
+ status = __ParseTcpHeader(StartVa, Offset, Pullup, Argument, Statistics, Payload, Info);
+ break;
+
+ case IPPROTO_UDP:
+ status = __ParseUdpHeader(StartVa, Offset, Pullup, Argument, Statistics, Payload, Info);
+ break;
+
+ default:
+ status = STATUS_SUCCESS;
+ break;
+ }
+
+done:
+ return status;
+
+fail5:
+fail4:
+ Info->IpOptions.Offset = 0;
+
+fail3:
+fail2:
+ Info->IpHeader.Length = 0;
+
+fail1:
+ Info->IpHeader.Offset = 0;
+
+ return STATUS_UNSUCCESSFUL;
+}
+
+static FORCEINLINE NTSTATUS
+__ParseLLCSnapHeader(
+ IN PUCHAR StartVa,
+ IN ULONG Offset,
+ IN BOOLEAN (*Pullup)(PVOID, PUCHAR, PXENVIF_PACKET_PAYLOAD, ULONG),
+ IN PVOID Argument,
+ IN OUT PXENVIF_HEADER_STATISTICS Statistics,
+ IN OUT PXENVIF_PACKET_PAYLOAD Payload,
+ OUT PXENVIF_PACKET_INFO Info
+ )
+{
+ PLLC_SNAP_HEADER Header;
+
+ Info->LLCSnapHeader.Offset = Offset;
+
+ if (!Pullup(Argument, StartVa + Offset, Payload, sizeof (LLC_U_HEADER)))
+ goto fail1;
+
+ Header = (PLLC_SNAP_HEADER)(StartVa + Offset);
+ Offset += sizeof (LLC_U_HEADER);
+
+ if ((Header->DestinationSAP & LLC_SAP_MASK) == 0xAA &&
+ (Header->SourceSAP & LLC_SAP_MASK) == 0xAA &&
+ Header->Control == LLC_U_FRAME) {
+ ULONG Extra;
+
+ Extra = sizeof (LLC_SNAP_HEADER) - sizeof (LLC_U_HEADER);
+
+ if (!Pullup(Argument, StartVa + Offset, Payload, Extra))
+ goto fail2;
+
+ Offset += Extra;
+ }
+
+ Info->LLCSnapHeader.Length = Offset - Info->LLCSnapHeader.Offset;
+ Statistics->LLC++;
+
+ Info->Length += Info->LLCSnapHeader.Length;
+
+ return STATUS_SUCCESS;
+
+fail2:
+fail1:
+ Info->LLCSnapHeader.Offset = 0;
+
+ return STATUS_UNSUCCESSFUL;
+}
+
+static FORCEINLINE NTSTATUS
+__ParseEthernetHeader(
+ IN PUCHAR StartVa,
+ IN ULONG Offset,
+ IN BOOLEAN (*Pullup)(PVOID, PUCHAR, PXENVIF_PACKET_PAYLOAD, ULONG),
+ IN PVOID Argument,
+ IN OUT PXENVIF_HEADER_STATISTICS Statistics,
+ IN OUT PXENVIF_PACKET_PAYLOAD Payload,
+ OUT PXENVIF_PACKET_INFO Info
+ )
+{
+ PETHERNET_HEADER Header;
+ USHORT TypeOrLength;
+ BOOLEAN IsLLC;
+ NTSTATUS status;
+
+ Info->EthernetHeader.Offset = Offset;
+
+ if (!Pullup(Argument, StartVa + Offset, Payload, sizeof (ETHERNET_UNTAGGED_HEADER)))
+ goto fail1;
+
+ Header = (PETHERNET_HEADER)(StartVa + Offset);
+ Offset += sizeof (ETHERNET_UNTAGGED_HEADER);
+
+ IsLLC = FALSE;
+
+ TypeOrLength = NTOHS(Header->Untagged.TypeOrLength);
+ if (TypeOrLength == ETHERTYPE_TPID) {
+ ULONG Extra;
+
+ Extra = sizeof (ETHERNET_TAGGED_HEADER) - sizeof (ETHERNET_UNTAGGED_HEADER);
+
+ if (!Pullup(Argument, StartVa + Offset, Payload, Extra))
+ goto fail2;
+
+ Offset += Extra;
+
+ TypeOrLength = NTOHS(Header->Tagged.TypeOrLength);
+ Statistics->Tagged++;
+ }
+
+ if (TypeOrLength <= ETHERNET_MTU)
+ IsLLC = TRUE;
+
+ Info->EthernetHeader.Length = Offset - Info->EthernetHeader.Offset;
+ Info->Length += Info->EthernetHeader.Length;
+
+ if (IsLLC) {
+ status = __ParseLLCSnapHeader(StartVa, Offset, Pullup, Argument, Statistics, Payload, Info);
+ } else {
+ switch (TypeOrLength) {
+ case ETHERTYPE_IPV4:
+ status = __ParseIpVersion4Header(StartVa, Offset, Pullup, Argument, Statistics, Payload, Info);
+ break;
+
+ case ETHERTYPE_IPV6:
+ status = __ParseIpVersion6Header(StartVa, Offset, Pullup, Argument, Statistics, Payload, Info);
+ break;
+
+ default:
+ status = STATUS_SUCCESS;
+ break;
+ }
+ }
+
+ return status;
+
+fail2:
+fail1:
+ Info->EthernetHeader.Offset = 0;
+
+ return STATUS_UNSUCCESSFUL;
+}
+
+NTSTATUS
+ParsePacket(
+ IN PUCHAR StartVa,
+ IN BOOLEAN (*Pullup)(PVOID, PUCHAR, PXENVIF_PACKET_PAYLOAD, ULONG),
+ IN PVOID Argument,
+ IN OUT PXENVIF_HEADER_STATISTICS Statistics,
+ IN OUT PXENVIF_PACKET_PAYLOAD Payload,
+ OUT PXENVIF_PACKET_INFO Info
+ )
+{
+ NTSTATUS status;
+
+ ASSERT(IsZeroMemory(Info, sizeof (XENVIF_PACKET_INFO)));
+
+ status = __ParseEthernetHeader(StartVa, 0, Pullup, Argument, Statistics, Payload, Info);
+ if (!NT_SUCCESS(status))
+ goto fail1;
+
+ return STATUS_SUCCESS;
+
+fail1:
+ return status;
+}
--- /dev/null
+/* Copyright (c) Citrix Systems Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms,
+ * with or without modification, are permitted provided
+ * that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer in the documentation and/or other
+ * materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef _XENVIF_PARSE_H
+#define _XENVIF_PARSE_H
+
+#include <vif_interface.h>
+
+typedef struct _XENVIF_HEADER_STATISTICS {
+ ULONGLONG Tagged;
+ ULONGLONG LLC;
+ ULONGLONG IpVersion4;
+ ULONGLONG IpVersion6;
+ ULONGLONG IpOptions;
+ ULONGLONG Tcp;
+ ULONGLONG TcpOptions;
+ ULONGLONG Udp;
+} XENVIF_HEADER_STATISTICS, *PXENVIF_HEADER_STATISTICS;
+
+typedef struct _XENVIF_PACKET_PAYLOAD {
+ PMDL Mdl;
+ ULONG Offset;
+ ULONG Length;
+} XENVIF_PACKET_PAYLOAD, *PXENVIF_PACKET_PAYLOAD;
+
+extern NTSTATUS
+ParsePacket(
+ IN PUCHAR StartVa,
+ IN BOOLEAN (*Pullup)(PVOID, PUCHAR, PXENVIF_PACKET_PAYLOAD, ULONG),
+ IN PVOID Argument,
+ IN OUT PXENVIF_HEADER_STATISTICS Statistics,
+ IN OUT PXENVIF_PACKET_PAYLOAD Payload,
+ OUT PXENVIF_PACKET_INFO Info
+ );
+
+#endif // _XENVIF_PARSE_H
--- /dev/null
+/* Copyright (c) Citrix Systems Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms,
+ * with or without modification, are permitted provided
+ * that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer in the documentation and/or other
+ * materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#define INITGUID 1
+
+#include <ntddk.h>
+#include <wdmguid.h>
+#include <ntstrsafe.h>
+#include <stdlib.h>
+#include <util.h>
+#include <xen.h>
+#include <store_interface.h>
+#include <emulated_interface.h>
+
+#include "names.h"
+#include "fdo.h"
+#include "pdo.h"
+#include "frontend.h"
+#include "vif.h"
+#include "driver.h"
+#include "registry.h"
+#include "thread.h"
+#include "log.h"
+#include "assert.h"
+
+#define PDO_REVISION 0x02
+
+#define PDO_POOL 'ODP'
+
+typedef enum _PDO_RESOURCE_TYPE {
+ MEMORY_RESOURCE = 0,
+ INTERRUPT_RESOURCE,
+ RESOURCE_COUNT
+} PDO_RESOURCE_TYPE, *PPDO_RESOURCE_TYPE;
+
+typedef struct _PDO_RESOURCE {
+ CM_PARTIAL_RESOURCE_DESCRIPTOR Raw;
+ CM_PARTIAL_RESOURCE_DESCRIPTOR Translated;
+} PDO_RESOURCE, *PPDO_RESOURCE;
+
+struct _XENVIF_PDO {
+ PXENVIF_DX Dx;
+
+ PXENVIF_THREAD SystemPowerThread;
+ PIRP SystemPowerIrp;
+ PXENVIF_THREAD DevicePowerThread;
+ PIRP DevicePowerIrp;
+
+ PXENVIF_FDO Fdo;
+ BOOLEAN Missing;
+ const CHAR *Reason;
+ ULONG LuidIndex;
+ PCHAR Address;
+
+ PXENVIF_FRONTEND Frontend;
+ XENVIF_VIF_INTERFACE VifInterface;
+
+ PXENBUS_SUSPEND_INTERFACE SuspendInterface;
+
+ PXENBUS_SUSPEND_CALLBACK SuspendCallbackLate;
+};
+
+static FORCEINLINE PVOID
+__PdoAllocate(
+ IN ULONG Length
+ )
+{
+ return __AllocateNonPagedPoolWithTag(Length, PDO_POOL);
+}
+
+static FORCEINLINE VOID
+__PdoFree(
+ IN PVOID Buffer
+ )
+{
+ __FreePoolWithTag(Buffer, PDO_POOL);
+}
+
+static FORCEINLINE VOID
+__PdoSetDevicePnpState(
+ IN PXENVIF_PDO Pdo,
+ IN DEVICE_PNP_STATE State
+ )
+{
+ PXENVIF_DX Dx = Pdo->Dx;
+
+ // We can never transition out of the deleted state
+ ASSERT(Dx->DevicePnpState != Deleted || State == Deleted);
+
+ Dx->PreviousDevicePnpState = Dx->DevicePnpState;
+ Dx->DevicePnpState = State;
+}
+
+VOID
+PdoSetDevicePnpState(
+ IN PXENVIF_PDO Pdo,
+ IN DEVICE_PNP_STATE State
+ )
+{
+ __PdoSetDevicePnpState(Pdo, State);
+}
+
+static FORCEINLINE VOID
+__PdoRestoreDevicePnpState(
+ IN PXENVIF_PDO Pdo,
+ IN DEVICE_PNP_STATE State
+ )
+{
+ PXENVIF_DX Dx = Pdo->Dx;
+
+ if (Dx->DevicePnpState == State)
+ Dx->DevicePnpState = Dx->PreviousDevicePnpState;
+}
+
+static FORCEINLINE DEVICE_PNP_STATE
+__PdoGetDevicePnpState(
+ IN PXENVIF_PDO Pdo
+ )
+{
+ PXENVIF_DX Dx = Pdo->Dx;
+
+ return Dx->DevicePnpState;
+}
+
+DEVICE_PNP_STATE
+PdoGetDevicePnpState(
+ IN PXENVIF_PDO Pdo
+ )
+{
+ return __PdoGetDevicePnpState(Pdo);
+}
+
+static FORCEINLINE VOID
+__PdoSetSystemPowerState(
+ IN PXENVIF_PDO Pdo,
+ IN SYSTEM_POWER_STATE State
+ )
+{
+ PXENVIF_DX Dx = Pdo->Dx;
+
+ Dx->SystemPowerState = State;
+}
+
+static FORCEINLINE SYSTEM_POWER_STATE
+__PdoGetSystemPowerState(
+ IN PXENVIF_PDO Pdo
+ )
+{
+ PXENVIF_DX Dx = Pdo->Dx;
+
+ return Dx->SystemPowerState;
+}
+
+static FORCEINLINE VOID
+__PdoSetDevicePowerState(
+ IN PXENVIF_PDO Pdo,
+ IN DEVICE_POWER_STATE State
+ )
+{
+ PXENVIF_DX Dx = Pdo->Dx;
+
+ Dx->DevicePowerState = State;
+}
+
+static FORCEINLINE DEVICE_POWER_STATE
+__PdoGetDevicePowerState(
+ IN PXENVIF_PDO Pdo
+ )
+{
+ PXENVIF_DX Dx = Pdo->Dx;
+
+ return Dx->DevicePowerState;
+}
+
+static FORCEINLINE VOID
+__PdoSetMissing(
+ IN PXENVIF_PDO Pdo,
+ IN const CHAR *Reason
+ )
+{
+ Pdo->Reason = Reason;
+ Pdo->Missing = TRUE;
+}
+
+VOID
+PdoSetMissing(
+ IN PXENVIF_PDO Pdo,
+ IN const CHAR *Reason
+ )
+{
+ __PdoSetMissing(Pdo, Reason);
+}
+
+static FORCEINLINE BOOLEAN
+__PdoIsMissing(
+ IN PXENVIF_PDO Pdo
+ )
+{
+ return Pdo->Missing;
+}
+
+BOOLEAN
+PdoIsMissing(
+ IN PXENVIF_PDO Pdo
+ )
+{
+ return __PdoIsMissing(Pdo);
+}
+
+static FORCEINLINE VOID
+__PdoSetName(
+ IN PXENVIF_PDO Pdo,
+ IN PANSI_STRING Ansi
+ )
+{
+ PXENVIF_DX Dx = Pdo->Dx;
+ NTSTATUS status;
+
+ status = RtlStringCbPrintfA(Dx->Name, MAX_DEVICE_ID_LEN, "%Z", Ansi);
+ ASSERT(NT_SUCCESS(status));
+}
+
+static FORCEINLINE PCHAR
+__PdoGetName(
+ IN PXENVIF_PDO Pdo
+ )
+{
+ PXENVIF_DX Dx = Pdo->Dx;
+
+ return Dx->Name;
+}
+
+PCHAR
+PdoGetName(
+ IN PXENVIF_PDO Pdo
+ )
+{
+ return __PdoGetName(Pdo);
+}
+
+static FORCEINLINE PDEVICE_OBJECT
+__PdoGetDeviceObject(
+ IN PXENVIF_PDO Pdo
+ )
+{
+ PXENVIF_DX Dx = Pdo->Dx;
+
+ return (Dx->DeviceObject);
+}
+
+PDEVICE_OBJECT
+PdoGetDeviceObject(
+ IN PXENVIF_PDO Pdo
+ )
+{
+ return __PdoGetDeviceObject(Pdo);
+}
+
+ULONG
+PdoGetLuidIndex(
+ IN PXENVIF_PDO Pdo
+ )
+{
+ return Pdo->LuidIndex;
+}
+
+PCHAR
+PdoGetAddress(
+ IN PXENVIF_PDO Pdo
+ )
+{
+ return Pdo->Address;
+}
+
+static FORCEINLINE VOID
+__PdoLink(
+ IN PXENVIF_PDO Pdo,
+ IN PXENVIF_FDO Fdo
+ )
+{
+ Pdo->Fdo = Fdo;
+
+ FdoAddPhysicalDeviceObject(Fdo, Pdo);
+}
+
+static FORCEINLINE VOID
+__PdoUnlink(
+ IN PXENVIF_PDO Pdo
+ )
+{
+ PXENVIF_FDO Fdo = Pdo->Fdo;
+
+ ASSERT(Fdo != NULL);
+
+ FdoRemovePhysicalDeviceObject(Fdo, Pdo);
+
+ Pdo->Fdo = NULL;
+}
+
+static FORCEINLINE PXENVIF_FDO
+__PdoGetFdo(
+ IN PXENVIF_PDO Pdo
+ )
+{
+ return Pdo->Fdo;
+}
+
+static FORCEINLINE PXENVIF_FRONTEND
+__PdoGetFrontend(
+ IN PXENVIF_PDO Pdo
+ )
+{
+ return Pdo->Frontend;
+}
+
+PXENVIF_FRONTEND
+PdoGetFrontend(
+ IN PXENVIF_PDO Pdo
+ )
+{
+ return __PdoGetFrontend(Pdo);
+}
+
+static FORCEINLINE PXENBUS_EVTCHN_INTERFACE
+__PdoGetEvtchnInterface(
+ IN PXENVIF_PDO Pdo
+ )
+{
+ return FdoGetEvtchnInterface(__PdoGetFdo(Pdo));
+}
+
+PXENBUS_EVTCHN_INTERFACE
+PdoGetEvtchnInterface(
+ IN PXENVIF_PDO Pdo
+ )
+{
+ return __PdoGetEvtchnInterface(Pdo);
+}
+
+static FORCEINLINE PXENBUS_DEBUG_INTERFACE
+__PdoGetDebugInterface(
+ IN PXENVIF_PDO Pdo
+ )
+{
+ return FdoGetDebugInterface(__PdoGetFdo(Pdo));
+}
+
+PXENBUS_DEBUG_INTERFACE
+PdoGetDebugInterface(
+ IN PXENVIF_PDO Pdo
+ )
+{
+ return __PdoGetDebugInterface(Pdo);
+}
+
+static FORCEINLINE PXENBUS_GNTTAB_INTERFACE
+__PdoGetGnttabInterface(
+ IN PXENVIF_PDO Pdo
+ )
+{
+ return FdoGetGnttabInterface(__PdoGetFdo(Pdo));
+}
+
+PXENBUS_GNTTAB_INTERFACE
+PdoGetGnttabInterface(
+ IN PXENVIF_PDO Pdo
+ )
+{
+ return __PdoGetGnttabInterface(Pdo);
+}
+
+static FORCEINLINE PXENBUS_SUSPEND_INTERFACE
+__PdoGetSuspendInterface(
+ IN PXENVIF_PDO Pdo
+ )
+{
+ return FdoGetSuspendInterface(__PdoGetFdo(Pdo));
+}
+
+PXENBUS_SUSPEND_INTERFACE
+PdoGetSuspendInterface(
+ IN PXENVIF_PDO Pdo
+ )
+{
+ return __PdoGetSuspendInterface(Pdo);
+}
+
+static FORCEINLINE PXENBUS_STORE_INTERFACE
+__PdoGetStoreInterface(
+ IN PXENVIF_PDO Pdo
+ )
+{
+ return FdoGetStoreInterface(__PdoGetFdo(Pdo));
+}
+
+PXENBUS_STORE_INTERFACE
+PdoGetStoreInterface(
+ IN PXENVIF_PDO Pdo
+ )
+{
+ return __PdoGetStoreInterface(Pdo);
+}
+
+static FORCEINLINE PXENFILT_EMULATED_INTERFACE
+__PdoGetEmulatedInterface(
+ IN PXENVIF_PDO Pdo
+ )
+{
+ return FdoGetEmulatedInterface(__PdoGetFdo(Pdo));
+}
+
+PXENFILT_EMULATED_INTERFACE
+PdoGetEmulatedInterface(
+ IN PXENVIF_PDO Pdo
+ )
+{
+ return __PdoGetEmulatedInterface(Pdo);
+}
+
+static FORCEINLINE PXENVIF_VIF_INTERFACE
+__PdoGetVifInterface(
+ IN PXENVIF_PDO Pdo
+ )
+{
+ return &Pdo->VifInterface;
+}
+
+PXENVIF_VIF_INTERFACE
+PdoGetVifInterface(
+ IN PXENVIF_PDO Pdo
+ )
+{
+ return __PdoGetVifInterface(Pdo);
+}
+
+VOID
+PdoRequestEject(
+ IN PXENVIF_PDO Pdo
+ )
+{
+ IoRequestDeviceEject(__PdoGetDeviceObject(Pdo));
+}
+
+static FORCEINLINE NTSTATUS
+__PdoD3ToD0(
+ IN PXENVIF_PDO Pdo
+ )
+{
+ POWER_STATE PowerState;
+ PXENFILT_EMULATED_INTERFACE EmulatedInterface;
+ BOOLEAN Present;
+ NTSTATUS status;
+
+ Trace("(%s) ====>\n", __PdoGetName(Pdo));
+
+ ASSERT3U(KeGetCurrentIrql(), ==, DISPATCH_LEVEL);
+ ASSERT3U(__PdoGetDevicePowerState(Pdo), ==, PowerDeviceD3);
+
+ EmulatedInterface = __PdoGetEmulatedInterface(Pdo);
+ if (EmulatedInterface != NULL) {
+ EMULATED(Acquire, EmulatedInterface);
+
+ Present = EMULATED(IsPresent,
+ EmulatedInterface,
+ "VIF",
+ __PdoGetName(Pdo));
+
+ EMULATED(Release, EmulatedInterface);
+ } else {
+ // If we cannot confirm presence of the emulated device then
+ // we must assume it is present.
+ Present = TRUE;
+ }
+
+ status = STATUS_UNSUCCESSFUL;
+ if (Present)
+ goto fail1;
+
+ status = FrontendSetState(__PdoGetFrontend(Pdo), FRONTEND_CONNECTED);
+ if (!NT_SUCCESS(status))
+ goto fail2;
+
+ __PdoSetDevicePowerState(Pdo, PowerDeviceD0);
+
+ PowerState.DeviceState = PowerDeviceD0;
+ PoSetPowerState(__PdoGetDeviceObject(Pdo),
+ DevicePowerState,
+ PowerState);
+
+ Trace("(%s) <====\n", __PdoGetName(Pdo));
+
+ return STATUS_SUCCESS;
+
+fail2:
+ Error("fail2\n");
+
+fail1:
+ Error("fail1 (%08x)\n", status);
+
+ return status;
+}
+
+static FORCEINLINE VOID
+__PdoD0ToD3(
+ IN PXENVIF_PDO Pdo
+ )
+{
+ POWER_STATE PowerState;
+
+ Trace("(%s) ====>\n", __PdoGetName(Pdo));
+
+ ASSERT3U(KeGetCurrentIrql(), ==, DISPATCH_LEVEL);
+ ASSERT3U(__PdoGetDevicePowerState(Pdo), ==, PowerDeviceD0);
+
+ PowerState.DeviceState = PowerDeviceD3;
+ PoSetPowerState(__PdoGetDeviceObject(Pdo),
+ DevicePowerState,
+ PowerState);
+
+ __PdoSetDevicePowerState(Pdo, PowerDeviceD3);
+
+ (VOID) FrontendSetState(__PdoGetFrontend(Pdo), FRONTEND_CLOSED);
+
+ Trace("(%s) <====\n", __PdoGetName(Pdo));
+}
+
+static DECLSPEC_NOINLINE VOID
+PdoSuspendCallbackLate(
+ IN PVOID Argument
+ )
+{
+ PXENVIF_PDO Pdo = Argument;
+ NTSTATUS status;
+
+ __PdoD0ToD3(Pdo);
+
+ status = __PdoD3ToD0(Pdo);
+ ASSERT(NT_SUCCESS(status));
+}
+
+static DECLSPEC_NOINLINE NTSTATUS
+PdoD3ToD0(
+ IN PXENVIF_PDO Pdo
+ )
+{
+ KIRQL Irql;
+ NTSTATUS status;
+
+ ASSERT3U(KeGetCurrentIrql(), ==, PASSIVE_LEVEL);
+
+ KeRaiseIrql(DISPATCH_LEVEL, &Irql);
+ status = __PdoD3ToD0(Pdo);
+ KeLowerIrql(Irql);
+
+ if (!NT_SUCCESS(status))
+ goto fail1;
+
+ Pdo->SuspendInterface = __PdoGetSuspendInterface(Pdo);
+
+ SUSPEND(Acquire, Pdo->SuspendInterface);
+
+ status = SUSPEND(Register,
+ Pdo->SuspendInterface,
+ SUSPEND_CALLBACK_LATE,
+ PdoSuspendCallbackLate,
+ Pdo,
+ &Pdo->SuspendCallbackLate);
+ if (!NT_SUCCESS(status))
+ goto fail2;
+
+ return STATUS_SUCCESS;
+
+fail2:
+ Error("fail2\n");
+
+ SUSPEND(Release, Pdo->SuspendInterface);
+ Pdo->SuspendInterface = NULL;
+
+ KeRaiseIrql(DISPATCH_LEVEL, &Irql);
+ __PdoD0ToD3(Pdo);
+ KeLowerIrql(Irql);
+
+fail1:
+ Error("fail1 (%08x)\n", status);
+
+ return status;
+}
+
+static DECLSPEC_NOINLINE VOID
+PdoD0ToD3(
+ IN PXENVIF_PDO Pdo
+ )
+{
+ KIRQL Irql;
+
+ ASSERT3U(KeGetCurrentIrql(), ==, PASSIVE_LEVEL);
+
+ SUSPEND(Deregister,
+ Pdo->SuspendInterface,
+ Pdo->SuspendCallbackLate);
+ Pdo->SuspendCallbackLate = NULL;
+
+ SUSPEND(Release, Pdo->SuspendInterface);
+ Pdo->SuspendInterface = NULL;
+
+ KeRaiseIrql(DISPATCH_LEVEL, &Irql);
+ __PdoD0ToD3(Pdo);
+ KeLowerIrql(Irql);
+}
+
+static DECLSPEC_NOINLINE VOID
+PdoS4ToS3(
+ IN PXENVIF_PDO Pdo
+ )
+{
+ Trace("(%s) ====>\n", __PdoGetName(Pdo));
+
+ ASSERT3U(KeGetCurrentIrql(), ==, PASSIVE_LEVEL);
+ ASSERT3U(__PdoGetSystemPowerState(Pdo), ==, PowerSystemHibernate);
+
+ __PdoSetSystemPowerState(Pdo, PowerSystemSleeping3);
+
+ Trace("(%s) <====\n", __PdoGetName(Pdo));
+}
+
+static DECLSPEC_NOINLINE VOID
+PdoS3ToS4(
+ IN PXENVIF_PDO Pdo
+ )
+{
+ Trace("(%s) ====>\n", __PdoGetName(Pdo));
+
+ ASSERT3U(KeGetCurrentIrql(), ==, PASSIVE_LEVEL);
+ ASSERT3U(__PdoGetSystemPowerState(Pdo), ==, PowerSystemSleeping3);
+
+ __PdoSetSystemPowerState(Pdo, PowerSystemHibernate);
+
+ Trace("(%s) <====\n", __PdoGetName(Pdo));
+}
+
+static DECLSPEC_NOINLINE VOID
+PdoParseResources(
+ IN PXENVIF_PDO Pdo,
+ IN PCM_RESOURCE_LIST RawResourceList,
+ IN PCM_RESOURCE_LIST TranslatedResourceList
+ )
+{
+ PCM_PARTIAL_RESOURCE_LIST RawPartialList;
+ PCM_PARTIAL_RESOURCE_LIST TranslatedPartialList;
+ ULONG Index;
+
+ UNREFERENCED_PARAMETER(Pdo);
+
+ ASSERT3U(RawResourceList->Count, ==, 1);
+ RawPartialList = &RawResourceList->List[0].PartialResourceList;
+
+ ASSERT3U(RawPartialList->Version, ==, 1);
+ ASSERT3U(RawPartialList->Revision, ==, 1);
+
+ ASSERT3U(TranslatedResourceList->Count, ==, 1);
+ TranslatedPartialList = &TranslatedResourceList->List[0].PartialResourceList;
+
+ ASSERT3U(TranslatedPartialList->Version, ==, 1);
+ ASSERT3U(TranslatedPartialList->Revision, ==, 1);
+
+ for (Index = 0; Index < TranslatedPartialList->Count; Index++) {
+ PCM_PARTIAL_RESOURCE_DESCRIPTOR RawPartialDescriptor;
+ PCM_PARTIAL_RESOURCE_DESCRIPTOR TranslatedPartialDescriptor;
+
+ RawPartialDescriptor = &RawPartialList->PartialDescriptors[Index];
+ TranslatedPartialDescriptor = &TranslatedPartialList->PartialDescriptors[Index];
+
+ Info("%s: [%d] %02x:%s\n",
+ __PdoGetName(Pdo),
+ Index,
+ TranslatedPartialDescriptor->Type,
+ PartialResourceDescriptorTypeName(TranslatedPartialDescriptor->Type));
+
+ switch (TranslatedPartialDescriptor->Type) {
+ case CmResourceTypeMemory:
+ Info("RAW: SharedDisposition=%02x Flags=%04x Start = %08x.%08x Length = %08x\n",
+ RawPartialDescriptor->ShareDisposition,
+ RawPartialDescriptor->Flags,
+ RawPartialDescriptor->u.Memory.Start.HighPart,
+ RawPartialDescriptor->u.Memory.Start.LowPart,
+ RawPartialDescriptor->u.Memory.Length);
+
+ Info("TRANSLATED: SharedDisposition=%02x Flags=%04x Start = %08x.%08x Length = %08x\n",
+ TranslatedPartialDescriptor->ShareDisposition,
+ TranslatedPartialDescriptor->Flags,
+ TranslatedPartialDescriptor->u.Memory.Start.HighPart,
+ TranslatedPartialDescriptor->u.Memory.Start.LowPart,
+ TranslatedPartialDescriptor->u.Memory.Length);
+ break;
+
+ case CmResourceTypeInterrupt:
+ Info("RAW: SharedDisposition=%02x Flags=%04x MessageCount=%04x Vector = %08x Affinity = %p\n",
+ RawPartialDescriptor->ShareDisposition,
+ RawPartialDescriptor->Flags,
+ RawPartialDescriptor->u.MessageInterrupt.Raw.MessageCount,
+ RawPartialDescriptor->u.MessageInterrupt.Raw.Vector,
+ (PVOID)RawPartialDescriptor->u.MessageInterrupt.Raw.Affinity);
+
+ Info("TRANSLATED: SharedDisposition=%02x Flags=%04x Level = %08x Vector = %08x Affinity = %p\n",
+ TranslatedPartialDescriptor->ShareDisposition,
+ TranslatedPartialDescriptor->Flags,
+ TranslatedPartialDescriptor->u.MessageInterrupt.Translated.Level,
+ TranslatedPartialDescriptor->u.MessageInterrupt.Translated.Vector,
+ (PVOID)TranslatedPartialDescriptor->u.MessageInterrupt.Translated.Affinity);
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ Trace("<====\n");
+}
+
+static DECLSPEC_NOINLINE NTSTATUS
+PdoStartDevice(
+ IN PXENVIF_PDO Pdo,
+ IN PIRP Irp
+ )
+{
+ PIO_STACK_LOCATION StackLocation;
+ HANDLE Key;
+ PANSI_STRING Ansi;
+ NTSTATUS status;
+
+ StackLocation = IoGetCurrentIrpStackLocation(Irp);
+
+ PdoParseResources(Pdo,
+ StackLocation->Parameters.StartDevice.AllocatedResources,
+ StackLocation->Parameters.StartDevice.AllocatedResourcesTranslated);
+
+ status = RegistryOpenSoftwareKey(__PdoGetDeviceObject(Pdo),
+ KEY_READ,
+ &Key);
+ if (!NT_SUCCESS(status))
+ goto fail1;
+
+ status = RegistryQueryDwordValue(Key, "NetLuidIndex", &Pdo->LuidIndex);
+ if (!NT_SUCCESS(status))
+ goto fail2;
+
+ status = RegistryQuerySzValue(Key, "NetworkAddress", &Ansi);
+ if (NT_SUCCESS(status)) {
+ Pdo->Address = __PdoAllocate(Ansi[0].Length + sizeof (CHAR));
+
+ status = STATUS_NO_MEMORY;
+ if (Pdo->Address == NULL)
+ goto fail3;
+
+ RtlCopyMemory(Pdo->Address, Ansi[0].Buffer, Ansi[0].Length);
+
+ RegistryFreeSzValue(Ansi);
+ }
+
+ __PdoSetSystemPowerState(Pdo, PowerSystemHibernate);
+ PdoS4ToS3(Pdo);
+ __PdoSetSystemPowerState(Pdo, PowerSystemWorking);
+
+ status = PdoD3ToD0(Pdo);
+ if (!NT_SUCCESS(status))
+ goto fail4;
+
+ __PdoSetDevicePnpState(Pdo, Started);
+
+ RegistryCloseKey(Key);
+
+ Irp->IoStatus.Status = STATUS_SUCCESS;
+ IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+ return STATUS_SUCCESS;
+
+fail4:
+ Error("fail4\n");
+
+ __PdoSetSystemPowerState(Pdo, PowerSystemSleeping3);
+ PdoS3ToS4(Pdo);
+ __PdoSetSystemPowerState(Pdo, PowerSystemShutdown);
+
+fail3:
+ Error("fail3\n");
+
+ Pdo->LuidIndex = 0;
+
+fail2:
+ Error("fail2\n");
+
+ RegistryCloseKey(Key);
+
+fail1:
+ Error("fail1 (%08x)\n", status);
+
+
+ Irp->IoStatus.Status = status;
+ IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+ return status;
+}
+
+static DECLSPEC_NOINLINE NTSTATUS
+PdoQueryStopDevice(
+ IN PXENVIF_PDO Pdo,
+ IN PIRP Irp
+ )
+{
+ NTSTATUS status;
+
+ __PdoSetDevicePnpState(Pdo, StopPending);
+ status = STATUS_SUCCESS;
+
+ Irp->IoStatus.Status = status;
+ IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+ return status;
+}
+
+static DECLSPEC_NOINLINE NTSTATUS
+PdoCancelStopDevice(
+ IN PXENVIF_PDO Pdo,
+ IN PIRP Irp
+ )
+{
+ NTSTATUS status;
+
+ __PdoRestoreDevicePnpState(Pdo, StopPending);
+
+ status = STATUS_SUCCESS;
+
+ Irp->IoStatus.Status = status;
+ IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+ return status;
+}
+
+static DECLSPEC_NOINLINE NTSTATUS
+PdoStopDevice(
+ IN PXENVIF_PDO Pdo,
+ IN PIRP Irp
+ )
+{
+ NTSTATUS status;
+
+ PdoD0ToD3(Pdo);
+
+ __PdoSetSystemPowerState(Pdo, PowerSystemSleeping3);
+ PdoS3ToS4(Pdo);
+ __PdoSetSystemPowerState(Pdo, PowerSystemShutdown);
+
+ Pdo->LuidIndex = 0;
+
+ if (Pdo->Address != NULL) {
+ __PdoFree(Pdo->Address);
+ Pdo->Address = NULL;
+ }
+
+ __PdoSetDevicePnpState(Pdo, Stopped);
+ status = STATUS_SUCCESS;
+
+ Irp->IoStatus.Status = status;
+ IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+ return status;
+}
+
+static DECLSPEC_NOINLINE NTSTATUS
+PdoQueryRemoveDevice(
+ IN PXENVIF_PDO Pdo,
+ IN PIRP Irp
+ )
+{
+ NTSTATUS status;
+
+ __PdoSetDevicePnpState(Pdo, RemovePending);
+ status = STATUS_SUCCESS;
+
+ Irp->IoStatus.Status = status;
+ IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+ return status;
+}
+
+static DECLSPEC_NOINLINE NTSTATUS
+PdoCancelRemoveDevice(
+ IN PXENVIF_PDO Pdo,
+ IN PIRP Irp
+ )
+{
+ NTSTATUS status;
+
+ FrontendRemoveFailed(__PdoGetFrontend(Pdo));
+
+ __PdoRestoreDevicePnpState(Pdo, RemovePending);
+
+ status = STATUS_SUCCESS;
+
+ Irp->IoStatus.Status = status;
+ IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+ return status;
+}
+
+static DECLSPEC_NOINLINE NTSTATUS
+PdoSurpriseRemoval(
+ IN PXENVIF_PDO Pdo,
+ IN PIRP Irp
+ )
+{
+ NTSTATUS status;
+
+ Warning("%s\n", __PdoGetName(Pdo));
+
+ __PdoSetDevicePnpState(Pdo, SurpriseRemovePending);
+ status = STATUS_SUCCESS;
+
+ Irp->IoStatus.Status = status;
+ IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+ return status;
+}
+
+static DECLSPEC_NOINLINE NTSTATUS
+PdoRemoveDevice(
+ IN PXENVIF_PDO Pdo,
+ IN PIRP Irp
+ )
+{
+ PXENVIF_FDO Fdo = __PdoGetFdo(Pdo);
+ BOOLEAN NeedInvalidate;
+ NTSTATUS status;
+
+ if (__PdoGetDevicePowerState(Pdo) != PowerDeviceD0)
+ goto done;
+
+ PdoD0ToD3(Pdo);
+
+ __PdoSetSystemPowerState(Pdo, PowerSystemSleeping3);
+ PdoS3ToS4(Pdo);
+ __PdoSetSystemPowerState(Pdo, PowerSystemShutdown);
+
+done:
+ Pdo->LuidIndex = 0;
+
+ if (Pdo->Address != NULL) {
+ __PdoFree(Pdo->Address);
+ Pdo->Address = NULL;
+ }
+
+ NeedInvalidate = FALSE;
+
+ FdoAcquireMutex(Fdo);
+
+ if (__PdoIsMissing(Pdo) ||
+ __PdoGetDevicePnpState(Pdo) == SurpriseRemovePending)
+ __PdoSetDevicePnpState(Pdo, Deleted);
+ else
+ __PdoSetDevicePnpState(Pdo, Enumerated);
+
+ if (__PdoIsMissing(Pdo)) {
+ if (__PdoGetDevicePnpState(Pdo) == Deleted)
+ PdoDestroy(Pdo);
+ else
+ NeedInvalidate = TRUE;
+ }
+
+ FdoReleaseMutex(Fdo);
+
+ if (NeedInvalidate)
+ IoInvalidateDeviceRelations(FdoGetPhysicalDeviceObject(Fdo),
+ BusRelations);
+
+ status = STATUS_SUCCESS;
+
+ Irp->IoStatus.Status = status;
+ IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+ return status;
+}
+
+static DECLSPEC_NOINLINE NTSTATUS
+PdoQueryDeviceRelations(
+ IN PXENVIF_PDO Pdo,
+ IN PIRP Irp
+ )
+{
+ PIO_STACK_LOCATION StackLocation;
+ PDEVICE_RELATIONS Relations;
+ NTSTATUS status;
+
+ StackLocation = IoGetCurrentIrpStackLocation(Irp);
+
+ status = Irp->IoStatus.Status;
+
+ if (StackLocation->Parameters.QueryDeviceRelations.Type != TargetDeviceRelation)
+ goto done;
+
+ Relations = ExAllocatePoolWithTag(PagedPool, sizeof (DEVICE_RELATIONS), 'FIV');
+
+ status = STATUS_NO_MEMORY;
+ if (Relations == NULL)
+ goto done;
+
+ RtlZeroMemory(Relations, sizeof (DEVICE_RELATIONS));
+
+ Relations->Count = 1;
+ ObReferenceObject(__PdoGetDeviceObject(Pdo));
+ Relations->Objects[0] = __PdoGetDeviceObject(Pdo);
+
+ Irp->IoStatus.Information = (ULONG_PTR)Relations;
+ status = STATUS_SUCCESS;
+
+done:
+ Irp->IoStatus.Status = status;
+ IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+ return status;
+}
+
+static FORCEINLINE NTSTATUS
+__PdoDelegateIrp(
+ IN PXENVIF_PDO Pdo,
+ IN PIRP Irp
+ )
+{
+ return FdoDelegateIrp(Pdo->Fdo, Irp);
+}
+
+static NTSTATUS
+PdoQueryVifInterface(
+ IN PXENVIF_PDO Pdo,
+ IN PIRP Irp
+ )
+{
+ PIO_STACK_LOCATION StackLocation;
+ USHORT Size;
+ USHORT Version;
+ PINTERFACE Interface;
+ NTSTATUS status;
+
+ status = Irp->IoStatus.Status;
+
+ StackLocation = IoGetCurrentIrpStackLocation(Irp);
+ Size = StackLocation->Parameters.QueryInterface.Size;
+ Version = StackLocation->Parameters.QueryInterface.Version;
+ Interface = StackLocation->Parameters.QueryInterface.Interface;
+
+ if (StackLocation->Parameters.QueryInterface.Version != VIF_INTERFACE_VERSION)
+ goto done;
+
+ status = STATUS_BUFFER_TOO_SMALL;
+ if (StackLocation->Parameters.QueryInterface.Size < sizeof (INTERFACE))
+ goto done;
+
+ Interface->Size = sizeof (INTERFACE);
+ Interface->Version = VIF_INTERFACE_VERSION;
+ Interface->Context = __PdoGetVifInterface(Pdo);
+ Interface->InterfaceReference = NULL;
+ Interface->InterfaceDereference = NULL;
+
+ Irp->IoStatus.Information = 0;
+ status = STATUS_SUCCESS;
+
+done:
+ return status;
+}
+
+struct _INTERFACE_ENTRY {
+ const GUID *Guid;
+ const CHAR *Name;
+ NTSTATUS (*Handler)(PXENVIF_PDO, PIRP);
+};
+
+#define DEFINE_HANDLER(_Guid, _Function) \
+ { &GUID_ ## _Guid, #_Guid, (_Function) }
+
+struct _INTERFACE_ENTRY PdoInterfaceTable[] = {
+ DEFINE_HANDLER(VIF_INTERFACE, PdoQueryVifInterface),
+ { NULL, NULL, NULL }
+};
+
+static DECLSPEC_NOINLINE NTSTATUS
+PdoQueryInterface(
+ IN PXENVIF_PDO Pdo,
+ IN PIRP Irp
+ )
+{
+ PIO_STACK_LOCATION StackLocation;
+ const GUID *InterfaceType;
+ struct _INTERFACE_ENTRY *Entry;
+ NTSTATUS status;
+
+ status = Irp->IoStatus.Status;
+
+ if (status != STATUS_NOT_SUPPORTED)
+ goto done;
+
+ StackLocation = IoGetCurrentIrpStackLocation(Irp);
+ InterfaceType = StackLocation->Parameters.QueryInterface.InterfaceType;
+
+ for (Entry = PdoInterfaceTable; Entry->Guid != NULL; Entry++) {
+ if (IsEqualGUID(InterfaceType, Entry->Guid)) {
+ Trace("%s: %s\n",
+ __PdoGetName(Pdo),
+ Entry->Name);
+ status = Entry->Handler(Pdo, Irp);
+ goto done;
+ }
+ }
+
+ Trace("%s: (%08x-%04x-%04x-%02x%02x%02x%02x%02x%02x%02x%02x)\n",
+ __PdoGetName(Pdo),
+ InterfaceType->Data1,
+ InterfaceType->Data2,
+ InterfaceType->Data3,
+ InterfaceType->Data4[0],
+ InterfaceType->Data4[1],
+ InterfaceType->Data4[2],
+ InterfaceType->Data4[3],
+ InterfaceType->Data4[4],
+ InterfaceType->Data4[5],
+ InterfaceType->Data4[6],
+ InterfaceType->Data4[7]);
+
+ status = __PdoDelegateIrp(Pdo, Irp);
+
+done:
+ Irp->IoStatus.Status = status;
+ IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+ return status;
+}
+
+static DECLSPEC_NOINLINE NTSTATUS
+PdoQueryCapabilities(
+ IN PXENVIF_PDO Pdo,
+ IN PIRP Irp
+ )
+{
+ PIO_STACK_LOCATION StackLocation;
+ PDEVICE_CAPABILITIES Capabilities;
+ SYSTEM_POWER_STATE SystemPowerState;
+ NTSTATUS status;
+
+ UNREFERENCED_PARAMETER(Pdo);
+
+ StackLocation = IoGetCurrentIrpStackLocation(Irp);
+ Capabilities = StackLocation->Parameters.DeviceCapabilities.Capabilities;
+
+ status = STATUS_INVALID_PARAMETER;
+ if (Capabilities->Version != 1)
+ goto done;
+
+ Capabilities->DeviceD1 = 0;
+ Capabilities->DeviceD2 = 0;
+ Capabilities->LockSupported = 0;
+ Capabilities->EjectSupported = 1;
+ Capabilities->Removable = 1;
+ Capabilities->DockDevice = 0;
+ Capabilities->UniqueID = 1;
+ Capabilities->SilentInstall = 1;
+ Capabilities->RawDeviceOK = 0;
+ Capabilities->SurpriseRemovalOK = 1;
+ Capabilities->HardwareDisabled = 0;
+ Capabilities->NoDisplayInUI = 0;
+
+ Capabilities->Address = 0xffffffff;
+ Capabilities->UINumber = 0xffffffff;
+
+ for (SystemPowerState = 0; SystemPowerState < PowerSystemMaximum; SystemPowerState++) {
+ switch (SystemPowerState) {
+ case PowerSystemUnspecified:
+ case PowerSystemSleeping1:
+ case PowerSystemSleeping2:
+ break;
+
+ case PowerSystemWorking:
+ Capabilities->DeviceState[SystemPowerState] = PowerDeviceD0;
+ break;
+
+ default:
+ Capabilities->DeviceState[SystemPowerState] = PowerDeviceD3;
+ break;
+ }
+ }
+
+ Capabilities->SystemWake = PowerSystemUnspecified;
+ Capabilities->DeviceWake = PowerDeviceUnspecified;
+ Capabilities->D1Latency = 0;
+ Capabilities->D2Latency = 0;
+ Capabilities->D3Latency = 0;
+
+ status = STATUS_SUCCESS;
+
+done:
+ Irp->IoStatus.Status = status;
+ IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+ return status;
+}
+
+static DECLSPEC_NOINLINE NTSTATUS
+PdoQueryResourceRequirements(
+ IN PXENVIF_PDO Pdo,
+ IN PIRP Irp
+ )
+{
+ IO_RESOURCE_DESCRIPTOR Memory;
+ IO_RESOURCE_DESCRIPTOR Interrupt;
+ ULONG Count;
+ ULONG Size;
+ PIO_RESOURCE_REQUIREMENTS_LIST Requirements;
+ PIO_RESOURCE_LIST List;
+ NTSTATUS status;
+
+ UNREFERENCED_PARAMETER(Pdo);
+
+ RtlZeroMemory(&Memory, sizeof (IO_RESOURCE_DESCRIPTOR));
+ Memory.Type = CmResourceTypeMemory;
+ Memory.ShareDisposition = CmResourceShareDeviceExclusive;
+ Memory.Flags = CM_RESOURCE_MEMORY_READ_WRITE |
+ CM_RESOURCE_MEMORY_PREFETCHABLE |
+ CM_RESOURCE_MEMORY_CACHEABLE;
+
+ Memory.u.Memory.Length = PAGE_SIZE;
+ Memory.u.Memory.Alignment = PAGE_SIZE;
+ Memory.u.Memory.MinimumAddress.QuadPart = 0;
+ Memory.u.Memory.MaximumAddress.QuadPart = -1;
+
+ RtlZeroMemory(&Interrupt, sizeof (IO_RESOURCE_DESCRIPTOR));
+ Interrupt.Type = CmResourceTypeInterrupt;
+ Interrupt.ShareDisposition = CmResourceShareDeviceExclusive;
+ Interrupt.Flags = CM_RESOURCE_INTERRUPT_MESSAGE;
+
+ Count = 1; // Hard-code for now
+
+ Interrupt.u.Interrupt.MinimumVector = (ULONG)CM_RESOURCE_INTERRUPT_MESSAGE_TOKEN - Count + 1;
+ Interrupt.u.Interrupt.MaximumVector = (ULONG)CM_RESOURCE_INTERRUPT_MESSAGE_TOKEN;
+ Interrupt.u.Interrupt.AffinityPolicy = IrqPolicyOneCloseProcessor;
+ Interrupt.u.Interrupt.PriorityPolicy = IrqPriorityUndefined;
+
+ Size = sizeof (IO_RESOURCE_DESCRIPTOR) * 2;
+ Size += FIELD_OFFSET(IO_RESOURCE_LIST, Descriptors);
+ Size += FIELD_OFFSET(IO_RESOURCE_REQUIREMENTS_LIST, List);
+
+ Requirements = ExAllocatePoolWithTag(PagedPool, Size, 'FIV');
+
+ status = STATUS_NO_MEMORY;
+ if (Requirements == NULL)
+ goto fail1;
+
+ RtlZeroMemory(Requirements, Size);
+
+ Requirements->ListSize = Size;
+ Requirements->InterfaceType = PNPBus;
+ Requirements->BusNumber = 0;
+ Requirements->SlotNumber = 0;
+ Requirements->AlternativeLists = 1;
+
+ List = &Requirements->List[0];
+ List->Version = 1;
+ List->Revision = 1;
+ List->Count = 2;
+ List->Descriptors[0] = Memory;
+ List->Descriptors[1] = Interrupt;
+
+ Irp->IoStatus.Information = (ULONG_PTR)Requirements;
+
+ Irp->IoStatus.Status = STATUS_SUCCESS;
+ IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+ return STATUS_SUCCESS;
+
+fail1:
+ Error("fail1 (%08x)\n", status);
+
+ Irp->IoStatus.Status = status;
+ IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+ return status;
+}
+
+#define MAXTEXTLEN 128
+
+static DECLSPEC_NOINLINE NTSTATUS
+PdoQueryDeviceText(
+ IN PXENVIF_PDO Pdo,
+ IN PIRP Irp
+ )
+{
+ PIO_STACK_LOCATION StackLocation;
+ PWCHAR Buffer;
+ UNICODE_STRING Text;
+ NTSTATUS status;
+
+ StackLocation = IoGetCurrentIrpStackLocation(Irp);
+
+ switch (StackLocation->Parameters.QueryDeviceText.DeviceTextType) {
+ case DeviceTextDescription:
+ Trace("DeviceTextDescription\n");
+ break;
+
+ case DeviceTextLocationInformation:
+ Trace("DeviceTextLocationInformation\n");
+ break;
+
+ default:
+ Irp->IoStatus.Information = 0;
+ status = STATUS_NOT_SUPPORTED;
+ goto done;
+ }
+
+ Buffer = ExAllocatePoolWithTag(PagedPool, MAXTEXTLEN, 'FIV');
+
+ status = STATUS_NO_MEMORY;
+ if (Buffer == NULL)
+ goto done;
+
+ RtlZeroMemory(Buffer, MAXTEXTLEN);
+
+ Text.Buffer = Buffer;
+ Text.MaximumLength = MAXTEXTLEN;
+ Text.Length = 0;
+
+ switch (StackLocation->Parameters.QueryDeviceText.DeviceTextType) {
+ case DeviceTextDescription: {
+ status = RtlStringCbPrintfW(Buffer,
+ MAXTEXTLEN,
+ L"%hs %hs",
+ FdoGetName(Pdo->Fdo),
+ __PdoGetName(Pdo));
+ ASSERT(NT_SUCCESS(status));
+
+ Buffer += wcslen(Buffer);
+
+ break;
+ }
+ case DeviceTextLocationInformation:
+ status = RtlStringCbPrintfW(Buffer,
+ MAXTEXTLEN,
+ L"%hs",
+ __PdoGetName(Pdo));
+ ASSERT(NT_SUCCESS(status));
+
+ Buffer += wcslen(Buffer);
+
+ break;
+
+ default:
+ ASSERT(FALSE);
+ break;
+ }
+
+ Text.Length = (USHORT)((ULONG_PTR)Buffer - (ULONG_PTR)Text.Buffer);
+
+ ASSERT3U(KeGetCurrentIrql(), ==, PASSIVE_LEVEL);
+
+ Trace("%s: %wZ\n", __PdoGetName(Pdo), &Text);
+
+ Irp->IoStatus.Information = (ULONG_PTR)Text.Buffer;
+ status = STATUS_SUCCESS;
+
+done:
+ Irp->IoStatus.Status = status;
+ IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+ return status;
+}
+
+static DECLSPEC_NOINLINE NTSTATUS
+PdoReadConfig(
+ IN PXENVIF_PDO Pdo,
+ IN PIRP Irp
+ )
+{
+ UNREFERENCED_PARAMETER(Pdo);
+
+ Irp->IoStatus.Status = STATUS_NOT_SUPPORTED;
+ IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+ return STATUS_NOT_SUPPORTED;
+}
+
+static DECLSPEC_NOINLINE NTSTATUS
+PdoWriteConfig(
+ IN PXENVIF_PDO Pdo,
+ IN PIRP Irp
+ )
+{
+ UNREFERENCED_PARAMETER(Pdo);
+
+ Irp->IoStatus.Status = STATUS_NOT_SUPPORTED;
+ IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+ return STATUS_NOT_SUPPORTED;
+}
+
+static DECLSPEC_NOINLINE NTSTATUS
+PdoQueryId(
+ IN PXENVIF_PDO Pdo,
+ IN PIRP Irp
+ )
+{
+ PIO_STACK_LOCATION StackLocation;
+ PWCHAR Buffer;
+ UNICODE_STRING Id;
+ ULONG Type;
+ NTSTATUS status;
+
+ StackLocation = IoGetCurrentIrpStackLocation(Irp);
+
+ switch (StackLocation->Parameters.QueryId.IdType) {
+ case BusQueryInstanceID:
+ Trace("BusQueryInstanceID\n");
+ break;
+
+ case BusQueryDeviceID:
+ Trace("BusQueryDeviceID\n");
+ break;
+
+ case BusQueryHardwareIDs:
+ Trace("BusQueryHardwareIDs\n");
+ break;
+
+ case BusQueryCompatibleIDs:
+ Trace("BusQueryCompatibleIDs\n");
+ break;
+
+ case BusQueryContainerID:
+ Trace("BusQueryContainerID\n");
+ break;
+
+ default:
+ Irp->IoStatus.Information = 0;
+ status = STATUS_NOT_SUPPORTED;
+ goto done;
+ }
+
+ Buffer = ExAllocatePoolWithTag(PagedPool, MAX_DEVICE_ID_LEN, 'FIV');
+
+ status = STATUS_NO_MEMORY;
+ if (Buffer == NULL)
+ goto done;
+
+ RtlZeroMemory(Buffer, MAX_DEVICE_ID_LEN);
+
+ Id.Buffer = Buffer;
+ Id.MaximumLength = MAX_DEVICE_ID_LEN - 1;
+ Id.Length = 0;
+
+ switch (StackLocation->Parameters.QueryId.IdType) {
+ case BusQueryInstanceID:
+ case BusQueryContainerID:
+ Type = REG_SZ;
+
+ status = RtlStringCbPrintfW(Buffer,
+ MAX_DEVICE_ID_LEN - 1,
+ L"%hs",
+ __PdoGetName(Pdo));
+ ASSERT(NT_SUCCESS(status));
+
+ Buffer += wcslen(Buffer);
+
+ break;
+
+ case BusQueryDeviceID:
+ Type = REG_SZ;
+
+ status = RtlStringCbPrintfW(Buffer,
+ MAX_DEVICE_ID_LEN - 1,
+ L"XENVIF\\DEVICE&REV_%02X",
+ PDO_REVISION);
+ ASSERT(NT_SUCCESS(status));
+
+ Buffer += wcslen(Buffer);
+
+ break;
+
+ case BusQueryHardwareIDs:
+ case BusQueryCompatibleIDs: {
+ ULONG Length;
+
+ Type = REG_MULTI_SZ;
+
+ Length = MAX_DEVICE_ID_LEN;
+ status = RtlStringCbPrintfW(Buffer,
+ Length,
+ L"XENVIF\\DEVICE&REV_%02X",
+ PDO_REVISION);
+ ASSERT(NT_SUCCESS(status));
+
+ Buffer += wcslen(Buffer);
+ Buffer++;
+
+ Length = MAX_DEVICE_ID_LEN - (ULONG)((ULONG_PTR)Buffer - (ULONG_PTR)Id.Buffer);
+ status = RtlStringCbPrintfW(Buffer,
+ Length,
+ L"XENDEVICE");
+ ASSERT(NT_SUCCESS(status));
+
+ Buffer += wcslen(Buffer);
+ Buffer++;
+
+ break;
+ }
+ default:
+ Type = REG_NONE;
+
+ ASSERT(FALSE);
+ break;
+ }
+
+ ASSERT3U(KeGetCurrentIrql(), ==, PASSIVE_LEVEL);
+
+ Id.Length = (USHORT)((ULONG_PTR)Buffer - (ULONG_PTR)Id.Buffer);
+ Buffer = Id.Buffer;
+
+ switch (Type) {
+ case REG_SZ:
+ Trace("- %ws\n", Buffer);
+ break;
+
+ case REG_MULTI_SZ:
+ do {
+ Trace("- %ws\n", Buffer);
+ Buffer += wcslen(Buffer);
+ Buffer++;
+ } while (*Buffer != L'\0');
+ break;
+
+ default:
+ ASSERT(FALSE);
+ break;
+ }
+
+ Irp->IoStatus.Information = (ULONG_PTR)Id.Buffer;
+ status = STATUS_SUCCESS;
+
+done:
+ Irp->IoStatus.Status = status;
+ IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+ return status;
+}
+
+static DECLSPEC_NOINLINE NTSTATUS
+PdoQueryBusInformation(
+ IN PXENVIF_PDO Pdo,
+ IN PIRP Irp
+ )
+{
+ PPNP_BUS_INFORMATION Info;
+ NTSTATUS status;
+
+ UNREFERENCED_PARAMETER(Pdo);
+
+ Info = ExAllocatePoolWithTag(PagedPool, sizeof (PNP_BUS_INFORMATION), 'FIV');
+
+ status = STATUS_NO_MEMORY;
+ if (Info == NULL)
+ goto done;
+
+ RtlZeroMemory(Info, sizeof (PNP_BUS_INFORMATION));
+
+ Info->BusTypeGuid = GUID_BUS_TYPE_INTERNAL;
+ Info->LegacyBusType = PNPBus;
+ Info->BusNumber = 0;
+
+ Irp->IoStatus.Information = (ULONG_PTR)Info;
+ status = STATUS_SUCCESS;
+
+done:
+ Irp->IoStatus.Status = status;
+ IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+ return status;
+}
+
+static DECLSPEC_NOINLINE NTSTATUS
+PdoDeviceUsageNotification(
+ IN PXENVIF_PDO Pdo,
+ IN PIRP Irp
+ )
+{
+ NTSTATUS status;
+
+ status = __PdoDelegateIrp(Pdo, Irp);
+
+ Irp->IoStatus.Status = status;
+ IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+ return status;
+}
+
+static DECLSPEC_NOINLINE NTSTATUS
+PdoEject(
+ IN PXENVIF_PDO Pdo,
+ IN PIRP Irp
+ )
+{
+ PXENVIF_FDO Fdo = __PdoGetFdo(Pdo);
+ NTSTATUS status;
+
+ Trace("%s\n", __PdoGetName(Pdo));
+
+ FdoAcquireMutex(Fdo);
+
+ __PdoSetDevicePnpState(Pdo, Deleted);
+ __PdoSetMissing(Pdo, "device ejected");
+
+ PdoDestroy(Pdo);
+
+ FdoReleaseMutex(Fdo);
+
+ status = STATUS_SUCCESS;
+
+ Irp->IoStatus.Status = status;
+ IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+ return status;
+}
+
+static DECLSPEC_NOINLINE NTSTATUS
+PdoDispatchPnp(
+ IN PXENVIF_PDO Pdo,
+ IN PIRP Irp
+ )
+{
+ PIO_STACK_LOCATION StackLocation;
+ UCHAR MinorFunction;
+ NTSTATUS status;
+
+ StackLocation = IoGetCurrentIrpStackLocation(Irp);
+ MinorFunction = StackLocation->MinorFunction;
+
+ Trace("====> (%s) (%02x:%s)\n",
+ __PdoGetName(Pdo),
+ MinorFunction,
+ PnpMinorFunctionName(MinorFunction));
+
+ switch (StackLocation->MinorFunction) {
+ case IRP_MN_START_DEVICE:
+ status = PdoStartDevice(Pdo, Irp);
+ break;
+
+ case IRP_MN_QUERY_STOP_DEVICE:
+ status = PdoQueryStopDevice(Pdo, Irp);
+ break;
+
+ case IRP_MN_CANCEL_STOP_DEVICE:
+ status = PdoCancelStopDevice(Pdo, Irp);
+ break;
+
+ case IRP_MN_STOP_DEVICE:
+ status = PdoStopDevice(Pdo, Irp);
+ break;
+
+ case IRP_MN_QUERY_REMOVE_DEVICE:
+ status = PdoQueryRemoveDevice(Pdo, Irp);
+ break;
+
+ case IRP_MN_CANCEL_REMOVE_DEVICE:
+ status = PdoCancelRemoveDevice(Pdo, Irp);
+ break;
+
+ case IRP_MN_SURPRISE_REMOVAL:
+ status = PdoSurpriseRemoval(Pdo, Irp);
+ break;
+
+ case IRP_MN_REMOVE_DEVICE:
+ status = PdoRemoveDevice(Pdo, Irp);
+ break;
+
+ case IRP_MN_QUERY_DEVICE_RELATIONS:
+ status = PdoQueryDeviceRelations(Pdo, Irp);
+ break;
+
+ case IRP_MN_QUERY_INTERFACE:
+ status = PdoQueryInterface(Pdo, Irp);
+ break;
+
+ case IRP_MN_QUERY_CAPABILITIES:
+ status = PdoQueryCapabilities(Pdo, Irp);
+ break;
+
+ case IRP_MN_QUERY_RESOURCE_REQUIREMENTS:
+ status = PdoQueryResourceRequirements(Pdo, Irp);
+ break;
+
+ case IRP_MN_QUERY_DEVICE_TEXT:
+ status = PdoQueryDeviceText(Pdo, Irp);
+ break;
+
+ case IRP_MN_READ_CONFIG:
+ status = PdoReadConfig(Pdo, Irp);
+ break;
+
+ case IRP_MN_WRITE_CONFIG:
+ status = PdoWriteConfig(Pdo, Irp);
+ break;
+
+ case IRP_MN_QUERY_ID:
+ status = PdoQueryId(Pdo, Irp);
+ break;
+
+ case IRP_MN_QUERY_BUS_INFORMATION:
+ status = PdoQueryBusInformation(Pdo, Irp);
+ break;
+
+ case IRP_MN_DEVICE_USAGE_NOTIFICATION:
+ status = PdoDeviceUsageNotification(Pdo, Irp);
+ break;
+
+ case IRP_MN_EJECT:
+ status = PdoEject(Pdo, Irp);
+ break;
+
+ default:
+ status = Irp->IoStatus.Status;
+ IoCompleteRequest(Irp, IO_NO_INCREMENT);
+ break;
+ }
+
+ Trace("<==== (%02x:%s)(%08x)\n",
+ MinorFunction,
+ PnpMinorFunctionName(MinorFunction),
+ status);
+
+ return status;
+}
+
+static FORCEINLINE NTSTATUS
+__PdoSetDevicePower(
+ IN PXENVIF_PDO Pdo,
+ IN PIRP Irp
+ )
+{
+ PIO_STACK_LOCATION StackLocation;
+ DEVICE_POWER_STATE DeviceState;
+ POWER_ACTION PowerAction;
+ NTSTATUS status;
+
+ StackLocation = IoGetCurrentIrpStackLocation(Irp);
+ DeviceState = StackLocation->Parameters.Power.State.DeviceState;
+ PowerAction = StackLocation->Parameters.Power.ShutdownType;
+
+ Trace("====> (%s) (%s:%s)\n",
+ __PdoGetName(Pdo),
+ PowerDeviceStateName(DeviceState),
+ PowerActionName(PowerAction));
+
+ ASSERT3U(PowerAction, <, PowerActionShutdown);
+
+ if (__PdoGetDevicePowerState(Pdo) > DeviceState) {
+ Trace("%s: POWERING UP: %s -> %s\n",
+ __PdoGetName(Pdo),
+ PowerDeviceStateName(__PdoGetDevicePowerState(Pdo)),
+ PowerDeviceStateName(DeviceState));
+
+ ASSERT3U(DeviceState, ==, PowerDeviceD0);
+ status = PdoD3ToD0(Pdo);
+ ASSERT(NT_SUCCESS(status));
+ } else if (__PdoGetDevicePowerState(Pdo) < DeviceState) {
+ Trace("%s: POWERING DOWN: %s -> %s\n",
+ __PdoGetName(Pdo),
+ PowerDeviceStateName(__PdoGetDevicePowerState(Pdo)),
+ PowerDeviceStateName(DeviceState));
+
+ ASSERT3U(DeviceState, ==, PowerDeviceD3);
+ PdoD0ToD3(Pdo);
+ }
+
+ Irp->IoStatus.Status = STATUS_SUCCESS;
+ IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+ Trace("<==== (%s:%s)\n",
+ PowerDeviceStateName(DeviceState),
+ PowerActionName(PowerAction));
+
+ return STATUS_SUCCESS;
+}
+
+static NTSTATUS
+PdoDevicePower(
+ IN PXENVIF_THREAD Self,
+ IN PVOID Context
+ )
+{
+ PXENVIF_PDO Pdo = Context;
+ PKEVENT Event;
+
+ Event = ThreadGetEvent(Self);
+
+ for (;;) {
+ PIRP Irp;
+
+ if (Pdo->DevicePowerIrp == NULL) {
+ (VOID) KeWaitForSingleObject(Event,
+ Executive,
+ KernelMode,
+ FALSE,
+ NULL);
+ KeClearEvent(Event);
+ }
+
+ if (ThreadIsAlerted(Self))
+ break;
+
+ Irp = Pdo->DevicePowerIrp;
+
+ if (Irp == NULL)
+ continue;
+
+ Pdo->DevicePowerIrp = NULL;
+ KeMemoryBarrier();
+
+ (VOID) __PdoSetDevicePower(Pdo, Irp);
+ }
+
+ return STATUS_SUCCESS;
+}
+
+static FORCEINLINE NTSTATUS
+__PdoSetSystemPower(
+ IN PXENVIF_PDO Pdo,
+ IN PIRP Irp
+ )
+{
+ PIO_STACK_LOCATION StackLocation;
+ SYSTEM_POWER_STATE SystemState;
+ POWER_ACTION PowerAction;
+
+ StackLocation = IoGetCurrentIrpStackLocation(Irp);
+ SystemState = StackLocation->Parameters.Power.State.SystemState;
+ PowerAction = StackLocation->Parameters.Power.ShutdownType;
+
+ Trace("====> (%s) (%s:%s)\n",
+ __PdoGetName(Pdo),
+ PowerSystemStateName(SystemState),
+ PowerActionName(PowerAction));
+
+ ASSERT3U(PowerAction, <, PowerActionShutdown);
+
+ if (__PdoGetSystemPowerState(Pdo) > SystemState) {
+ if (SystemState < PowerSystemHibernate &&
+ __PdoGetSystemPowerState(Pdo) >= PowerSystemHibernate) {
+ __PdoSetSystemPowerState(Pdo, PowerSystemHibernate);
+ PdoS4ToS3(Pdo);
+ }
+
+ Trace("%s: POWERING UP: %s -> %s\n",
+ __PdoGetName(Pdo),
+ PowerSystemStateName(__PdoGetSystemPowerState(Pdo)),
+ PowerSystemStateName(SystemState));
+ } else if (__PdoGetSystemPowerState(Pdo) < SystemState) {
+ Trace("%s: POWERING DOWN: %s -> %s\n",
+ __PdoGetName(Pdo),
+ PowerSystemStateName(__PdoGetSystemPowerState(Pdo)),
+ PowerSystemStateName(SystemState));
+
+ if (SystemState >= PowerSystemHibernate &&
+ __PdoGetSystemPowerState(Pdo) < PowerSystemHibernate) {
+ __PdoSetSystemPowerState(Pdo, PowerSystemSleeping3);
+ PdoS3ToS4(Pdo);
+ }
+ }
+
+ __PdoSetSystemPowerState(Pdo, SystemState);
+
+ Irp->IoStatus.Status = STATUS_SUCCESS;
+ IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+ Trace("<==== (%s:%s)\n",
+ PowerSystemStateName(SystemState),
+ PowerActionName(PowerAction));
+
+ return STATUS_SUCCESS;
+}
+
+static NTSTATUS
+PdoSystemPower(
+ IN PXENVIF_THREAD Self,
+ IN PVOID Context
+ )
+{
+ PXENVIF_PDO Pdo = Context;
+ PKEVENT Event;
+
+ Event = ThreadGetEvent(Self);
+
+ for (;;) {
+ PIRP Irp;
+
+ if (Pdo->SystemPowerIrp == NULL) {
+ (VOID) KeWaitForSingleObject(Event,
+ Executive,
+ KernelMode,
+ FALSE,
+ NULL);
+ KeClearEvent(Event);
+ }
+
+ if (ThreadIsAlerted(Self))
+ break;
+
+ Irp = Pdo->SystemPowerIrp;
+
+ if (Irp == NULL)
+ continue;
+
+ Pdo->SystemPowerIrp = NULL;
+ KeMemoryBarrier();
+
+ (VOID) __PdoSetSystemPower(Pdo, Irp);
+ }
+
+ return STATUS_SUCCESS;
+}
+
+static DECLSPEC_NOINLINE NTSTATUS
+PdoSetPower(
+ IN PXENVIF_PDO Pdo,
+ IN PIRP Irp
+ )
+{
+ PIO_STACK_LOCATION StackLocation;
+ POWER_STATE_TYPE PowerType;
+ POWER_ACTION PowerAction;
+ NTSTATUS status;
+
+ StackLocation = IoGetCurrentIrpStackLocation(Irp);
+ PowerType = StackLocation->Parameters.Power.Type;
+ PowerAction = StackLocation->Parameters.Power.ShutdownType;
+
+ if (PowerAction >= PowerActionShutdown) {
+ Irp->IoStatus.Status = STATUS_SUCCESS;
+
+ status = Irp->IoStatus.Status;
+ IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+ goto done;
+ }
+
+ switch (PowerType) {
+ case DevicePowerState:
+ IoMarkIrpPending(Irp);
+
+ ASSERT3P(Pdo->DevicePowerIrp, ==, NULL);
+ Pdo->DevicePowerIrp = Irp;
+ KeMemoryBarrier();
+
+ ThreadWake(Pdo->DevicePowerThread);
+
+ status = STATUS_PENDING;
+ break;
+
+ case SystemPowerState:
+ IoMarkIrpPending(Irp);
+
+ ASSERT3P(Pdo->SystemPowerIrp, ==, NULL);
+ Pdo->SystemPowerIrp = Irp;
+ KeMemoryBarrier();
+
+ ThreadWake(Pdo->SystemPowerThread);
+
+ status = STATUS_PENDING;
+ break;
+
+ default:
+ status = Irp->IoStatus.Status;
+ IoCompleteRequest(Irp, IO_NO_INCREMENT);
+ break;
+ }
+
+done:
+ return status;
+}
+
+static DECLSPEC_NOINLINE NTSTATUS
+PdoQueryPower(
+ IN PXENVIF_PDO Pdo,
+ IN PIRP Irp
+ )
+{
+ NTSTATUS status;
+
+ UNREFERENCED_PARAMETER(Pdo);
+
+ Irp->IoStatus.Status = STATUS_SUCCESS;
+
+ status = Irp->IoStatus.Status;
+ IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+ return status;
+}
+
+static DECLSPEC_NOINLINE NTSTATUS
+PdoDispatchPower(
+ IN PXENVIF_PDO Pdo,
+ IN PIRP Irp
+ )
+{
+ PIO_STACK_LOCATION StackLocation;
+ UCHAR MinorFunction;
+ NTSTATUS status;
+
+ StackLocation = IoGetCurrentIrpStackLocation(Irp);
+ MinorFunction = StackLocation->MinorFunction;
+
+ switch (StackLocation->MinorFunction) {
+ case IRP_MN_SET_POWER:
+ status = PdoSetPower(Pdo, Irp);
+ break;
+
+ case IRP_MN_QUERY_POWER:
+ status = PdoQueryPower(Pdo, Irp);
+ break;
+
+ default:
+ status = Irp->IoStatus.Status;
+ IoCompleteRequest(Irp, IO_NO_INCREMENT);
+ break;
+ }
+
+ return status;
+}
+
+static DECLSPEC_NOINLINE NTSTATUS
+PdoDispatchDefault(
+ IN PXENVIF_PDO Pdo,
+ IN PIRP Irp
+ )
+{
+ NTSTATUS status;
+
+ UNREFERENCED_PARAMETER(Pdo);
+
+ status = Irp->IoStatus.Status;
+ IoCompleteRequest(Irp, IO_NO_INCREMENT);
+
+ return status;
+}
+
+NTSTATUS
+PdoDispatch(
+ IN PXENVIF_PDO Pdo,
+ IN PIRP Irp
+ )
+{
+ PIO_STACK_LOCATION StackLocation;
+ NTSTATUS status;
+
+ StackLocation = IoGetCurrentIrpStackLocation(Irp);
+
+ switch (StackLocation->MajorFunction) {
+ case IRP_MJ_PNP:
+ status = PdoDispatchPnp(Pdo, Irp);
+ break;
+
+ case IRP_MJ_POWER:
+ status = PdoDispatchPower(Pdo, Irp);
+ break;
+
+ default:
+ status = PdoDispatchDefault(Pdo, Irp);
+ break;
+ }
+
+ return status;
+}
+
+VOID
+PdoResume(
+ IN PXENVIF_PDO Pdo
+ )
+{
+ FrontendResume(__PdoGetFrontend(Pdo));
+}
+
+VOID
+PdoSuspend(
+ IN PXENVIF_PDO Pdo
+ )
+{
+ FrontendSuspend(__PdoGetFrontend(Pdo));
+}
+
+NTSTATUS
+PdoCreate(
+ IN PXENVIF_FDO Fdo,
+ IN PANSI_STRING Name
+ )
+{
+ PDEVICE_OBJECT PhysicalDeviceObject;
+ PXENVIF_DX Dx;
+ PXENVIF_PDO Pdo;
+ NTSTATUS status;
+
+#pragma prefast(suppress:28197) // Possibly leaking memory 'PhysicalDeviceObject'
+ status = IoCreateDevice(DriverObject,
+ sizeof(XENVIF_DX),
+ NULL,
+ FILE_DEVICE_UNKNOWN,
+ FILE_DEVICE_SECURE_OPEN | FILE_AUTOGENERATED_DEVICE_NAME,
+ FALSE,
+ &PhysicalDeviceObject);
+ if (!NT_SUCCESS(status))
+ goto fail1;
+
+ Dx = (PXENVIF_DX)PhysicalDeviceObject->DeviceExtension;
+ RtlZeroMemory(Dx, sizeof (XENVIF_DX));
+
+ Dx->Type = PHYSICAL_DEVICE_OBJECT;
+ Dx->DeviceObject = PhysicalDeviceObject;
+ Dx->DevicePnpState = Present;
+ Dx->SystemPowerState = PowerSystemShutdown;
+ Dx->DevicePowerState = PowerDeviceD3;
+
+ Pdo = __PdoAllocate(sizeof (XENVIF_PDO));
+
+ status = STATUS_NO_MEMORY;
+ if (Pdo == NULL)
+ goto fail2;
+
+ Pdo->Dx = Dx;
+
+ status = ThreadCreate(PdoSystemPower, Pdo, &Pdo->SystemPowerThread);
+ if (!NT_SUCCESS(status))
+ goto fail3;
+
+ status = ThreadCreate(PdoDevicePower, Pdo, &Pdo->DevicePowerThread);
+ if (!NT_SUCCESS(status))
+ goto fail4;
+
+ __PdoSetName(Pdo, Name);
+
+ status = FrontendInitialize(Pdo, &Pdo->Frontend);
+ if (!NT_SUCCESS(status))
+ goto fail5;
+
+ status = VifInitialize(Pdo, &Pdo->VifInterface);
+ if (!NT_SUCCESS(status))
+ goto fail6;
+
+ Info("%p (XENVIF\\DEVICE&REV_%02X#%s)\n",
+ PhysicalDeviceObject,
+ PDO_REVISION,
+ __PdoGetName(Pdo));
+
+ Dx->Pdo = Pdo;
+ PhysicalDeviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
+
+ __PdoLink(Pdo, Fdo);
+
+ return STATUS_SUCCESS;
+
+fail6:
+ Error("fail6\n");
+
+ FrontendTeardown(Pdo->Frontend);
+ Pdo->Frontend = NULL;
+
+fail5:
+ Error("fail5\n");
+
+ ThreadAlert(Pdo->DevicePowerThread);
+ ThreadJoin(Pdo->DevicePowerThread);
+ Pdo->DevicePowerThread = NULL;
+
+fail4:
+ Error("fail4\n");
+
+ ThreadAlert(Pdo->SystemPowerThread);
+ ThreadJoin(Pdo->SystemPowerThread);
+ Pdo->SystemPowerThread = NULL;
+
+fail3:
+ Error("fail3\n");
+
+ Pdo->Dx = NULL;
+
+ ASSERT(IsZeroMemory(Pdo, sizeof (XENVIF_PDO)));
+ __PdoFree(Pdo);
+
+fail2:
+ Error("fail2\n");
+
+ IoDeleteDevice(PhysicalDeviceObject);
+
+fail1:
+ Error("fail1 (%08x)\n", status);
+
+ return status;
+}
+
+VOID
+PdoDestroy(
+ IN PXENVIF_PDO Pdo
+ )
+{
+ PXENVIF_DX Dx = Pdo->Dx;
+ PDEVICE_OBJECT PhysicalDeviceObject = Dx->DeviceObject;
+
+ ASSERT3U(__PdoGetDevicePnpState(Pdo), ==, Deleted);
+
+ ASSERT(__PdoIsMissing(Pdo));
+ Pdo->Missing = FALSE;
+
+ __PdoUnlink(Pdo);
+
+ Info("%p (XENVIF\\DEVICE&REV_%02X#%s) (%s)\n",
+ PhysicalDeviceObject,
+ PDO_REVISION,
+ __PdoGetName(Pdo),
+ Pdo->Reason);
+ Pdo->Reason = NULL;
+
+ Dx->Pdo = NULL;
+
+ VifTeardown(__PdoGetVifInterface(Pdo));
+
+ FrontendTeardown(__PdoGetFrontend(Pdo));
+ Pdo->Frontend = NULL;
+
+ ThreadAlert(Pdo->DevicePowerThread);
+ ThreadJoin(Pdo->DevicePowerThread);
+ Pdo->DevicePowerThread = NULL;
+
+ ThreadAlert(Pdo->SystemPowerThread);
+ ThreadJoin(Pdo->SystemPowerThread);
+ Pdo->SystemPowerThread = NULL;
+
+ Pdo->Dx = NULL;
+
+ ASSERT(IsZeroMemory(Pdo, sizeof (XENVIF_PDO)));
+ __PdoFree(Pdo);
+
+ IoDeleteDevice(PhysicalDeviceObject);
+}
+
+
--- /dev/null
+/* Copyright (c) Citrix Systems Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms,
+ * with or without modification, are permitted provided
+ * that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer in the documentation and/or other
+ * materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef _XENVIF_PDO_H
+#define _XENVIF_PDO_H
+
+#include <ntddk.h>
+
+#include "driver.h"
+#include "types.h"
+
+extern VOID
+PdoSetDevicePnpState(
+ IN PXENVIF_PDO Pdo,
+ IN DEVICE_PNP_STATE State
+ );
+
+extern DEVICE_PNP_STATE
+PdoGetDevicePnpState(
+ IN PXENVIF_PDO Pdo
+ );
+
+extern VOID
+PdoSetMissing(
+ IN PXENVIF_PDO Pdo,
+ IN const CHAR *Reason
+ );
+
+extern BOOLEAN
+PdoIsMissing(
+ IN PXENVIF_PDO Pdo
+ );
+
+extern BOOLEAN
+PdoIsEjected(
+ IN PXENVIF_PDO Pdo
+ );
+
+extern PCHAR
+PdoGetName(
+ IN PXENVIF_PDO Pdo
+ );
+
+extern PDEVICE_OBJECT
+PdoGetDeviceObject(
+ IN PXENVIF_PDO Pdo
+ );
+
+extern ULONG
+PdoGetLuidIndex(
+ IN PXENVIF_PDO Pdo
+ );
+
+extern PCHAR
+PdoGetAddress(
+ IN PXENVIF_PDO Pdo
+ );
+
+extern NTSTATUS
+PdoCreate(
+ IN PXENVIF_FDO Fdo,
+ IN PANSI_STRING Name
+ );
+
+extern VOID
+PdoResume(
+ IN PXENVIF_PDO Pdo
+ );
+
+extern VOID
+PdoSuspend(
+ IN PXENVIF_PDO Pdo
+ );
+
+extern VOID
+PdoDestroy(
+ IN PXENVIF_PDO Pdo
+ );
+
+#include "frontend.h"
+
+extern PXENVIF_FRONTEND
+PdoGetFrontend(
+ IN PXENVIF_PDO Pdo
+ );
+
+extern PXENBUS_EVTCHN_INTERFACE
+PdoGetEvtchnInterface(
+ IN PXENVIF_PDO Pdo
+ );
+
+extern PXENBUS_DEBUG_INTERFACE
+PdoGetDebugInterface(
+ IN PXENVIF_PDO Pdo
+ );
+
+extern PXENBUS_STORE_INTERFACE
+PdoGetStoreInterface(
+ IN PXENVIF_PDO Pdo
+ );
+
+extern PXENBUS_GNTTAB_INTERFACE
+PdoGetGnttabInterface(
+ IN PXENVIF_PDO Pdo
+ );
+
+extern PXENBUS_SUSPEND_INTERFACE
+PdoGetSuspendInterface(
+ IN PXENVIF_PDO Pdo
+ );
+
+#include "vif.h"
+
+extern PXENVIF_VIF_INTERFACE
+PdoGetVifInterface(
+ IN PXENVIF_PDO Pdo
+ );
+
+extern VOID
+PdoRequestEject(
+ IN PXENVIF_PDO Pdo
+ );
+
+extern NTSTATUS
+PdoDispatch(
+ IN PXENVIF_PDO Pdo,
+ IN PIRP Irp
+ );
+
+#endif // _XENVIF_PDO_H
--- /dev/null
+/* Copyright (c) Citrix Systems Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms,
+ * with or without modification, are permitted provided
+ * that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer in the documentation and/or other
+ * materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <ntddk.h>
+#include <ntstrsafe.h>
+#include <util.h>
+
+#include "pool.h"
+#include "thread.h"
+#include "log.h"
+#include "assert.h"
+
+#define POOL_POOL 'OBJE'
+
+typedef struct _OBJECT_HEADER {
+ ULONG Magic;
+
+#define OBJECT_HEADER_MAGIC 0x02121996
+
+ LIST_ENTRY ListEntry;
+} OBJECT_HEADER, *POBJECT_HEADER;
+
+#define MAXIMUM_SLOTS 6
+
+typedef struct _POOL_MAGAZINE {
+ PVOID Slot[MAXIMUM_SLOTS];
+} POOL_MAGAZINE, *PPOOL_MAGAZINE;
+
+struct _XENVIF_POOL {
+ const CHAR *Name;
+ ULONG Size;
+ NTSTATUS (*Ctor)(PVOID, PVOID);
+ VOID (*Dtor)(PVOID, PVOID);
+ VOID (*AcquireLock)(PVOID);
+ VOID (*ReleaseLock)(PVOID);
+ PVOID Argument;
+ PXENVIF_THREAD Thread;
+ LIST_ENTRY GetList;
+ PLIST_ENTRY PutList;
+ POOL_MAGAZINE Magazine[MAXIMUM_PROCESSORS];
+ LONG Allocated;
+ LONG MaximumAllocated;
+ LONG Count;
+ LONG MinimumCount;
+};
+
+static FORCEINLINE PVOID
+__PoolAllocate(
+ IN ULONG Length
+ )
+{
+ return __AllocateNonPagedPoolWithTag(Length, POOL_POOL);
+}
+
+static FORCEINLINE VOID
+__PoolFree(
+ IN PVOID Buffer
+ )
+{
+ __FreePoolWithTag(Buffer, POOL_POOL);
+}
+
+static FORCEINLINE VOID
+__PoolSwizzle(
+ IN PXENVIF_POOL Pool
+ )
+{
+ PLIST_ENTRY ListEntry;
+
+ ListEntry = InterlockedExchangePointer(&Pool->PutList, NULL);
+
+ while (ListEntry != NULL) {
+ PLIST_ENTRY Next;
+
+ Next = ListEntry->Flink;
+ ListEntry->Flink = NULL;
+ ASSERT3P(ListEntry->Blink, ==, NULL);
+
+ InsertTailList(&Pool->GetList, ListEntry);
+
+ ListEntry = Next;
+ }
+}
+
+static FORCEINLINE PVOID
+__PoolGetShared(
+ IN PXENVIF_POOL Pool,
+ IN BOOLEAN Locked
+ )
+{
+ LONG Count;
+ POBJECT_HEADER Header;
+ PVOID Object;
+ LONG Allocated;
+ NTSTATUS status;
+
+ Count = InterlockedDecrement(&Pool->Count);
+
+ if (Count >= 0) {
+ PLIST_ENTRY ListEntry;
+
+ if (!Locked)
+ Pool->AcquireLock(Pool->Argument);
+
+ if (Count < Pool->MinimumCount)
+ Pool->MinimumCount = Count;
+
+ if (IsListEmpty(&Pool->GetList))
+ __PoolSwizzle(Pool);
+
+ ListEntry = RemoveHeadList(&Pool->GetList);
+ ASSERT(ListEntry != &Pool->GetList);
+
+ if (!Locked)
+ Pool->ReleaseLock(Pool->Argument);
+
+ RtlZeroMemory(ListEntry, sizeof (LIST_ENTRY));
+
+ Header = CONTAINING_RECORD(ListEntry, OBJECT_HEADER, ListEntry);
+ ASSERT3U(Header->Magic, ==, OBJECT_HEADER_MAGIC);
+
+ Object = Header + 1;
+ goto done;
+ }
+
+ (VOID) InterlockedIncrement(&Pool->Count);
+
+ Header = __PoolAllocate(sizeof (OBJECT_HEADER) + Pool->Size);
+
+ status = STATUS_NO_MEMORY;
+ if (Header == NULL)
+ goto fail1;
+
+ Header->Magic = OBJECT_HEADER_MAGIC;
+
+ Object = Header + 1;
+
+ status = Pool->Ctor(Pool->Argument, Object);
+ if (!NT_SUCCESS(status))
+ goto fail2;
+
+ Allocated = InterlockedIncrement(&Pool->Allocated);
+
+ if (Allocated > Pool->MaximumAllocated) {
+ if (!Locked)
+ Pool->AcquireLock(Pool->Argument);
+
+ if (Allocated > Pool->MaximumAllocated)
+ Pool->MaximumAllocated = Allocated;
+
+ if (!Locked)
+ Pool->ReleaseLock(Pool->Argument);
+ }
+
+done:
+ return Object;
+
+fail2:
+ Error("fail2\n");
+
+ Header->Magic = 0;
+
+ ASSERT(IsZeroMemory(Header, sizeof (OBJECT_HEADER)));
+ __PoolFree(Header);
+
+fail1:
+ Error("fail1 (%08x)\n", status);
+
+ return NULL;
+}
+
+static FORCEINLINE VOID
+__PoolPutShared(
+ IN PXENVIF_POOL Pool,
+ IN PVOID Object,
+ IN BOOLEAN Locked
+ )
+{
+ POBJECT_HEADER Header;
+ PLIST_ENTRY Old;
+ PLIST_ENTRY New;
+
+ ASSERT(Object != NULL);
+
+ Header = Object;
+ --Header;
+ ASSERT3U(Header->Magic, ==, OBJECT_HEADER_MAGIC);
+
+ ASSERT(IsZeroMemory(&Header->ListEntry, sizeof (LIST_ENTRY)));
+
+ if (!Locked) {
+ New = &Header->ListEntry;
+
+ do {
+ Old = Pool->PutList;
+ New->Flink = Old;
+ } while (InterlockedCompareExchangePointer(&Pool->PutList, New, Old) != Old);
+ } else {
+ InsertTailList(&Pool->GetList, &Header->ListEntry);
+ }
+
+ KeMemoryBarrier();
+
+ (VOID) InterlockedIncrement(&Pool->Count);
+}
+
+static FORCEINLINE PVOID
+__PoolGetMagazine(
+ IN PXENVIF_POOL Pool,
+ IN ULONG Cpu
+ )
+{
+ PPOOL_MAGAZINE Magazine;
+ ULONG Index;
+
+ Magazine = &Pool->Magazine[Cpu];
+
+ for (Index = 0; Index < MAXIMUM_SLOTS; Index++) {
+ PVOID Object;
+
+ if (Magazine->Slot[Index] != NULL) {
+ Object = Magazine->Slot[Index];
+ Magazine->Slot[Index] = NULL;
+
+ return Object;
+ }
+ }
+
+ return NULL;
+}
+
+static FORCEINLINE BOOLEAN
+__PoolPutMagazine(
+ IN PXENVIF_POOL Pool,
+ IN ULONG Cpu,
+ IN PVOID Object
+ )
+{
+ PPOOL_MAGAZINE Magazine;
+ ULONG Index;
+
+ Magazine = &Pool->Magazine[Cpu];
+
+ for (Index = 0; Index < MAXIMUM_SLOTS; Index++) {
+ if (Magazine->Slot[Index] == NULL) {
+ Magazine->Slot[Index] = Object;
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+PVOID
+PoolGet(
+ IN PXENVIF_POOL Pool,
+ IN BOOLEAN Locked
+ )
+{
+ KIRQL Irql;
+ ULONG Cpu;
+ PVOID Object;
+
+ KeRaiseIrql(DISPATCH_LEVEL, &Irql);
+ Cpu = KeGetCurrentProcessorNumber();
+
+ Object = __PoolGetMagazine(Pool, Cpu);
+ if (Object == NULL)
+ Object = __PoolGetShared(Pool, Locked);
+
+ KeLowerIrql(Irql);
+
+ return Object;
+}
+
+VOID
+PoolPut(
+ IN PXENVIF_POOL Pool,
+ IN PVOID Object,
+ IN BOOLEAN Locked
+ )
+{
+ KIRQL Irql;
+ ULONG Cpu;
+
+ KeRaiseIrql(DISPATCH_LEVEL, &Irql);
+ Cpu = KeGetCurrentProcessorNumber();
+
+ if (!__PoolPutMagazine(Pool, Cpu, Object))
+ __PoolPutShared(Pool, Object, Locked);
+
+ KeLowerIrql(Irql);
+}
+
+VOID
+PoolGetStatistics(
+ IN PXENVIF_POOL Pool,
+ OUT PULONG Allocated,
+ OUT PULONG MaximumAllocated,
+ OUT PULONG Count,
+ OUT PULONG MinimumCount
+ )
+{
+ *Allocated = Pool->Allocated;
+ *MaximumAllocated = Pool->MaximumAllocated;
+
+ *Count = Pool->Count;
+ *MinimumCount = Pool->MinimumCount;
+}
+
+static FORCEINLINE
+__PoolFlushMagazines(
+ IN PXENVIF_POOL Pool
+ )
+{
+ ULONG Cpu;
+
+ for (Cpu = 0; Cpu < MAXIMUM_PROCESSORS; Cpu++) {
+ PVOID Object;
+
+ while ((Object = __PoolGetMagazine(Pool, Cpu)) != NULL)
+ __PoolPutShared(Pool, Object, TRUE);
+ }
+}
+
+static FORCEINLINE VOID
+__PoolTrimShared(
+ IN PXENVIF_POOL Pool,
+ IN OUT PLIST_ENTRY List
+ )
+{
+ LONG Count;
+ LONG Excess;
+
+ Count = Pool->Count;
+
+ KeMemoryBarrier();
+
+ Excess = Pool->MinimumCount;
+
+ while (Excess != 0) {
+ PLIST_ENTRY ListEntry;
+
+ Count = InterlockedDecrement(&Pool->Count);
+ if (Count < 0) {
+ Count = InterlockedIncrement(&Pool->Count);
+ break;
+ }
+
+ if (IsListEmpty(&Pool->GetList))
+ __PoolSwizzle(Pool);
+
+ ListEntry = RemoveHeadList(&Pool->GetList);
+ ASSERT(ListEntry != &Pool->GetList);
+
+ InsertTailList(List, ListEntry);
+
+ InterlockedDecrement(&Pool->Allocated);
+ --Excess;
+ }
+
+ Pool->MinimumCount = Count;
+}
+
+static FORCEINLINE VOID
+__PoolEmpty(
+ IN PXENVIF_POOL Pool,
+ IN OUT PLIST_ENTRY List
+ )
+{
+ while (!IsListEmpty(List)) {
+ PLIST_ENTRY ListEntry;
+ POBJECT_HEADER Header;
+ PVOID Object;
+
+ ListEntry = RemoveHeadList(List);
+ RtlZeroMemory(ListEntry, sizeof (LIST_ENTRY));
+
+ Header = CONTAINING_RECORD(ListEntry, OBJECT_HEADER, ListEntry);
+ ASSERT3U(Header->Magic, ==, OBJECT_HEADER_MAGIC);
+
+ Object = Header + 1;
+
+ Pool->Dtor(Pool->Argument, Object);
+
+ Header->Magic = 0;
+
+ ASSERT(IsZeroMemory(Header, sizeof (OBJECT_HEADER)));
+ __PoolFree(Header);
+ }
+}
+
+#define TIME_US(_us) ((_us) * 10)
+#define TIME_MS(_ms) (TIME_US((_ms) * 1000))
+#define TIME_RELATIVE(_t) (-(_t))
+
+#define POOL_PERIOD 1000
+
+static NTSTATUS
+PoolMonitor(
+ IN PXENVIF_THREAD Self,
+ IN PVOID Context
+ )
+{
+ PXENVIF_POOL Pool = Context;
+ LARGE_INTEGER Timeout;
+
+ Trace("====> (%s)\n", Pool->Name);
+
+ Timeout.QuadPart = TIME_RELATIVE(TIME_MS(POOL_PERIOD));
+
+ for (;;) {
+ PKEVENT Event;
+ KIRQL Irql;
+ LIST_ENTRY List;
+
+ Event = ThreadGetEvent(Self);
+
+ (VOID) KeWaitForSingleObject(Event,
+ Executive,
+ KernelMode,
+ FALSE,
+ &Timeout);
+ KeClearEvent(Event);
+
+ if (ThreadIsAlerted(Self))
+ break;
+
+ InitializeListHead(&List);
+
+ KeRaiseIrql(DISPATCH_LEVEL, &Irql);
+ Pool->AcquireLock(Pool->Argument);
+ __PoolTrimShared(Pool, &List);
+ Pool->ReleaseLock(Pool->Argument);
+ KeLowerIrql(Irql);
+
+ __PoolEmpty(Pool, &List);
+ ASSERT(IsListEmpty(&List));
+ }
+
+ Trace("<==== (%s)\n", Pool->Name);
+
+ return STATUS_SUCCESS;
+}
+
+NTSTATUS
+PoolInitialize(
+ IN const CHAR *Name,
+ IN ULONG Size,
+ IN NTSTATUS (*Ctor)(PVOID, PVOID),
+ IN VOID (*Dtor)(PVOID, PVOID),
+ IN VOID (*AcquireLock)(PVOID),
+ IN VOID (*ReleaseLock)(PVOID),
+ IN PVOID Argument,
+ OUT PXENVIF_POOL *Pool
+ )
+{
+ NTSTATUS status;
+
+ *Pool = __PoolAllocate(sizeof (XENVIF_POOL));
+
+ status = STATUS_NO_MEMORY;
+ if (*Pool == NULL)
+ goto fail1;
+
+ (*Pool)->Name = Name;
+ (*Pool)->Size = Size;
+ (*Pool)->Ctor = Ctor;
+ (*Pool)->Dtor = Dtor;
+ (*Pool)->AcquireLock = AcquireLock;
+ (*Pool)->ReleaseLock = ReleaseLock;
+ (*Pool)->Argument = Argument;
+
+ InitializeListHead(&(*Pool)->GetList);
+
+ status = ThreadCreate(PoolMonitor, *Pool, &(*Pool)->Thread);
+ if (!NT_SUCCESS(status))
+ goto fail2;
+
+ return STATUS_SUCCESS;
+
+fail2:
+ Error("fail2\n");
+
+ RtlZeroMemory(&(*Pool)->GetList, sizeof (LIST_ENTRY));
+
+ (*Pool)->Argument = NULL;
+ (*Pool)->ReleaseLock = NULL;
+ (*Pool)->AcquireLock = NULL;
+ (*Pool)->Dtor = NULL;
+ (*Pool)->Ctor = NULL;
+ (*Pool)->Size = 0;
+ (*Pool)->Name = NULL;
+
+ ASSERT(IsZeroMemory(*Pool, sizeof (XENVIF_POOL)));
+ __PoolFree(*Pool);
+ *Pool = NULL;
+
+fail1:
+ Error("fail1 (%08x)\n", status);
+
+ return status;
+}
+
+VOID
+PoolTeardown(
+ IN PXENVIF_POOL Pool
+ )
+{
+ LIST_ENTRY List;
+
+ ThreadAlert(Pool->Thread);
+ ThreadJoin(Pool->Thread);
+ Pool->Thread = NULL;
+
+ InitializeListHead(&List);
+
+ __PoolFlushMagazines(Pool);
+
+ Pool->MinimumCount = Pool->Count;
+ __PoolTrimShared(Pool, &List);
+ __PoolEmpty(Pool, &List);
+
+ ASSERT3U(Pool->Count, ==, 0);
+ ASSERT3U(Pool->Allocated, ==, 0);
+ Pool->MaximumAllocated = 0;
+
+ RtlZeroMemory(&Pool->GetList, sizeof (LIST_ENTRY));
+
+ Pool->Argument = NULL;
+ Pool->ReleaseLock = NULL;
+ Pool->AcquireLock = NULL;
+ Pool->Dtor = NULL;
+ Pool->Ctor = NULL;
+ Pool->Size = 0;
+ Pool->Name = NULL;
+
+ ASSERT(IsZeroMemory(Pool, sizeof (XENVIF_POOL)));
+ __PoolFree(Pool);
+}
--- /dev/null
+/* Copyright (c) Citrix Systems Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms,
+ * with or without modification, are permitted provided
+ * that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer in the documentation and/or other
+ * materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef _XENVIF_POOL_H
+#define _XENVIF_POOL_H
+
+#include <ntddk.h>
+
+typedef struct _XENVIF_POOL XENVIF_POOL, *PXENVIF_POOL;
+
+extern NTSTATUS
+PoolInitialize(
+ IN const CHAR *Name,
+ IN ULONG Size,
+ IN NTSTATUS (*Ctor)(PVOID, PVOID),
+ IN VOID (*Dtor)(PVOID, PVOID),
+ IN VOID (*AcquireLock)(PVOID),
+ IN VOID (*ReleaseLock)(PVOID),
+ IN PVOID Argument,
+ OUT PXENVIF_POOL *Pool
+ );
+
+extern VOID
+PoolTeardown(
+ IN PXENVIF_POOL Pool
+ );
+
+extern PVOID
+PoolGet(
+ IN PXENVIF_POOL Pool,
+ IN BOOLEAN Locked
+ );
+
+extern VOID
+PoolPut(
+ IN PXENVIF_POOL Pool,
+ IN PVOID Object,
+ IN BOOLEAN Locked
+ );
+
+extern VOID
+PoolGetStatistics(
+ IN PXENVIF_POOL Pool,
+ OUT PULONG Allocated,
+ OUT PULONG MaximumAllocated,
+ OUT PULONG Count,
+ OUT PULONG MinimumCount
+ );
+
+#endif // _XENVIF_POOL_H
--- /dev/null
+/* Copyright (c) Citrix Systems Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms,
+ * with or without modification, are permitted provided
+ * that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer in the documentation and/or other
+ * materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <ntddk.h>
+#include <ntstrsafe.h>
+#include <stdlib.h>
+#include <netioapi.h>
+#include <util.h>
+#include <xen.h>
+#include <debug_interface.h>
+#include <store_interface.h>
+#include <gnttab_interface.h>
+
+// This should be in public/io/netif.h
+#define _NETRXF_gso_prefix (4)
+#define NETRXF_gso_prefix (1U<<_NETRXF_gso_prefix)
+
+#include "ethernet.h"
+#include "tcpip.h"
+#include "pdo.h"
+#include "frontend.h"
+#include "pool.h"
+#include "checksum.h"
+#include "parse.h"
+#include "mac.h"
+#include "vif.h"
+#include "receiver.h"
+#include "driver.h"
+#include "log.h"
+#include "assert.h"
+
+#define RECEIVER_POOL 'ECER'
+
+typedef struct _RECEIVER_TAG {
+ LIST_ENTRY ListEntry;
+ ULONG Next;
+ PVOID Context;
+ ULONG Reference;
+} RECEIVER_TAG, *PRECEIVER_TAG;
+
+typedef struct _RECEIVER_OFFLOAD_STATISTICS {
+ ULONGLONG IpVersion4LargePacketSegment;
+ ULONGLONG IpVersion6LargePacketSegment;
+ ULONGLONG IpVersion4HeaderChecksumCalculated;
+ ULONGLONG IpVersion4HeaderChecksumSucceeded;
+ ULONGLONG IpVersion4HeaderChecksumFailed;
+ ULONGLONG IpVersion4HeaderChecksumPresent;
+ ULONGLONG IpVersion4TcpChecksumCalculated;
+ ULONGLONG IpVersion4TcpChecksumSucceeded;
+ ULONGLONG IpVersion4TcpChecksumFailed;
+ ULONGLONG IpVersion4TcpChecksumPresent;
+ ULONGLONG IpVersion6TcpChecksumCalculated;
+ ULONGLONG IpVersion6TcpChecksumSucceeded;
+ ULONGLONG IpVersion6TcpChecksumFailed;
+ ULONGLONG IpVersion6TcpChecksumPresent;
+ ULONGLONG IpVersion4UdpChecksumCalculated;
+ ULONGLONG IpVersion4UdpChecksumSucceeded;
+ ULONGLONG IpVersion4UdpChecksumFailed;
+ ULONGLONG IpVersion4UdpChecksumPresent;
+ ULONGLONG IpVersion6UdpChecksumCalculated;
+ ULONGLONG IpVersion6UdpChecksumSucceeded;
+ ULONGLONG IpVersion6UdpChecksumFailed;
+ ULONGLONG IpVersion6UdpChecksumPresent;
+ ULONGLONG TagRemoved;
+} RECEIVER_OFFLOAD_STATISTICS, *PRECEIVER_OFFLOAD_STATISTICS;
+
+#define PROTOCOL0_RING_SIZE (__CONST_RING_SIZE(netif_rx, PAGE_SIZE))
+#define MAXIMUM_TAG_COUNT (PROTOCOL0_RING_SIZE * 2)
+
+#define TAG_INDEX_INVALID 0xFFFFFFFF
+
+typedef struct _RECEIVER_RING_PROTOCOL0 {
+ netif_rx_front_ring_t Front;
+ netif_rx_sring_t *Shared;
+ ULONG Reference;
+ ULONG HeadFreeTag;
+ RECEIVER_TAG Tag[MAXIMUM_TAG_COUNT];
+ netif_rx_request_t Pending[MAXIMUM_TAG_COUNT];
+ ULONG RequestsPosted;
+ ULONG RequestsPushed;
+ ULONG ResponsesProcessed;
+} RECEIVER_RING_PROTOCOL0, *PRECEIVER_RING_PROTOCOL0;
+
+#define PROTOCOL1_RING_SIZE (__CONST_RING_SIZE(netif_tx, PAGE_SIZE))
+#define MAXIMUM_OPERATION_COUNT ((MAX_SKB_FRAGS + 1) * 2)
+
+typedef struct _RECEIVER_RING_PROTOCOL1 {
+ netif_tx_back_ring_t Back;
+ netif_tx_sring_t *Shared;
+ ULONG Reference;
+ LIST_ENTRY List;
+ XENBUS_GNTTAB_COPY_OPERATION Operation[MAXIMUM_OPERATION_COUNT];
+ ULONG RequestsProcessed;
+ ULONG ResponsesPosted;
+ ULONG ResponsesPushed;
+} RECEIVER_RING_PROTOCOL1, *PRECEIVER_RING_PROTOCOL1;
+
+typedef NTSTATUS (*RECEIVER_RING_CONNECT)(PRECEIVER_RING);
+typedef NTSTATUS (*RECEIVER_RING_STORE_WRITE)(PRECEIVER_RING, PXENBUS_STORE_TRANSACTION);
+typedef NTSTATUS (*RECEIVER_RING_ENABLE)(PRECEIVER_RING);
+typedef VOID (*RECEIVER_RING_POLL)(PRECEIVER_RING, PLIST_ENTRY, PULONG);
+typedef VOID (*RECEIVER_RING_DISABLE)(PRECEIVER_RING);
+typedef VOID (*RECEIVER_RING_DISCONNECT)(PRECEIVER_RING);
+typedef VOID (*RECEIVER_RING_DEBUG_CALLBACK)(PRECEIVER_RING);
+typedef ULONG (*RECEIVER_RING_GET_SIZE)(PRECEIVER_RING);
+
+typedef struct _RECEIVER_RING_OPERATIONS {
+ RECEIVER_RING_CONNECT Connect;
+ RECEIVER_RING_STORE_WRITE StoreWrite;
+ RECEIVER_RING_ENABLE Enable;
+ RECEIVER_RING_POLL Poll;
+ RECEIVER_RING_DISABLE Disable;
+ RECEIVER_RING_DISCONNECT Disconnect;
+ RECEIVER_RING_DEBUG_CALLBACK DebugCallback;
+ RECEIVER_RING_GET_SIZE GetSize;
+} RECEIVER_RING_OPERATIONS, *PRECEIVER_RING_OPERATIONS;
+
+#pragma warning(push)
+#pragma warning(disable:4201) // nonstandard extension used : nameless struct/union
+
+typedef struct _RECEIVER_RING {
+ PXENVIF_RECEIVER Receiver;
+ LIST_ENTRY ListEntry;
+ KSPIN_LOCK Lock;
+ PXENVIF_POOL PacketPool;
+ PMDL Mdl;
+ union {
+ RECEIVER_RING_PROTOCOL0 Protocol0;
+ RECEIVER_RING_PROTOCOL1 Protocol1;
+ };
+ BOOLEAN Enabled;
+ BOOLEAN Stopped;
+ PRECEIVER_RING_OPERATIONS Operations;
+ XENVIF_OFFLOAD_OPTIONS OffloadOptions;
+ XENVIF_RECEIVER_PACKET_STATISTICS PacketStatistics;
+ XENVIF_HEADER_STATISTICS HeaderStatistics;
+ RECEIVER_OFFLOAD_STATISTICS OffloadStatistics;
+ LIST_ENTRY PacketList;
+} RECEIVER_RING, *PRECEIVER_RING;
+
+#pragma warning(pop)
+
+struct _XENVIF_RECEIVER {
+ PXENVIF_FRONTEND Frontend;
+ ULONG Protocol;
+ LIST_ENTRY List;
+ LONG Loaned;
+ LONG Returned;
+ KEVENT Event;
+
+ PXENBUS_DEBUG_INTERFACE DebugInterface;
+ PXENBUS_STORE_INTERFACE StoreInterface;
+ PXENBUS_GNTTAB_INTERFACE GnttabInterface;
+ PXENVIF_VIF_INTERFACE VifInterface;
+
+ PXENBUS_DEBUG_CALLBACK DebugCallback;
+};
+
+#define REQ_ID_INTEGRITY_CHECK 0xF000
+
+static FORCEINLINE PVOID
+__ReceiverAllocate(
+ IN ULONG Length
+ )
+{
+ return __AllocateNonPagedPoolWithTag(Length, RECEIVER_POOL);
+}
+
+static FORCEINLINE VOID
+__ReceiverFree(
+ IN PVOID Buffer
+ )
+{
+ __FreePoolWithTag(Buffer, RECEIVER_POOL);
+}
+
+static NTSTATUS
+ReceiverPacketCtor(
+ IN PVOID Argument,
+ IN PVOID Object
+ )
+{
+ PXENVIF_RECEIVER_PACKET Packet = Object;
+ PMDL Mdl;
+ PUCHAR StartVa;
+ NTSTATUS status;
+
+ UNREFERENCED_PARAMETER(Argument);
+
+ ASSERT(IsZeroMemory(Packet, sizeof (XENVIF_RECEIVER_PACKET)));
+
+ Mdl = __AllocatePage();
+
+ status = STATUS_NO_MEMORY;
+ if (Mdl == NULL)
+ goto fail1;
+
+ StartVa = MmGetSystemAddressForMdlSafe(Mdl, NormalPagePriority);
+ RtlFillMemory(StartVa, PAGE_SIZE, 0xAA);
+
+ ASSERT3U(Mdl->ByteOffset, ==, 0);
+ Mdl->StartVa = StartVa;
+ Mdl->ByteCount = 0;
+
+ Packet->Mdl = *Mdl;
+ Packet->__Pfn = MmGetMdlPfnArray(Mdl)[0];
+
+ ExFreePool(Mdl);
+
+ return STATUS_SUCCESS;
+
+fail1:
+ Error("fail1 (%08x)\n", status);
+
+ ASSERT(IsZeroMemory(Packet, sizeof (XENVIF_RECEIVER_PACKET)));
+
+ return status;
+}
+
+static VOID
+ReceiverPacketDtor(
+ IN PVOID Argument,
+ IN PVOID Object
+ )
+{
+ PXENVIF_RECEIVER_PACKET Packet = Object;
+ PMDL Mdl;
+
+ UNREFERENCED_PARAMETER(Argument);
+
+ Mdl = &Packet->Mdl;
+
+ Mdl->ByteCount = PAGE_SIZE;
+
+ __FreePage(Mdl);
+
+ RtlZeroMemory(Mdl, sizeof (MDL) + sizeof (PFN_NUMBER));
+
+ ASSERT(IsZeroMemory(Packet, sizeof (XENVIF_RECEIVER_PACKET)));
+}
+
+static FORCEINLINE PXENVIF_RECEIVER_PACKET
+__RingGetPacket(
+ IN PRECEIVER_RING Ring,
+ IN BOOLEAN Locked
+ )
+{
+ PXENVIF_RECEIVER_PACKET Packet;
+
+ Packet = PoolGet(Ring->PacketPool, Locked);
+
+ ASSERT(Packet == NULL || IsZeroMemory(Packet, FIELD_OFFSET(XENVIF_RECEIVER_PACKET, Mdl)));
+
+ return Packet;
+}
+
+static FORCEINLINE VOID
+__RingPutPacket(
+ IN PRECEIVER_RING Ring,
+ IN PXENVIF_RECEIVER_PACKET Packet,
+ IN BOOLEAN Locked
+ )
+{
+ PMDL Mdl = &Packet->Mdl;
+
+ RtlZeroMemory(Packet, FIELD_OFFSET(XENVIF_RECEIVER_PACKET, Mdl));
+
+ Mdl->MappedSystemVa = Mdl->StartVa;
+ Mdl->ByteOffset = 0;
+ Mdl->ByteCount = 0;
+ ASSERT3P(Mdl->Next, ==, NULL);
+
+ PoolPut(Ring->PacketPool, Packet, Locked);
+}
+
+static FORCEINLINE PMDL
+__RingGetMdl(
+ IN PRECEIVER_RING Ring,
+ IN BOOLEAN Locked
+ )
+{
+ PXENVIF_RECEIVER_PACKET Packet;
+
+ Packet = __RingGetPacket(Ring, Locked);
+ if (Packet == NULL)
+ return NULL;
+
+ return &Packet->Mdl;
+}
+
+static FORCEINLINE VOID
+__RingPutMdl(
+ IN PRECEIVER_RING Ring,
+ IN PMDL Mdl,
+ IN BOOLEAN Locked
+ )
+{
+ PXENVIF_RECEIVER_PACKET Packet;
+
+ Packet = CONTAINING_RECORD(Mdl, XENVIF_RECEIVER_PACKET, Mdl);
+ __RingPutPacket(Ring, Packet, Locked);
+}
+
+static DECLSPEC_NOINLINE VOID
+PacketProcessTag(
+ IN PXENVIF_RECEIVER_PACKET Packet,
+ IN PXENVIF_OFFLOAD_OPTIONS OffloadOptions,
+ IN PRECEIVER_OFFLOAD_STATISTICS OffloadStatistics
+ )
+{
+ PXENVIF_PACKET_INFO Info;
+ ULONG PayloadLength;
+ PUCHAR StartVa;
+ PETHERNET_HEADER EthernetHeader;
+ ULONG Offset;
+
+ Info = &Packet->Info;
+
+ PayloadLength = Packet->Length - Info->Length;
+
+ StartVa = MmGetSystemAddressForMdlSafe(&Packet->Mdl, NormalPagePriority);
+ StartVa += Packet->Offset;
+
+ ASSERT(Info->EthernetHeader.Length != 0);
+ EthernetHeader = (PETHERNET_HEADER)(StartVa + Info->EthernetHeader.Offset);
+
+ if (!ETHERNET_HEADER_IS_TAGGED(EthernetHeader) ||
+ OffloadOptions->OffloadTagManipulation == 0)
+ return;
+
+ Packet->TagControlInformation = NTOHS(EthernetHeader->Tagged.Tag.ControlInformation);
+
+ Offset = FIELD_OFFSET(ETHERNET_TAGGED_HEADER, Tag);
+ RtlMoveMemory((PUCHAR)EthernetHeader + sizeof (ETHERNET_TAG),
+ (PUCHAR)EthernetHeader,
+ Offset);
+
+ // Fix up the packet information
+ Packet->Offset += sizeof (ETHERNET_TAG);
+ Packet->Length -= sizeof (ETHERNET_TAG);
+
+ Info->EthernetHeader.Length -= sizeof (ETHERNET_TAG);
+
+ if (Info->IpHeader.Length != 0)
+ Info->IpHeader.Offset -= sizeof (ETHERNET_TAG);
+
+ if (Info->IpOptions.Length != 0)
+ Info->IpOptions.Offset -= sizeof (ETHERNET_TAG);
+
+ if (Info->UdpHeader.Length != 0)
+ Info->UdpHeader.Offset -= sizeof (ETHERNET_TAG);
+
+ if (Info->TcpHeader.Length != 0)
+ Info->TcpHeader.Offset -= sizeof (ETHERNET_TAG);
+
+ if (Info->TcpOptions.Length != 0)
+ Info->TcpOptions.Offset -= sizeof (ETHERNET_TAG);
+
+ Info->Length -= sizeof (ETHERNET_TAG);
+
+ StartVa = MmGetSystemAddressForMdlSafe(&Packet->Mdl, NormalPagePriority);
+ StartVa += Packet->Offset;
+
+ EthernetHeader = (PETHERNET_HEADER)(StartVa + Info->EthernetHeader.Offset);
+ ASSERT(!ETHERNET_HEADER_IS_TAGGED(EthernetHeader));
+
+ ASSERT3U(PayloadLength, ==, Packet->Length - Info->Length);
+
+ OffloadStatistics->TagRemoved++;
+}
+
+static DECLSPEC_NOINLINE VOID
+PacketProcessChecksum(
+ IN PXENVIF_RECEIVER_PACKET Packet,
+ IN PXENVIF_OFFLOAD_OPTIONS OffloadOptions,
+ IN PRECEIVER_OFFLOAD_STATISTICS OffloadStatistics
+ )
+{
+ PXENVIF_PACKET_INFO Info;
+ XENVIF_PACKET_PAYLOAD Payload;
+ uint16_t flags;
+ PUCHAR StartVa;
+ PIP_HEADER IpHeader;
+ BOOLEAN IsAFragment;
+
+ Info = &Packet->Info;
+
+ Payload.Mdl = Packet->Mdl.Next;
+ Payload.Offset = 0;
+ Payload.Length = Packet->Length - Info->Length;
+
+ flags = (uint16_t)(ULONG_PTR)Packet->Cookie;
+
+ if (Info->IpHeader.Length == 0)
+ return;
+
+ StartVa = MmGetSystemAddressForMdlSafe(&Packet->Mdl, NormalPagePriority);
+ StartVa += Packet->Offset;
+
+ IpHeader = (PIP_HEADER)(StartVa + Info->IpHeader.Offset);
+
+ if (IpHeader->Version == 4) {
+ BOOLEAN OffloadChecksum;
+
+ if (OffloadOptions->OffloadIpVersion4HeaderChecksum)
+ OffloadChecksum = TRUE;
+ else
+ OffloadChecksum = FALSE;
+
+ // IP header checksums are always present and not validated
+
+ if (OffloadChecksum) {
+ USHORT Embedded;
+ USHORT Calculated;
+
+ Embedded = IpHeader->Version4.Checksum;
+
+ Calculated = ChecksumIpVersion4Header(StartVa, Info);
+ OffloadStatistics->IpVersion4HeaderChecksumCalculated++;
+
+ if (Embedded == Calculated) {
+ Packet->Flags.IpChecksumSucceeded = 1;
+ OffloadStatistics->IpVersion4HeaderChecksumSucceeded++;
+ } else {
+ Packet->Flags.IpChecksumFailed = 1;
+ OffloadStatistics->IpVersion4HeaderChecksumFailed++;
+ }
+ }
+
+ if (!OffloadChecksum ||
+ OffloadOptions->NeedChecksumValue ||
+ DriverParameters.ReceiverCalculateChecksums) { // Checksum must be present
+ Packet->Flags.IpChecksumPresent = 1;
+ OffloadStatistics->IpVersion4HeaderChecksumPresent++;
+ } else {
+ IpHeader->Version4.Checksum = 0;
+ }
+
+ IsAFragment = IPV4_IS_A_FRAGMENT(NTOHS(IpHeader->Version4.FragmentOffsetAndFlags)) ? TRUE : FALSE;
+ } else {
+ ASSERT3U(IpHeader->Version, ==, 6);
+
+ IsAFragment = FALSE; // No fragmentation in IPv6
+ }
+
+ if (Info->TcpHeader.Length != 0 && !IsAFragment) {
+ PTCP_HEADER TcpHeader;
+ BOOLEAN OffloadChecksum;
+
+ TcpHeader = (PTCP_HEADER)(StartVa + Info->TcpHeader.Offset);
+
+ if (IpHeader->Version == 4 && OffloadOptions->OffloadIpVersion4TcpChecksum)
+ OffloadChecksum = TRUE;
+ else if (IpHeader->Version == 6 && OffloadOptions->OffloadIpVersion6TcpChecksum)
+ OffloadChecksum = TRUE;
+ else
+ OffloadChecksum = FALSE;
+
+ if (OffloadChecksum) {
+ if (flags & NETRXF_data_validated) { // Checksum may not be present but it is validated
+ Packet->Flags.TcpChecksumSucceeded = 1;
+
+ if (IpHeader->Version == 4)
+ OffloadStatistics->IpVersion4TcpChecksumSucceeded++;
+ else
+ OffloadStatistics->IpVersion6TcpChecksumSucceeded++;
+
+ } else { // Checksum is present but is not validated
+ USHORT Embedded;
+ USHORT Calculated;
+
+ ASSERT(~flags & NETRXF_csum_blank);
+
+ Embedded = TcpHeader->Checksum;
+
+ Calculated = ChecksumPseudoHeader(StartVa, Info);
+ Calculated = ChecksumTcpPacket(StartVa, Info, Calculated, &Payload);
+
+ if (IpHeader->Version == 4)
+ OffloadStatistics->IpVersion4TcpChecksumCalculated++;
+ else
+ OffloadStatistics->IpVersion6TcpChecksumCalculated++;
+
+ if (Embedded == Calculated) {
+ Packet->Flags.TcpChecksumSucceeded = 1;
+
+ if (IpHeader->Version == 4)
+ OffloadStatistics->IpVersion4TcpChecksumSucceeded++;
+ else
+ OffloadStatistics->IpVersion6TcpChecksumSucceeded++;
+
+ } else {
+ Packet->Flags.TcpChecksumFailed = 1;
+
+ if (IpHeader->Version == 4)
+ OffloadStatistics->IpVersion4TcpChecksumFailed++;
+ else
+ OffloadStatistics->IpVersion6TcpChecksumFailed++;
+ }
+ }
+ }
+
+ if (!OffloadChecksum ||
+ OffloadOptions->NeedChecksumValue ||
+ DriverParameters.ReceiverCalculateChecksums) { // Checksum must be present
+ if (flags & NETRXF_csum_blank) { // Checksum is not present
+ USHORT Calculated;
+
+ Calculated = ChecksumPseudoHeader(StartVa, Info);
+ Calculated = ChecksumTcpPacket(StartVa, Info, Calculated, &Payload);
+
+ if (IpHeader->Version == 4)
+ OffloadStatistics->IpVersion4TcpChecksumCalculated++;
+ else
+ OffloadStatistics->IpVersion6TcpChecksumCalculated++;
+
+ TcpHeader->Checksum = Calculated;
+ }
+
+ Packet->Flags.TcpChecksumPresent = 1;
+
+ if (IpHeader->Version == 4)
+ OffloadStatistics->IpVersion4TcpChecksumPresent++;
+ else
+ OffloadStatistics->IpVersion6TcpChecksumPresent++;
+ } else {
+ TcpHeader->Checksum = 0;
+ }
+
+ } else if (Info->UdpHeader.Length != 0 && !IsAFragment) {
+ PUDP_HEADER UdpHeader;
+ BOOLEAN OffloadChecksum;
+
+ UdpHeader = (PUDP_HEADER)(StartVa + Info->UdpHeader.Offset);
+
+ if (IpHeader->Version == 4 && OffloadOptions->OffloadIpVersion4UdpChecksum)
+ OffloadChecksum = TRUE;
+ else if (IpHeader->Version == 6 && OffloadOptions->OffloadIpVersion6UdpChecksum)
+ OffloadChecksum = TRUE;
+ else
+ OffloadChecksum = FALSE;
+
+ if (OffloadChecksum) {
+ if (flags & NETRXF_data_validated) { // Checksum may not be present but it is validated
+ Packet->Flags.UdpChecksumSucceeded = 1;
+
+ if (IpHeader->Version == 4)
+ OffloadStatistics->IpVersion4UdpChecksumSucceeded++;
+ else
+ OffloadStatistics->IpVersion6UdpChecksumSucceeded++;
+
+ } else { // Checksum is present but is not validated
+ USHORT Embedded;
+ USHORT Calculated;
+
+ ASSERT(~flags & NETRXF_csum_blank);
+
+ Embedded = UdpHeader->Checksum;
+
+ Calculated = ChecksumPseudoHeader(StartVa, Info);
+ Calculated = ChecksumUdpPacket(StartVa, Info, Calculated, &Payload);
+
+ if (IpHeader->Version == 4)
+ OffloadStatistics->IpVersion4UdpChecksumCalculated++;
+ else
+ OffloadStatistics->IpVersion6UdpChecksumCalculated++;
+
+ if (Embedded == Calculated) {
+ Packet->Flags.UdpChecksumSucceeded = 1;
+
+ if (IpHeader->Version == 4)
+ OffloadStatistics->IpVersion4UdpChecksumSucceeded++;
+ else
+ OffloadStatistics->IpVersion6UdpChecksumSucceeded++;
+
+ } else {
+ Packet->Flags.UdpChecksumFailed = 1;
+
+ if (IpHeader->Version == 4)
+ OffloadStatistics->IpVersion4UdpChecksumFailed++;
+ else
+ OffloadStatistics->IpVersion6UdpChecksumFailed++;
+
+ }
+ }
+
+ }
+
+ if (!OffloadChecksum ||
+ OffloadOptions->NeedChecksumValue ||
+ DriverParameters.ReceiverCalculateChecksums) { // Checksum must be present
+ if (flags & NETRXF_csum_blank) { // Checksum is not present
+ USHORT Calculated;
+
+ Calculated = ChecksumPseudoHeader(StartVa, Info);
+ Calculated = ChecksumUdpPacket(StartVa, Info, Calculated, &Payload);
+
+ if (IpHeader->Version == 4)
+ OffloadStatistics->IpVersion4UdpChecksumCalculated++;
+ else
+ OffloadStatistics->IpVersion6UdpChecksumCalculated++;
+
+ UdpHeader->Checksum = Calculated;
+ }
+
+ Packet->Flags.UdpChecksumPresent = 1;
+
+ if (IpHeader->Version == 4)
+ OffloadStatistics->IpVersion4UdpChecksumPresent++;
+ else
+ OffloadStatistics->IpVersion6UdpChecksumPresent++;
+ } else {
+ UdpHeader->Checksum = 0;
+ }
+ }
+}
+
+static FORCEINLINE VOID
+__drv_requiresIRQL(DISPATCH_LEVEL)
+__RingAcquireLock(
+ IN PRECEIVER_RING Ring
+ )
+{
+ ASSERT3U(KeGetCurrentIrql(), ==, DISPATCH_LEVEL);
+
+ KeAcquireSpinLockAtDpcLevel(&Ring->Lock);
+}
+
+static DECLSPEC_NOINLINE VOID
+RingAcquireLock(
+ IN PRECEIVER_RING Ring
+ )
+{
+ __RingAcquireLock(Ring);
+}
+
+static FORCEINLINE VOID
+__drv_requiresIRQL(DISPATCH_LEVEL)
+__RingReleaseLock(
+ IN PRECEIVER_RING Ring
+ )
+{
+ ASSERT3U(KeGetCurrentIrql(), ==, DISPATCH_LEVEL);
+
+#pragma prefast(disable:26110)
+ KeReleaseSpinLockFromDpcLevel(&Ring->Lock);
+}
+
+static DECLSPEC_NOINLINE VOID
+RingReleaseLock(
+ IN PRECEIVER_RING Ring
+ )
+{
+ __RingReleaseLock(Ring);
+}
+
+static FORCEINLINE VOID
+__RingStop(
+ IN PRECEIVER_RING Ring
+ )
+{
+ Info("<===>\n");
+
+ Ring->Stopped = TRUE;
+}
+
+static FORCEINLINE VOID
+__RingStart(
+ IN PRECEIVER_RING Ring
+ )
+{
+ Ring->Stopped = FALSE;
+}
+
+static FORCEINLINE BOOLEAN
+__RingIsStopped(
+ IN PRECEIVER_RING Ring
+ )
+{
+ return Ring->Stopped;
+}
+
+static FORCEINLINE VOID
+__RingReturnPacket(
+ IN PRECEIVER_RING Ring,
+ IN PXENVIF_RECEIVER_PACKET Packet,
+ IN BOOLEAN Locked
+ )
+{
+ PMDL Mdl;
+
+ Mdl = &Packet->Mdl;
+
+ while (Mdl != NULL) {
+ PMDL Next;
+
+ Next = Mdl->Next;
+ Mdl->Next = NULL;
+
+ __RingPutMdl(Ring, Mdl, Locked);
+
+ Mdl = Next;
+ }
+
+ if (__RingIsStopped(Ring)) {
+ KIRQL Irql;
+
+ KeRaiseIrql(DISPATCH_LEVEL, &Irql);
+
+ if (!Locked)
+ __RingAcquireLock(Ring);
+
+ if (__RingIsStopped(Ring)) {
+ PXENVIF_RECEIVER Receiver;
+ PXENVIF_FRONTEND Frontend;
+
+ __RingStart(Ring);
+
+ Receiver = Ring->Receiver;
+ Frontend = Receiver->Frontend;
+
+ NotifierTrigger(FrontendGetNotifier(Frontend));
+ }
+
+ if (!Locked)
+ __RingReleaseLock(Ring);
+
+ KeLowerIrql(Irql);
+ }
+}
+
+static BOOLEAN
+ReceiverPullup(
+ IN PVOID Argument,
+ IN PUCHAR DestinationVa,
+ IN OUT PXENVIF_PACKET_PAYLOAD Payload,
+ IN ULONG Length
+ )
+{
+ PMDL Mdl;
+
+ Mdl = Payload->Mdl;
+ ASSERT3U(Payload->Offset, ==, 0);
+
+ if (Payload->Length < Length)
+ goto fail1;
+
+ Payload->Length -= Length;
+
+ while (Length != 0) {
+ PUCHAR SourceVa;
+ ULONG CopyLength;
+
+ SourceVa = MmGetSystemAddressForMdlSafe(Mdl, NormalPagePriority);
+ ASSERT(SourceVa != NULL);
+
+ CopyLength = __min(Mdl->ByteCount, Length);
+
+ RtlCopyMemory(DestinationVa, SourceVa, CopyLength);
+
+ DestinationVa += CopyLength;
+
+ Mdl->ByteOffset += CopyLength;
+ Mdl->MappedSystemVa = (PUCHAR)Mdl->MappedSystemVa + CopyLength;
+ Length -= CopyLength;
+
+ Mdl->ByteCount -= CopyLength;
+ if (Mdl->ByteCount == 0) {
+ PRECEIVER_RING Ring = Argument;
+ PMDL Next;
+
+ Next = Mdl->Next;
+ Mdl->Next = NULL;
+
+ __RingPutMdl(Ring, Mdl, TRUE);
+
+ Mdl = Next;
+ }
+ }
+
+ Payload->Mdl = Mdl;
+
+ return TRUE;
+
+fail1:
+ Error("fail1\n");
+
+ return FALSE;
+}
+
+static FORCEINLINE PXENVIF_RECEIVER_PACKET
+__RingBuildSegment(
+ IN PRECEIVER_RING Ring,
+ IN PXENVIF_RECEIVER_PACKET Packet,
+ IN ULONG SegmentSize,
+ IN PXENVIF_PACKET_PAYLOAD Payload
+ )
+{
+ PXENVIF_PACKET_INFO Info;
+ PXENVIF_RECEIVER_PACKET Segment;
+ PMDL Mdl;
+ PUCHAR InfoVa;
+ PUCHAR StartVa;
+ PIP_HEADER IpHeader;
+ PTCP_HEADER TcpHeader;
+ ULONG Seq;
+ NTSTATUS status;
+
+ Info = &Packet->Info;
+
+ InfoVa = MmGetSystemAddressForMdlSafe(&Packet->Mdl, NormalPagePriority);
+ InfoVa += Packet->Offset;
+
+ Segment = __RingGetPacket(Ring, TRUE);
+
+ status = STATUS_NO_MEMORY;
+ if (Segment == NULL)
+ goto fail1;
+
+ Segment->Offset = Packet->Offset;
+
+ Mdl = &Segment->Mdl;
+
+ StartVa = MmGetSystemAddressForMdlSafe(Mdl, NormalPagePriority);
+ StartVa += Segment->Offset;
+
+ Mdl->ByteCount = Segment->Offset;
+
+ // Copy in the header
+ RtlCopyMemory(StartVa, InfoVa, Info->Length);
+ Mdl->ByteCount += Info->Length;
+
+ Segment->Info = Packet->Info;
+ Segment->Cookie = Packet->Cookie;
+
+ // Adjust the info for the next segment
+ IpHeader = (PIP_HEADER)(InfoVa + Info->IpHeader.Offset);
+ if (IpHeader->Version == 4) {
+ USHORT PacketID;
+ USHORT PacketLength;
+
+ PacketID = NTOHS(IpHeader->Version4.PacketID);
+ IpHeader->Version4.PacketID = HTONS(PacketID + 1);
+
+ PacketLength = NTOHS(IpHeader->Version4.PacketLength);
+ IpHeader->Version4.PacketLength = HTONS(PacketLength - (USHORT)SegmentSize);
+ } else {
+ USHORT PayloadLength;
+
+ ASSERT3U(IpHeader->Version, ==, 6);
+
+ PayloadLength = NTOHS(IpHeader->Version6.PayloadLength);
+ IpHeader->Version6.PayloadLength = HTONS(PayloadLength - (USHORT)SegmentSize);
+ }
+
+ TcpHeader = (PTCP_HEADER)(InfoVa + Info->TcpHeader.Offset);
+
+ Seq = NTOHL(TcpHeader->Seq);
+ TcpHeader->Seq = HTONL(Seq + SegmentSize);
+
+ TcpHeader->Flags &= ~TCP_CWR;
+
+ // Adjust the segment IP header
+ IpHeader = (PIP_HEADER)(StartVa + Info->IpHeader.Offset);
+ if (IpHeader->Version == 4) {
+ ULONG Length;
+
+ Length = Info->IpHeader.Length +
+ Info->IpOptions.Length +
+ Info->TcpHeader.Length +
+ Info->TcpOptions.Length +
+ SegmentSize;
+
+ IpHeader->Version4.PacketLength = HTONS((USHORT)Length);
+ IpHeader->Version4.Checksum = ChecksumIpVersion4Header(StartVa, Info);
+
+ Ring->OffloadStatistics.IpVersion4LargePacketSegment++;
+ } else {
+ ULONG Length;
+
+ ASSERT3U(IpHeader->Version, ==, 6);
+
+ Length = Info->IpOptions.Length +
+ Info->TcpHeader.Length +
+ Info->TcpOptions.Length +
+ SegmentSize;
+
+ IpHeader->Version6.PayloadLength = HTONS((USHORT)Length);
+
+ Ring->OffloadStatistics.IpVersion6LargePacketSegment++;
+ }
+
+ // Adjust the segment TCP header
+ TcpHeader = (PTCP_HEADER)(StartVa + Info->TcpHeader.Offset);
+
+ TcpHeader->Flags &= ~(TCP_PSH | TCP_FIN);
+
+ // Copy in the payload
+ for (;;) {
+ ULONG Length;
+
+ Mdl->Next = __RingGetMdl(Ring, TRUE);
+
+ status = STATUS_NO_MEMORY;
+ if (Mdl->Next == NULL)
+ goto fail2;
+
+ Mdl = Mdl->Next;
+ StartVa = MmGetSystemAddressForMdlSafe(Mdl, NormalPagePriority);
+
+ Length = __min(SegmentSize - Segment->Length, PAGE_SIZE);
+ ASSERT(Length != 0);
+
+ ReceiverPullup(Ring, StartVa, Payload, Length);
+ Mdl->ByteCount += Length;
+ Segment->Length += Length;
+
+ ASSERT3U(Segment->Length, <=, SegmentSize);
+ if (Segment->Length == SegmentSize)
+ break;
+
+ ASSERT3U(Mdl->ByteCount, ==, PAGE_SIZE);
+ }
+
+ Segment->Length += Info->Length;
+
+ return Segment;
+
+fail2:
+ Error("fail2\n");
+
+ if (IpHeader->Version == 4) {
+ --Ring->OffloadStatistics.IpVersion4LargePacketSegment;
+ } else {
+ ASSERT3U(IpHeader->Version, ==, 6);
+ --Ring->OffloadStatistics.IpVersion6LargePacketSegment;
+ }
+
+ Mdl = Segment->Mdl.Next;
+ Segment->Mdl.Next = NULL;
+
+ while (Mdl != NULL) {
+ PMDL Next;
+
+ Next = Mdl->Next;
+ Mdl->Next = NULL;
+
+ __RingPutMdl(Ring, Mdl, TRUE);
+
+ Mdl = Next;
+ }
+
+ __RingPutPacket(Ring, Segment, TRUE);
+
+fail1:
+ Error("fail1 (%08x)\n", status);
+
+ return NULL;
+}
+
+static FORCEINLINE VOID
+__RingProcessLargePacket(
+ IN PRECEIVER_RING Ring,
+ IN PXENVIF_RECEIVER_PACKET Packet,
+ OUT PLIST_ENTRY List
+ )
+{
+ BOOLEAN Offload;
+ PXENVIF_PACKET_INFO Info;
+ uint16_t flags;
+ XENVIF_PACKET_PAYLOAD Payload;
+ PUCHAR InfoVa;
+ PIP_HEADER IpHeader;
+ ULONG Length;
+ NTSTATUS status;
+
+ Info = &Packet->Info;
+
+ flags = (uint16_t)(ULONG_PTR)Packet->Cookie;
+ ASSERT(flags & NETRXF_csum_blank);
+ ASSERT(flags & NETRXF_data_validated);
+
+ Payload.Mdl = Packet->Mdl.Next;
+ Payload.Offset = 0;
+ Payload.Length = Packet->Length - Info->Length;
+
+ Packet->Mdl.Next = NULL;
+
+ InfoVa = MmGetSystemAddressForMdlSafe(&Packet->Mdl, NormalPagePriority);
+ InfoVa += Packet->Offset;
+
+ IpHeader = (PIP_HEADER)(InfoVa + Info->IpHeader.Offset);
+
+ if (IpHeader->Version == 4) {
+ Offload = (Ring->OffloadOptions.OffloadIpVersion4LargePacket) ? TRUE : FALSE;
+ } else {
+ ASSERT3U(IpHeader->Version, ==, 6);
+ Offload = (Ring->OffloadOptions.OffloadIpVersion6LargePacket) ? TRUE : FALSE;
+ }
+
+ if (IpHeader->Version == 4) {
+ USHORT PacketLength;
+
+ PacketLength = NTOHS(IpHeader->Version4.PacketLength);
+
+ Length = (ULONG)PacketLength -
+ Info->TcpOptions.Length -
+ Info->TcpHeader.Length -
+ Info->IpOptions.Length -
+ Info->IpHeader.Length;
+ } else {
+ USHORT PayloadLength;
+
+ ASSERT3U(IpHeader->Version, ==, 6);
+
+ PayloadLength = NTOHS(IpHeader->Version6.PayloadLength);
+
+ Length = (ULONG)PayloadLength -
+ Info->TcpOptions.Length -
+ Info->TcpHeader.Length -
+ Info->IpOptions.Length;
+ }
+
+ while (Length > 0) {
+ ULONG SegmentSize;
+ PXENVIF_RECEIVER_PACKET Segment;
+
+ if (Offload &&
+ Ring->OffloadOptions.NeedLargePacketSplit == 0)
+ break;
+
+ SegmentSize = __min(Length, Packet->MaximumSegmentSize);
+
+ Segment = __RingBuildSegment(Ring, Packet, SegmentSize, &Payload);
+
+ status = STATUS_NO_MEMORY;
+ if (Segment == NULL)
+ goto fail1;
+
+ ASSERT3U(Length, >=, SegmentSize);
+ Length -= SegmentSize;
+
+ ASSERT(IsZeroMemory(&Segment->ListEntry, sizeof (LIST_ENTRY)));
+ InsertTailList(List, &Segment->ListEntry);
+
+ if (Offload) {
+ ASSERT(Ring->OffloadOptions.NeedLargePacketSplit);
+ break;
+ }
+ }
+
+ if (Length != 0) {
+ ASSERT(Payload.Mdl != NULL);
+
+ if (IpHeader->Version == 4) {
+ USHORT PacketLength;
+
+ PacketLength = NTOHS(IpHeader->Version4.PacketLength);
+
+ ASSERT3U(Length,
+ ==,
+ (ULONG)PacketLength -
+ Info->TcpOptions.Length -
+ Info->TcpHeader.Length -
+ Info->IpOptions.Length -
+ Info->IpHeader.Length);
+
+ IpHeader->Version4.Checksum = ChecksumIpVersion4Header(InfoVa, Info);
+
+ Ring->OffloadStatistics.IpVersion4LargePacketSegment++;
+ } else {
+ USHORT PayloadLength;
+
+ ASSERT3U(IpHeader->Version, ==, 6);
+
+ PayloadLength = NTOHS(IpHeader->Version6.PayloadLength);
+
+ ASSERT3U(Length,
+ ==,
+ (ULONG)PayloadLength -
+ Info->TcpOptions.Length -
+ Info->TcpHeader.Length -
+ Info->IpOptions.Length);
+
+ Ring->OffloadStatistics.IpVersion6LargePacketSegment++;
+ }
+
+ Packet->Mdl.Next = Payload.Mdl;
+ Packet->Length = Info->Length + Payload.Length;
+
+ if (Payload.Length < Packet->MaximumSegmentSize)
+ Packet->MaximumSegmentSize = 0;
+
+ ASSERT(IsZeroMemory(&Packet->ListEntry, sizeof (LIST_ENTRY)));
+ InsertTailList(List, &Packet->ListEntry);
+ } else {
+ __RingPutPacket(Ring, Packet, TRUE);
+ }
+
+ return;
+
+fail1:
+ Error("fail1 (%08x)\n", status);
+
+ if (Payload.Length != 0) {
+ PMDL Mdl = Payload.Mdl;
+
+ ASSERT(Mdl != NULL);
+
+ while (Mdl != NULL) {
+ PMDL Next;
+
+ Next = Mdl->Next;
+ Mdl->Next = NULL;
+
+ __RingPutMdl(Ring, Mdl, TRUE);
+
+ Mdl = Next;
+ }
+ }
+
+ __RingPutPacket(Ring, Packet, TRUE);
+
+ Ring->PacketStatistics.Drop++;
+}
+
+static FORCEINLINE VOID
+__RingProcessPacket(
+ IN PRECEIVER_RING Ring,
+ IN PXENVIF_RECEIVER_PACKET Packet,
+ OUT PLIST_ENTRY List
+ )
+{
+ PXENVIF_RECEIVER Receiver;
+ PXENVIF_FRONTEND Frontend;
+ ULONG Length;
+ USHORT MaximumSegmentSize;
+ PVOID Cookie;
+ XENVIF_PACKET_PAYLOAD Payload;
+ PXENVIF_PACKET_INFO Info;
+ PUCHAR StartVa;
+ PETHERNET_HEADER EthernetHeader;
+ PETHERNET_ADDRESS DestinationAddress;
+ ETHERNET_ADDRESS_TYPE Type;
+ NTSTATUS status;
+
+ Receiver = Ring->Receiver;
+ Frontend = Receiver->Frontend;
+
+ ASSERT3U(Packet->Offset, ==, 0);
+ Length = Packet->Length;
+ MaximumSegmentSize = Packet->MaximumSegmentSize;
+ Cookie = Packet->Cookie;
+
+ // Clean the packet metadata since this structure now becomes payload
+ RtlZeroMemory(Packet, FIELD_OFFSET(XENVIF_RECEIVER_PACKET, Mdl));
+
+ Payload.Mdl = &Packet->Mdl;
+ Payload.Offset = 0;
+ Payload.Length = Length;
+
+ // Get a new packet structure that will just contain the header after parsing
+ Packet = __RingGetPacket(Ring, TRUE);
+
+ status = STATUS_NO_MEMORY;
+ if (Packet == NULL) {
+ Ring->PacketStatistics.FrontendError++;
+ goto fail1;
+ }
+
+ // Copy in the extracted metadata
+ Packet->Offset = DriverParameters.ReceiverIpAlignOffset;
+ Packet->Length = Length;
+ Packet->MaximumSegmentSize = MaximumSegmentSize;
+ Packet->Cookie = Cookie;
+
+ StartVa = MmGetSystemAddressForMdlSafe(&Packet->Mdl, NormalPagePriority);
+ StartVa += Packet->Offset;
+
+ Packet->Mdl.ByteCount = Packet->Offset;
+
+ Info = &Packet->Info;
+
+ status = ParsePacket(StartVa, ReceiverPullup, Ring, &Ring->HeaderStatistics, &Payload, Info);
+ if (!NT_SUCCESS(status)) {
+ Ring->PacketStatistics.FrontendError++;
+ goto fail2;
+ }
+
+ ASSERT3U(Packet->Length, ==, Info->Length + Payload.Length);
+
+ Packet->Mdl.ByteCount += Info->Length;
+
+ // Certain HCK tests (e.g. the NDISTest 2c_Priority test) are sufficiently brain-dead
+ // that they cannot cope with multi-fragment packets, or at least packets where
+ // headers are in different fragments. All these tests seem to use IPX packets and,
+ // in practice, little else uses LLC so pull up all LLC packets into a single fragment.
+ if (Info->LLCSnapHeader.Length != 0) {
+ ULONG PayloadLength = Payload.Length;
+
+ ASSERT3U(Packet->Length, <=, PAGE_SIZE);
+
+ status = ReceiverPullup(Ring, StartVa + Info->Length, &Payload, PayloadLength);
+ ASSERT(NT_SUCCESS(status));
+
+ ASSERT3U(Payload.Length, ==, 0);
+ Packet->Mdl.ByteCount += PayloadLength;
+ }
+
+ if (Payload.Length != 0) {
+ PMDL Mdl = Payload.Mdl;
+
+ ASSERT(Mdl != NULL);
+
+ Packet->Mdl.Next = Mdl;
+ }
+
+ ASSERT(Info->EthernetHeader.Length != 0);
+ EthernetHeader = (PETHERNET_HEADER)(StartVa + Info->EthernetHeader.Offset);
+
+ DestinationAddress = &EthernetHeader->DestinationAddress;
+
+ status = STATUS_UNSUCCESSFUL;
+ if (!MacApplyFilters(FrontendGetMac(Frontend),
+ DestinationAddress))
+ goto fail3;
+
+ Type = GET_ETHERNET_ADDRESS_TYPE(DestinationAddress);
+
+ switch (Type) {
+ case ETHERNET_ADDRESS_UNICAST:
+ Ring->PacketStatistics.Unicast++;
+ Ring->PacketStatistics.UnicastBytes += Packet->Length;
+ break;
+
+ case ETHERNET_ADDRESS_MULTICAST:
+ Ring->PacketStatistics.Multicast++;
+ Ring->PacketStatistics.MulticastBytes += Packet->Length;
+ break;
+
+ case ETHERNET_ADDRESS_BROADCAST:
+ Ring->PacketStatistics.Broadcast++;
+ Ring->PacketStatistics.BroadcastBytes += Packet->Length;
+ break;
+
+ default:
+ ASSERT(FALSE);
+ break;
+ }
+
+ if (Packet->MaximumSegmentSize != 0) {
+ __RingProcessLargePacket(Ring, Packet, List);
+ } else {
+ ASSERT(IsZeroMemory(&Packet->ListEntry, sizeof (LIST_ENTRY)));
+ InsertTailList(List, &Packet->ListEntry);
+ }
+
+ return;
+
+fail3:
+ Packet->Mdl.Next = NULL;
+ __RingPutPacket(Ring, Packet, TRUE);
+
+fail2:
+fail1:
+ if (Payload.Length != 0) {
+ PMDL Mdl = Payload.Mdl;
+
+ ASSERT(Mdl != NULL);
+
+ while (Mdl != NULL) {
+ PMDL Next;
+
+ Next = Mdl->Next;
+ Mdl->Next = NULL;
+
+ __RingPutMdl(Ring, Mdl, TRUE);
+
+ Mdl = Next;
+ }
+ }
+
+ Ring->PacketStatistics.Drop++;
+}
+
+static VOID
+RingProcessPackets(
+ IN PRECEIVER_RING Ring,
+ OUT PLIST_ENTRY List,
+ OUT PULONG Count
+ )
+{
+ PLIST_ENTRY ListEntry;
+
+ while (!IsListEmpty(&Ring->PacketList)) {
+ PXENVIF_RECEIVER_PACKET Packet;
+
+ ListEntry = RemoveHeadList(&Ring->PacketList);
+ ASSERT3P(ListEntry, !=, &Ring->PacketList);
+
+ RtlZeroMemory(ListEntry, sizeof (LIST_ENTRY));
+
+ Packet = CONTAINING_RECORD(ListEntry, XENVIF_RECEIVER_PACKET, ListEntry);
+ __RingProcessPacket(Ring, Packet, List);
+ }
+
+ for (ListEntry = List->Flink;
+ ListEntry != List;
+ ListEntry = ListEntry->Flink) {
+ PXENVIF_RECEIVER_PACKET Packet;
+
+ Packet = CONTAINING_RECORD(ListEntry, XENVIF_RECEIVER_PACKET, ListEntry);
+
+ PacketProcessTag(Packet,
+ &Ring->OffloadOptions,
+ &Ring->OffloadStatistics);
+
+ PacketProcessChecksum(Packet,
+ &Ring->OffloadOptions,
+ &Ring->OffloadStatistics);
+
+ Packet->Cookie = Ring;
+
+ (*Count)++;
+ }
+}
+
+static FORCEINLINE PRECEIVER_TAG
+__Protocol0GetTag(
+ IN PRECEIVER_RING Ring
+ )
+{
+ PRECEIVER_RING_PROTOCOL0 Protocol0;
+ ULONG Index;
+ PRECEIVER_TAG Tag;
+
+ Protocol0 = &Ring->Protocol0;
+
+ Index = Protocol0->HeadFreeTag;
+ ASSERT3U(Index, <, MAXIMUM_TAG_COUNT);
+
+ Tag = &Protocol0->Tag[Index];
+ Protocol0->HeadFreeTag = Tag->Next;
+ Tag->Next = TAG_INDEX_INVALID;
+
+ return Tag;
+}
+
+static FORCEINLINE
+__Protocol0PutTag(
+ IN PRECEIVER_RING Ring,
+ IN PRECEIVER_TAG Tag
+ )
+{
+ PRECEIVER_RING_PROTOCOL0 Protocol0;
+ ULONG Index;
+
+ Protocol0 = &Ring->Protocol0;
+
+ ASSERT3P(Tag->Context, ==, NULL);
+
+ Index = (ULONG)(Tag - &Protocol0->Tag[0]);
+ ASSERT3U(Index, <, MAXIMUM_TAG_COUNT);
+
+ ASSERT3U(Tag->Next, ==, TAG_INDEX_INVALID);
+ Tag->Next = Protocol0->HeadFreeTag;
+ Protocol0->HeadFreeTag = Index;
+}
+
+static FORCEINLINE PRECEIVER_TAG
+__Protocol0PreparePacket(
+ IN PRECEIVER_RING Ring,
+ IN PMDL Mdl
+ )
+{
+ PXENVIF_RECEIVER Receiver;
+ PXENVIF_FRONTEND Frontend;
+ PRECEIVER_TAG Tag;
+
+ Receiver = Ring->Receiver;
+ Frontend = Receiver->Frontend;
+
+ Tag = __Protocol0GetTag(Ring);
+
+ Tag->Context = Mdl;
+
+ GNTTAB(PermitForeignAccess,
+ Receiver->GnttabInterface,
+ Tag->Reference,
+ FrontendGetBackendDomain(Frontend),
+ GNTTAB_ENTRY_FULL_PAGE,
+ MmGetMdlPfnArray(Mdl)[0],
+ FALSE);
+
+ return Tag;
+}
+
+static VOID
+Protocol0ReleaseTag(
+ IN PRECEIVER_RING Ring,
+ IN PRECEIVER_TAG Tag
+ )
+{
+ PXENVIF_RECEIVER Receiver;
+
+ Receiver = Ring->Receiver;
+
+ GNTTAB(RevokeForeignAccess,
+ Receiver->GnttabInterface,
+ Tag->Reference);
+
+ __Protocol0PutTag(Ring, Tag);
+}
+
+static FORCEINLINE VOID
+__Protocol0PushRequests(
+ IN PRECEIVER_RING Ring
+ )
+{
+ PRECEIVER_RING_PROTOCOL0 Protocol0;
+ BOOLEAN Notify;
+
+ Protocol0 = &Ring->Protocol0;
+
+ if (Protocol0->RequestsPosted == Protocol0->RequestsPushed)
+ return;
+
+#pragma warning (push)
+#pragma warning (disable:4244)
+
+ // Make the requests visible to the backend
+ RING_PUSH_REQUESTS_AND_CHECK_NOTIFY(&Protocol0->Front, Notify);
+
+#pragma warning (pop)
+
+ if (Notify) {
+ PXENVIF_RECEIVER Receiver;
+ PXENVIF_FRONTEND Frontend;
+
+ Receiver = Ring->Receiver;
+ Frontend = Receiver->Frontend;
+
+ NotifierSend(FrontendGetNotifier(Frontend));
+ }
+
+ Protocol0->RequestsPushed = Protocol0->RequestsPosted;
+}
+
+static VOID
+Protocol0Fill(
+ IN PRECEIVER_RING Ring
+ )
+{
+ PRECEIVER_RING_PROTOCOL0 Protocol0;
+ RING_IDX req_prod;
+ RING_IDX rsp_cons;
+
+ Protocol0 = &Ring->Protocol0;
+
+ req_prod = Protocol0->Front.req_prod_pvt;
+ rsp_cons = Protocol0->Front.rsp_cons;
+
+ while (req_prod - rsp_cons < RING_SIZE(&Protocol0->Front)) {
+ PXENVIF_RECEIVER_PACKET Packet;
+ PRECEIVER_TAG Tag;
+ netif_rx_request_t *req;
+ uint16_t id;
+
+ Packet = __RingGetPacket(Ring, TRUE);
+
+ if (Packet == NULL) {
+ __RingStop(Ring);
+ break;
+ }
+
+ Tag = __Protocol0PreparePacket(Ring, &Packet->Mdl);
+ ASSERT(Tag != NULL);
+
+ req = RING_GET_REQUEST(&Protocol0->Front, req_prod);
+ req_prod++;
+ Protocol0->RequestsPosted++;
+
+ id = (USHORT)(Tag - &Protocol0->Tag[0]);
+ ASSERT3U(id, <, MAXIMUM_TAG_COUNT);
+
+ req->id = id | REQ_ID_INTEGRITY_CHECK;
+ req->gref = Tag->Reference;
+
+ // Store a copy of the request in case we need to fake a response ourselves
+ ASSERT(IsZeroMemory(&Protocol0->Pending[id], sizeof (netif_rx_request_t)));
+ Protocol0->Pending[id] = *req;
+ }
+
+ Protocol0->Front.req_prod_pvt = req_prod;
+
+ __Protocol0PushRequests(Ring);
+}
+
+#define PROTOCOL0_BATCH(_Ring) (RING_SIZE(&(_Ring)->Protocol0.Front) / 4)
+
+static DECLSPEC_NOINLINE VOID
+Protocol0Poll(
+ IN PRECEIVER_RING Ring
+ )
+{
+ PXENVIF_RECEIVER Receiver;
+ PXENVIF_FRONTEND Frontend;
+ PRECEIVER_RING_PROTOCOL0 Protocol0;
+
+ Receiver = Ring->Receiver;
+ Frontend = Receiver->Frontend;
+
+ Protocol0 = &Ring->Protocol0;
+
+ for (;;) {
+ BOOLEAN Error;
+ PXENVIF_RECEIVER_PACKET Packet;
+ uint16_t flags;
+ USHORT MaximumSegmentSize;
+ PMDL TailMdl;
+ RING_IDX rsp_prod;
+ RING_IDX rsp_cons;
+
+ Error = FALSE;
+ Packet = NULL;
+ flags = 0;
+ MaximumSegmentSize = 0;
+ TailMdl = NULL;
+
+ KeMemoryBarrier();
+
+ rsp_prod = Protocol0->Shared->rsp_prod;
+ rsp_cons = Protocol0->Front.rsp_cons;
+
+ KeMemoryBarrier();
+
+ if (rsp_cons == rsp_prod)
+ break;
+
+ while (rsp_cons != rsp_prod) {
+ netif_rx_response_t *rsp;
+ uint16_t id;
+ netif_rx_request_t *req;
+ PRECEIVER_TAG Tag;
+ PMDL Mdl;
+ RING_IDX req_prod;
+
+ rsp = RING_GET_RESPONSE(&Protocol0->Front, rsp_cons);
+ rsp_cons++;
+ Protocol0->ResponsesProcessed++;
+
+ ASSERT3U((rsp->id & REQ_ID_INTEGRITY_CHECK), ==, REQ_ID_INTEGRITY_CHECK);
+ id = rsp->id & ~REQ_ID_INTEGRITY_CHECK;
+
+ ASSERT3U(id, <, MAXIMUM_TAG_COUNT);
+ req = &Protocol0->Pending[id];
+
+ ASSERT3U(req->id, ==, rsp->id);
+ RtlZeroMemory(req, sizeof (netif_rx_request_t));
+
+ Tag = &Protocol0->Tag[id];
+
+ Mdl = Tag->Context;
+ Tag->Context = NULL;
+
+ Protocol0ReleaseTag(Ring, Tag);
+ Tag = NULL;
+
+ ASSERT(Mdl != NULL);
+
+ if (rsp->status < 0)
+ Error = TRUE;
+
+ if (rsp->flags & NETRXF_gso_prefix) {
+ __RingPutMdl(Ring, Mdl, TRUE);
+
+ flags = NETRXF_gso_prefix;
+ MaximumSegmentSize = rsp->offset;
+
+ ASSERT(rsp->flags & NETRXF_more_data);
+ continue;
+ } else {
+ Mdl->ByteOffset = rsp->offset;
+ Mdl->MappedSystemVa = (PUCHAR)Mdl->StartVa + rsp->offset;
+ Mdl->ByteCount = rsp->status;
+ }
+
+ if (Packet == NULL) { // SOP
+ Packet = CONTAINING_RECORD(Mdl, XENVIF_RECEIVER_PACKET, Mdl);
+
+ ASSERT3P(TailMdl, ==, NULL);
+ TailMdl = Mdl;
+
+ ASSERT3U((flags & ~NETRXF_gso_prefix), ==, 0);
+ flags |= rsp->flags;
+
+ Packet->Length = Mdl->ByteCount;
+ } else {
+ ASSERT3P(Mdl->Next, ==, NULL);
+
+ ASSERT(TailMdl != NULL);
+ TailMdl->Next = Mdl;
+ TailMdl = Mdl;
+
+ flags |= rsp->flags;
+
+ Packet->Length += Mdl->ByteCount;
+ }
+
+ if (~rsp->flags & NETRXF_more_data) { // EOP
+ ASSERT(Packet != NULL);
+ ASSERT3P(Packet->Cookie, ==, NULL);
+
+ if (Error) {
+ Ring->PacketStatistics.BackendError++;
+
+ __RingReturnPacket(Ring, Packet, TRUE);
+ } else {
+ if (flags & NETRXF_gso_prefix) {
+ ASSERT(MaximumSegmentSize != 0);
+ Packet->MaximumSegmentSize = MaximumSegmentSize;
+ }
+
+ Packet->Cookie = (PVOID)(flags & (NETRXF_csum_blank | NETRXF_data_validated));
+
+ ASSERT(IsZeroMemory(&Packet->ListEntry, sizeof (LIST_ENTRY)));
+ InsertTailList(&Ring->PacketList, &Packet->ListEntry);
+ }
+
+ Error = FALSE;
+ Packet = NULL;
+ flags = 0;
+ MaximumSegmentSize = 0;
+ TailMdl = NULL;
+ }
+
+ KeMemoryBarrier();
+
+ req_prod = Protocol0->Front.req_prod_pvt;
+
+ if (req_prod - rsp_cons < PROTOCOL0_BATCH(Ring) &&
+ !__RingIsStopped(Ring)) {
+ Protocol0->Front.rsp_cons = rsp_cons;
+ Protocol0Fill(Ring);
+ }
+ }
+ ASSERT(!Error);
+ ASSERT3P(Packet, ==, NULL);
+ ASSERT3U(flags, ==, 0);
+ ASSERT3U(MaximumSegmentSize, ==, 0);
+ ASSERT3P(TailMdl, ==, NULL);
+
+ KeMemoryBarrier();
+
+ Protocol0->Front.rsp_cons = rsp_cons;
+ Protocol0->Shared->rsp_event = rsp_cons + 1;
+ }
+
+ if (!__RingIsStopped(Ring))
+ Protocol0Fill(Ring);
+}
+
+static FORCEINLINE VOID
+__Protocol0Empty(
+ IN PRECEIVER_RING Ring
+ )
+{
+ PRECEIVER_RING_PROTOCOL0 Protocol0;
+ RING_IDX rsp_cons;
+ RING_IDX rsp_prod;
+ uint16_t id;
+
+ Protocol0 = &Ring->Protocol0;
+
+ KeMemoryBarrier();
+
+ // Clean up any unprocessed responses
+ rsp_prod = Protocol0->Shared->rsp_prod;
+ rsp_cons = Protocol0->Front.rsp_cons;
+
+ KeMemoryBarrier();
+
+ while (rsp_cons != rsp_prod) {
+ netif_rx_response_t *rsp;
+ netif_rx_request_t *req;
+ PRECEIVER_TAG Tag;
+ PMDL Mdl;
+
+ rsp = RING_GET_RESPONSE(&Protocol0->Front, rsp_cons);
+ rsp_cons++;
+ Protocol0->ResponsesProcessed++;
+
+ ASSERT3U((rsp->id & REQ_ID_INTEGRITY_CHECK), ==, REQ_ID_INTEGRITY_CHECK);
+ id = rsp->id & ~REQ_ID_INTEGRITY_CHECK;
+
+ ASSERT3U(id, <, MAXIMUM_TAG_COUNT);
+ req = &Protocol0->Pending[id];
+
+ ASSERT3U(req->id, ==, rsp->id);
+ RtlZeroMemory(req, sizeof (netif_rx_request_t));
+
+ Tag = &Protocol0->Tag[id];
+
+ Mdl = Tag->Context;
+ Tag->Context = NULL;
+
+ Protocol0ReleaseTag(Ring, Tag);
+
+ __RingPutMdl(Ring, Mdl, TRUE);
+
+ RtlZeroMemory(rsp, sizeof (netif_rx_response_t));
+ }
+
+ Protocol0->Front.rsp_cons = rsp_cons;
+
+ // Clean up any pending requests
+ for (id = 0; id < MAXIMUM_TAG_COUNT; id++) {
+ netif_rx_request_t *req;
+ PRECEIVER_TAG Tag;
+ PMDL Mdl;
+
+ req = &Protocol0->Pending[id];
+ if (req->id == 0)
+ continue;
+
+ --Protocol0->RequestsPosted;
+ --Protocol0->RequestsPushed;
+
+ ASSERT3U((req->id & REQ_ID_INTEGRITY_CHECK), ==, REQ_ID_INTEGRITY_CHECK);
+ ASSERT3U((req->id & ~REQ_ID_INTEGRITY_CHECK), ==, id);
+
+ RtlZeroMemory(req, sizeof (netif_rx_request_t));
+
+ Tag = &Protocol0->Tag[id];
+
+ Mdl = Tag->Context;
+ Tag->Context = NULL;
+
+ Protocol0ReleaseTag(Ring, Tag);
+
+ __RingPutMdl(Ring, Mdl, TRUE);
+
+ RtlZeroMemory(req, sizeof (netif_rx_request_t));
+ }
+}
+
+static DECLSPEC_NOINLINE VOID
+Protocol0DebugCallback(
+ IN PRECEIVER_RING Ring
+ )
+{
+ PXENVIF_RECEIVER Receiver;
+ PRECEIVER_RING_PROTOCOL0 Protocol0;
+
+ Receiver = Ring->Receiver;
+ Protocol0 = &Ring->Protocol0;
+
+ // Dump front ring
+ DEBUG(Printf,
+ Receiver->DebugInterface,
+ Receiver->DebugCallback,
+ "FRONT: req_prod_pvt = %u rsp_cons = %u nr_ents = %u sring = %p\n",
+ Protocol0->Front.req_prod_pvt,
+ Protocol0->Front.rsp_cons,
+ Protocol0->Front.nr_ents,
+ Protocol0->Front.sring);
+
+ // Dump shared ring
+ DEBUG(Printf,
+ Receiver->DebugInterface,
+ Receiver->DebugCallback,
+ "SHARED: req_prod = %u req_event = %u rsp_prod = %u rsp_event = %u\n",
+ Protocol0->Shared->req_prod,
+ Protocol0->Shared->req_event,
+ Protocol0->Shared->rsp_prod,
+ Protocol0->Shared->rsp_event);
+
+ DEBUG(Printf,
+ Receiver->DebugInterface,
+ Receiver->DebugCallback,
+ "RequestsPosted = %u RequestsPushed = %u ResponsesProcessed = %u\n",
+ Protocol0->RequestsPosted,
+ Protocol0->RequestsPushed,
+ Protocol0->ResponsesProcessed);
+}
+
+static DECLSPEC_NOINLINE NTSTATUS
+Protocol0Connect(
+ IN PRECEIVER_RING Ring
+ )
+{
+ PXENVIF_RECEIVER Receiver;
+ PXENVIF_FRONTEND Frontend;
+ PRECEIVER_RING_PROTOCOL0 Protocol0;
+ ULONG Index;
+ PFN_NUMBER Pfn;
+ NTSTATUS status;
+
+ Receiver = Ring->Receiver;
+ Frontend = Receiver->Frontend;
+
+ Ring->Mdl = __AllocatePage();
+
+ status = STATUS_NO_MEMORY;
+ if (Ring->Mdl == NULL)
+ goto fail1;
+
+ Protocol0 = &Ring->Protocol0;
+ ASSERT(IsZeroMemory(Protocol0, sizeof (RECEIVER_RING_PROTOCOL0)));
+
+ Protocol0->Shared = MmGetSystemAddressForMdlSafe(Ring->Mdl, NormalPagePriority);
+ ASSERT(Protocol0->Shared != NULL);
+
+ SHARED_RING_INIT(Protocol0->Shared);
+ FRONT_RING_INIT(&Protocol0->Front, Protocol0->Shared, PAGE_SIZE);
+ ASSERT3P(Protocol0->Front.sring, ==, Protocol0->Shared);
+
+ Receiver->GnttabInterface = FrontendGetGnttabInterface(Frontend);
+
+ GNTTAB(Acquire, Receiver->GnttabInterface);
+
+ status = GNTTAB(Get,
+ Receiver->GnttabInterface,
+ &Protocol0->Reference);
+ if (!NT_SUCCESS(status))
+ goto fail2;
+
+ Protocol0->HeadFreeTag = TAG_INDEX_INVALID;
+ for (Index = 0; Index < MAXIMUM_TAG_COUNT; Index++) {
+ PRECEIVER_TAG Tag = &Protocol0->Tag[Index];
+
+ status = GNTTAB(Get,
+ Receiver->GnttabInterface,
+ &Tag->Reference);
+ if (!NT_SUCCESS(status))
+ goto fail3;
+
+ Tag->Next = Protocol0->HeadFreeTag;
+ Protocol0->HeadFreeTag = Index;
+ }
+
+ Pfn = MmGetMdlPfnArray(Ring->Mdl)[0];
+
+ GNTTAB(PermitForeignAccess,
+ Receiver->GnttabInterface,
+ Protocol0->Reference,
+ FrontendGetBackendDomain(Frontend),
+ GNTTAB_ENTRY_FULL_PAGE,
+ Pfn,
+ FALSE);
+
+ return STATUS_SUCCESS;
+
+fail3:
+ Error("fail3\n");
+
+ while (Protocol0->HeadFreeTag != TAG_INDEX_INVALID) {
+ PRECEIVER_TAG Tag = &Protocol0->Tag[Protocol0->HeadFreeTag];
+
+ Protocol0->HeadFreeTag = Tag->Next;
+ Tag->Next = 0;
+
+ GNTTAB(Put,
+ Receiver->GnttabInterface,
+ Tag->Reference);
+ Tag->Reference = 0;
+ }
+ Protocol0->HeadFreeTag = 0;
+
+ GNTTAB(Put,
+ Receiver->GnttabInterface,
+ Protocol0->Reference);
+ Protocol0->Reference = 0;
+
+fail2:
+ Error("fail2\n");
+
+ GNTTAB(Release, Receiver->GnttabInterface);
+ Receiver->GnttabInterface = NULL;
+
+ RtlZeroMemory(&Protocol0->Front, sizeof (netif_rx_front_ring_t));
+ RtlZeroMemory(Protocol0->Shared, PAGE_SIZE);
+
+ Protocol0->Shared = NULL;
+ ASSERT(IsZeroMemory(Protocol0, sizeof (RECEIVER_RING_PROTOCOL0)));
+
+ __FreePage(Ring->Mdl);
+ Ring->Mdl = NULL;
+
+
+fail1:
+ Error("fail1 (%08x)\n", status);
+
+ return status;
+}
+
+static DECLSPEC_NOINLINE NTSTATUS
+Protocol0StoreWrite(
+ IN PRECEIVER_RING Ring,
+ IN PXENBUS_STORE_TRANSACTION Transaction
+ )
+{
+ PXENVIF_RECEIVER Receiver;
+ PXENVIF_FRONTEND Frontend;
+ PRECEIVER_RING_PROTOCOL0 Protocol0;
+ NTSTATUS status;
+
+ Receiver = Ring->Receiver;
+ Frontend = Receiver->Frontend;
+
+ Protocol0 = &Ring->Protocol0;
+
+ status = STORE(Printf,
+ Receiver->StoreInterface,
+ Transaction,
+ FrontendGetPath(Frontend),
+ "rx-ring-ref",
+ "%u",
+ Protocol0->Reference);
+
+ if (!NT_SUCCESS(status))
+ goto fail1;
+
+ return STATUS_SUCCESS;
+
+fail1:
+ Error("fail1 (%08x)\n", status);
+
+ return status;
+}
+
+static DECLSPEC_NOINLINE NTSTATUS
+Protocol0Enable(
+ IN PRECEIVER_RING Ring
+ )
+{
+ PXENVIF_RECEIVER Receiver;
+ PXENVIF_FRONTEND Frontend;
+ PRECEIVER_RING_PROTOCOL0 Protocol0;
+ NTSTATUS status;
+
+ Receiver = Ring->Receiver;
+ Frontend = Receiver->Frontend;
+
+ Protocol0 = &Ring->Protocol0;
+
+ Protocol0Fill(Ring);
+
+ status = STATUS_INSUFFICIENT_RESOURCES;
+ if (RING_FREE_REQUESTS(&Protocol0->Front) != 0)
+ goto fail1;
+
+ return STATUS_SUCCESS;
+
+fail1:
+ Error("fail1 (%08x)\n", status);
+
+ return status;
+}
+
+static FORCEINLINE ULONG
+Protocol0GetSize(
+ IN PRECEIVER_RING Ring
+ )
+{
+ UNREFERENCED_PARAMETER(Ring);
+
+ return PROTOCOL0_RING_SIZE;
+}
+
+static DECLSPEC_NOINLINE VOID
+Protocol0Disable(
+ IN PRECEIVER_RING Ring
+ )
+{
+ UNREFERENCED_PARAMETER(Ring);
+}
+
+static DECLSPEC_NOINLINE VOID
+Protocol0Disconnect(
+ IN PRECEIVER_RING Ring
+ )
+{
+ PXENVIF_RECEIVER Receiver;
+ PXENVIF_FRONTEND Frontend;
+ PRECEIVER_RING_PROTOCOL0 Protocol0;
+ ULONG Count;
+
+ Receiver = Ring->Receiver;
+ Frontend = Receiver->Frontend;
+
+ Protocol0 = &Ring->Protocol0;
+
+ __Protocol0Empty(Ring);
+
+ ASSERT3U(Protocol0->ResponsesProcessed, ==, Protocol0->RequestsPushed);
+ ASSERT3U(Protocol0->RequestsPushed, ==, Protocol0->RequestsPosted);
+
+ Protocol0->ResponsesProcessed = 0;
+ Protocol0->RequestsPushed = 0;
+ Protocol0->RequestsPosted = 0;
+
+ GNTTAB(RevokeForeignAccess,
+ Receiver->GnttabInterface,
+ Protocol0->Reference);
+
+ Count = 0;
+ while (Protocol0->HeadFreeTag != TAG_INDEX_INVALID) {
+ ULONG Index = Protocol0->HeadFreeTag;
+ PRECEIVER_TAG Tag = &Protocol0->Tag[Index];
+
+ Protocol0->HeadFreeTag = Tag->Next;
+ Tag->Next = 0;
+
+ GNTTAB(Put,
+ Receiver->GnttabInterface,
+ Tag->Reference);
+ Tag->Reference = 0;
+
+ Count++;
+ }
+ ASSERT3U(Count, ==, MAXIMUM_TAG_COUNT);
+
+ Protocol0->HeadFreeTag = 0;
+
+ GNTTAB(Put,
+ Receiver->GnttabInterface,
+ Protocol0->Reference);
+ Protocol0->Reference = 0;
+
+ GNTTAB(Release, Receiver->GnttabInterface);
+ Receiver->GnttabInterface = NULL;
+
+ RtlZeroMemory(&Protocol0->Front, sizeof (netif_rx_front_ring_t));
+ RtlZeroMemory(Protocol0->Shared, PAGE_SIZE);
+
+ Protocol0->Shared = NULL;
+ ASSERT(IsZeroMemory(Protocol0, sizeof (RECEIVER_RING_PROTOCOL0)));
+
+ __FreePage(Ring->Mdl);
+ Ring->Mdl = NULL;
+}
+
+static RECEIVER_RING_OPERATIONS Protocol0Operations = {
+ Protocol0Connect,
+ Protocol0StoreWrite,
+ Protocol0Enable,
+ Protocol0Poll,
+ Protocol0Disable,
+ Protocol0Disconnect,
+ Protocol0DebugCallback,
+ Protocol0GetSize
+};
+
+static DECLSPEC_NOINLINE NTSTATUS
+Protocol1Connect(
+ IN PRECEIVER_RING Ring
+ )
+{
+ PXENVIF_RECEIVER Receiver;
+ PXENVIF_FRONTEND Frontend;
+ PRECEIVER_RING_PROTOCOL1 Protocol1;
+ ULONG Index;
+ PFN_NUMBER Pfn;
+ NTSTATUS status;
+
+ Receiver = Ring->Receiver;
+ Frontend = Receiver->Frontend;
+
+ Ring->Mdl = __AllocatePage();
+
+ status = STATUS_NO_MEMORY;
+ if (Ring->Mdl == NULL)
+ goto fail1;
+
+ Protocol1 = &Ring->Protocol1;
+ ASSERT(IsZeroMemory(Protocol1, sizeof (RECEIVER_RING_PROTOCOL1)));
+
+ Protocol1->Shared = MmGetSystemAddressForMdlSafe(Ring->Mdl, NormalPagePriority);
+ ASSERT(Protocol1->Shared != NULL);
+
+ SHARED_RING_INIT(Protocol1->Shared);
+ BACK_RING_INIT(&Protocol1->Back, Protocol1->Shared, PAGE_SIZE);
+ ASSERT3P(Protocol1->Back.sring, ==, Protocol1->Shared);
+
+ Receiver->GnttabInterface = FrontendGetGnttabInterface(Frontend);
+
+ GNTTAB(Acquire, Receiver->GnttabInterface);
+
+ status = GNTTAB(Get,
+ Receiver->GnttabInterface,
+ &Protocol1->Reference);
+ if (!NT_SUCCESS(status))
+ goto fail2;
+
+ InitializeListHead(&Protocol1->List);
+ for (Index = 0; Index < MAXIMUM_OPERATION_COUNT; Index++) {
+ PXENBUS_GNTTAB_COPY_OPERATION Operation = &Protocol1->Operation[Index];
+
+ InsertTailList(&Protocol1->List, &Operation->ListEntry);
+ }
+
+ Pfn = (PFN_NUMBER)(MmGetPhysicalAddress(Protocol1->Shared).QuadPart >> PAGE_SHIFT);
+
+ GNTTAB(PermitForeignAccess,
+ Receiver->GnttabInterface,
+ Protocol1->Reference,
+ FrontendGetBackendDomain(Frontend),
+ GNTTAB_ENTRY_FULL_PAGE,
+ Pfn,
+ FALSE);
+
+ return STATUS_SUCCESS;
+
+fail2:
+ Error("fail2\n");
+
+ GNTTAB(Release, Receiver->GnttabInterface);
+ Receiver->GnttabInterface = NULL;
+
+ RtlZeroMemory(&Protocol1->Back, sizeof (netif_tx_back_ring_t));
+ RtlZeroMemory(Protocol1->Shared, PAGE_SIZE);
+
+ Protocol1->Shared = NULL;
+ ASSERT(IsZeroMemory(Protocol1, sizeof (RECEIVER_RING_PROTOCOL1)));
+
+ __FreePage(Ring->Mdl);
+ Ring->Mdl = NULL;
+
+fail1:
+ Error("fail1 (%08x)\n", status);
+
+ return status;
+}
+
+static FORCEINLINE PXENBUS_GNTTAB_COPY_OPERATION
+__Protocol1GetOperation(
+ IN PRECEIVER_RING Ring
+ )
+{
+ PRECEIVER_RING_PROTOCOL1 Protocol1;
+ PLIST_ENTRY ListEntry;
+ PXENBUS_GNTTAB_COPY_OPERATION Operation;
+
+ Protocol1 = &Ring->Protocol1;
+
+ ListEntry = RemoveHeadList(&Protocol1->List);
+ ASSERT3P(ListEntry, !=, &Protocol1->List);
+
+ RtlZeroMemory(ListEntry, sizeof (LIST_ENTRY));
+
+ Operation = CONTAINING_RECORD(ListEntry, XENBUS_GNTTAB_COPY_OPERATION, ListEntry);
+
+ return Operation;
+}
+
+static FORCEINLINE VOID
+__Protocol1PutOperation(
+ IN PRECEIVER_RING Ring,
+ IN PXENBUS_GNTTAB_COPY_OPERATION Operation
+ )
+{
+ PRECEIVER_RING_PROTOCOL1 Protocol1;
+
+ Protocol1 = &Ring->Protocol1;
+
+ ASSERT(IsZeroMemory(&Operation->ListEntry, sizeof (LIST_ENTRY)));
+
+ RtlZeroMemory(Operation, sizeof (XENBUS_GNTTAB_COPY_OPERATION));
+ InsertHeadList(&Protocol1->List, &Operation->ListEntry);
+}
+
+static DECLSPEC_NOINLINE NTSTATUS
+Protocol1Enable(
+ IN PRECEIVER_RING Ring
+ )
+{
+ UNREFERENCED_PARAMETER(Ring);
+
+ return STATUS_SUCCESS;
+}
+
+static FORCEINLINE ULONG
+Protocol1GetSize(
+ IN PRECEIVER_RING Ring
+ )
+{
+ UNREFERENCED_PARAMETER(Ring);
+
+ return PROTOCOL1_RING_SIZE;
+}
+
+static DECLSPEC_NOINLINE NTSTATUS
+Protocol1StoreWrite(
+ IN PRECEIVER_RING Ring,
+ IN PXENBUS_STORE_TRANSACTION Transaction
+ )
+{
+ PXENVIF_RECEIVER Receiver;
+ PXENVIF_FRONTEND Frontend;
+ PRECEIVER_RING_PROTOCOL1 Protocol1;
+ NTSTATUS status;
+
+ Receiver = Ring->Receiver;
+ Frontend = Receiver->Frontend;
+
+ Protocol1 = &Ring->Protocol1;
+
+ status = STORE(Printf,
+ Receiver->StoreInterface,
+ Transaction,
+ FrontendGetPath(Frontend),
+ "rx-ring-ref",
+ "%u",
+ Protocol1->Reference);
+
+ if (!NT_SUCCESS(status))
+ goto fail1;
+
+ return STATUS_SUCCESS;
+
+fail1:
+ Error("fail1 (%08x)\n", status);
+
+ return status;
+}
+
+static FORCEINLINE NTSTATUS
+__Protocol1BuildOperations(
+ IN PRECEIVER_RING Ring,
+ IN netif_tx_request_t *req,
+ IN ULONG nr_reqs,
+ OUT PXENVIF_RECEIVER_PACKET *Packet,
+ IN OUT PLIST_ENTRY List,
+ OUT PULONG Count
+ )
+{
+ PXENVIF_RECEIVER Receiver;
+ PXENVIF_FRONTEND Frontend;
+ ULONG Index;
+ ULONG Length;
+ PMDL Mdl;
+ ULONG MdlOffset;
+ ULONG MdlByteCount;
+ uint16_t flags;
+ netif_tx_request_t current;
+ PXENBUS_GNTTAB_COPY_OPERATION Operation;
+ NTSTATUS status;
+
+ ASSERT(req != NULL);
+ ASSERT(nr_reqs != 0);
+ ASSERT(~req[nr_reqs - 1].flags & NETTXF_more_data);
+
+ Receiver = Ring->Receiver;
+ Frontend = Receiver->Frontend;
+
+ // The first request specifies the total packet length. Sample it and
+ // then adjust it to match all other requests in specifying it's fragment
+ // size.
+ Length = req[0].size;
+ Index = 1;
+
+ if (req[0].flags & NETTXF_extra_info) {
+ struct netif_extra_info *extra;
+
+ ASSERT3U(nr_reqs, >=, 2);
+ extra = (struct netif_extra_info *)&req[1];
+
+ ASSERT3U(extra->type, ==, XEN_NETIF_EXTRA_TYPE_GSO);
+ ASSERT(extra->u.gso.type == XEN_NETIF_GSO_TYPE_TCPV4 ||
+ extra->u.gso.type == XEN_NETIF_GSO_TYPE_TCPV6);
+
+ Index++;
+ }
+
+ while (Index < nr_reqs) {
+ ASSERT3U(req[0].size, >, req[Index].size);
+ req[0].size = req[0].size - req[Index].size;
+
+ Index++;
+ }
+
+ *Packet = __RingGetPacket(Ring, TRUE);
+
+ status = STATUS_NO_MEMORY;
+ if (*Packet == NULL)
+ goto fail1;
+
+ (*Packet)->Offset = 0;
+ (*Packet)->Length = Length;
+
+ Mdl = &(*Packet)->Mdl;
+
+ ASSERT3P((*Packet)->Cookie, ==, NULL);
+
+ // Unfortunately NETTXF_... don't match NETRXF_...
+ flags = 0;
+ if (req[0].flags & NETTXF_csum_blank)
+ flags |= NETRXF_csum_blank;
+ if (req[0].flags & NETTXF_data_validated)
+ flags |= NETRXF_data_validated;
+
+ (*Packet)->Cookie = (PVOID)flags;
+
+ ASSERT3U(Mdl->ByteCount, ==, 0);
+
+ MdlOffset = 0;
+ MdlByteCount = PAGE_SIZE; // Remaining byte count
+
+ Index = 0;
+ *Count = 0;
+
+ current = req[Index];
+
+ while (Length != 0) {
+ USHORT SourceLength;
+ USHORT DestLength;
+ USHORT CopyLength;
+
+ if (current.size == 0) {
+ Index++;
+
+ if (current.flags & NETTXF_extra_info) {
+ struct netif_extra_info *extra;
+
+ ASSERT3U(Index, ==, 1);
+
+ extra = (struct netif_extra_info *)&req[Index];
+
+ ASSERT3U(extra->type, ==, XEN_NETIF_EXTRA_TYPE_GSO);
+
+ (*Packet)->MaximumSegmentSize = extra->u.gso.size;
+ ASSERT((*Packet)->MaximumSegmentSize != 0);
+
+ Index++;
+ }
+
+ current = req[Index];
+ }
+ ASSERT(current.size != 0);
+
+ if (MdlByteCount == 0) {
+ PXENVIF_RECEIVER_PACKET NextPacket;
+ PMDL NextMdl;
+
+ NextPacket = __RingGetPacket(Ring, TRUE);
+
+ status = STATUS_NO_MEMORY;
+ if (NextPacket == NULL)
+ goto fail2;
+
+ NextMdl = &NextPacket->Mdl;
+
+ ASSERT3P(Mdl->Next, ==, NULL);
+ Mdl->Next = NextMdl;
+ Mdl = NextMdl;
+
+ MdlOffset = 0;
+ MdlByteCount = PAGE_SIZE;
+ }
+
+ Operation = __Protocol1GetOperation(Ring);
+
+ Operation->RemoteDomain = FrontendGetBackendDomain(Frontend);
+ Operation->RemoteReference = current.gref;
+ Operation->RemoteOffset = current.offset;
+
+ SourceLength = current.size;
+
+ ASSERT3U(MdlOffset, <, PAGE_SIZE);
+ Operation->Pfn = MmGetMdlPfnArray(Mdl)[0];
+ Operation->Offset = (uint16_t)MdlOffset;
+
+ DestLength = (USHORT)__min(MdlByteCount, PAGE_SIZE - MdlOffset);
+
+ CopyLength = __min(SourceLength, DestLength);
+
+ Operation->Length = CopyLength;
+
+ Mdl->ByteCount += CopyLength;
+
+ current.offset = current.offset + CopyLength;
+ ASSERT3U(current.size, >=, CopyLength);
+ current.size = current.size - CopyLength;
+
+ MdlOffset += CopyLength;
+ ASSERT3U(MdlByteCount, >=, CopyLength);
+ MdlByteCount -= CopyLength;
+
+ ASSERT3U(Length, >=, CopyLength);
+ Length -= CopyLength;
+
+ ASSERT(IsZeroMemory(&Operation->ListEntry, sizeof (LIST_ENTRY)));
+ InsertTailList(List, &Operation->ListEntry);
+ (*Count)++;
+ }
+ ASSERT3U(current.size, ==, 0);
+ ASSERT(IMPLY(current.flags & NETTXF_extra_info, Index == nr_reqs - 2));
+ ASSERT(IMPLY(~current.flags & NETTXF_extra_info, Index == nr_reqs - 1));
+
+ return STATUS_SUCCESS;
+
+fail2:
+ Error("fail2\n");
+
+ (*Packet)->Cookie = NULL;
+ __RingReturnPacket(Ring, *Packet, TRUE);
+
+fail1:
+ Error("fail1 (%08x)\n", status);
+
+ return status;
+}
+
+static FORCEINLINE VOID
+__Protocol1DisposeOperations(
+ IN PRECEIVER_RING Ring,
+ IN OUT PLIST_ENTRY List,
+ IN ULONG Count
+ )
+{
+ while (!IsListEmpty(List)) {
+ PLIST_ENTRY ListEntry;
+ PXENBUS_GNTTAB_COPY_OPERATION Operation;
+
+ ListEntry = RemoveTailList(List);
+ ASSERT3P(ListEntry, !=, List);
+
+ --Count;
+
+ RtlZeroMemory(ListEntry, sizeof (LIST_ENTRY));
+
+ Operation = CONTAINING_RECORD(ListEntry, XENBUS_GNTTAB_COPY_OPERATION, ListEntry);
+
+ __Protocol1PutOperation(Ring, Operation);
+ }
+ ASSERT3U(Count, ==, 0);
+}
+
+static FORCEINLINE VOID
+__Protocol1PostResponses(
+ IN PRECEIVER_RING Ring,
+ IN netif_tx_request_t *req,
+ IN ULONG nr_reqs,
+ IN NTSTATUS status
+ )
+{
+ PRECEIVER_RING_PROTOCOL1 Protocol1;
+ RING_IDX rsp_prod;
+
+ Protocol1 = &Ring->Protocol1;
+
+ rsp_prod = Protocol1->Back.rsp_prod_pvt;
+
+ while (nr_reqs != 0) {
+ netif_tx_response_t *rsp;
+
+ rsp = RING_GET_RESPONSE(&Protocol1->Back, rsp_prod);
+ rsp_prod++;
+ Protocol1->ResponsesPosted++;
+
+ rsp->id = req->id;
+ rsp->status = NT_SUCCESS(status) ? NETIF_RSP_OKAY : NETIF_RSP_ERROR;
+
+ if (req->flags & NETTXF_extra_info) {
+ rsp = RING_GET_RESPONSE(&Protocol1->Back, rsp_prod);
+ rsp_prod++;
+ Protocol1->ResponsesPosted++;
+
+ rsp->status = NETIF_RSP_NULL;
+
+ req++;
+ --nr_reqs;
+ }
+
+ req++;
+ --nr_reqs;
+ }
+
+ Protocol1->Back.rsp_prod_pvt = rsp_prod;
+}
+
+static FORCEINLINE VOID
+__Protocol1PushResponses(
+ IN PRECEIVER_RING Ring
+ )
+{
+ PRECEIVER_RING_PROTOCOL1 Protocol1;
+ BOOLEAN Notify;
+
+ Protocol1 = &Ring->Protocol1;
+
+ if (Protocol1->ResponsesPosted == Protocol1->ResponsesPushed)
+ return;
+
+#pragma warning (push)
+#pragma warning (disable:4244)
+
+ // Make the responses visible to the backend
+ RING_PUSH_RESPONSES_AND_CHECK_NOTIFY(&Protocol1->Back, Notify);
+
+#pragma warning (pop)
+
+ if (Notify) {
+ PXENVIF_RECEIVER Receiver;
+ PXENVIF_FRONTEND Frontend;
+
+ Receiver = Ring->Receiver;
+ Frontend = Receiver->Frontend;
+
+ NotifierSend(FrontendGetNotifier(Frontend));
+ }
+
+ Protocol1->ResponsesPushed = Protocol1->ResponsesPosted;
+}
+
+static DECLSPEC_NOINLINE VOID
+Protocol1Poll(
+ IN PRECEIVER_RING Ring
+ )
+{
+ PXENVIF_RECEIVER Receiver;
+ PRECEIVER_RING_PROTOCOL1 Protocol1;
+
+ Receiver = Ring->Receiver;
+
+ Protocol1 = &Ring->Protocol1;
+
+ for (;;) {
+ RING_IDX req_prod;
+ RING_IDX req_cons;
+ RING_IDX rsp_prod;
+
+ if (__RingIsStopped(Ring))
+ break;
+
+ KeMemoryBarrier();
+
+ req_prod = Protocol1->Shared->req_prod;
+ req_cons = Protocol1->Back.req_cons;
+
+ KeMemoryBarrier();
+
+ if (req_cons == req_prod)
+ break;
+
+ while (req_cons != req_prod) {
+ netif_tx_request_t req[MAX_SKB_FRAGS + 2];
+ ULONG nr_reqs;
+ PXENVIF_RECEIVER_PACKET Packet;
+ LIST_ENTRY List;
+ ULONG Count;
+ NTSTATUS status;
+
+ RtlZeroMemory(req, sizeof (req));
+
+ nr_reqs = 0;
+ for (;;) {
+ uint16_t flags;
+
+ ASSERT3U(nr_reqs, <, MAX_SKB_FRAGS + 2);
+
+ req[nr_reqs] = *RING_GET_REQUEST(&Protocol1->Back, req_cons + nr_reqs);
+
+ flags = req[nr_reqs].flags;
+ nr_reqs++;
+
+ if (flags & NETTXF_extra_info) {
+ req[nr_reqs] = *RING_GET_REQUEST(&Protocol1->Back, req_cons + nr_reqs);
+
+ nr_reqs++;
+ }
+
+ if (~flags & NETTXF_more_data)
+ break;
+ }
+
+ InitializeListHead(&List);
+
+ status = __Protocol1BuildOperations(Ring, req, nr_reqs, &Packet, &List, &Count);
+ if (!NT_SUCCESS(status)) {
+ __RingStop(Ring);
+ break;
+ }
+
+ req_cons += nr_reqs;
+ Protocol1->RequestsProcessed += nr_reqs;
+
+ Protocol1->Back.req_cons = req_cons;
+
+ status = GNTTAB(Copy,
+ Receiver->GnttabInterface,
+ &List,
+ Count);
+
+ __Protocol1DisposeOperations(Ring, &List, Count);
+
+ if (NT_SUCCESS(status)) {
+ ASSERT(IsZeroMemory(&Packet->ListEntry, sizeof (LIST_ENTRY)));
+ InsertTailList(&Ring->PacketList, &Packet->ListEntry);
+ } else {
+ Ring->PacketStatistics.BackendError++;
+
+ Packet->Cookie = NULL;
+ __RingReturnPacket(Ring, Packet, TRUE);
+ }
+
+ __Protocol1PostResponses(Ring,
+ req,
+ nr_reqs,
+ status);
+
+ rsp_prod = Protocol1->Back.rsp_prod_pvt;
+ ASSERT3U(rsp_prod, ==, req_cons);
+
+ KeMemoryBarrier();
+ }
+
+ Protocol1->Shared->req_event = req_cons + 1;
+
+ KeMemoryBarrier();
+
+ __Protocol1PushResponses(Ring);
+ }
+}
+
+static DECLSPEC_NOINLINE VOID
+Protocol1Disable(
+ IN PRECEIVER_RING Ring
+ )
+{
+ UNREFERENCED_PARAMETER(Ring);
+}
+
+static DECLSPEC_NOINLINE VOID
+Protocol1Disconnect(
+ IN PRECEIVER_RING Ring
+ )
+{
+ PXENVIF_RECEIVER Receiver;
+ PRECEIVER_RING_PROTOCOL1 Protocol1;
+ ULONG Count;
+
+ Receiver = Ring->Receiver;
+
+ Protocol1 = &Ring->Protocol1;
+
+ ASSERT3U(Protocol1->ResponsesPushed, ==, Protocol1->ResponsesPosted);
+ ASSERT3U(Protocol1->ResponsesPosted, ==, Protocol1->RequestsProcessed);
+
+ Protocol1->RequestsProcessed = 0;
+ Protocol1->ResponsesPushed = 0;
+ Protocol1->ResponsesPosted = 0;
+
+ GNTTAB(RevokeForeignAccess,
+ Receiver->GnttabInterface,
+ Protocol1->Reference);
+
+ Count = MAXIMUM_OPERATION_COUNT;
+ while (!IsListEmpty(&Protocol1->List)) {
+ PLIST_ENTRY ListEntry;
+
+ ListEntry = RemoveHeadList(&Protocol1->List);
+ ASSERT3P(ListEntry, !=, &Protocol1->List);
+
+ --Count;
+
+ RtlZeroMemory(ListEntry, sizeof (LIST_ENTRY));
+ }
+ ASSERT3U(Count, ==, 0);
+
+ RtlZeroMemory(&Protocol1->List, sizeof (LIST_ENTRY));
+
+ GNTTAB(Put,
+ Receiver->GnttabInterface,
+ Protocol1->Reference);
+ Protocol1->Reference = 0;
+
+ GNTTAB(Release, Receiver->GnttabInterface);
+ Receiver->GnttabInterface = NULL;
+
+ RtlZeroMemory(&Protocol1->Back, sizeof (netif_tx_back_ring_t));
+ RtlZeroMemory(Protocol1->Shared, PAGE_SIZE);
+
+ Protocol1->Shared = NULL;
+ ASSERT(IsZeroMemory(Protocol1, sizeof (RECEIVER_RING_PROTOCOL1)));
+
+ __FreePage(Ring->Mdl);
+ Ring->Mdl = NULL;
+}
+
+static DECLSPEC_NOINLINE VOID
+Protocol1DebugCallback(
+ IN PRECEIVER_RING Ring
+ )
+{
+ PXENVIF_RECEIVER Receiver;
+ PRECEIVER_RING_PROTOCOL1 Protocol1;
+
+ Receiver = Ring->Receiver;
+ Protocol1 = &Ring->Protocol1;
+
+ // Dump back ring
+ DEBUG(Printf,
+ Receiver->DebugInterface,
+ Receiver->DebugCallback,
+ "BACK: rsp_prod_pvt = %u req_cons = %u nr_ents = %u sring = %p\n",
+ Protocol1->Back.rsp_prod_pvt,
+ Protocol1->Back.req_cons,
+ Protocol1->Back.nr_ents,
+ Protocol1->Back.sring);
+
+ // Dump shared ring
+ DEBUG(Printf,
+ Receiver->DebugInterface,
+ Receiver->DebugCallback,
+ "SHARED: req_prod = %u req_event = %u rsp_prod = %u rsp_event = %u\n",
+ Protocol1->Shared->req_prod,
+ Protocol1->Shared->req_event,
+ Protocol1->Shared->rsp_prod,
+ Protocol1->Shared->rsp_event);
+
+ DEBUG(Printf,
+ Receiver->DebugInterface,
+ Receiver->DebugCallback,
+ "RequestsProcessed = %u ResponsesPosted = %u ResponsesPushed = %u\n",
+ Protocol1->RequestsProcessed,
+ Protocol1->ResponsesPosted,
+ Protocol1->ResponsesPushed);
+}
+
+static RECEIVER_RING_OPERATIONS Protocol1Operations = {
+ Protocol1Connect,
+ Protocol1StoreWrite,
+ Protocol1Enable,
+ Protocol1Poll,
+ Protocol1Disable,
+ Protocol1Disconnect,
+ Protocol1DebugCallback,
+ Protocol1GetSize
+};
+
+static PRECEIVER_RING_OPERATIONS RingOperations[] = {
+ &Protocol0Operations,
+ &Protocol1Operations
+};
+
+#define SUPPORTED_PROTOCOLS \
+ (sizeof (RingOperations) / sizeof (RingOperations[0]))
+
+C_ASSERT((RECEIVER_MINIMUM_PROTOCOL + SUPPORTED_PROTOCOLS - 1) == RECEIVER_MAXIMUM_PROTOCOL);
+
+static FORCEINLINE VOID
+__RingDebugCallback(
+ IN PRECEIVER_RING Ring
+ )
+{
+ PXENVIF_RECEIVER Receiver;
+ ULONG Allocated;
+ ULONG MaximumAllocated;
+ ULONG Count;
+ ULONG MinimumCount;
+ PRECEIVER_RING_OPERATIONS Operations;
+
+ Receiver = Ring->Receiver;
+
+ DEBUG(Printf,
+ Receiver->DebugInterface,
+ Receiver->DebugCallback,
+ "0x%p [%s][%s]\n",
+ Ring,
+ (Ring->Enabled) ? "ENABLED" : "DISABLED",
+ (__RingIsStopped(Ring)) ? "STOPPED" : "RUNNING");
+
+ PoolGetStatistics(Ring->PacketPool,
+ &Allocated,
+ &MaximumAllocated,
+ &Count,
+ &MinimumCount);
+
+ DEBUG(Printf,
+ Receiver->DebugInterface,
+ Receiver->DebugCallback,
+ "PACKET POOL: Allocated = %u (Maximum = %u)\n",
+ Allocated,
+ MaximumAllocated);
+
+ DEBUG(Printf,
+ Receiver->DebugInterface,
+ Receiver->DebugCallback,
+ "PACKET POOL: Count = %u (Minimum = %u)\n",
+ Count,
+ MinimumCount);
+
+ Operations = Ring->Operations;
+ if (Operations != NULL)
+ Operations->DebugCallback(Ring);
+
+ DEBUG(Printf,
+ Receiver->DebugInterface,
+ Receiver->DebugCallback,
+ "OffloadOptions = %02x\n",
+ Ring->OffloadOptions.Value);
+
+ if (Ring->OffloadOptions.OffloadTagManipulation)
+ DEBUG(Printf,
+ Receiver->DebugInterface,
+ Receiver->DebugCallback,
+ "- TAG MANIPULATION\n");
+
+ if (Ring->OffloadOptions.OffloadIpVersion4LargePacket)
+ DEBUG(Printf,
+ Receiver->DebugInterface,
+ Receiver->DebugCallback,
+ "- IPV4 LARGE PACKET\n");
+
+ if (Ring->OffloadOptions.OffloadIpVersion6LargePacket)
+ DEBUG(Printf,
+ Receiver->DebugInterface,
+ Receiver->DebugCallback,
+ "- IPV6 LARGE PACKET\n");
+
+ if (Ring->OffloadOptions.OffloadIpVersion4HeaderChecksum)
+ DEBUG(Printf,
+ Receiver->DebugInterface,
+ Receiver->DebugCallback,
+ "- IPV4 HEADER CHECKSUM\n");
+
+ if (Ring->OffloadOptions.OffloadIpVersion4TcpChecksum)
+ DEBUG(Printf,
+ Receiver->DebugInterface,
+ Receiver->DebugCallback,
+ "- IPV4 TCP CHECKSUM\n");
+
+ if (Ring->OffloadOptions.OffloadIpVersion4UdpChecksum)
+ DEBUG(Printf,
+ Receiver->DebugInterface,
+ Receiver->DebugCallback,
+ "- IPV4 UDP CHECKSUM\n");
+
+ if (Ring->OffloadOptions.OffloadIpVersion6TcpChecksum)
+ DEBUG(Printf,
+ Receiver->DebugInterface,
+ Receiver->DebugCallback,
+ "- IPV6 TCP CHECKSUM\n");
+
+ if (Ring->OffloadOptions.OffloadIpVersion6UdpChecksum)
+ DEBUG(Printf,
+ Receiver->DebugInterface,
+ Receiver->DebugCallback,
+ "- IPV6 UDP CHECKSUM\n");
+
+ if (Ring->OffloadOptions.NeedChecksumValue)
+ DEBUG(Printf,
+ Receiver->DebugInterface,
+ Receiver->DebugCallback,
+ "- NEED CHECKSUM VALUE\n");
+
+ DEBUG(Printf,
+ Receiver->DebugInterface,
+ Receiver->DebugCallback,
+ "PacketStatistics:\n");
+
+ DEBUG(Printf,
+ Receiver->DebugInterface,
+ Receiver->DebugCallback,
+ "- Drop = %u\n",
+ Ring->PacketStatistics.Drop);
+
+ DEBUG(Printf,
+ Receiver->DebugInterface,
+ Receiver->DebugCallback,
+ "- BackendError = %u\n",
+ Ring->PacketStatistics.BackendError);
+
+ DEBUG(Printf,
+ Receiver->DebugInterface,
+ Receiver->DebugCallback,
+ "- FrontendError = %u\n",
+ Ring->PacketStatistics.FrontendError);
+
+ DEBUG(Printf,
+ Receiver->DebugInterface,
+ Receiver->DebugCallback,
+ "- Unicast = %u\n",
+ Ring->PacketStatistics.Unicast);
+
+ DEBUG(Printf,
+ Receiver->DebugInterface,
+ Receiver->DebugCallback,
+ "- UnicastBytes = %u\n",
+ Ring->PacketStatistics.UnicastBytes);
+
+ DEBUG(Printf,
+ Receiver->DebugInterface,
+ Receiver->DebugCallback,
+ "- Multicast = %u\n",
+ Ring->PacketStatistics.Multicast);
+
+ DEBUG(Printf,
+ Receiver->DebugInterface,
+ Receiver->DebugCallback,
+ "- MulticastBytes = %u\n",
+ Ring->PacketStatistics.MulticastBytes);
+
+ DEBUG(Printf,
+ Receiver->DebugInterface,
+ Receiver->DebugCallback,
+ "- Broadcast = %u\n",
+ Ring->PacketStatistics.Broadcast);
+
+ DEBUG(Printf,
+ Receiver->DebugInterface,
+ Receiver->DebugCallback,
+ "- BroadcastBytes = %u\n",
+ Ring->PacketStatistics.BroadcastBytes);
+
+ DEBUG(Printf,
+ Receiver->DebugInterface,
+ Receiver->DebugCallback,
+ "HeaderStatistics:\n");
+
+ DEBUG(Printf,
+ Receiver->DebugInterface,
+ Receiver->DebugCallback,
+ "- Tagged = %u\n",
+ Ring->HeaderStatistics.Tagged);
+
+ DEBUG(Printf,
+ Receiver->DebugInterface,
+ Receiver->DebugCallback,
+ "- LLC = %u\n",
+ Ring->HeaderStatistics.LLC);
+
+ DEBUG(Printf,
+ Receiver->DebugInterface,
+ Receiver->DebugCallback,
+ "- Ip Version4 = %u\n",
+ Ring->HeaderStatistics.IpVersion4);
+
+ DEBUG(Printf,
+ Receiver->DebugInterface,
+ Receiver->DebugCallback,
+ "- Ip Version6 = %u\n",
+ Ring->HeaderStatistics.IpVersion6);
+
+ DEBUG(Printf,
+ Receiver->DebugInterface,
+ Receiver->DebugCallback,
+ "- Ip Options = %u\n",
+ Ring->HeaderStatistics.IpOptions);
+
+ DEBUG(Printf,
+ Receiver->DebugInterface,
+ Receiver->DebugCallback,
+ "- Tcp = %u\n",
+ Ring->HeaderStatistics.Tcp);
+
+ DEBUG(Printf,
+ Receiver->DebugInterface,
+ Receiver->DebugCallback,
+ "- Tcp Options = %u\n",
+ Ring->HeaderStatistics.TcpOptions);
+
+ DEBUG(Printf,
+ Receiver->DebugInterface,
+ Receiver->DebugCallback,
+ "- Udp = %u\n",
+ Ring->HeaderStatistics.Udp);
+
+ DEBUG(Printf,
+ Receiver->DebugInterface,
+ Receiver->DebugCallback,
+ "OffloadStatistics:\n");
+
+ DEBUG(Printf,
+ Receiver->DebugInterface,
+ Receiver->DebugCallback,
+ "- IpVersion4LargePacketSegment = %u\n",
+ Ring->OffloadStatistics.IpVersion4LargePacketSegment);
+
+ DEBUG(Printf,
+ Receiver->DebugInterface,
+ Receiver->DebugCallback,
+ "- IpVersion6LargePacketSegment = %u\n",
+ Ring->OffloadStatistics.IpVersion6LargePacketSegment);
+
+ DEBUG(Printf,
+ Receiver->DebugInterface,
+ Receiver->DebugCallback,
+ "- IpVersion4HeaderChecksumCalculated = %u\n",
+ Ring->OffloadStatistics.IpVersion4HeaderChecksumCalculated);
+
+ DEBUG(Printf,
+ Receiver->DebugInterface,
+ Receiver->DebugCallback,
+ "- IpVersion4HeaderChecksumSucceeded = %u\n",
+ Ring->OffloadStatistics.IpVersion4HeaderChecksumSucceeded);
+
+ DEBUG(Printf,
+ Receiver->DebugInterface,
+ Receiver->DebugCallback,
+ "- IpVersion4HeaderChecksumFailed = %u\n",
+ Ring->OffloadStatistics.IpVersion4HeaderChecksumFailed);
+
+ DEBUG(Printf,
+ Receiver->DebugInterface,
+ Receiver->DebugCallback,
+ "- IpVersion4HeaderChecksumPresent = %u\n",
+ Ring->OffloadStatistics.IpVersion4HeaderChecksumPresent);
+
+ DEBUG(Printf,
+ Receiver->DebugInterface,
+ Receiver->DebugCallback,
+ "- IpVersion4TcpChecksumCalculated = %u\n",
+ Ring->OffloadStatistics.IpVersion4TcpChecksumCalculated);
+
+ DEBUG(Printf,
+ Receiver->DebugInterface,
+ Receiver->DebugCallback,
+ "- IpVersion4TcpChecksumSucceeded = %u\n",
+ Ring->OffloadStatistics.IpVersion4TcpChecksumSucceeded);
+
+ DEBUG(Printf,
+ Receiver->DebugInterface,
+ Receiver->DebugCallback,
+ "- IpVersion4TcpChecksumFailed = %u\n",
+ Ring->OffloadStatistics.IpVersion4TcpChecksumFailed);
+
+ DEBUG(Printf,
+ Receiver->DebugInterface,
+ Receiver->DebugCallback,
+ "- IpVersion4TcpChecksumPresent = %u\n",
+ Ring->OffloadStatistics.IpVersion4TcpChecksumPresent);
+
+ DEBUG(Printf,
+ Receiver->DebugInterface,
+ Receiver->DebugCallback,
+ "- IpVersion6TcpChecksumCalculated = %u\n",
+ Ring->OffloadStatistics.IpVersion6TcpChecksumCalculated);
+
+ DEBUG(Printf,
+ Receiver->DebugInterface,
+ Receiver->DebugCallback,
+ "- IpVersion6TcpChecksumSucceeded = %u\n",
+ Ring->OffloadStatistics.IpVersion6TcpChecksumSucceeded);
+
+ DEBUG(Printf,
+ Receiver->DebugInterface,
+ Receiver->DebugCallback,
+ "- IpVersion6TcpChecksumFailed = %u\n",
+ Ring->OffloadStatistics.IpVersion6TcpChecksumFailed);
+
+ DEBUG(Printf,
+ Receiver->DebugInterface,
+ Receiver->DebugCallback,
+ "- IpVersion6TcpChecksumPresent = %u\n",
+ Ring->OffloadStatistics.IpVersion6TcpChecksumPresent);
+
+ DEBUG(Printf,
+ Receiver->DebugInterface,
+ Receiver->DebugCallback,
+ "- IpVersion4UdpChecksumCalculated = %u\n",
+ Ring->OffloadStatistics.IpVersion4UdpChecksumCalculated);
+
+ DEBUG(Printf,
+ Receiver->DebugInterface,
+ Receiver->DebugCallback,
+ "- IpVersion4UdpChecksumSucceeded = %u\n",
+ Ring->OffloadStatistics.IpVersion4UdpChecksumSucceeded);
+
+ DEBUG(Printf,
+ Receiver->DebugInterface,
+ Receiver->DebugCallback,
+ "- IpVersion4UdpChecksumFailed = %u\n",
+ Ring->OffloadStatistics.IpVersion4UdpChecksumFailed);
+
+ DEBUG(Printf,
+ Receiver->DebugInterface,
+ Receiver->DebugCallback,
+ "- IpVersion4UdpChecksumPresent = %u\n",
+ Ring->OffloadStatistics.IpVersion4UdpChecksumPresent);
+
+ DEBUG(Printf,
+ Receiver->DebugInterface,
+ Receiver->DebugCallback,
+ "- IpVersion6UdpChecksumCalculated = %u\n",
+ Ring->OffloadStatistics.IpVersion6UdpChecksumCalculated);
+
+ DEBUG(Printf,
+ Receiver->DebugInterface,
+ Receiver->DebugCallback,
+ "- IpVersion6UdpChecksumSucceeded = %u\n",
+ Ring->OffloadStatistics.IpVersion6UdpChecksumSucceeded);
+
+ DEBUG(Printf,
+ Receiver->DebugInterface,
+ Receiver->DebugCallback,
+ "- IpVersion6UdpChecksumFailed = %u\n",
+ Ring->OffloadStatistics.IpVersion6UdpChecksumFailed);
+
+ DEBUG(Printf,
+ Receiver->DebugInterface,
+ Receiver->DebugCallback,
+ "- IpVersion6UdpChecksumPresent = %u\n",
+ Ring->OffloadStatistics.IpVersion6UdpChecksumPresent);
+
+ DEBUG(Printf,
+ Receiver->DebugInterface,
+ Receiver->DebugCallback,
+ "- TagRemoved = %u\n",
+ Ring->OffloadStatistics.TagRemoved);
+}
+
+static FORCEINLINE NTSTATUS
+__RingInitialize(
+ IN PXENVIF_RECEIVER Receiver,
+ OUT PRECEIVER_RING *Ring
+ )
+{
+ NTSTATUS status;
+
+ *Ring = __ReceiverAllocate(sizeof (RECEIVER_RING));
+
+ status = STATUS_NO_MEMORY;
+ if (Ring == NULL)
+ goto fail1;
+
+ KeInitializeSpinLock(&(*Ring)->Lock);
+
+ status = PoolInitialize("ReceiverPacket",
+ sizeof (XENVIF_RECEIVER_PACKET),
+ ReceiverPacketCtor,
+ ReceiverPacketDtor,
+ RingAcquireLock,
+ RingReleaseLock,
+ *Ring,
+ &(*Ring)->PacketPool);
+ if (!NT_SUCCESS(status))
+ goto fail2;
+
+ InitializeListHead(&(*Ring)->PacketList);
+
+ (*Ring)->Receiver = Receiver;
+
+ return STATUS_SUCCESS;
+
+fail2:
+ RtlZeroMemory(&(*Ring)->Lock, sizeof (KSPIN_LOCK));
+
+ ASSERT(IsZeroMemory(*Ring, sizeof (RECEIVER_RING)));
+ __ReceiverFree(*Ring);
+ *Ring = NULL;
+
+fail1:
+ Error("fail1 (%08x)\n", status);
+
+ return status;
+}
+
+static FORCEINLINE NTSTATUS
+__RingConnect(
+ IN PRECEIVER_RING Ring
+ )
+{
+ PXENVIF_RECEIVER Receiver;
+ PRECEIVER_RING_OPERATIONS Operations;
+ NTSTATUS status;
+
+ Receiver = Ring->Receiver;
+
+ Info("Protocol %d\n", Receiver->Protocol);
+
+ ASSERT3P(Ring->Operations, ==, NULL);
+ ASSERT3U(Receiver->Protocol - RECEIVER_MINIMUM_PROTOCOL, <=, SUPPORTED_PROTOCOLS);
+ Ring->Operations = Operations = RingOperations[Receiver->Protocol - RECEIVER_MINIMUM_PROTOCOL];
+ ASSERT(Operations != NULL);
+
+ status = Operations->Connect(Ring);
+ if (!NT_SUCCESS(status))
+ goto fail1;
+
+ return STATUS_SUCCESS;
+
+fail1:
+ Error("fail1 (%08x)\n", status);
+
+ Ring->Operations = NULL;
+
+ return status;
+}
+
+static FORCEINLINE NTSTATUS
+__RingStoreWrite(
+ IN PRECEIVER_RING Ring,
+ IN PXENBUS_STORE_TRANSACTION Transaction
+ )
+{
+ PRECEIVER_RING_OPERATIONS Operations;
+ NTSTATUS status;
+
+ Operations = Ring->Operations;
+ ASSERT(Operations != NULL);
+
+ status = Operations->StoreWrite(Ring, Transaction);
+ if (!NT_SUCCESS(status))
+ goto fail1;
+
+ return STATUS_SUCCESS;
+
+fail1:
+ Error("fail1 (%08x)\n", status);
+
+ return status;
+}
+
+static FORCEINLINE NTSTATUS
+__RingEnable(
+ IN PRECEIVER_RING Ring
+ )
+{
+ PRECEIVER_RING_OPERATIONS Operations;
+ NTSTATUS status;
+
+ __RingAcquireLock(Ring);
+
+ ASSERT(!Ring->Enabled);
+
+ Operations = Ring->Operations;
+ ASSERT(Operations != NULL);
+
+ status = Operations->Enable(Ring);
+ if (!NT_SUCCESS(status))
+ goto fail1;
+
+ Ring->Enabled = TRUE;
+
+ __RingReleaseLock(Ring);
+
+ return STATUS_SUCCESS;
+
+fail1:
+ Error("fail1 (%08x)\n", status);
+
+ __RingReleaseLock(Ring);
+
+ return status;
+}
+
+static FORCEINLINE ULONG
+__RingGetSize(
+ IN PRECEIVER_RING Ring
+ )
+{
+ PRECEIVER_RING_OPERATIONS Operations;
+
+ Operations = Ring->Operations;
+ ASSERT(Operations != NULL);
+
+ return Operations->GetSize(Ring);
+}
+
+static FORCEINLINE VOID
+__RingPoll(
+ IN PRECEIVER_RING Ring
+ )
+{
+ PRECEIVER_RING_OPERATIONS Operations;
+
+ if (!(Ring->Enabled))
+ return;
+
+ Operations = Ring->Operations;
+ ASSERT(Operations != NULL);
+
+ Operations->Poll(Ring);
+}
+
+static FORCEINLINE VOID
+__RingDisable(
+ IN PRECEIVER_RING Ring
+ )
+{
+ PRECEIVER_RING_OPERATIONS Operations;
+
+ __RingAcquireLock(Ring);
+
+ ASSERT(Ring->Enabled);
+
+ Ring->Enabled = FALSE;
+ Ring->Stopped = FALSE;
+
+ Operations = Ring->Operations;
+ ASSERT(Operations != NULL);
+
+ Operations->Disable(Ring);
+
+ __RingReleaseLock(Ring);
+}
+
+static FORCEINLINE VOID
+__RingDisconnect(
+ IN PRECEIVER_RING Ring
+ )
+{
+ PRECEIVER_RING_OPERATIONS Operations;
+
+ Operations = Ring->Operations;
+ ASSERT(Operations != NULL);
+
+ Operations->Disconnect(Ring);
+
+ Ring->Operations = NULL;
+}
+
+static FORCEINLINE VOID
+__RingTeardown(
+ IN PRECEIVER_RING Ring
+ )
+{
+ Ring->Receiver = NULL;
+
+ Ring->OffloadOptions.Value = 0;
+
+ RtlZeroMemory(&Ring->HeaderStatistics, sizeof (XENVIF_HEADER_STATISTICS));
+ RtlZeroMemory(&Ring->OffloadStatistics, sizeof (RECEIVER_OFFLOAD_STATISTICS));
+ RtlZeroMemory(&Ring->PacketStatistics, sizeof (XENVIF_RECEIVER_PACKET_STATISTICS));
+
+ ASSERT(IsListEmpty(&Ring->PacketList));
+ RtlZeroMemory(&Ring->PacketList, sizeof (LIST_ENTRY));
+
+ PoolTeardown(Ring->PacketPool);
+ Ring->PacketPool = NULL;
+
+ RtlZeroMemory(&Ring->Lock, sizeof (KSPIN_LOCK));
+
+ ASSERT(IsZeroMemory(Ring, sizeof (RECEIVER_RING)));
+ __ReceiverFree(Ring);
+}
+
+static FORCEINLINE VOID
+__RingNotify(
+ IN PRECEIVER_RING Ring
+ )
+{
+ PXENVIF_RECEIVER Receiver;
+ PXENVIF_FRONTEND Frontend;
+ LIST_ENTRY List;
+ ULONG Count;
+
+ Receiver = Ring->Receiver;
+ Frontend = Receiver->Frontend;
+
+ InitializeListHead(&List);
+ Count = 0;
+
+ __RingAcquireLock(Ring);
+
+ __RingPoll(Ring);
+
+ RingProcessPackets(Ring, &List, &Count);
+ ASSERT(EQUIV(IsListEmpty(&List), Count == 0));
+ ASSERT(IsListEmpty(&Ring->PacketList));
+
+ // We need to bump Loaned before dropping the lock to avoid VifDisable()
+ // returning prematurely.
+ if (!IsListEmpty(&List))
+ __InterlockedAdd(&Receiver->Loaned, Count);
+
+ __RingReleaseLock(Ring);
+
+ if (!IsListEmpty(&List))
+ VifReceivePackets(Receiver->VifInterface, &List);
+
+ ASSERT(IsListEmpty(&List));
+}
+
+static FORCEINLINE VOID
+__RingSetOffloadOptions(
+ IN PRECEIVER_RING Ring,
+ IN XENVIF_OFFLOAD_OPTIONS Options
+ )
+{
+ KIRQL Irql;
+
+ KeRaiseIrql(DISPATCH_LEVEL, &Irql);
+
+ __RingAcquireLock(Ring);
+ Ring->OffloadOptions = Options;
+ __RingReleaseLock(Ring);
+
+ KeLowerIrql(Irql);
+}
+
+static FORCEINLINE VOID
+__RingAddPacketStatistics(
+ IN PRECEIVER_RING Ring,
+ IN OUT PXENVIF_RECEIVER_PACKET_STATISTICS Statistics
+ )
+{
+ // Don't bother locking
+
+ Statistics->Drop += Ring->PacketStatistics.Drop;
+ Statistics->BackendError += Ring->PacketStatistics.BackendError;
+ Statistics->FrontendError += Ring->PacketStatistics.FrontendError;
+ Statistics->Unicast += Ring->PacketStatistics.Unicast;
+ Statistics->UnicastBytes += Ring->PacketStatistics.UnicastBytes;
+ Statistics->Multicast += Ring->PacketStatistics.Multicast;
+ Statistics->MulticastBytes += Ring->PacketStatistics.MulticastBytes;
+ Statistics->Broadcast += Ring->PacketStatistics.Broadcast;
+ Statistics->BroadcastBytes += Ring->PacketStatistics.BroadcastBytes;
+}
+
+static VOID
+ReceiverDebugCallback(
+ IN PVOID Argument,
+ IN BOOLEAN Crashing
+ )
+{
+ PXENVIF_RECEIVER Receiver = Argument;
+ PLIST_ENTRY ListEntry;
+
+ UNREFERENCED_PARAMETER(Crashing);
+
+ for (ListEntry = Receiver->List.Flink;
+ ListEntry != &Receiver->List;
+ ListEntry = ListEntry->Flink) {
+ PRECEIVER_RING Ring;
+
+ Ring = CONTAINING_RECORD(ListEntry, RECEIVER_RING, ListEntry);
+
+ __RingDebugCallback(Ring);
+ }
+
+ DEBUG(Printf,
+ Receiver->DebugInterface,
+ Receiver->DebugCallback,
+ "Loaned = %d Returned = %d\n",
+ Receiver->Loaned,
+ Receiver->Returned);
+}
+
+NTSTATUS
+ReceiverInitialize(
+ IN PXENVIF_FRONTEND Frontend,
+ IN ULONG Count,
+ OUT PXENVIF_RECEIVER *Receiver
+ )
+{
+ ULONG Done;
+ NTSTATUS status;
+
+ *Receiver = __ReceiverAllocate(sizeof (XENVIF_RECEIVER));
+
+ status = STATUS_NO_MEMORY;
+ if (*Receiver == NULL)
+ goto fail1;
+
+ InitializeListHead(&(*Receiver)->List);
+ KeInitializeEvent(&(*Receiver)->Event, NotificationEvent, FALSE);
+
+ Done = 0;
+ while (Done < Count) {
+ PRECEIVER_RING Ring;
+
+ status = __RingInitialize(*Receiver, &Ring);
+ if (!NT_SUCCESS(status))
+ goto fail2;
+
+ InsertTailList(&(*Receiver)->List, &Ring->ListEntry);
+ Done++;
+ }
+
+ (*Receiver)->Frontend = Frontend;
+
+ return STATUS_SUCCESS;
+
+fail2:
+ Error("fail2\n");
+
+ while (!IsListEmpty(&(*Receiver)->List)) {
+ PLIST_ENTRY ListEntry;
+ PRECEIVER_RING Ring;
+
+ ListEntry = RemoveTailList(&(*Receiver)->List);
+ ASSERT3P(ListEntry, !=, &(*Receiver)->List);
+
+ RtlZeroMemory(ListEntry, sizeof (LIST_ENTRY));
+
+ Ring = CONTAINING_RECORD(ListEntry, RECEIVER_RING, ListEntry);
+
+ __RingTeardown(Ring);
+
+ --Done;
+ }
+ ASSERT3U(Done, ==, 0);
+
+ RtlZeroMemory(&(*Receiver)->Event, sizeof (KEVENT));
+ RtlZeroMemory(&(*Receiver)->List, sizeof (LIST_ENTRY));
+
+ ASSERT(IsZeroMemory(*Receiver, sizeof (XENVIF_RECEIVER)));
+ __ReceiverFree(*Receiver);
+
+fail1:
+ Error("fail1 (%08x)\n", status);
+
+ return status;
+}
+
+static FORCEINLINE VOID
+__ReceiverSetGsoFeatureFlag(
+ IN PXENVIF_RECEIVER Receiver
+ )
+{
+ PXENVIF_FRONTEND Frontend;
+
+ Frontend = Receiver->Frontend;
+
+ (VOID) STORE(Printf,
+ Receiver->StoreInterface,
+ NULL,
+ FrontendGetPath(Frontend),
+ "feature-gso-tcpv4-prefix",
+ "%u",
+ (Receiver->Protocol == 0) ? TRUE : FALSE);
+
+ (VOID) STORE(Printf,
+ Receiver->StoreInterface,
+ NULL,
+ FrontendGetPath(Frontend),
+ "feature-gso-tcpv6-prefix",
+ "%u",
+ (Receiver->Protocol == 0) ? TRUE : FALSE);
+
+ (VOID) STORE(Printf,
+ Receiver->StoreInterface,
+ NULL,
+ FrontendGetPath(Frontend),
+ "feature-gso-tcpv4",
+ "%u",
+ (Receiver->Protocol == 1) ? TRUE : FALSE);
+
+ (VOID) STORE(Printf,
+ Receiver->StoreInterface,
+ NULL,
+ FrontendGetPath(Frontend),
+ "feature-gso-tcpv6",
+ "%u",
+ (Receiver->Protocol == 1) ? TRUE : FALSE);
+}
+
+NTSTATUS
+ReceiverConnect(
+ IN PXENVIF_RECEIVER Receiver
+ )
+{
+ PXENVIF_FRONTEND Frontend;
+ PLIST_ENTRY ListEntry;
+ PCHAR Buffer;
+ ULONG MinimumProtocol;
+ ULONG MaximumProtocol;
+ NTSTATUS status;
+
+ Frontend = Receiver->Frontend;
+
+ Receiver->StoreInterface = FrontendGetStoreInterface(Frontend);
+
+ STORE(Acquire, Receiver->StoreInterface);
+
+ status = STORE(Read,
+ Receiver->StoreInterface,
+ NULL,
+ FrontendGetBackendPath(Frontend),
+ "min-rx-protocol",
+ &Buffer);
+ if (!NT_SUCCESS(status)) {
+ MinimumProtocol = 0;
+ } else {
+ MinimumProtocol = (ULONG)strtol(Buffer, NULL, 10);
+
+ STORE(Free,
+ Receiver->StoreInterface,
+ Buffer);
+ }
+
+ status = STORE(Read,
+ Receiver->StoreInterface,
+ NULL,
+ FrontendGetBackendPath(Frontend),
+ "max-rx-protocol",
+ &Buffer);
+ if (!NT_SUCCESS(status)) {
+ MaximumProtocol = 0;
+ } else {
+ MaximumProtocol = (ULONG)strtol(Buffer, NULL, 10);
+
+ STORE(Free,
+ Receiver->StoreInterface,
+ Buffer);
+ }
+
+ MinimumProtocol = __max(MinimumProtocol, RECEIVER_MINIMUM_PROTOCOL);
+ MaximumProtocol = __min(MaximumProtocol, DriverParameters.ReceiverMaximumProtocol);
+
+ status = STATUS_NOT_SUPPORTED;
+ if (MaximumProtocol < MinimumProtocol)
+ goto fail1;
+
+ Receiver->Protocol = MaximumProtocol;
+
+ for (ListEntry = Receiver->List.Flink;
+ ListEntry != &Receiver->List;
+ ListEntry = ListEntry->Flink) {
+ PRECEIVER_RING Ring;
+
+ Ring = CONTAINING_RECORD(ListEntry, RECEIVER_RING, ListEntry);
+
+ status = __RingConnect(Ring);
+ if (!NT_SUCCESS(status))
+ goto fail2;
+ }
+
+ __ReceiverSetGsoFeatureFlag(Receiver);
+
+ Receiver->DebugInterface = FrontendGetDebugInterface(Frontend);
+
+ DEBUG(Acquire, Receiver->DebugInterface);
+
+ status = DEBUG(Register,
+ Receiver->DebugInterface,
+ __MODULE__ "|RECEIVER",
+ ReceiverDebugCallback,
+ Receiver,
+ &Receiver->DebugCallback);
+ if (!NT_SUCCESS(status))
+ goto fail3;
+
+ return STATUS_SUCCESS;
+
+fail3:
+ Error("fail3\n");
+
+ DEBUG(Release, Receiver->DebugInterface);
+ Receiver->DebugInterface = NULL;
+
+ ListEntry = &Receiver->List;
+
+fail2:
+ Error("fail2\n");
+
+ ListEntry = ListEntry->Blink;
+
+ while (ListEntry != &Receiver->List) {
+ PLIST_ENTRY Prev = ListEntry->Blink;
+ PRECEIVER_RING Ring;
+
+ Ring = CONTAINING_RECORD(ListEntry, RECEIVER_RING, ListEntry);
+
+ __RingDisconnect(Ring);
+
+ ListEntry = Prev;
+ }
+
+fail1:
+ Error("fail1 (%08x)\n", status);
+
+ Receiver->Protocol = 0;
+
+ STORE(Release, Receiver->StoreInterface);
+ Receiver->StoreInterface = NULL;
+
+ return status;
+}
+
+NTSTATUS
+ReceiverStoreWrite(
+ IN PXENVIF_RECEIVER Receiver,
+ IN PXENBUS_STORE_TRANSACTION Transaction
+ )
+{
+ PXENVIF_FRONTEND Frontend;
+ PLIST_ENTRY ListEntry;
+ NTSTATUS status;
+
+ Frontend = Receiver->Frontend;
+
+ status = STORE(Printf,
+ Receiver->StoreInterface,
+ Transaction,
+ FrontendGetPath(Frontend),
+ "request-rx-copy",
+ "%u",
+ TRUE);
+ if (!NT_SUCCESS(status))
+ goto fail1;
+
+ status = STORE(Printf,
+ Receiver->StoreInterface,
+ Transaction,
+ FrontendGetPath(Frontend),
+ "feature-sg",
+ "%u",
+ TRUE);
+ if (!NT_SUCCESS(status))
+ goto fail2;
+
+ status = STORE(Printf,
+ Receiver->StoreInterface,
+ Transaction,
+ FrontendGetPath(Frontend),
+ "feature-no-csum-offload",
+ "%u",
+ FALSE);
+ if (!NT_SUCCESS(status))
+ goto fail3;
+
+ status = STORE(Printf,
+ Receiver->StoreInterface,
+ Transaction,
+ FrontendGetPath(Frontend),
+ "feature-ipv6-csum-offload",
+ "%u",
+ TRUE);
+ if (!NT_SUCCESS(status))
+ goto fail4;
+
+ status = STORE(Printf,
+ Receiver->StoreInterface,
+ Transaction,
+ FrontendGetPath(Frontend),
+ "feature-rx-notify",
+ "%u",
+ TRUE);
+ if (!NT_SUCCESS(status))
+ goto fail5;
+
+ status = STORE(Printf,
+ Receiver->StoreInterface,
+ Transaction,
+ FrontendGetPath(Frontend),
+ "rx-protocol",
+ "%u",
+ Receiver->Protocol);
+ if (!NT_SUCCESS(status))
+ goto fail6;
+
+ for (ListEntry = Receiver->List.Flink;
+ ListEntry != &Receiver->List;
+ ListEntry = ListEntry->Flink) {
+ PRECEIVER_RING Ring;
+
+ Ring = CONTAINING_RECORD(ListEntry, RECEIVER_RING, ListEntry);
+
+ status = __RingStoreWrite(Ring, Transaction);
+ if (!NT_SUCCESS(status))
+ goto fail7;
+ }
+
+ return STATUS_SUCCESS;
+
+fail7:
+ Error("fail7\n");
+
+fail6:
+ Error("fail6\n");
+
+fail5:
+ Error("fail5\n");
+
+fail4:
+ Error("fail4\n");
+
+fail3:
+ Error("fail3\n");
+
+fail2:
+ Error("fail2\n");
+
+fail1:
+ Error("fail1 (%08x)\n", status);
+
+ return status;
+}
+
+NTSTATUS
+ReceiverEnable(
+ IN PXENVIF_RECEIVER Receiver
+ )
+{
+ PXENVIF_FRONTEND Frontend;
+ PLIST_ENTRY ListEntry;
+ NTSTATUS status;
+
+ Frontend = Receiver->Frontend;
+
+ Receiver->VifInterface = FrontendGetVifInterface(Frontend);
+
+ for (ListEntry = Receiver->List.Flink;
+ ListEntry != &Receiver->List;
+ ListEntry = ListEntry->Flink) {
+ PRECEIVER_RING Ring;
+
+ Ring = CONTAINING_RECORD(ListEntry, RECEIVER_RING, ListEntry);
+
+ status = __RingEnable(Ring);
+ if (!NT_SUCCESS(status))
+ goto fail1;
+ }
+
+ return STATUS_SUCCESS;
+
+fail1:
+ Error("fail1 (%08x)\n", status);
+
+ ListEntry = ListEntry->Blink;
+
+ while (ListEntry != &Receiver->List) {
+ PRECEIVER_RING Ring;
+
+ Ring = CONTAINING_RECORD(ListEntry, RECEIVER_RING, ListEntry);
+
+ __RingDisable(Ring);
+ }
+
+ Receiver->VifInterface = NULL;
+
+ return status;
+}
+
+VOID
+ReceiverDisable(
+ IN PXENVIF_RECEIVER Receiver
+ )
+{
+ PLIST_ENTRY ListEntry;
+
+ for (ListEntry = Receiver->List.Blink;
+ ListEntry != &Receiver->List;
+ ListEntry = ListEntry->Blink) {
+ PRECEIVER_RING Ring;
+
+ Ring = CONTAINING_RECORD(ListEntry, RECEIVER_RING, ListEntry);
+
+ __RingDisable(Ring);
+ }
+
+ Receiver->VifInterface = NULL;
+}
+
+VOID
+ReceiverDisconnect(
+ IN PXENVIF_RECEIVER Receiver
+ )
+{
+ PLIST_ENTRY ListEntry;
+
+ DEBUG(Deregister,
+ Receiver->DebugInterface,
+ Receiver->DebugCallback);
+ Receiver->DebugCallback = NULL;
+
+ DEBUG(Release, Receiver->DebugInterface);
+ Receiver->DebugInterface = NULL;
+
+ for (ListEntry = Receiver->List.Blink; ListEntry != &Receiver->List; ListEntry = ListEntry->Blink) {
+ PRECEIVER_RING Ring;
+
+ Ring = CONTAINING_RECORD(ListEntry, RECEIVER_RING, ListEntry);
+
+ __RingDisconnect(Ring);
+ }
+
+ Receiver->Protocol = 0;
+
+ STORE(Release, Receiver->StoreInterface);
+ Receiver->StoreInterface = NULL;
+}
+
+VOID
+ReceiverTeardown(
+ IN PXENVIF_RECEIVER Receiver
+ )
+{
+ ASSERT3U(Receiver->Returned, ==, Receiver->Loaned);
+ Receiver->Loaned = 0;
+ Receiver->Returned = 0;
+
+ Receiver->Frontend = NULL;
+
+ while (!IsListEmpty(&Receiver->List)) {
+ PLIST_ENTRY ListEntry;
+ PRECEIVER_RING Ring;
+
+ ListEntry = RemoveTailList(&Receiver->List);
+ ASSERT3P(ListEntry, !=, &Receiver->List);
+ RtlZeroMemory(ListEntry, sizeof (LIST_ENTRY));
+
+ Ring = CONTAINING_RECORD(ListEntry, RECEIVER_RING, ListEntry);
+
+ __RingTeardown(Ring);
+ }
+
+ RtlZeroMemory(&Receiver->Event, sizeof (KEVENT));
+ RtlZeroMemory(&Receiver->List, sizeof (LIST_ENTRY));
+
+ ASSERT(IsZeroMemory(Receiver, sizeof (XENVIF_RECEIVER)));
+ __ReceiverFree(Receiver);
+}
+
+NTSTATUS
+ReceiverSetOffloadOptions(
+ IN PXENVIF_RECEIVER Receiver,
+ IN XENVIF_OFFLOAD_OPTIONS Options
+ )
+{
+ PLIST_ENTRY ListEntry;
+
+ if (DriverParameters.ReceiverAllowGsoPackets == 0) {
+ Options.OffloadIpVersion4LargePacket = 0;
+ Options.OffloadIpVersion6LargePacket = 0;
+ }
+
+ for (ListEntry = Receiver->List.Flink;
+ ListEntry != &Receiver->List;
+ ListEntry = ListEntry->Flink) {
+ PRECEIVER_RING Ring;
+
+ Ring = CONTAINING_RECORD(ListEntry, RECEIVER_RING, ListEntry);
+
+ __RingSetOffloadOptions(Ring, Options);
+ }
+
+ return STATUS_SUCCESS;
+}
+
+VOID
+ReceiverGetPacketStatistics(
+ IN PXENVIF_RECEIVER Receiver,
+ OUT PXENVIF_RECEIVER_PACKET_STATISTICS Statistics
+ )
+{
+ PLIST_ENTRY ListEntry;
+
+ RtlZeroMemory(Statistics, sizeof (XENVIF_RECEIVER_PACKET_STATISTICS));
+
+ for (ListEntry = Receiver->List.Flink;
+ ListEntry != &Receiver->List;
+ ListEntry = ListEntry->Flink) {
+ PRECEIVER_RING Ring;
+
+ Ring = CONTAINING_RECORD(ListEntry, RECEIVER_RING, ListEntry);
+
+ __RingAddPacketStatistics(Ring, Statistics);
+ }
+}
+
+ULONG
+ReceiverGetRingSize(
+ IN PXENVIF_RECEIVER Receiver
+ )
+{
+ PLIST_ENTRY ListEntry;
+ PRECEIVER_RING Ring;
+
+ // Use the first ring
+ ListEntry = Receiver->List.Flink;
+ Ring = CONTAINING_RECORD(ListEntry, RECEIVER_RING, ListEntry);
+
+ return __RingGetSize(Ring);
+}
+
+VOID
+ReceiverReturnPacket(
+ IN PXENVIF_RECEIVER Receiver,
+ IN PXENVIF_RECEIVER_PACKET Packet
+ )
+{
+ PRECEIVER_RING Ring;
+ LONG Loaned;
+ LONG Returned;
+
+ Ring = Packet->Cookie;
+ Packet->Cookie = NULL;
+
+ __RingReturnPacket(Ring, Packet, FALSE);
+
+ Returned = InterlockedIncrement(&Receiver->Returned);
+
+ // Make sure Loaned is not sampled before Returned
+ KeMemoryBarrier();
+
+ Loaned = Receiver->Loaned;
+
+ ASSERT3S(Loaned - Returned, >=, 0);
+
+ KeSetEvent(&Receiver->Event, 0, FALSE);
+}
+
+VOID
+ReceiverWaitForPackets(
+ IN PXENVIF_RECEIVER Receiver
+ )
+{
+ LONG Loaned;
+
+ ASSERT3U(KeGetCurrentIrql(), <, DISPATCH_LEVEL);
+
+ Loaned = Receiver->Loaned;
+
+ while (Receiver->Returned != Loaned) {
+ Trace("waiting for packets\n");
+
+ (VOID) KeWaitForSingleObject(&Receiver->Event,
+ Executive,
+ KernelMode,
+ FALSE,
+ NULL);
+ KeClearEvent(&Receiver->Event);
+
+ ASSERT3U(Loaned, ==, Receiver->Loaned);
+
+ KeMemoryBarrier();
+ }
+}
+
+VOID
+ReceiverNotify(
+ IN PXENVIF_RECEIVER Receiver
+ )
+{
+ PLIST_ENTRY ListEntry;
+
+ for (ListEntry = Receiver->List.Flink;
+ ListEntry != &Receiver->List;
+ ListEntry = ListEntry->Flink) {
+ PRECEIVER_RING Ring;
+
+ Ring = CONTAINING_RECORD(ListEntry, RECEIVER_RING, ListEntry);
+
+ __RingNotify(Ring);
+ }
+}
--- /dev/null
+/* Copyright (c) Citrix Systems Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms,
+ * with or without modification, are permitted provided
+ * that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer in the documentation and/or other
+ * materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef _XENVIF_RECEIVER_H
+#define _XENVIF_RECEIVER_H
+
+#include <ntddk.h>
+#include <ifdef.h>
+#include <vif_interface.h>
+
+#include "frontend.h"
+
+typedef struct _XENVIF_RECEIVER XENVIF_RECEIVER, *PXENVIF_RECEIVER;
+
+#define RECEIVER_MINIMUM_PROTOCOL 0
+#define RECEIVER_MAXIMUM_PROTOCOL 1
+
+extern NTSTATUS
+ReceiverInitialize(
+ IN PXENVIF_FRONTEND Frontend,
+ IN ULONG Count,
+ OUT PXENVIF_RECEIVER *Receiver
+ );
+
+extern NTSTATUS
+ReceiverConnect(
+ IN PXENVIF_RECEIVER Receiver
+ );
+
+extern NTSTATUS
+ReceiverStoreWrite(
+ IN PXENVIF_RECEIVER Receiver,
+ IN PXENBUS_STORE_TRANSACTION Transaction
+ );
+
+extern NTSTATUS
+ReceiverEnable(
+ IN PXENVIF_RECEIVER Receiver
+ );
+
+extern VOID
+ReceiverDisable(
+ IN PXENVIF_RECEIVER Receiver
+ );
+
+extern VOID
+ReceiverDisconnect(
+ IN PXENVIF_RECEIVER Receiver
+ );
+
+extern VOID
+ReceiverTeardown(
+ IN PXENVIF_RECEIVER Receiver
+ );
+
+extern VOID
+ReceiverNotify(
+ IN PXENVIF_RECEIVER Receiver
+ );
+
+extern VOID
+ReceiverWaitForPackets(
+ IN PXENVIF_RECEIVER Receiver
+ );
+
+extern VOID
+ReceiverGetPacketStatistics(
+ IN PXENVIF_RECEIVER Receiver,
+ OUT PXENVIF_RECEIVER_PACKET_STATISTICS Statistics
+ );
+
+extern ULONG
+ReceiverGetRingSize(
+ IN PXENVIF_RECEIVER Receiver
+ );
+
+extern VOID
+ReceiverReturnPacket(
+ IN PXENVIF_RECEIVER Receiver,
+ IN PXENVIF_RECEIVER_PACKET Packet
+ );
+
+extern NTSTATUS
+ReceiverSetOffloadOptions(
+ IN PXENVIF_RECEIVER Receiver,
+ IN XENVIF_OFFLOAD_OPTIONS Options
+ );
+
+
+#endif // _XENVIF_RECEIVER_H
--- /dev/null
+/* Copyright (c) Citrix Systems Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms,
+ * with or without modification, are permitted provided
+ * that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer in the documentation and/or other
+ * materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <ntddk.h>
+#include <util.h>
+
+#include "registry.h"
+#include "log.h"
+#include "assert.h"
+
+#define REGISTRY_POOL 'GERX'
+
+static UNICODE_STRING RegistryPath;
+
+static FORCEINLINE PVOID
+__RegistryAllocate(
+ IN ULONG Length
+ )
+{
+ return __AllocateNonPagedPoolWithTag(Length, REGISTRY_POOL);
+}
+
+static FORCEINLINE VOID
+__RegistryFree(
+ IN PVOID Buffer
+ )
+{
+ __FreePoolWithTag(Buffer, REGISTRY_POOL);
+}
+
+NTSTATUS
+RegistryInitialize(
+ IN PUNICODE_STRING Path
+ )
+{
+ NTSTATUS status;
+
+ ASSERT3P(RegistryPath.Buffer, ==, NULL);
+
+ status = RtlUpcaseUnicodeString(&RegistryPath, Path, TRUE);
+ if (!NT_SUCCESS(status))
+ goto fail1;
+
+ return STATUS_SUCCESS;
+
+fail1:
+ Error("fail1 (%08x)\n", status);
+
+ return status;
+}
+
+VOID
+RegistryTeardown(
+ VOID
+ )
+{
+ RtlFreeUnicodeString(&RegistryPath);
+ RegistryPath.Buffer = NULL;
+ RegistryPath.MaximumLength = RegistryPath.Length = 0;
+}
+
+NTSTATUS
+RegistryOpenSubKey(
+ IN PCHAR Name,
+ IN ACCESS_MASK DesiredAccess,
+ OUT PHANDLE Key
+ )
+{
+ ANSI_STRING Ansi;
+ UNICODE_STRING Unicode;
+ OBJECT_ATTRIBUTES Attributes;
+ HANDLE ServiceKey;
+ NTSTATUS status;
+
+ RtlInitAnsiString(&Ansi, Name);
+
+ status = RtlAnsiStringToUnicodeString(&Unicode, &Ansi, TRUE);
+ if (!NT_SUCCESS(status))
+ goto fail1;
+
+ InitializeObjectAttributes(&Attributes,
+ &RegistryPath,
+ OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
+ NULL,
+ NULL);
+
+ status = ZwOpenKey(&ServiceKey,
+ KEY_ALL_ACCESS,
+ &Attributes);
+ if (!NT_SUCCESS(status))
+ goto fail2;
+
+ InitializeObjectAttributes(&Attributes,
+ &Unicode,
+ OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
+ ServiceKey,
+ NULL);
+
+ status = ZwOpenKey(Key,
+ DesiredAccess,
+ &Attributes);
+ if (!NT_SUCCESS(status))
+ goto fail3;
+
+ ZwClose(ServiceKey);
+
+ RtlFreeUnicodeString(&Unicode);
+
+ return STATUS_SUCCESS;
+
+fail3:
+ ZwClose(ServiceKey);
+
+fail2:
+ RtlFreeUnicodeString(&Unicode);
+
+fail1:
+ return status;
+}
+
+NTSTATUS
+RegistryCreateSubKey(
+ IN PCHAR Name
+ )
+{
+ ANSI_STRING Ansi;
+ UNICODE_STRING Unicode;
+ OBJECT_ATTRIBUTES Attributes;
+ HANDLE ServiceKey;
+ HANDLE Key;
+ NTSTATUS status;
+
+ RtlInitAnsiString(&Ansi, Name);
+
+ status = RtlAnsiStringToUnicodeString(&Unicode, &Ansi, TRUE);
+ if (!NT_SUCCESS(status))
+ goto fail1;
+
+ InitializeObjectAttributes(&Attributes,
+ &RegistryPath,
+ OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
+ NULL,
+ NULL);
+
+ status = ZwOpenKey(&ServiceKey,
+ KEY_ALL_ACCESS,
+ &Attributes);
+ if (!NT_SUCCESS(status))
+ goto fail2;
+
+ InitializeObjectAttributes(&Attributes,
+ &Unicode,
+ OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
+ ServiceKey,
+ NULL);
+
+ status = ZwCreateKey(&Key,
+ KEY_ALL_ACCESS,
+ &Attributes,
+ 0,
+ NULL,
+ REG_OPTION_VOLATILE,
+ NULL
+ );
+ if (!NT_SUCCESS(status))
+ goto fail3;
+
+ ZwClose(Key);
+
+ ZwClose(ServiceKey);
+
+ RtlFreeUnicodeString(&Unicode);
+
+ return STATUS_SUCCESS;
+
+fail3:
+ ZwClose(ServiceKey);
+
+fail2:
+ RtlFreeUnicodeString(&Unicode);
+
+fail1:
+ return status;
+}
+
+NTSTATUS
+RegistryDeleteSubKey(
+ IN PCHAR Name
+ )
+{
+ ANSI_STRING Ansi;
+ UNICODE_STRING Unicode;
+ OBJECT_ATTRIBUTES Attributes;
+ HANDLE ServiceKey;
+ HANDLE Key;
+ NTSTATUS status;
+
+ RtlInitAnsiString(&Ansi, Name);
+
+ status = RtlAnsiStringToUnicodeString(&Unicode, &Ansi, TRUE);
+ if (!NT_SUCCESS(status))
+ goto fail1;
+
+ InitializeObjectAttributes(&Attributes,
+ &RegistryPath,
+ OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
+ NULL,
+ NULL);
+
+ status = ZwOpenKey(&ServiceKey,
+ KEY_ALL_ACCESS,
+ &Attributes);
+ if (!NT_SUCCESS(status))
+ goto fail2;
+
+ InitializeObjectAttributes(&Attributes,
+ &Unicode,
+ OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
+ ServiceKey,
+ NULL);
+
+ status = ZwOpenKey(&Key,
+ KEY_ALL_ACCESS,
+ &Attributes);
+ if (!NT_SUCCESS(status))
+ goto fail3;
+
+ status = ZwDeleteKey(Key);
+ if (!NT_SUCCESS(status))
+ goto fail4;
+
+ ZwClose(Key);
+
+ ZwClose(ServiceKey);
+
+ RtlFreeUnicodeString(&Unicode);
+
+ return STATUS_SUCCESS;
+
+fail4:
+ ZwClose(Key);
+
+fail3:
+ ZwClose(ServiceKey);
+
+fail2:
+ RtlFreeUnicodeString(&Unicode);
+
+fail1:
+ return status;
+}
+
+
+NTSTATUS
+RegistryOpenSoftwareKey(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN ACCESS_MASK DesiredAccess,
+ OUT PHANDLE Key
+ )
+{
+ NTSTATUS status;
+
+ status = IoOpenDeviceRegistryKey(DeviceObject,
+ PLUGPLAY_REGKEY_DRIVER,
+ DesiredAccess,
+ Key);
+ if (!NT_SUCCESS(status))
+ goto fail1;
+
+ return STATUS_SUCCESS;
+
+fail1:
+ return status;
+}
+
+NTSTATUS
+RegistryOpenHardwareKey(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN ACCESS_MASK DesiredAccess,
+ OUT PHANDLE Key
+ )
+{
+ NTSTATUS status;
+
+ status = IoOpenDeviceRegistryKey(DeviceObject,
+ PLUGPLAY_REGKEY_DEVICE,
+ DesiredAccess,
+ Key);
+ if (!NT_SUCCESS(status))
+ goto fail1;
+
+ return STATUS_SUCCESS;
+
+fail1:
+ return status;
+}
+
+NTSTATUS
+RegistryQueryDwordValue(
+ IN HANDLE Key,
+ IN PCHAR Name,
+ OUT PULONG Value
+ )
+{
+ ANSI_STRING Ansi;
+ UNICODE_STRING Unicode;
+ PKEY_VALUE_PARTIAL_INFORMATION Partial;
+ ULONG Size;
+ NTSTATUS status;
+
+ RtlInitAnsiString(&Ansi, Name);
+
+ status = RtlAnsiStringToUnicodeString(&Unicode, &Ansi, TRUE);
+ if (!NT_SUCCESS(status))
+ goto fail1;
+
+ status = ZwQueryValueKey(Key,
+ &Unicode,
+ KeyValuePartialInformation,
+ NULL,
+ 0,
+ &Size);
+ if (status != STATUS_BUFFER_TOO_SMALL)
+ goto fail2;
+
+ Partial = __RegistryAllocate(Size);
+
+ status = STATUS_NO_MEMORY;
+ if (Partial == NULL)
+ goto fail3;
+
+ status = ZwQueryValueKey(Key,
+ &Unicode,
+ KeyValuePartialInformation,
+ Partial,
+ Size,
+ &Size);
+ if (!NT_SUCCESS(status))
+ goto fail4;
+
+ status = STATUS_INVALID_PARAMETER;
+ if (Partial->Type != REG_DWORD ||
+ Partial->DataLength != sizeof (ULONG))
+ goto fail5;
+
+ *Value = *(PULONG)Partial->Data;
+
+ __RegistryFree(Partial);
+
+ RtlFreeUnicodeString(&Unicode);
+
+ return STATUS_SUCCESS;
+
+fail5:
+fail4:
+ __RegistryFree(Partial);
+
+fail3:
+fail2:
+ RtlFreeUnicodeString(&Unicode);
+
+fail1:
+ return status;
+}
+
+NTSTATUS
+RegistryUpdateDwordValue(
+ IN HANDLE Key,
+ IN PCHAR Name,
+ IN ULONG Value
+ )
+{
+ ANSI_STRING Ansi;
+ UNICODE_STRING Unicode;
+ PKEY_VALUE_PARTIAL_INFORMATION Partial;
+ NTSTATUS status;
+
+ RtlInitAnsiString(&Ansi, Name);
+
+ status = RtlAnsiStringToUnicodeString(&Unicode, &Ansi, TRUE);
+ if (!NT_SUCCESS(status))
+ goto fail1;
+
+ Partial = __RegistryAllocate(FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data) +
+ sizeof (ULONG));
+
+ status = STATUS_NO_MEMORY;
+ if (Partial == NULL)
+ goto fail2;
+
+ Partial->TitleIndex = 0;
+ Partial->Type = REG_DWORD;
+ Partial->DataLength = sizeof (ULONG);
+ *(PULONG)Partial->Data = Value;
+
+ status = ZwSetValueKey(Key,
+ &Unicode,
+ Partial->TitleIndex,
+ Partial->Type,
+ Partial->Data,
+ Partial->DataLength);
+ if (!NT_SUCCESS(status))
+ goto fail3;
+
+ __RegistryFree(Partial);
+
+ RtlFreeUnicodeString(&Unicode);
+
+ return STATUS_SUCCESS;
+
+fail3:
+ __RegistryFree(Partial);
+
+fail2:
+ RtlFreeUnicodeString(&Unicode);
+
+fail1:
+
+ return status;
+}
+
+static PANSI_STRING
+RegistrySzToAnsi(
+ IN PWCHAR Buffer
+ )
+{
+ PANSI_STRING Ansi;
+ ULONG Length;
+ UNICODE_STRING Unicode;
+ NTSTATUS status;
+
+ Ansi = __RegistryAllocate(sizeof (ANSI_STRING) * 2);
+
+ status = STATUS_NO_MEMORY;
+ if (Ansi == NULL)
+ goto fail1;
+
+ Length = (ULONG)wcslen(Buffer);
+ Ansi[0].MaximumLength = (USHORT)(Length + 1) * sizeof (CHAR);
+ Ansi[0].Buffer = __RegistryAllocate(Ansi[0].MaximumLength);
+
+ status = STATUS_NO_MEMORY;
+ if (Ansi[0].Buffer == NULL)
+ goto fail2;
+
+ RtlInitUnicodeString(&Unicode, Buffer);
+ status = RtlUnicodeStringToAnsiString(&Ansi[0], &Unicode, FALSE);
+ ASSERT(NT_SUCCESS(status));
+
+ Ansi[0].Length = (USHORT)Length * sizeof (CHAR);
+
+ return Ansi;
+
+fail2:
+ __RegistryFree(Ansi);
+
+fail1:
+ return NULL;
+}
+
+static PANSI_STRING
+RegistryMultiSzToAnsi(
+ IN PWCHAR Buffer
+ )
+{
+ PANSI_STRING Ansi;
+ LONG Index;
+ LONG Count;
+ NTSTATUS status;
+
+ Index = 0;
+ Count = 0;
+ for (;;) {
+ ULONG Length;
+
+ Length = (ULONG)wcslen(&Buffer[Index]);
+ if (Length == 0)
+ break;
+
+ Index += Length + 1;
+ Count++;
+ }
+
+ Ansi = __RegistryAllocate(sizeof (ANSI_STRING) * (Count + 1));
+
+ status = STATUS_NO_MEMORY;
+ if (Ansi == NULL)
+ goto fail1;
+
+ for (Index = 0; Index < Count; Index++) {
+ ULONG Length;
+ UNICODE_STRING Unicode;
+
+ Length = (ULONG)wcslen(Buffer);
+ Ansi[Index].MaximumLength = (USHORT)(Length + 1) * sizeof (CHAR);
+ Ansi[Index].Buffer = __RegistryAllocate(Ansi[Index].MaximumLength);
+
+ status = STATUS_NO_MEMORY;
+ if (Ansi[Index].Buffer == NULL)
+ goto fail2;
+
+ RtlInitUnicodeString(&Unicode, Buffer);
+
+ status = RtlUnicodeStringToAnsiString(&Ansi[Index], &Unicode, FALSE);
+ ASSERT(NT_SUCCESS(status));
+
+ Ansi[Index].Length = (USHORT)Length * sizeof (CHAR);
+ Buffer += Length + 1;
+ }
+
+ return Ansi;
+
+fail2:
+ while (--Index >= 0)
+ __RegistryFree(Ansi[Index].Buffer);
+
+ __RegistryFree(Ansi);
+
+fail1:
+ return NULL;
+}
+
+NTSTATUS
+RegistryQuerySzValue(
+ IN HANDLE Key,
+ IN PCHAR Name,
+ OUT PANSI_STRING *Array
+ )
+{
+ ANSI_STRING Ansi;
+ UNICODE_STRING Unicode;
+ PKEY_VALUE_PARTIAL_INFORMATION Value;
+ ULONG Size;
+ NTSTATUS status;
+
+ RtlInitAnsiString(&Ansi, Name);
+
+ status = RtlAnsiStringToUnicodeString(&Unicode, &Ansi, TRUE);
+ if (!NT_SUCCESS(status))
+ goto fail1;
+
+ status = ZwQueryValueKey(Key,
+ &Unicode,
+ KeyValuePartialInformation,
+ NULL,
+ 0,
+ &Size);
+ if (status != STATUS_BUFFER_TOO_SMALL)
+ goto fail2;
+
+ Value = __RegistryAllocate(Size);
+
+ status = STATUS_NO_MEMORY;
+ if (Value == NULL)
+ goto fail3;
+
+ status = ZwQueryValueKey(Key,
+ &Unicode,
+ KeyValuePartialInformation,
+ Value,
+ Size,
+ &Size);
+ if (!NT_SUCCESS(status))
+ goto fail4;
+
+ switch (Value->Type) {
+ case REG_SZ:
+ status = STATUS_NO_MEMORY;
+ *Array = RegistrySzToAnsi((PWCHAR)Value->Data);
+ break;
+
+ case REG_MULTI_SZ:
+ status = STATUS_NO_MEMORY;
+ *Array = RegistryMultiSzToAnsi((PWCHAR)Value->Data);
+ break;
+
+ default:
+ status = STATUS_INVALID_PARAMETER;
+ *Array = NULL;
+ break;
+ }
+
+ if (*Array == NULL)
+ goto fail5;
+
+ __RegistryFree(Value);
+
+ RtlFreeUnicodeString(&Unicode);
+
+ return STATUS_SUCCESS;
+
+fail5:
+fail4:
+ __RegistryFree(Value);
+
+fail3:
+fail2:
+ RtlFreeUnicodeString(&Unicode);
+
+fail1:
+ return status;
+}
+
+static PKEY_VALUE_PARTIAL_INFORMATION
+RegistryAnsiToSz(
+ PANSI_STRING Ansi
+ )
+{
+ ULONG Length;
+ PKEY_VALUE_PARTIAL_INFORMATION Partial;
+ UNICODE_STRING Unicode;
+ NTSTATUS status;
+
+ Length = Ansi->Length + 1;
+ Partial = __RegistryAllocate(FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data) +
+ Length * sizeof (WCHAR));
+
+ status = STATUS_NO_MEMORY;
+ if (Partial == NULL)
+ goto fail1;
+
+ Partial->TitleIndex = 0;
+ Partial->Type = REG_SZ;
+ Partial->DataLength = Length * sizeof (WCHAR);
+
+ Unicode.MaximumLength = (UCHAR)Partial->DataLength;
+ Unicode.Buffer = (PWCHAR)Partial->Data;
+ Unicode.Length = 0;
+
+ status = RtlAnsiStringToUnicodeString(&Unicode, Ansi, FALSE);
+ if (!NT_SUCCESS(status))
+ goto fail2;
+
+ return Partial;
+
+fail2:
+ __RegistryFree(Partial);
+
+fail1:
+ return NULL;
+}
+
+static PKEY_VALUE_PARTIAL_INFORMATION
+RegistryAnsiToMultiSz(
+ PANSI_STRING Ansi
+ )
+{
+ ULONG Length;
+ ULONG Index;
+ PKEY_VALUE_PARTIAL_INFORMATION Partial;
+ UNICODE_STRING Unicode;
+ NTSTATUS status;
+
+ Length = 1;
+ for (Index = 0; Ansi[Index].Buffer != NULL; Index++)
+ Length += Ansi[Index].Length + 1;
+
+ Partial = __RegistryAllocate(FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data) +
+ Length * sizeof (WCHAR));
+
+ status = STATUS_NO_MEMORY;
+ if (Partial == NULL)
+ goto fail1;
+
+ Partial->TitleIndex = 0;
+ Partial->Type = REG_MULTI_SZ;
+ Partial->DataLength = Length * sizeof (WCHAR);
+
+ Unicode.MaximumLength = (USHORT)Partial->DataLength;
+ Unicode.Buffer = (PWCHAR)Partial->Data;
+ Unicode.Length = 0;
+
+ for (Index = 0; Ansi[Index].Buffer != NULL; Index++) {
+ status = RtlAnsiStringToUnicodeString(&Unicode, &Ansi[Index], FALSE);
+ if (!NT_SUCCESS(status))
+ goto fail2;
+
+ Length = Unicode.Length / sizeof (WCHAR);
+
+ ASSERT3U(Unicode.MaximumLength, >=, (Length + 1) * sizeof (WCHAR));
+ Unicode.MaximumLength -= (USHORT)((Length + 1) * sizeof (WCHAR));
+ Unicode.Buffer += Length + 1;
+ Unicode.Length = 0;
+ }
+ *Unicode.Buffer = L'\0';
+
+ return Partial;
+
+fail2:
+ __RegistryFree(Partial);
+
+fail1:
+ return NULL;
+}
+
+NTSTATUS
+RegistryUpdateSzValue(
+ IN HANDLE Key,
+ IN PCHAR Name,
+ IN ULONG Type,
+ ...
+ )
+{
+ ANSI_STRING Ansi;
+ UNICODE_STRING Unicode;
+ va_list Arguments;
+ PKEY_VALUE_PARTIAL_INFORMATION Partial;
+ NTSTATUS status;
+
+ RtlInitAnsiString(&Ansi, Name);
+
+ status = RtlAnsiStringToUnicodeString(&Unicode, &Ansi, TRUE);
+ if (!NT_SUCCESS(status))
+ goto fail1;
+
+ va_start(Arguments, Type);
+ switch (Type) {
+ case REG_SZ: {
+ PANSI_STRING Argument;
+
+ Argument = va_arg(Arguments, PANSI_STRING);
+
+ status = STATUS_NO_MEMORY;
+ Partial = RegistryAnsiToSz(Argument);
+ break;
+ }
+ case REG_MULTI_SZ: {
+ PANSI_STRING Argument;
+
+ Argument = va_arg(Arguments, PANSI_STRING);
+
+ status = STATUS_NO_MEMORY;
+ Partial = RegistryAnsiToMultiSz(Argument);
+ break;
+ }
+ default:
+ status = STATUS_INVALID_PARAMETER;
+ Partial = NULL;
+ break;
+ }
+ va_end(Arguments);
+
+ if (Partial == NULL)
+ goto fail2;
+
+ status = ZwSetValueKey(Key,
+ &Unicode,
+ Partial->TitleIndex,
+ Partial->Type,
+ Partial->Data,
+ Partial->DataLength);
+ if (!NT_SUCCESS(status))
+ goto fail3;
+
+ __RegistryFree(Partial);
+
+ RtlFreeUnicodeString(&Unicode);
+
+ return STATUS_SUCCESS;
+
+fail3:
+ __RegistryFree(Partial);
+
+fail2:
+ RtlFreeUnicodeString(&Unicode);
+
+fail1:
+ return status;
+}
+
+VOID
+RegistryFreeSzValue(
+ IN PANSI_STRING Array
+ )
+{
+ ULONG Index;
+
+ if (Array == NULL)
+ return;
+
+ for (Index = 0; Array[Index].Buffer != NULL; Index++)
+ __RegistryFree(Array[Index].Buffer);
+
+ __RegistryFree(Array);
+}
+
+VOID
+RegistryCloseKey(
+ IN HANDLE Key
+ )
+{
+ ZwClose(Key);
+}
--- /dev/null
+/* Copyright (c) Citrix Systems Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms,
+ * with or without modification, are permitted provided
+ * that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer in the documentation and/or other
+ * materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef _XENVIF_REGISTRY_H
+#define _XENVIF_REGISTRY_H
+
+#include <ntddk.h>
+
+extern NTSTATUS
+RegistryInitialize(
+ IN PUNICODE_STRING Path
+ );
+
+extern VOID
+RegistryTeardown(
+ VOID
+ );
+
+extern NTSTATUS
+RegistryOpenSubKey(
+ IN PCHAR Name,
+ IN ACCESS_MASK DesiredAccess,
+ OUT PHANDLE Key
+ );
+
+extern NTSTATUS
+RegistryCreateSubKey(
+ IN PCHAR Name
+ );
+
+extern NTSTATUS
+RegistryDeleteSubKey(
+ IN PCHAR Name
+ );
+
+extern NTSTATUS
+RegistryOpenSoftwareKey(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN ACCESS_MASK DesiredAccess,
+ OUT PHANDLE Key
+ );
+
+extern NTSTATUS
+RegistryOpenHardwareKey(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN ACCESS_MASK DesiredAccess,
+ OUT PHANDLE Key
+ );
+
+extern NTSTATUS
+RegistryQueryDwordValue(
+ IN HANDLE Key,
+ IN PCHAR Name,
+ OUT PULONG Value
+ );
+
+extern NTSTATUS
+RegistryUpdateDwordValue(
+ IN HANDLE Key,
+ IN PCHAR Name,
+ IN ULONG Value
+ );
+
+extern NTSTATUS
+RegistryQuerySzValue(
+ IN HANDLE Key,
+ IN PCHAR Name,
+ OUT PANSI_STRING *Array
+ );
+
+extern VOID
+RegistryFreeSzValue(
+ IN PANSI_STRING Array
+ );
+
+extern NTSTATUS
+RegistryUpdateSzValue(
+ IN HANDLE Key,
+ IN PCHAR Name,
+ IN ULONG Type,
+ ...
+ );
+
+extern VOID
+RegistryCloseKey(
+ IN HANDLE Key
+ );
+
+#endif // _XENVIF_REGISTRY_H
--- /dev/null
+/* Copyright (c) Citrix Systems Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms,
+ * with or without modification, are permitted provided
+ * that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer in the documentation and/or other
+ * materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <ntddk.h>
+#include <util.h>
+
+#include "thread.h"
+#include "log.h"
+#include "assert.h"
+
+#define THREAD_POOL 'ERHT'
+
+struct _XENVIF_THREAD {
+ XENVIF_THREAD_FUNCTION Function;
+ PVOID Context;
+ KEVENT Event;
+ BOOLEAN Alerted;
+ LONG References;
+ PKTHREAD Thread;
+};
+
+static FORCEINLINE PVOID
+__ThreadAllocate(
+ IN ULONG Length
+ )
+{
+ return __AllocateNonPagedPoolWithTag(Length, THREAD_POOL);
+}
+
+static FORCEINLINE VOID
+__ThreadFree(
+ IN PVOID Buffer
+ )
+{
+ __FreePoolWithTag(Buffer, THREAD_POOL);
+}
+
+static FORCEINLINE VOID
+__ThreadWake(
+ IN PXENVIF_THREAD Thread
+ )
+{
+ KeSetEvent(&Thread->Event, IO_NO_INCREMENT, FALSE);
+}
+
+VOID
+ThreadWake(
+ IN PXENVIF_THREAD Thread
+ )
+{
+ __ThreadWake(Thread);
+}
+
+static FORCEINLINE VOID
+__ThreadAlert(
+ IN PXENVIF_THREAD Thread
+ )
+{
+ Thread->Alerted = TRUE;
+ __ThreadWake(Thread);
+}
+
+VOID
+ThreadAlert(
+ IN PXENVIF_THREAD Thread
+ )
+{
+ __ThreadAlert(Thread);
+}
+
+KSTART_ROUTINE ThreadFunction;
+
+VOID
+ThreadFunction(
+ IN PVOID Argument
+ )
+{
+ PXENVIF_THREAD Self = Argument;
+ NTSTATUS status;
+
+ status = Self->Function(Self, Self->Context);
+
+ if (InterlockedDecrement(&Self->References) == 0)
+ __ThreadFree(Self);
+
+ PsTerminateSystemThread(status);
+ // NOT REACHED
+}
+
+NTSTATUS
+ThreadCreate(
+ IN XENVIF_THREAD_FUNCTION Function,
+ IN PVOID Context,
+ OUT PXENVIF_THREAD *Thread
+ )
+{
+ HANDLE Handle;
+ NTSTATUS status;
+
+ ASSERT3U(KeGetCurrentIrql(), ==, PASSIVE_LEVEL);
+
+ (*Thread) = __ThreadAllocate(sizeof (XENVIF_THREAD));
+
+ status = STATUS_NO_MEMORY;
+ if (*Thread == NULL)
+ goto fail1;
+
+ (*Thread)->Function = Function;
+ (*Thread)->Context = Context;
+ (*Thread)->Alerted = FALSE;
+ (*Thread)->References = 2; // One for us, one for the thread function
+
+ KeInitializeEvent(&(*Thread)->Event, NotificationEvent, FALSE);
+
+ status = PsCreateSystemThread(&Handle,
+ STANDARD_RIGHTS_ALL | SPECIFIC_RIGHTS_ALL,
+ NULL,
+ NULL,
+ NULL,
+ ThreadFunction,
+ *Thread);
+ if (!NT_SUCCESS(status)) {
+ --(*Thread)->References; // Fake thread function termination
+ goto fail2;
+ }
+
+ status = ObReferenceObjectByHandle(Handle,
+ SYNCHRONIZE,
+ *PsThreadType,
+ KernelMode,
+ &(*Thread)->Thread,
+ NULL);
+ if (!NT_SUCCESS(status))
+ goto fail3;
+
+ ZwClose(Handle);
+
+ return STATUS_SUCCESS;
+
+fail3:
+ Error("fail3\n");
+
+ __ThreadAlert(*Thread);
+ ZwClose(Handle);
+
+fail2:
+ Error("fail2\n");
+
+ if (InterlockedDecrement(&(*Thread)->References) == 0)
+ __ThreadFree(*Thread);
+
+ *Thread = NULL;
+
+fail1:
+ Error("fail1 (%08x)\n", status);
+
+ return status;
+}
+
+PKEVENT
+ThreadGetEvent(
+ IN PXENVIF_THREAD Thread
+ )
+{
+ return &Thread->Event;
+}
+
+BOOLEAN
+ThreadIsAlerted(
+ IN PXENVIF_THREAD Thread
+ )
+{
+ return Thread->Alerted;
+}
+
+VOID
+ThreadJoin(
+ IN PXENVIF_THREAD Thread
+ )
+{
+ LONG References;
+
+ ASSERT3U(KeGetCurrentIrql(), ==, PASSIVE_LEVEL);
+ ASSERT3P(KeGetCurrentThread(), !=, Thread->Thread);
+
+ (VOID) KeWaitForSingleObject(Thread->Thread,
+ Executive,
+ KernelMode,
+ FALSE,
+ NULL);
+
+ References = InterlockedDecrement(&Thread->References);
+ ASSERT3U(References, ==, 0);
+
+ __ThreadFree(Thread);
+}
+
--- /dev/null
+/* Copyright (c) Citrix Systems Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms,
+ * with or without modification, are permitted provided
+ * that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer in the documentation and/or other
+ * materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef _XENVIF_THREAD_H
+#define _XENVIF_THREAD_H
+
+#include <ntddk.h>
+
+typedef struct _XENVIF_THREAD XENVIF_THREAD, *PXENVIF_THREAD;
+
+typedef NTSTATUS (*XENVIF_THREAD_FUNCTION)(PXENVIF_THREAD, PVOID);
+
+extern NTSTATUS
+ThreadCreate(
+ IN XENVIF_THREAD_FUNCTION Function,
+ IN PVOID Context,
+ OUT PXENVIF_THREAD *Thread
+ );
+
+extern PKEVENT
+ThreadGetEvent(
+ IN PXENVIF_THREAD Self
+ );
+
+extern BOOLEAN
+ThreadIsAlerted(
+ IN PXENVIF_THREAD Self
+ );
+
+extern VOID
+ThreadWake(
+ IN PXENVIF_THREAD Thread
+ );
+
+extern VOID
+ThreadAlert(
+ IN PXENVIF_THREAD Thread
+ );
+
+extern VOID
+ThreadJoin(
+ IN PXENVIF_THREAD Thread
+ );
+
+#endif // _XENVIF_THREAD_H
+
--- /dev/null
+/* Copyright (c) Citrix Systems Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms,
+ * with or without modification, are permitted provided
+ * that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer in the documentation and/or other
+ * materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <ntddk.h>
+#include <ntstrsafe.h>
+#include <stdlib.h>
+#include <netioapi.h>
+#include <util.h>
+#include <xen.h>
+#include <debug_interface.h>
+#include <store_interface.h>
+#include <gnttab_interface.h>
+
+#include "ethernet.h"
+#include "tcpip.h"
+#include "pdo.h"
+#include "frontend.h"
+#include "pool.h"
+#include "checksum.h"
+#include "parse.h"
+#include "transmitter.h"
+#include "mac.h"
+#include "vif.h"
+#include "thread.h"
+#include "log.h"
+#include "assert.h"
+
+#define TRANSMITTER_POOL 'NART'
+
+typedef struct _TRANSMITTER_BUFFER {
+ PMDL Mdl;
+ PVOID Context;
+ ULONG Reference;
+} TRANSMITTER_BUFFER, *PTRANSMITTER_BUFFER;
+
+typedef enum _TRANSMITTER_TAG_TYPE {
+ TAG_TYPE_INVALID = 0,
+ TAG_PACKET,
+ TAG_BUFFER
+} TRANSMITTER_TAG_TYPE, *PTRANSMITTER_TAG_TYPE;
+
+typedef struct _TRANSMITTER_TAG {
+ LIST_ENTRY ListEntry;
+ ULONG Next;
+ TRANSMITTER_TAG_TYPE Type;
+ PVOID Context;
+ ULONG Reference;
+ ULONG Offset;
+ ULONG Length;
+} TRANSMITTER_TAG, *PTRANSMITTER_TAG;
+
+typedef struct _TRANSMITTER_OFFLOAD_STATISTICS {
+ ULONGLONG TagManipulation;
+ ULONGLONG IpVersion4LargePacket;
+ ULONGLONG IpVersion6LargePacket;
+ ULONGLONG IpVersion4HeaderChecksum;
+ ULONGLONG IpVersion4TcpChecksum;
+ ULONGLONG IpVersion6TcpChecksum;
+ ULONGLONG IpVersion4UdpChecksum;
+ ULONGLONG IpVersion6UdpChecksum;
+} TRANSMITTER_OFFLOAD_STATISTICS, *PTRANSMITTER_OFFLOAD_STATISTICS;
+
+typedef struct _TRANSMITTER_STATE {
+ PXENVIF_TRANSMITTER_PACKET Packet;
+ XENVIF_SEND_INFO Send;
+ PUCHAR StartVa;
+ XENVIF_PACKET_INFO Info;
+ XENVIF_PACKET_PAYLOAD Payload;
+ LIST_ENTRY List;
+ ULONG Count;
+} TRANSMITTER_STATE, *PTRANSMITTER_STATE;
+
+#define TRANSMITTER_RING_SIZE (__CONST_RING_SIZE(netif_tx, PAGE_SIZE))
+#define MAXIMUM_TAG_COUNT (TRANSMITTER_RING_SIZE * 2)
+
+#define TAG_INDEX_INVALID 0xFFFFFFFF
+
+#define REQ_ID_INTEGRITY_CHECK 0xF000
+
+typedef struct _TRANSMITTER_PACKET_LIST {
+ PXENVIF_TRANSMITTER_PACKET HeadPacket;
+ PXENVIF_TRANSMITTER_PACKET *TailPacket;
+} TRANSMITTER_PACKET_LIST, *PTRANSMITTER_PACKET_LIST;
+
+typedef struct _TRANSMITTER_RING {
+ PXENVIF_TRANSMITTER Transmitter;
+ LIST_ENTRY ListEntry;
+ PXENVIF_POOL BufferPool;
+ PMDL Mdl;
+ netif_tx_front_ring_t Front;
+ netif_tx_sring_t *Shared;
+ ULONG Reference;
+ BOOLEAN Connected;
+ BOOLEAN Enabled;
+ BOOLEAN Stopped;
+ PXENVIF_TRANSMITTER_PACKET Lock;
+ PKTHREAD LockThread;
+ TRANSMITTER_PACKET_LIST Queued;
+ TRANSMITTER_STATE State;
+ ULONG PacketsQueued;
+ ULONG PacketsGranted;
+ ULONG PacketsCopied;
+ ULONG PacketsFaked;
+ ULONG PacketsUnprepared;
+ ULONG PacketsPrepared;
+ ULONG HeadFreeTag;
+ TRANSMITTER_TAG Tag[MAXIMUM_TAG_COUNT];
+ netif_tx_request_t Pending[MAXIMUM_TAG_COUNT];
+ ULONG RequestsPosted;
+ ULONG RequestsPushed;
+ ULONG ResponsesProcessed;
+ ULONG PacketsSent;
+ TRANSMITTER_PACKET_LIST Completed;
+ ULONG PacketsCompleted;
+ PSOCKADDR_INET AddressTable;
+ ULONG AddressCount;
+ ULONG AddressIndex;
+ PXENVIF_THREAD Thread;
+ XENVIF_TRANSMITTER_PACKET_STATISTICS PacketStatistics;
+ XENVIF_HEADER_STATISTICS HeaderStatistics;
+ TRANSMITTER_OFFLOAD_STATISTICS OffloadStatistics;
+} TRANSMITTER_RING, *PTRANSMITTER_RING;
+
+struct _XENVIF_TRANSMITTER {
+ PXENVIF_FRONTEND Frontend;
+ LIST_ENTRY List;
+ XENVIF_TRANSMITTER_PACKET_METADATA Metadata;
+
+ PXENBUS_DEBUG_INTERFACE DebugInterface;
+ PXENBUS_STORE_INTERFACE StoreInterface;
+ PXENBUS_GNTTAB_INTERFACE GnttabInterface;
+ PXENVIF_VIF_INTERFACE VifInterface;
+
+ PXENBUS_DEBUG_CALLBACK DebugCallback;
+};
+
+static FORCEINLINE PVOID
+__TransmitterAllocate(
+ IN ULONG Length
+ )
+{
+ return __AllocateNonPagedPoolWithTag(Length, TRANSMITTER_POOL);
+}
+
+static FORCEINLINE VOID
+__TransmitterFree(
+ IN PVOID Buffer
+ )
+{
+ __FreePoolWithTag(Buffer, TRANSMITTER_POOL);
+}
+
+static NTSTATUS
+TransmitterBufferCtor(
+ IN PVOID Argument,
+ IN PVOID Object
+ )
+{
+ PTRANSMITTER_BUFFER Buffer = Object;
+ PMDL Mdl;
+ PUCHAR MdlMappedSystemVa;
+ NTSTATUS status;
+
+ UNREFERENCED_PARAMETER(Argument);
+
+ ASSERT(IsZeroMemory(Buffer, sizeof (TRANSMITTER_BUFFER)));
+
+ Mdl = __AllocatePage();
+
+ status = STATUS_NO_MEMORY;
+ if (Mdl == NULL)
+ goto fail1;
+
+ MdlMappedSystemVa = MmGetSystemAddressForMdlSafe(Mdl, NormalPagePriority);
+ RtlFillMemory(MdlMappedSystemVa, PAGE_SIZE, 0xAA);
+
+ Mdl->ByteCount = 0;
+ Buffer->Mdl = Mdl;
+
+ return STATUS_SUCCESS;
+
+fail1:
+ Error("fail1 (%08x)\n", status);
+
+ ASSERT(IsZeroMemory(Buffer, sizeof (TRANSMITTER_BUFFER)));
+
+ return status;
+}
+
+static VOID
+TransmitterBufferDtor(
+ IN PVOID Argument,
+ IN PVOID Object
+ )
+{
+ PTRANSMITTER_BUFFER Buffer = Object;
+ PMDL Mdl;
+
+ UNREFERENCED_PARAMETER(Argument);
+
+ Mdl = Buffer->Mdl;
+ Buffer->Mdl = NULL;
+
+ Mdl->ByteCount = PAGE_SIZE;
+
+ __FreePage(Mdl);
+ ExFreePool(Mdl);
+
+ ASSERT(IsZeroMemory(Buffer, sizeof (TRANSMITTER_BUFFER)));
+}
+
+static FORCEINLINE PTRANSMITTER_BUFFER
+__TransmitterGetBuffer(
+ IN PTRANSMITTER_RING Ring
+ )
+{
+ PTRANSMITTER_BUFFER Buffer;
+
+ Buffer = PoolGet(Ring->BufferPool, TRUE);
+
+ ASSERT(IMPLY(Buffer != NULL, Buffer->Mdl->ByteCount == 0));
+
+ return Buffer;
+}
+
+static FORCEINLINE VOID
+__TransmitterPutBuffer(
+ IN PTRANSMITTER_RING Ring,
+ IN PTRANSMITTER_BUFFER Buffer
+ )
+{
+ ASSERT3U(Buffer->Reference, ==, 0);
+ ASSERT3P(Buffer->Context, ==, NULL);
+
+ Buffer->Mdl->ByteCount = 0;
+
+ PoolPut(Ring->BufferPool, Buffer, TRUE);
+}
+
+static FORCEINLINE PTRANSMITTER_TAG
+__TransmitterGetTag(
+ IN PTRANSMITTER_RING Ring
+ )
+{
+ ULONG Index;
+ PTRANSMITTER_TAG Tag;
+
+ Index = Ring->HeadFreeTag;
+ ASSERT3U(Index, <, MAXIMUM_TAG_COUNT);
+
+ Tag = &Ring->Tag[Index];
+ Ring->HeadFreeTag = Tag->Next;
+ Tag->Next = TAG_INDEX_INVALID;
+
+ return Tag;
+}
+
+static FORCEINLINE
+__TransmitterPutTag(
+ IN PTRANSMITTER_RING Ring,
+ IN PTRANSMITTER_TAG Tag
+ )
+{
+ ULONG Index;
+
+ ASSERT3U(Tag->Length, ==, 0);
+ ASSERT3U(Tag->Offset, ==, 0);
+ ASSERT3U(Tag->Type, ==, TAG_TYPE_INVALID);
+ ASSERT3P(Tag->Context, ==, NULL);
+
+ Index = (ULONG)(Tag - &Ring->Tag[0]);
+ ASSERT3U(Index, <, MAXIMUM_TAG_COUNT);
+
+ ASSERT3U(Tag->Next, ==, TAG_INDEX_INVALID);
+ Tag->Next = Ring->HeadFreeTag;
+ Ring->HeadFreeTag = Index;
+}
+
+static FORCEINLINE VOID
+__RingDebugCallback(
+ IN PTRANSMITTER_RING Ring
+ )
+{
+ PXENVIF_TRANSMITTER Transmitter;
+ ULONG Allocated;
+ ULONG MaximumAllocated;
+ ULONG Count;
+ ULONG MinimumCount;
+
+ Transmitter = Ring->Transmitter;
+
+ DEBUG(Printf,
+ Transmitter->DebugInterface,
+ Transmitter->DebugCallback,
+ "0x%p [%s]\n",
+ Ring,
+ (Ring->Enabled) ? "ENABLED" : "DISABLED");
+
+ PoolGetStatistics(Ring->BufferPool,
+ &Allocated,
+ &MaximumAllocated,
+ &Count,
+ &MinimumCount);
+
+ DEBUG(Printf,
+ Transmitter->DebugInterface,
+ Transmitter->DebugCallback,
+ "BUFFER POOL: Allocated = %u (Maximum = %u)\n",
+ Allocated,
+ MaximumAllocated);
+
+ DEBUG(Printf,
+ Transmitter->DebugInterface,
+ Transmitter->DebugCallback,
+ "BUFFER POOL: Count = %u (Minimum = %u)\n",
+ Count,
+ MinimumCount);
+
+ // Dump front ring
+ DEBUG(Printf,
+ Transmitter->DebugInterface,
+ Transmitter->DebugCallback,
+ "FRONT: req_prod_pvt = %u rsp_cons = %u nr_ents = %u sring = %p\n",
+ Ring->Front.req_prod_pvt,
+ Ring->Front.rsp_cons,
+ Ring->Front.nr_ents,
+ Ring->Front.sring);
+
+ // Dump shared ring
+ DEBUG(Printf,
+ Transmitter->DebugInterface,
+ Transmitter->DebugCallback,
+ "SHARED: req_prod = %u req_event = %u rsp_prod = %u rsp_event = %u\n",
+ Ring->Shared->req_prod,
+ Ring->Shared->req_event,
+ Ring->Shared->rsp_prod,
+ Ring->Shared->rsp_event);
+
+ DEBUG(Printf,
+ Transmitter->DebugInterface,
+ Transmitter->DebugCallback,
+ "RequestsPosted = %u RequestsPushed = %u ResponsesProcessed = %u\n",
+ Ring->RequestsPosted,
+ Ring->RequestsPushed,
+ Ring->ResponsesProcessed);
+
+ DEBUG(Printf,
+ Transmitter->DebugInterface,
+ Transmitter->DebugCallback,
+ "State:\n");
+
+ DEBUG(Printf,
+ Transmitter->DebugInterface,
+ Transmitter->DebugCallback,
+ "- Packet = %p\n",
+ Ring->State.Packet);
+
+ DEBUG(Printf,
+ Transmitter->DebugInterface,
+ Transmitter->DebugCallback,
+ "- Count = %u\n",
+ Ring->State.Count);
+
+ DEBUG(Printf,
+ Transmitter->DebugInterface,
+ Transmitter->DebugCallback,
+ "PacketsGranted = %u PacketsCopied = %u PacketsFaked = %u\n",
+ Ring->PacketsGranted,
+ Ring->PacketsCopied,
+ Ring->PacketsFaked);
+
+ DEBUG(Printf,
+ Transmitter->DebugInterface,
+ Transmitter->DebugCallback,
+ "PacketsQueued = %u PacketsPrepared = %u PacketsUnprepared = %u PacketsSent = %u PacketsCompleted = %u\n",
+ Ring->PacketsQueued,
+ Ring->PacketsPrepared,
+ Ring->PacketsUnprepared,
+ Ring->PacketsSent,
+ Ring->PacketsCompleted);
+
+ DEBUG(Printf,
+ Transmitter->DebugInterface,
+ Transmitter->DebugCallback,
+ "PacketStatistics:\n");
+
+ DEBUG(Printf,
+ Transmitter->DebugInterface,
+ Transmitter->DebugCallback,
+ "- Drop = %u\n",
+ Ring->PacketStatistics.Drop);
+
+ DEBUG(Printf,
+ Transmitter->DebugInterface,
+ Transmitter->DebugCallback,
+ "- BackendError = %u\n",
+ Ring->PacketStatistics.BackendError);
+
+ DEBUG(Printf,
+ Transmitter->DebugInterface,
+ Transmitter->DebugCallback,
+ "- FrontendError = %u\n",
+ Ring->PacketStatistics.FrontendError);
+
+ DEBUG(Printf,
+ Transmitter->DebugInterface,
+ Transmitter->DebugCallback,
+ "- Unicast = %u\n",
+ Ring->PacketStatistics.Unicast);
+
+ DEBUG(Printf,
+ Transmitter->DebugInterface,
+ Transmitter->DebugCallback,
+ "- UnicastBytes = %u\n",
+ Ring->PacketStatistics.UnicastBytes);
+
+ DEBUG(Printf,
+ Transmitter->DebugInterface,
+ Transmitter->DebugCallback,
+ "- Multicast = %u\n",
+ Ring->PacketStatistics.Multicast);
+
+ DEBUG(Printf,
+ Transmitter->DebugInterface,
+ Transmitter->DebugCallback,
+ "- MulticastBytes = %u\n",
+ Ring->PacketStatistics.MulticastBytes);
+
+ DEBUG(Printf,
+ Transmitter->DebugInterface,
+ Transmitter->DebugCallback,
+ "- Broadcast = %u\n",
+ Ring->PacketStatistics.Broadcast);
+
+ DEBUG(Printf,
+ Transmitter->DebugInterface,
+ Transmitter->DebugCallback,
+ "- BroadcastBytes = %u\n",
+ Ring->PacketStatistics.BroadcastBytes);
+
+ DEBUG(Printf,
+ Transmitter->DebugInterface,
+ Transmitter->DebugCallback,
+ "HeaderStatistics:\n");
+
+ DEBUG(Printf,
+ Transmitter->DebugInterface,
+ Transmitter->DebugCallback,
+ "- Tagged = %u\n",
+ Ring->HeaderStatistics.Tagged);
+
+ DEBUG(Printf,
+ Transmitter->DebugInterface,
+ Transmitter->DebugCallback,
+ "- LLC = %u\n",
+ Ring->HeaderStatistics.LLC);
+
+ DEBUG(Printf,
+ Transmitter->DebugInterface,
+ Transmitter->DebugCallback,
+ "- Ip Version4 = %u\n",
+ Ring->HeaderStatistics.IpVersion4);
+
+ DEBUG(Printf,
+ Transmitter->DebugInterface,
+ Transmitter->DebugCallback,
+ "- Ip Version6 = %u\n",
+ Ring->HeaderStatistics.IpVersion6);
+
+ DEBUG(Printf,
+ Transmitter->DebugInterface,
+ Transmitter->DebugCallback,
+ "- Ip Options = %u\n",
+ Ring->HeaderStatistics.IpOptions);
+
+ DEBUG(Printf,
+ Transmitter->DebugInterface,
+ Transmitter->DebugCallback,
+ "- Tcp = %u\n",
+ Ring->HeaderStatistics.Tcp);
+
+ DEBUG(Printf,
+ Transmitter->DebugInterface,
+ Transmitter->DebugCallback,
+ "- Tcp Options = %u\n",
+ Ring->HeaderStatistics.TcpOptions);
+
+ DEBUG(Printf,
+ Transmitter->DebugInterface,
+ Transmitter->DebugCallback,
+ "- Udp = %u\n",
+ Ring->HeaderStatistics.Udp);
+
+ DEBUG(Printf,
+ Transmitter->DebugInterface,
+ Transmitter->DebugCallback,
+ "OffloadStatistics:\n");
+
+ DEBUG(Printf,
+ Transmitter->DebugInterface,
+ Transmitter->DebugCallback,
+ "- TagManipulation = %u\n",
+ Ring->OffloadStatistics.TagManipulation);
+
+ DEBUG(Printf,
+ Transmitter->DebugInterface,
+ Transmitter->DebugCallback,
+ "- IpVersion4LargePacket = %u\n",
+ Ring->OffloadStatistics.IpVersion4LargePacket);
+
+ DEBUG(Printf,
+ Transmitter->DebugInterface,
+ Transmitter->DebugCallback,
+ "- IpVersion6LargePacket = %u\n",
+ Ring->OffloadStatistics.IpVersion6LargePacket);
+
+ DEBUG(Printf,
+ Transmitter->DebugInterface,
+ Transmitter->DebugCallback,
+ "- IpVersion4HeaderChecksum = %u\n",
+ Ring->OffloadStatistics.IpVersion4HeaderChecksum);
+
+ DEBUG(Printf,
+ Transmitter->DebugInterface,
+ Transmitter->DebugCallback,
+ "- IpVersion4TcpChecksum = %u\n",
+ Ring->OffloadStatistics.IpVersion4TcpChecksum);
+
+ DEBUG(Printf,
+ Transmitter->DebugInterface,
+ Transmitter->DebugCallback,
+ "- IpVersion6TcpChecksum = %u\n",
+ Ring->OffloadStatistics.IpVersion6TcpChecksum);
+
+ DEBUG(Printf,
+ Transmitter->DebugInterface,
+ Transmitter->DebugCallback,
+ "- IpVersion4UdpChecksum = %u\n",
+ Ring->OffloadStatistics.IpVersion4UdpChecksum);
+
+ DEBUG(Printf,
+ Transmitter->DebugInterface,
+ Transmitter->DebugCallback,
+ "- IpVersion6UdpChecksum = %u\n",
+ Ring->OffloadStatistics.IpVersion6UdpChecksum);
+
+ if (Ring->AddressCount != 0) {
+ ULONG Index;
+
+ for (Index = 0; Index < Ring->AddressCount; Index++) {
+ switch (Ring->AddressTable[Index].si_family) {
+ case AF_INET: {
+ IPV4_ADDRESS Address;
+
+ RtlCopyMemory(Address.Byte,
+ &Ring->AddressTable[Index].Ipv4.sin_addr.s_addr,
+ IPV4_ADDRESS_LENGTH);
+
+ DEBUG(Printf,
+ Transmitter->DebugInterface,
+ Transmitter->DebugCallback,
+ "AddressTable[%u]: %u.%u.%u.%u\n",
+ Index,
+ Address.Byte[0],
+ Address.Byte[1],
+ Address.Byte[2],
+ Address.Byte[3]);
+ break;
+ }
+ case AF_INET6: {
+ IPV6_ADDRESS Address;
+
+ RtlCopyMemory(Address.Byte,
+ &Ring->AddressTable[Index].Ipv6.sin6_addr.s6_addr,
+ IPV6_ADDRESS_LENGTH);
+
+ DEBUG(Printf,
+ Transmitter->DebugInterface,
+ Transmitter->DebugCallback,
+ "AddressTable[%u]: %04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x\n",
+ Index,
+ NTOHS(Address.Word[0]),
+ NTOHS(Address.Word[1]),
+ NTOHS(Address.Word[2]),
+ NTOHS(Address.Word[3]),
+ NTOHS(Address.Word[4]),
+ NTOHS(Address.Word[5]),
+ NTOHS(Address.Word[6]),
+ NTOHS(Address.Word[7]));
+ break;
+ }
+ }
+ }
+ }
+}
+
+static BOOLEAN
+TransmitterPullup(
+ IN PVOID Argument,
+ IN PUCHAR DestinationVa,
+ IN OUT PXENVIF_PACKET_PAYLOAD Payload,
+ IN ULONG Length
+ )
+{
+ PMDL Mdl;
+ ULONG Offset;
+
+ UNREFERENCED_PARAMETER(Argument);
+
+ Mdl = Payload->Mdl;
+ Offset = Payload->Offset;
+
+ if (Payload->Length < Length)
+ goto fail1;
+
+ Payload->Length -= Length;
+
+ while (Length != 0) {
+ PUCHAR MdlMappedSystemVa;
+ ULONG MdlByteCount;
+ ULONG CopyLength;
+
+ ASSERT(Mdl != NULL);
+
+ MdlMappedSystemVa = MmGetSystemAddressForMdlSafe(Mdl, NormalPagePriority);
+ ASSERT(MdlMappedSystemVa != NULL);
+
+ MdlMappedSystemVa += Offset;
+
+ MdlByteCount = Mdl->ByteCount - Offset;
+
+ CopyLength = __min(MdlByteCount, Length);
+
+ RtlCopyMemory(DestinationVa, MdlMappedSystemVa, CopyLength);
+ DestinationVa += CopyLength;
+
+ Offset += CopyLength;
+ Length -= CopyLength;
+
+ MdlByteCount -= CopyLength;
+ if (MdlByteCount == 0) {
+ Mdl = Mdl->Next;
+ Offset = 0;
+ }
+ }
+
+ Payload->Mdl = Mdl;
+ Payload->Offset = Offset;
+
+ return TRUE;
+
+fail1:
+ Error("fail1\n");
+
+ return FALSE;
+}
+
+#define INCREMENT_PACKET_REFERENCE(_Packet) \
+ do { \
+ PULONG_PTR Reference = (PULONG_PTR)&(_Packet)->Next; \
+ \
+ (*Reference)++; \
+ } while (FALSE)
+
+#define DECREMENT_PACKET_REFERENCE(_Packet) \
+ do { \
+ PULONG_PTR Reference = (PULONG_PTR)&(_Packet)->Next; \
+ \
+ ASSERT(*Reference != 0); \
+ --(*Reference); \
+ } while (FALSE)
+
+#define PACKET_REFERENCE(_Packet) \
+ (*(PULONG_PTR)&(_Packet)->Next)
+
+static FORCEINLINE NTSTATUS
+__RingCopyPayload(
+ IN PTRANSMITTER_RING Ring
+ )
+{
+ PXENVIF_TRANSMITTER Transmitter;
+ PXENVIF_FRONTEND Frontend;
+ PTRANSMITTER_STATE State;
+ PXENVIF_TRANSMITTER_PACKET Packet;
+ XENVIF_PACKET_PAYLOAD Payload;
+ PTRANSMITTER_TAG Tag;
+ PTRANSMITTER_BUFFER Buffer;
+ NTSTATUS status;
+
+ Transmitter = Ring->Transmitter;
+ Frontend = Transmitter->Frontend;
+
+ State = &Ring->State;
+ Packet = State->Packet;
+
+ Payload = State->Payload;
+
+ ASSERT3U(PACKET_REFERENCE(Packet), ==, 1);
+
+ while (Payload.Length != 0) {
+ PMDL Mdl;
+ ULONG Length;
+ PUCHAR MdlMappedSystemVa;
+ PFN_NUMBER Pfn;
+
+ Buffer = __TransmitterGetBuffer(Ring);
+
+ status = STATUS_NO_MEMORY;
+ if (Buffer == NULL)
+ goto fail1;
+
+ Buffer->Context = Packet;
+ INCREMENT_PACKET_REFERENCE(Packet);
+
+ Mdl = Buffer->Mdl;
+
+ Length = __min(Payload.Length, PAGE_SIZE);
+
+ MdlMappedSystemVa = MmGetSystemAddressForMdlSafe(Mdl, NormalPagePriority);
+ TransmitterPullup(NULL, MdlMappedSystemVa, &Payload, Length);
+
+ Mdl->ByteCount = Length;
+
+ Tag = __TransmitterGetTag(Ring);
+
+ Tag->Type = TAG_BUFFER;
+ Tag->Context = Buffer;
+ Buffer->Reference++;
+
+ Pfn = MmGetMdlPfnArray(Mdl)[0];
+
+ GNTTAB(PermitForeignAccess,
+ Transmitter->GnttabInterface,
+ Tag->Reference,
+ FrontendGetBackendDomain(Frontend),
+ GNTTAB_ENTRY_FULL_PAGE,
+ Pfn,
+ TRUE);
+
+ Tag->Offset = 0;
+ Tag->Length = Mdl->ByteCount;
+
+ ASSERT(IsZeroMemory(&Tag->ListEntry, sizeof (LIST_ENTRY)));
+ InsertTailList(&State->List, &Tag->ListEntry);
+ State->Count++;
+
+ ASSERT3U(State->Count, <=, MAX_SKB_FRAGS + 1);
+ }
+
+ Ring->PacketsCopied++;
+ return STATUS_SUCCESS;
+
+fail1:
+ Error("fail1 (%08x)\n", status);
+
+ while (PACKET_REFERENCE(Packet) != 1) {
+ PLIST_ENTRY ListEntry;
+
+ ASSERT(State->Count != 0);
+ --State->Count;
+
+ ListEntry = RemoveTailList(&State->List);
+ ASSERT3P(ListEntry, !=, &State->List);
+
+ RtlZeroMemory(ListEntry, sizeof (LIST_ENTRY));
+
+ Tag = CONTAINING_RECORD(ListEntry, TRANSMITTER_TAG, ListEntry);
+
+ Tag->Length = 0;
+ Tag->Offset = 0;
+
+ GNTTAB(RevokeForeignAccess,
+ Transmitter->GnttabInterface,
+ Tag->Reference);
+
+ ASSERT3U(Tag->Type, ==, TAG_BUFFER);
+ Buffer = Tag->Context;
+ Tag->Context = NULL;
+ Tag->Type = TAG_TYPE_INVALID;
+
+ ASSERT(Buffer->Reference != 0);
+ --Buffer->Reference;
+
+ __TransmitterPutTag(Ring, Tag);
+
+ ASSERT3P(Buffer->Context, ==, Packet);
+ Buffer->Context = NULL;
+
+ DECREMENT_PACKET_REFERENCE(Packet);
+
+ __TransmitterPutBuffer(Ring, Buffer);
+ }
+
+ return status;
+}
+
+static FORCEINLINE NTSTATUS
+__RingGrantPayload(
+ IN PTRANSMITTER_RING Ring
+ )
+{
+ PXENVIF_TRANSMITTER Transmitter;
+ PXENVIF_FRONTEND Frontend;
+ PTRANSMITTER_STATE State;
+ PXENVIF_TRANSMITTER_PACKET Packet;
+ PXENVIF_PACKET_PAYLOAD Payload;
+ PMDL Mdl;
+ ULONG Offset;
+ ULONG Length;
+ NTSTATUS status;
+
+ Transmitter = Ring->Transmitter;
+ Frontend = Transmitter->Frontend;
+
+ State = &Ring->State;
+ Packet = State->Packet;
+ Payload = &State->Payload;
+
+ ASSERT3U(PACKET_REFERENCE(Packet), ==, 1);
+
+ Mdl = Payload->Mdl;
+ Offset = Payload->Offset;
+ Length = Payload->Length;
+
+ while (Length != 0) {
+ ULONG MdlOffset;
+ ULONG MdlByteCount;
+ ULONG MdlLength;
+
+ MdlOffset = Mdl->ByteOffset + Offset;
+ MdlByteCount = Mdl->ByteCount - Offset;
+
+ MdlLength = __min(MdlByteCount, Length);
+
+ while (MdlLength != 0) {
+ PTRANSMITTER_TAG Tag;
+ PFN_NUMBER Pfn;
+ ULONG PageOffset;
+ ULONG PageLength;
+
+ Tag = __TransmitterGetTag(Ring);
+
+ Tag->Type = TAG_PACKET;
+ Tag->Context = Packet;
+ INCREMENT_PACKET_REFERENCE(Packet);
+
+ Pfn = MmGetMdlPfnArray(Mdl)[MdlOffset / PAGE_SIZE];
+ PageOffset = MdlOffset & (PAGE_SIZE - 1);
+ PageLength = __min(MdlLength, PAGE_SIZE - PageOffset);
+
+ GNTTAB(PermitForeignAccess,
+ Transmitter->GnttabInterface,
+ Tag->Reference,
+ FrontendGetBackendDomain(Frontend),
+ GNTTAB_ENTRY_FULL_PAGE,
+ Pfn,
+ TRUE);
+
+ Tag->Offset = PageOffset;
+ Tag->Length = PageLength;
+
+ ASSERT(IsZeroMemory(&Tag->ListEntry, sizeof (LIST_ENTRY)));
+ InsertTailList(&State->List, &Tag->ListEntry);
+ State->Count++;
+
+ // Bounce the packet if it is too highly fragmented
+ status = STATUS_BUFFER_OVERFLOW;
+ if (State->Count > MAX_SKB_FRAGS + 1)
+ goto fail1;
+
+ MdlOffset += PageLength;
+
+ ASSERT3U(MdlLength, >=, PageLength);
+ MdlLength -= PageLength;
+
+ ASSERT3U(Length, >=, PageLength);
+ Length -= PageLength;
+ }
+
+ Mdl = Mdl->Next;
+ Offset = 0;
+ }
+
+ Ring->PacketsGranted++;
+ return STATUS_SUCCESS;
+
+fail1:
+ while (PACKET_REFERENCE(Packet) != 1) {
+ PLIST_ENTRY ListEntry;
+ PTRANSMITTER_TAG Tag;
+
+ ASSERT(State->Count != 0);
+ --State->Count;
+
+ ListEntry = RemoveTailList(&State->List);
+ ASSERT3P(ListEntry, !=, &State->List);
+
+ RtlZeroMemory(ListEntry, sizeof (LIST_ENTRY));
+
+ Tag = CONTAINING_RECORD(ListEntry, TRANSMITTER_TAG, ListEntry);
+
+ Tag->Length = 0;
+ Tag->Offset = 0;
+
+ GNTTAB(RevokeForeignAccess,
+ Transmitter->GnttabInterface,
+ Tag->Reference);
+
+ Tag->Context = NULL;
+ Tag->Type = TAG_TYPE_INVALID;
+
+ DECREMENT_PACKET_REFERENCE(Packet);
+
+ __TransmitterPutTag(Ring, Tag);
+ }
+
+ return status;
+}
+
+static FORCEINLINE NTSTATUS
+__RingPrepareHeader(
+ IN PTRANSMITTER_RING Ring
+ )
+{
+ PXENVIF_TRANSMITTER Transmitter;
+ PXENVIF_FRONTEND Frontend;
+ PXENVIF_MAC Mac;
+ PTRANSMITTER_STATE State;
+ PXENVIF_TRANSMITTER_PACKET Packet;
+ PXENVIF_PACKET_PAYLOAD Payload;
+ PXENVIF_PACKET_INFO Info;
+ PTRANSMITTER_TAG Tag;
+ PTRANSMITTER_BUFFER Buffer;
+ PMDL Mdl;
+ PUCHAR StartVa;
+ PFN_NUMBER Pfn;
+ PETHERNET_HEADER EthernetHeader;
+ NTSTATUS status;
+
+ Transmitter = Ring->Transmitter;
+ Frontend = Transmitter->Frontend;
+ Mac = FrontendGetMac(Frontend);
+
+ State = &Ring->State;
+ Packet = State->Packet;
+ Payload = &State->Payload;
+ Info = &State->Info;
+
+ ASSERT3U(PACKET_REFERENCE(Packet), ==, 0);
+
+ Buffer = __TransmitterGetBuffer(Ring);
+
+ status = STATUS_NO_MEMORY;
+ if (Buffer == NULL)
+ goto fail1;
+
+ Buffer->Context = Packet;
+ INCREMENT_PACKET_REFERENCE(Packet);
+
+ Mdl = Buffer->Mdl;
+
+ StartVa = MmGetSystemAddressForMdlSafe(Mdl, NormalPagePriority);
+ ASSERT(StartVa != NULL);
+
+ status = ParsePacket(StartVa, TransmitterPullup, NULL, &Ring->HeaderStatistics, Payload, Info);
+ if (!NT_SUCCESS(status))
+ goto fail2;
+
+ State->StartVa = StartVa;
+
+ Mdl->ByteCount = Info->Length;
+
+ Tag = __TransmitterGetTag(Ring);
+
+ Tag->Context = Buffer;
+ Tag->Type = TAG_BUFFER;
+ Buffer->Reference++;
+
+ Pfn = MmGetMdlPfnArray(Mdl)[0];
+
+ GNTTAB(PermitForeignAccess,
+ Transmitter->GnttabInterface,
+ Tag->Reference,
+ FrontendGetBackendDomain(Frontend),
+ GNTTAB_ENTRY_FULL_PAGE,
+ Pfn,
+ TRUE);
+
+ Tag->Offset = 0;
+ Tag->Length = Mdl->ByteCount + Payload->Length;
+
+ ASSERT(IsZeroMemory(&Tag->ListEntry, sizeof (LIST_ENTRY)));
+ InsertTailList(&State->List, &Tag->ListEntry);
+ State->Count++;
+
+ ASSERT(Info->EthernetHeader.Length != 0);
+ EthernetHeader = (PETHERNET_HEADER)(StartVa + Info->EthernetHeader.Offset);
+
+ if (State->Send.OffloadOptions.OffloadTagManipulation) {
+ ULONG Offset;
+
+ Ring->OffloadStatistics.TagManipulation++;
+
+ ASSERT(!ETHERNET_HEADER_IS_TAGGED(EthernetHeader));
+
+ Offset = FIELD_OFFSET(ETHERNET_TAGGED_HEADER, Tag);
+
+ RtlMoveMemory((PUCHAR)EthernetHeader + Offset + sizeof (ETHERNET_TAG),
+ (PUCHAR)EthernetHeader + Offset,
+ Mdl->ByteCount - Offset);
+
+ // Insert the tag
+ EthernetHeader->Tagged.Tag.ProtocolID = HTONS(ETHERTYPE_TPID);
+ EthernetHeader->Tagged.Tag.ControlInformation = HTONS(State->Send.TagControlInformation);
+ ASSERT(ETHERNET_HEADER_IS_TAGGED(EthernetHeader));
+
+ Ring->HeaderStatistics.Tagged++;
+
+ Mdl->ByteCount += sizeof (ETHERNET_TAG);
+ Tag->Length += sizeof (ETHERNET_TAG);
+
+ // Fix up the packet information
+ Info->EthernetHeader.Length += sizeof (ETHERNET_TAG);
+ Info->Length += sizeof (ETHERNET_TAG);
+
+ if (Info->IpHeader.Length != 0)
+ Info->IpHeader.Offset += sizeof (ETHERNET_TAG);
+
+ if (Info->IpOptions.Length != 0)
+ Info->IpOptions.Offset += sizeof (ETHERNET_TAG);
+
+ if (Info->UdpHeader.Length != 0)
+ Info->UdpHeader.Offset += sizeof (ETHERNET_TAG);
+
+ if (Info->TcpHeader.Length != 0)
+ Info->TcpHeader.Offset += sizeof (ETHERNET_TAG);
+
+ if (Info->TcpOptions.Length != 0)
+ Info->TcpOptions.Offset += sizeof (ETHERNET_TAG);
+ }
+
+ if (State->Send.OffloadOptions.OffloadIpVersion4LargePacket) {
+ PIP_HEADER IpHeader;
+ PTCP_HEADER TcpHeader;
+ ULONG Length;
+
+ Ring->OffloadStatistics.IpVersion4LargePacket++;
+
+ ASSERT(Info->IpHeader.Length != 0);
+ IpHeader = (PIP_HEADER)(StartVa + Info->IpHeader.Offset);
+
+ ASSERT(Info->TcpHeader.Length != 0);
+ TcpHeader = (PTCP_HEADER)(StartVa + Info->TcpHeader.Offset);
+
+ // Fix up the IP packet length
+ Length = Info->IpHeader.Length +
+ Info->IpOptions.Length +
+ Info->TcpHeader.Length +
+ Info->TcpOptions.Length +
+ Payload->Length;
+
+ ASSERT3U((USHORT)Length, ==, Length);
+
+ ASSERT3U(IpHeader->Version, ==, 4);
+
+ IpHeader->Version4.PacketLength = HTONS((USHORT)Length);
+
+ // IP checksum calulcation must be offloaded for large packets
+ State->Send.OffloadOptions.OffloadIpVersion4HeaderChecksum = 1;
+
+ // TCP checksum calulcation must be offloaded for large packets
+ TcpHeader->Checksum = ChecksumPseudoHeader(StartVa, Info);
+ State->Send.OffloadOptions.OffloadIpVersion4TcpChecksum = 1;
+
+ // If the MSS is such that the payload would constitute only a single fragment then
+ // we no longer need trate the packet as a large packet.
+ ASSERT3U(State->Send.MaximumSegmentSize, <=, Payload->Length);
+ if (State->Send.MaximumSegmentSize == Payload->Length)
+ State->Send.OffloadOptions.OffloadIpVersion4LargePacket = 0;
+ }
+
+ if (State->Send.OffloadOptions.OffloadIpVersion6LargePacket) {
+ PIP_HEADER IpHeader;
+ PTCP_HEADER TcpHeader;
+ ULONG Length;
+
+ Ring->OffloadStatistics.IpVersion6LargePacket++;
+
+ ASSERT(Info->IpHeader.Length != 0);
+ IpHeader = (PIP_HEADER)(StartVa + Info->IpHeader.Offset);
+
+ ASSERT(Info->TcpHeader.Length != 0);
+ TcpHeader = (PTCP_HEADER)(StartVa + Info->TcpHeader.Offset);
+
+ // Fix up the IP payload length
+ Length = Info->IpOptions.Length +
+ Info->TcpHeader.Length +
+ Info->TcpOptions.Length +
+ Payload->Length;
+
+ ASSERT3U((USHORT)Length, ==, Length);
+
+ ASSERT3U(IpHeader->Version, ==, 6);
+
+ IpHeader->Version6.PayloadLength = HTONS((USHORT)Length);
+
+ // TCP checksum calulcation must be offloaded for large packets
+ TcpHeader->Checksum = ChecksumPseudoHeader(StartVa, Info);
+ State->Send.OffloadOptions.OffloadIpVersion6TcpChecksum = 1;
+
+ // If the MSS is such that the payload would constitute only a single fragment then
+ // we no longer need trate the packet as a large packet.
+ ASSERT3U(State->Send.MaximumSegmentSize, <=, Payload->Length);
+ if (State->Send.MaximumSegmentSize == Payload->Length)
+ State->Send.OffloadOptions.OffloadIpVersion6LargePacket = 0;
+ }
+
+ // Non-GSO packets must not exceed MTU
+ if (!State->Send.OffloadOptions.OffloadIpVersion4LargePacket &&
+ !State->Send.OffloadOptions.OffloadIpVersion6LargePacket &&
+ Tag->Length > MacGetMaximumFrameSize(Mac)) {
+ status = STATUS_INVALID_PARAMETER;
+ goto fail3;
+ }
+
+ if (State->Send.OffloadOptions.OffloadIpVersion4HeaderChecksum) {
+ PIP_HEADER IpHeader;
+
+ ASSERT(Info->IpHeader.Length != 0);
+ IpHeader = (PIP_HEADER)(StartVa + Info->IpHeader.Offset);
+
+ ASSERT3U(IpHeader->Version, ==, 4);
+ IpHeader->Version4.Checksum = ChecksumIpVersion4Header(StartVa, Info);
+
+ Ring->OffloadStatistics.IpVersion4HeaderChecksum++;
+ }
+#ifdef VERIFY_CHECKSUMS
+ else if (Info->IpHeader.Length != 0) {
+ PIP_HEADER IpHeader;
+
+ ASSERT(Info->IpHeader.Length != 0);
+ IpHeader = (PIP_HEADER)(StartVa + Info->IpHeader.Offset);
+
+ if (IpHeader->Version == 4) {
+ USHORT Embedded;
+ USHORT Calculated;
+
+ Embedded = IpHeader->Version4.Checksum;
+ Calculated = ChecksumIpVersion4Header(StartVa, Info);
+
+ if (Embedded != Calculated)
+ Warning("Bad Ip Version 4 Header Checksum (expected %04x, found %04x)\n",
+ Calculated,
+ Embedded);
+ }
+ }
+#endif // VERIFY_CHECKSUMS
+
+ if (State->Send.OffloadOptions.OffloadIpVersion4TcpChecksum) {
+ Ring->OffloadStatistics.IpVersion4TcpChecksum++;
+ }
+#ifdef VERIFY_CHECKSUMS
+ else if (Info->TcpHeader.Length != 0) {
+ PIP_HEADER IpHeader;
+
+ ASSERT(Info->IpHeader.Length != 0);
+ IpHeader = (PIP_HEADER)(StartVa + Info->IpHeader.Offset);
+
+ if (IpHeader->Version == 4) {
+ PTCP_HEADER TcpHeader;
+ USHORT Embedded;
+ USHORT Calculated;
+
+ TcpHeader = (PTCP_HEADER)(StartVa + Info->TcpHeader.Offset);
+
+ Embedded = TcpHeader->Checksum;
+ Calculated = ChecksumPseudoHeader(StartVa, Info);
+
+ if (Embedded != Calculated) {
+ Calculated = ChecksumTcpPacket(StartVa, Info, Calculated, Payload);
+
+ if (Embedded != Calculated)
+ Warning("Bad Ip Version 4 TCP Checksum (expected %04x, found %04x)\n",
+ Calculated,
+ Embedded);
+ } else {
+ Warning("Ip Version 4 TCP Checksum only covers pseudo-header\n");
+ }
+ }
+ }
+#endif // VERIFY_CHECKSUMS
+
+ if (State->Send.OffloadOptions.OffloadIpVersion6TcpChecksum) {
+ Ring->OffloadStatistics.IpVersion6TcpChecksum++;
+ }
+#ifdef VERIFY_CHECKSUMS
+ else if (Info->TcpHeader.Length != 0) {
+ PIP_HEADER IpHeader;
+
+ ASSERT(Info->IpHeader.Length != 0);
+ IpHeader = (PIP_HEADER)(StartVa + Info->IpHeader.Offset);
+
+ if (IpHeader->Version == 6) {
+ PTCP_HEADER TcpHeader;
+ USHORT Embedded;
+ USHORT Calculated;
+
+ TcpHeader = (PTCP_HEADER)(StartVa + Info->TcpHeader.Offset);
+
+ Embedded = TcpHeader->Checksum;
+ Calculated = ChecksumPseudoHeader(StartVa, Info);
+
+ if (Embedded != Calculated) {
+ Calculated = ChecksumTcpPacket(StartVa, Info, Calculated, Payload);
+
+ if (Embedded != Calculated)
+ Warning("Bad Ip Version 6 TCP Checksum (expected %04x, found %04x)\n",
+ Calculated,
+ Embedded);
+ } else {
+ Warning("Ip Version 6 TCP Checksum only covers pseudo-header\n");
+ }
+ }
+ }
+#endif // VERIFY_CHECKSUMS
+
+ if (State->Send.OffloadOptions.OffloadIpVersion4UdpChecksum) {
+ Ring->OffloadStatistics.IpVersion4UdpChecksum++;
+ }
+#ifdef VERIFY_CHECKSUMS
+ else if (Info->UdpHeader.Length != 0) {
+ PIP_HEADER IpHeader;
+
+ ASSERT(Info->IpHeader.Length != 0);
+ IpHeader = (PIP_HEADER)(StartVa + Info->IpHeader.Offset);
+
+ if (IpHeader->Version == 4) {
+ PUDP_HEADER UdpHeader;
+ USHORT Embedded;
+ USHORT Calculated;
+
+ UdpHeader = (PUDP_HEADER)(StartVa + Info->UdpHeader.Offset);
+
+ Embedded = UdpHeader->Checksum;
+ Calculated = ChecksumPseudoHeader(StartVa, Info);
+
+ if (Embedded != Calculated) {
+ Calculated = ChecksumUdpPacket(StartVa, Info, Calculated, Payload);
+
+ if (Embedded != Calculated)
+ Warning("Bad Ip Version 4 UDP Checksum (expected %04x, found %04x)\n",
+ Calculated,
+ Embedded);
+ } else {
+ Warning("Ip Version 4 UDP Checksum only covers pseudo-header\n");
+ }
+ }
+ }
+#endif // VERIFY_CHECKSUMS
+
+ if (State->Send.OffloadOptions.OffloadIpVersion6UdpChecksum) {
+ Ring->OffloadStatistics.IpVersion6UdpChecksum++;
+ }
+#ifdef VERIFY_CHECKSUMS
+ else if (Info->UdpHeader.Length != 0) {
+ PIP_HEADER IpHeader;
+
+ ASSERT(Info->IpHeader.Length != 0);
+ IpHeader = (PIP_HEADER)(StartVa + Info->IpHeader.Offset);
+
+ if (IpHeader->Version == 6) {
+ PUDP_HEADER UdpHeader;
+ USHORT Embedded;
+ USHORT Calculated;
+
+ UdpHeader = (PUDP_HEADER)(StartVa + Info->UdpHeader.Offset);
+
+ Embedded = UdpHeader->Checksum;
+ Calculated = ChecksumPseudoHeader(StartVa, Info);
+
+ if (Embedded != Calculated) {
+ Calculated = ChecksumUdpPacket(StartVa, Info, Calculated, Payload);
+
+ if (Embedded != Calculated)
+ Warning("Bad Ip Version 6 UDP Checksum (expected %04x, found %04x)\n",
+ Calculated,
+ Embedded);
+ } else {
+ Warning("Ip Version 6 UDP Checksum only covers pseudo-header\n");
+ }
+ }
+ }
+#endif // VERIFY_CHECKSUMS
+
+ return STATUS_SUCCESS;
+
+fail3:
+ ASSERT(State->Count != 0);
+ --State->Count;
+
+ RemoveEntryList(&Tag->ListEntry);
+ RtlZeroMemory(&Tag->ListEntry, sizeof (LIST_ENTRY));
+
+ Tag->Length = 0;
+ Tag->Offset = 0;
+
+ GNTTAB(RevokeForeignAccess,
+ Transmitter->GnttabInterface,
+ Tag->Reference);
+
+ Tag->Context = NULL;
+ Tag->Type = TAG_TYPE_INVALID;
+
+ ASSERT(Buffer->Reference != 0);
+ --Buffer->Reference;
+
+ __TransmitterPutTag(Ring, Tag);
+
+ Mdl->ByteCount = 0;
+
+fail2:
+ DECREMENT_PACKET_REFERENCE(Packet);
+ Buffer->Context = NULL;
+
+ __TransmitterPutBuffer(Ring, Buffer);
+
+fail1:
+ ASSERT3U(PACKET_REFERENCE(Packet), ==, 0);
+
+ return status;
+}
+
+static FORCEINLINE VOID
+__RingUnprepareTags(
+ IN PTRANSMITTER_RING Ring
+ )
+{
+ PXENVIF_TRANSMITTER Transmitter;
+ PXENVIF_FRONTEND Frontend;
+ PTRANSMITTER_STATE State;
+
+ Transmitter = Ring->Transmitter;
+ Frontend = Transmitter->Frontend;
+
+ State = &Ring->State;
+
+ while (State->Count != 0) {
+ PLIST_ENTRY ListEntry;
+ PTRANSMITTER_TAG Tag;
+ PXENVIF_TRANSMITTER_PACKET Packet;
+
+ --State->Count;
+
+ ListEntry = RemoveTailList(&State->List);
+ ASSERT3P(ListEntry, !=, &State->List);
+
+ RtlZeroMemory(ListEntry, sizeof (LIST_ENTRY));
+
+ Tag = CONTAINING_RECORD(ListEntry, TRANSMITTER_TAG, ListEntry);
+
+ Tag->Length = 0;
+ Tag->Offset = 0;
+
+ GNTTAB(RevokeForeignAccess,
+ Transmitter->GnttabInterface,
+ Tag->Reference);
+
+ switch (Tag->Type) {
+ case TAG_BUFFER: {
+ PTRANSMITTER_BUFFER Buffer;
+
+ Buffer = Tag->Context;
+ Tag->Context = NULL;
+ Tag->Type = TAG_TYPE_INVALID;
+
+ Packet = Buffer->Context;
+ Buffer->Context = NULL;
+
+ ASSERT(Buffer->Reference != 0);
+ if (--Buffer->Reference == 0)
+ __TransmitterPutBuffer(Ring, Buffer);
+
+ break;
+ }
+ case TAG_PACKET:
+ Packet = Tag->Context;
+ Tag->Context = NULL;
+ Tag->Type = TAG_TYPE_INVALID;
+
+ break;
+
+ default:
+ Packet = NULL;
+ ASSERT(FALSE);
+ }
+
+ __TransmitterPutTag(Ring, Tag);
+
+ if (Packet != NULL)
+ DECREMENT_PACKET_REFERENCE(Packet);
+ }
+}
+
+static FORCEINLINE NTSTATUS
+__RingPreparePacket(
+ IN PTRANSMITTER_RING Ring,
+ IN PXENVIF_TRANSMITTER_PACKET Packet
+ )
+{
+#define METADATA_EXISTS(_Ring, _Packet, _Type) \
+ ((_Ring)->Transmitter->Metadata. _Type ## Offset != 0)
+
+#define METADATA(_Ring, _Packet, _Type) \
+ ((METADATA_EXISTS(_Ring, _Packet, _Type)) ? \
+ (PVOID)((PUCHAR)(_Packet) + (_Ring)->Transmitter->Metadata. _Type ## Offset) : \
+ NULL)
+
+ PTRANSMITTER_STATE State;
+ PXENVIF_PACKET_PAYLOAD Payload;
+ PXENVIF_PACKET_INFO Info;
+ NTSTATUS status;
+
+ ASSERT(IsZeroMemory(&Ring->State, sizeof (TRANSMITTER_STATE)));
+ ASSERT3P(Packet->Next, ==, NULL);
+
+ State = &Ring->State;
+
+ State->Packet = Packet;
+
+ State->Send = Packet->Send;
+ RtlZeroMemory(&Packet->Send, sizeof (XENVIF_SEND_INFO));
+
+ Payload = &State->Payload;
+
+ ASSERT(METADATA_EXISTS(Ring, Packet, Mdl));
+ Payload->Mdl = *(PMDL *)METADATA(Ring, Packet, Mdl);
+
+ if (METADATA_EXISTS(Ring, Packet, Offset))
+ Payload->Offset = *(PULONG)METADATA(Ring, Packet, Offset);
+ else
+ Payload->Offset = 0;
+
+ ASSERT(METADATA_EXISTS(Ring, Packet, Length));
+ Payload->Length = *(PULONG)METADATA(Ring, Packet, Length);
+
+ InitializeListHead(&State->List);
+ ASSERT3U(State->Count, ==, 0);
+
+ status = __RingPrepareHeader(Ring);
+ if (!NT_SUCCESS(status))
+ goto fail1;
+
+ ASSERT3U(State->Count, ==, PACKET_REFERENCE(Packet));
+
+ Info = &State->Info;
+
+ // Is the packet too short?
+ if (Info->Length + Payload->Length < ETHERNET_MIN) {
+ ULONG Trailer;
+ BOOLEAN SingleFragment;
+
+ Trailer = ETHERNET_MIN - Payload->Length - Info->Length;
+ SingleFragment = (Payload->Length == 0) ? TRUE : FALSE;
+
+ status = __RingCopyPayload(Ring);
+
+ if (NT_SUCCESS(status)) {
+ PLIST_ENTRY ListEntry;
+ PTRANSMITTER_TAG Tag;
+ PTRANSMITTER_BUFFER Buffer;
+ PMDL Mdl;
+ PUCHAR MdlMappedSystemVa;
+
+ // Add padding to the tail buffer
+ ListEntry = State->List.Blink;
+ Tag = CONTAINING_RECORD(ListEntry, TRANSMITTER_TAG, ListEntry);
+
+ ASSERT3U(Tag->Type, ==, TAG_BUFFER);
+ Buffer = Tag->Context;
+
+ Mdl = Buffer->Mdl;
+
+ ASSERT3U(Mdl->ByteCount, <=, PAGE_SIZE - Trailer);
+
+ MdlMappedSystemVa = MmGetSystemAddressForMdlSafe(Mdl, NormalPagePriority);
+ ASSERT(MdlMappedSystemVa != NULL);
+
+ MdlMappedSystemVa += Mdl->ByteCount;
+
+ RtlZeroMemory(MdlMappedSystemVa, Trailer);
+ Mdl->ByteCount += Trailer;
+
+ if (!SingleFragment) {
+ ASSERT3P(State->List.Flink, !=, ListEntry);
+ Tag->Length += Trailer;
+ }
+
+ // Adjust length of header tag
+ ListEntry = State->List.Flink;
+ Tag = CONTAINING_RECORD(ListEntry, TRANSMITTER_TAG, ListEntry);
+
+ Tag->Length += Trailer;
+ ASSERT3U(Tag->Length, ==, ETHERNET_MIN);
+ }
+ } else {
+ status = __RingGrantPayload(Ring);
+ if (!NT_SUCCESS(status) && status == STATUS_BUFFER_OVERFLOW) {
+ ASSERT3U(State->Count, ==, PACKET_REFERENCE(Packet));
+
+ status = __RingCopyPayload(Ring);
+ }
+ }
+
+ if (!NT_SUCCESS(status))
+ goto fail2;
+
+ ASSERT3U(State->Count, ==, PACKET_REFERENCE(Packet));
+
+ Ring->PacketsPrepared++;
+ return STATUS_SUCCESS;
+
+fail2:
+ __RingUnprepareTags(Ring);
+
+fail1:
+ State->StartVa = NULL;
+ RtlZeroMemory(&State->Info, sizeof (XENVIF_PACKET_INFO));
+
+ ASSERT(IsListEmpty(&State->List));
+ RtlZeroMemory(&State->List, sizeof (LIST_ENTRY));
+
+ RtlZeroMemory(&State->Payload, sizeof (XENVIF_PACKET_PAYLOAD));
+
+ Packet->Send = State->Send;
+ RtlZeroMemory(&State->Send, sizeof (XENVIF_SEND_INFO));
+
+ State->Packet = NULL;
+
+ ASSERT(IsZeroMemory(&Ring->State, sizeof (TRANSMITTER_STATE)));
+
+ return status;
+
+#undef METADATA
+#undef METADATA_EXISTS
+}
+
+static FORCEINLINE PXENVIF_TRANSMITTER_PACKET
+__RingUnpreparePacket(
+ IN PTRANSMITTER_RING Ring
+ )
+{
+ PTRANSMITTER_STATE State;
+ PXENVIF_TRANSMITTER_PACKET Packet;
+
+ State = &Ring->State;
+ Packet = State->Packet;
+
+ // This has the side effect of freeing up resources associated with a pending
+ // gratuitous ARP, which is why the call is not conditional on Packet being
+ // non-NULL
+ __RingUnprepareTags(Ring);
+ RtlZeroMemory(&State->Info, sizeof (XENVIF_PACKET_INFO));
+
+ if (Packet == NULL)
+ goto done;
+
+ Ring->PacketsUnprepared++;
+
+ ASSERT(IsListEmpty(&State->List));
+ RtlZeroMemory(&State->List, sizeof (LIST_ENTRY));
+
+ RtlZeroMemory(&State->Payload, sizeof (XENVIF_PACKET_PAYLOAD));
+
+ Packet->Send = State->Send;
+ RtlZeroMemory(&State->Send, sizeof (XENVIF_SEND_INFO));
+
+ State->Packet = NULL;
+
+ ASSERT(IsZeroMemory(&Ring->State, sizeof (TRANSMITTER_STATE)));
+
+done:
+ return Packet;
+}
+
+static FORCEINLINE NTSTATUS
+__RingPrepareGratuitousArp(
+ IN PTRANSMITTER_RING Ring,
+ IN PIPV4_ADDRESS Address
+ )
+{
+ PXENVIF_TRANSMITTER Transmitter;
+ PXENVIF_FRONTEND Frontend;
+ PXENVIF_MAC Mac;
+ PTRANSMITTER_STATE State;
+ PTRANSMITTER_TAG Tag;
+ PTRANSMITTER_BUFFER Buffer;
+ PMDL Mdl;
+ PUCHAR MdlMappedSystemVa;
+ PETHERNET_UNTAGGED_HEADER EthernetHeader;
+ PARP_HEADER ArpHeader;
+ PETHERNET_ADDRESS SenderHardwareAddress;
+ PIPV4_ADDRESS SenderProtocolAddress;
+ PETHERNET_ADDRESS TargetHardwareAddress;
+ PIPV4_ADDRESS TargetProtocolAddress;
+ PFN_NUMBER Pfn;
+ NTSTATUS status;
+
+ ASSERT(IsZeroMemory(&Ring->State, sizeof (TRANSMITTER_STATE)));
+
+ Info("%u.%u.%u.%u\n",
+ Address->Byte[0],
+ Address->Byte[1],
+ Address->Byte[2],
+ Address->Byte[3]);
+
+ Transmitter = Ring->Transmitter;
+ Frontend = Transmitter->Frontend;
+ Mac = FrontendGetMac(Frontend);
+
+ TargetProtocolAddress = SenderProtocolAddress = Address;
+ SenderHardwareAddress = MacGetCurrentAddress(Mac);
+ TargetHardwareAddress = MacGetBroadcastAddress(Mac);
+
+ State = &Ring->State;
+
+ Buffer = __TransmitterGetBuffer(Ring);
+
+ status = STATUS_NO_MEMORY;
+ if (Buffer == NULL)
+ goto fail1;
+
+ Mdl = Buffer->Mdl;
+
+ MdlMappedSystemVa = MmGetSystemAddressForMdlSafe(Mdl, NormalPagePriority);
+ ASSERT(MdlMappedSystemVa != NULL);
+
+ EthernetHeader = (PETHERNET_UNTAGGED_HEADER)MdlMappedSystemVa;
+
+ RtlCopyMemory(EthernetHeader->DestinationAddress.Byte, MacGetBroadcastAddress(Mac), ETHERNET_ADDRESS_LENGTH);
+ RtlCopyMemory(EthernetHeader->SourceAddress.Byte, MacGetCurrentAddress(Mac), ETHERNET_ADDRESS_LENGTH);
+ EthernetHeader->TypeOrLength = HTONS(ETHERTYPE_ARP);
+
+ MdlMappedSystemVa += sizeof (ETHERNET_UNTAGGED_HEADER);
+
+ ArpHeader = (PARP_HEADER)MdlMappedSystemVa;
+
+ ArpHeader->HardwareType = HTONS(HARDWARE_ETHER);
+ ArpHeader->ProtocolType = HTONS(PROTOCOL_IPV4);
+ ArpHeader->HardwareAddressLength = ETHERNET_ADDRESS_LENGTH;
+ ArpHeader->ProtocolAddressLength = IPV4_ADDRESS_LENGTH;
+ ArpHeader->Operation = HTONS(ARP_REQUEST);
+
+ MdlMappedSystemVa += sizeof (ARP_HEADER);
+
+ RtlCopyMemory(MdlMappedSystemVa, SenderHardwareAddress->Byte, ETHERNET_ADDRESS_LENGTH);
+ MdlMappedSystemVa += ETHERNET_ADDRESS_LENGTH;
+
+ RtlCopyMemory(MdlMappedSystemVa, SenderProtocolAddress->Byte, IPV4_ADDRESS_LENGTH);
+ MdlMappedSystemVa += IPV4_ADDRESS_LENGTH;
+
+ RtlCopyMemory(MdlMappedSystemVa, TargetHardwareAddress->Byte, ETHERNET_ADDRESS_LENGTH);
+ MdlMappedSystemVa += ETHERNET_ADDRESS_LENGTH;
+
+ RtlCopyMemory(MdlMappedSystemVa, TargetProtocolAddress->Byte, IPV4_ADDRESS_LENGTH);
+ MdlMappedSystemVa += IPV4_ADDRESS_LENGTH;
+
+ Mdl->ByteCount = (ULONG)(MdlMappedSystemVa - (PUCHAR)MmGetSystemAddressForMdlSafe(Mdl, NormalPagePriority));
+
+ Tag = __TransmitterGetTag(Ring);
+
+ Tag->Context = Buffer;
+ Tag->Type = TAG_BUFFER;
+ Buffer->Reference++;
+
+ Pfn = MmGetMdlPfnArray(Mdl)[0];
+
+ GNTTAB(PermitForeignAccess,
+ Transmitter->GnttabInterface,
+ Tag->Reference,
+ FrontendGetBackendDomain(Frontend),
+ GNTTAB_ENTRY_FULL_PAGE,
+ Pfn,
+ TRUE);
+
+ Tag->Offset = 0;
+ Tag->Length = Mdl->ByteCount;
+
+ InitializeListHead(&State->List);
+
+ ASSERT(IsZeroMemory(&Tag->ListEntry, sizeof (LIST_ENTRY)));
+ InsertTailList(&State->List, &Tag->ListEntry);
+ State->Count++;
+
+ return STATUS_SUCCESS;
+
+fail1:
+ Error("fail1 (%08x)\n", status);
+
+ ASSERT(IsZeroMemory(&Ring->State, sizeof (TRANSMITTER_STATE)));
+
+ return status;
+}
+
+static FORCEINLINE NTSTATUS
+__RingPrepareNeighbourAdvertisement(
+ IN PTRANSMITTER_RING Ring,
+ IN PIPV6_ADDRESS Address
+ )
+{
+ PXENVIF_TRANSMITTER Transmitter;
+ PXENVIF_FRONTEND Frontend;
+ PXENVIF_MAC Mac;
+ PTRANSMITTER_STATE State;
+ PTRANSMITTER_TAG Tag;
+ PTRANSMITTER_BUFFER Buffer;
+ PMDL Mdl;
+ PUCHAR MdlMappedSystemVa;
+ PETHERNET_UNTAGGED_HEADER EthernetHeader;
+ PIPV6_HEADER IpHeader;
+ PICMPV6_HEADER IcmpHeader;
+ PIPV6_ADDRESS TargetProtocolAddress;
+ PETHERNET_ADDRESS SenderHardwareAddress;
+ USHORT PayloadLength;
+ ULONG Accumulator;
+ PFN_NUMBER Pfn;
+ NTSTATUS status;
+
+ ASSERT(IsZeroMemory(&Ring->State, sizeof (TRANSMITTER_STATE)));
+
+ Info("%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x\n",
+ HTONS(Address->Word[0]),
+ HTONS(Address->Word[1]),
+ HTONS(Address->Word[2]),
+ HTONS(Address->Word[3]),
+ HTONS(Address->Word[4]),
+ HTONS(Address->Word[5]),
+ HTONS(Address->Word[6]),
+ HTONS(Address->Word[7]));
+
+ Transmitter = Ring->Transmitter;
+ Frontend = Transmitter->Frontend;
+ Mac = FrontendGetMac(Frontend);
+
+ TargetProtocolAddress = Address;
+ SenderHardwareAddress = MacGetCurrentAddress(Mac);
+
+ State = &Ring->State;
+
+ Buffer = __TransmitterGetBuffer(Ring);
+
+ status = STATUS_NO_MEMORY;
+ if (Buffer == NULL)
+ goto fail1;
+
+ Mdl = Buffer->Mdl;
+
+ MdlMappedSystemVa = MmGetSystemAddressForMdlSafe(Buffer->Mdl, NormalPagePriority);
+ ASSERT(MdlMappedSystemVa != NULL);
+
+ EthernetHeader = (PETHERNET_UNTAGGED_HEADER)MdlMappedSystemVa;
+
+ RtlCopyMemory(EthernetHeader->DestinationAddress.Byte, MacGetBroadcastAddress(Mac), ETHERNET_ADDRESS_LENGTH);
+ RtlCopyMemory(EthernetHeader->SourceAddress.Byte, MacGetCurrentAddress(Mac), ETHERNET_ADDRESS_LENGTH);
+ EthernetHeader->TypeOrLength = HTONS(ETHERTYPE_IPV6);
+
+ MdlMappedSystemVa += sizeof (ETHERNET_UNTAGGED_HEADER);
+
+ IpHeader = (PIPV6_HEADER)MdlMappedSystemVa;
+ RtlZeroMemory(IpHeader, sizeof (IPV6_HEADER));
+
+ IpHeader->Version = 6;
+ IpHeader->NextHeader = IPPROTO_ICMPV6;
+ IpHeader->HopLimit = 255;
+
+ RtlCopyMemory(IpHeader->SourceAddress.Byte, Address, IPV6_ADDRESS_LENGTH);
+
+ // Destination is all-nodes multicast address
+ IpHeader->DestinationAddress.Byte[0] = 0xFF;
+ IpHeader->DestinationAddress.Byte[1] = 0x02;
+ IpHeader->DestinationAddress.Byte[15] = 0x02;
+
+ PayloadLength = 0;
+ MdlMappedSystemVa += sizeof (IPV6_HEADER);
+
+ IcmpHeader = (PICMPV6_HEADER)MdlMappedSystemVa;
+
+ IcmpHeader->Type = ICMPV6_TYPE_NA;
+ IcmpHeader->Code = 0;
+ IcmpHeader->Data = HTONL(0x02); // Override flag
+
+ PayloadLength += sizeof (ICMPV6_HEADER);
+ MdlMappedSystemVa += sizeof (ICMPV6_HEADER);
+
+ RtlCopyMemory(MdlMappedSystemVa, TargetProtocolAddress->Byte, IPV6_ADDRESS_LENGTH);
+
+ PayloadLength += IPV6_ADDRESS_LENGTH;
+ MdlMappedSystemVa += IPV6_ADDRESS_LENGTH;
+
+ RtlCopyMemory(MdlMappedSystemVa, SenderHardwareAddress->Byte, ETHERNET_ADDRESS_LENGTH);
+
+ PayloadLength += ETHERNET_ADDRESS_LENGTH;
+ MdlMappedSystemVa += ETHERNET_ADDRESS_LENGTH;
+
+ Mdl->ByteCount = (ULONG)(MdlMappedSystemVa - (PUCHAR)MmGetSystemAddressForMdlSafe(Mdl, NormalPagePriority));
+
+ // Fix up IP payload length and ICMPv6 checksum
+ IpHeader->PayloadLength = HTONS(PayloadLength);
+
+ Accumulator = ChecksumIpVersion6PseudoHeader(&IpHeader->SourceAddress,
+ &IpHeader->DestinationAddress,
+ PayloadLength,
+ IPPROTO_ICMPV6);
+ AccumulateChecksum(&Accumulator, IcmpHeader, PayloadLength);
+
+ IcmpHeader->Checksum = FoldChecksum(Accumulator, TRUE);
+
+ Tag = __TransmitterGetTag(Ring);
+
+ Tag->Context = Buffer;
+ Tag->Type = TAG_BUFFER;
+ Buffer->Reference++;
+
+ Pfn = MmGetMdlPfnArray(Mdl)[0];
+
+ GNTTAB(PermitForeignAccess,
+ Transmitter->GnttabInterface,
+ Tag->Reference,
+ FrontendGetBackendDomain(Frontend),
+ GNTTAB_ENTRY_FULL_PAGE,
+ Pfn,
+ TRUE);
+
+ Tag->Offset = 0;
+ Tag->Length = Mdl->ByteCount;
+
+ InitializeListHead(&State->List);
+
+ ASSERT(IsZeroMemory(&Tag->ListEntry, sizeof (LIST_ENTRY)));
+ InsertTailList(&State->List, &Tag->ListEntry);
+ State->Count++;
+
+ return STATUS_SUCCESS;
+
+fail1:
+ Error("fail1 (%08x)\n", status);
+
+ ASSERT(IsZeroMemory(&Ring->State, sizeof (TRANSMITTER_STATE)));
+
+ return status;
+}
+
+static FORCEINLINE NTSTATUS
+__RingPostTags(
+ IN PTRANSMITTER_RING Ring
+ )
+{
+#define RING_SLOTS_AVAILABLE(_Front, _req_prod, _rsp_cons) \
+ (RING_SIZE(_Front) - ((_req_prod) - (_rsp_cons)))
+
+ PTRANSMITTER_STATE State;
+ PXENVIF_TRANSMITTER_PACKET Packet;
+ PXENVIF_PACKET_PAYLOAD Payload;
+ RING_IDX req_prod;
+ RING_IDX rsp_cons;
+ ULONG Extra;
+ ULONG PacketLength;
+ BOOLEAN FirstRequest;
+ netif_tx_request_t *req;
+ NTSTATUS status;
+
+ State = &Ring->State;
+ Packet = State->Packet;
+ Payload = &State->Payload;
+
+ ASSERT(!IsListEmpty(&State->List));
+ ASSERT(State->Count != 0);
+ ASSERT3U(State->Count, <=, MAX_SKB_FRAGS + 1);
+ ASSERT(IMPLY(Packet != NULL, State->Count == PACKET_REFERENCE(Packet)));
+
+ req_prod = Ring->Front.req_prod_pvt;
+ rsp_cons = Ring->Front.rsp_cons;
+
+ Extra = (State->Send.OffloadOptions.OffloadIpVersion4LargePacket ||
+ State->Send.OffloadOptions.OffloadIpVersion6LargePacket) ? 1 : 0;
+
+ ASSERT3U(State->Count + Extra, <=, RING_SIZE(&Ring->Front));
+
+ status = STATUS_ALLOTTED_SPACE_EXCEEDED;
+ if (State->Count + Extra > RING_SLOTS_AVAILABLE(&Ring->Front, req_prod, rsp_cons))
+ goto fail1;
+
+ req = NULL;
+
+ FirstRequest = TRUE;
+ PacketLength = 0;
+ while (State->Count != 0) {
+ PLIST_ENTRY ListEntry;
+ PTRANSMITTER_TAG Tag;
+ uint16_t id;
+
+ --State->Count;
+
+ ListEntry = RemoveHeadList(&State->List);
+ ASSERT3P(ListEntry, !=, &State->List);
+
+ RtlZeroMemory(ListEntry, sizeof (LIST_ENTRY));
+
+ Tag = CONTAINING_RECORD(ListEntry, TRANSMITTER_TAG, ListEntry);
+
+ req = RING_GET_REQUEST(&Ring->Front, req_prod);
+ req_prod++;
+ Ring->RequestsPosted++;
+
+ id = (USHORT)(Tag - &Ring->Tag[0]);
+
+ req->id = id | REQ_ID_INTEGRITY_CHECK;
+ req->gref = Tag->Reference;
+ req->offset = (USHORT)Tag->Offset;
+ req->size = (USHORT)Tag->Length;
+ req->flags = NETTXF_more_data;
+
+ if (FirstRequest) {
+ FirstRequest = FALSE;
+
+ if (State->Send.OffloadOptions.OffloadIpVersion4TcpChecksum ||
+ State->Send.OffloadOptions.OffloadIpVersion4UdpChecksum ||
+ State->Send.OffloadOptions.OffloadIpVersion6TcpChecksum ||
+ State->Send.OffloadOptions.OffloadIpVersion6UdpChecksum)
+ req->flags |= NETTXF_csum_blank | NETTXF_data_validated;
+
+ if (State->Send.OffloadOptions.OffloadIpVersion4LargePacket ||
+ State->Send.OffloadOptions.OffloadIpVersion6LargePacket) {
+ uint8_t type;
+ uint16_t size;
+ struct netif_extra_info *extra;
+
+ ASSERT(Extra != 0);
+
+ ASSERT(!(State->Send.OffloadOptions.OffloadIpVersion4LargePacket &&
+ State->Send.OffloadOptions.OffloadIpVersion6LargePacket));
+ type = (State->Send.OffloadOptions.OffloadIpVersion4LargePacket) ?
+ XEN_NETIF_GSO_TYPE_TCPV4 :
+ XEN_NETIF_GSO_TYPE_TCPV6;
+
+ ASSERT(State->Send.MaximumSegmentSize != 0);
+ size = State->Send.MaximumSegmentSize;
+
+ ASSERT(req->flags & (NETTXF_csum_blank | NETTXF_data_validated));
+ req->flags |= NETTXF_extra_info;
+
+ extra = (struct netif_extra_info *)RING_GET_REQUEST(&Ring->Front, req_prod);
+ req_prod++;
+ Ring->RequestsPosted++;
+
+ extra->type = XEN_NETIF_EXTRA_TYPE_GSO;
+ extra->flags = 0;
+
+ extra->u.gso.size = size;
+ extra->u.gso.type = type;
+ extra->u.gso.pad = 0;
+ extra->u.gso.features = 0;
+ }
+
+ // The first tag length is the length of the entire packet
+ PacketLength = Tag->Length;
+ }
+
+ // Store a copy of the request in case we need to fake a response ourselves
+ ASSERT3U(id, <, MAXIMUM_TAG_COUNT);
+ ASSERT(IsZeroMemory(&Ring->Pending[id], sizeof (netif_tx_request_t)));
+ Ring->Pending[id] = *req;
+ }
+ ASSERT(!FirstRequest);
+ ASSERT(PacketLength != 0);
+
+ ASSERT(req != NULL);
+ req->flags &= ~NETTXF_more_data;
+
+ Ring->Front.req_prod_pvt = req_prod;
+
+ ASSERT3U(State->Count, ==, 0);
+ RtlZeroMemory(&State->List, sizeof (LIST_ENTRY));
+
+ // Set the initial completion information
+ if (Packet != NULL) {
+ PUCHAR StartVa;
+ PXENVIF_PACKET_INFO Info;
+ PETHERNET_HEADER Header;
+
+ StartVa = State->StartVa;
+ Info = &State->Info;
+
+ ASSERT(IsZeroMemory(&Packet->Completion, sizeof (XENVIF_COMPLETION_INFO)));
+
+ ASSERT(Info->EthernetHeader.Length != 0);
+ Header = (PETHERNET_HEADER)(StartVa + Info->EthernetHeader.Offset);
+
+ Packet->Completion.Type = GET_ETHERNET_ADDRESS_TYPE(&Header->Untagged.DestinationAddress);
+ Packet->Completion.Status = PACKET_PENDING;
+ Packet->Completion.PacketLength = (USHORT)PacketLength;
+ Packet->Completion.PayloadLength = (USHORT)Payload->Length;
+
+ State->StartVa = NULL;
+ RtlZeroMemory(&State->Info, sizeof (XENVIF_PACKET_INFO));
+ RtlZeroMemory(&State->Payload, sizeof (XENVIF_PACKET_PAYLOAD));
+ RtlZeroMemory(&State->Send, sizeof (XENVIF_SEND_INFO));
+ State->Packet = NULL;
+
+ Ring->PacketsSent++;
+ }
+
+ ASSERT(IsZeroMemory(&Ring->State, sizeof (TRANSMITTER_STATE)));
+
+ return STATUS_SUCCESS;
+
+fail1:
+ return status;
+
+#undef RING_SLOTS_AVAILABLE
+}
+
+static FORCEINLINE VOID
+__RingFakeResponses(
+ IN PTRANSMITTER_RING Ring
+ )
+{
+ RING_IDX rsp_prod;
+ USHORT id;
+ ULONG Count;
+
+ // This is only called when the backend went away. We need
+ // to mimic the behavior of the backend and turn requests into
+ // appropriate responses.
+
+ KeMemoryBarrier();
+
+ rsp_prod = Ring->Shared->rsp_prod;
+
+ KeMemoryBarrier();
+
+ Count = 0;
+ for (id = 0; id < MAXIMUM_TAG_COUNT; id++) {
+ netif_tx_request_t *req;
+ netif_tx_response_t *rsp;
+
+ req = &Ring->Pending[id];
+ if (req->id == 0)
+ continue;
+
+ ASSERT3U((req->id & REQ_ID_INTEGRITY_CHECK), ==, REQ_ID_INTEGRITY_CHECK);
+ ASSERT3U((req->id & ~REQ_ID_INTEGRITY_CHECK), ==, id);
+
+ rsp = RING_GET_RESPONSE(&Ring->Front, rsp_prod);
+ rsp_prod++;
+ Count++;
+
+ rsp->id = req->id;
+ rsp->status = NETIF_RSP_DROPPED;
+
+ if (req->flags & NETTXF_extra_info) {
+ rsp = RING_GET_RESPONSE(&Ring->Front, rsp_prod);
+ rsp_prod++;
+ Count++;
+
+ rsp->status = NETIF_RSP_NULL;
+ }
+ }
+
+ KeMemoryBarrier();
+
+ Ring->Shared->rsp_prod = rsp_prod;
+
+ KeMemoryBarrier();
+
+ ASSERT3U(Ring->Shared->rsp_prod, ==, Ring->Front.req_prod_pvt);
+
+ if (Count != 0)
+ Info("Faked %lu responses\n", Count);
+}
+
+static FORCEINLINE VOID
+__RingReleaseTag(
+ IN PTRANSMITTER_RING Ring,
+ IN PTRANSMITTER_TAG Tag
+ )
+{
+ PXENVIF_TRANSMITTER Transmitter;
+
+ Transmitter = Ring->Transmitter;
+
+ Tag->Length = 0;
+ Tag->Offset = 0;
+
+ GNTTAB(RevokeForeignAccess,
+ Transmitter->GnttabInterface,
+ Tag->Reference);
+
+ __TransmitterPutTag(Ring, Tag);
+}
+
+static FORCEINLINE VOID
+__RingCompletePacket(
+ IN PTRANSMITTER_RING Ring,
+ IN PXENVIF_TRANSMITTER_PACKET Packet
+ )
+{
+ ASSERT(Packet->Completion.Status != PACKET_PENDING);
+
+ if (Packet->Completion.Status != PACKET_OK) {
+ Ring->PacketStatistics.Drop++;
+
+ if (Packet->Completion.Status == PACKET_ERROR)
+ Ring->PacketStatistics.BackendError++;
+ } else {
+ ULONG Length;
+
+ Length = (ULONG)Packet->Completion.PacketLength;
+
+ switch (Packet->Completion.Type) {
+ case ETHERNET_ADDRESS_UNICAST:
+ Ring->PacketStatistics.Unicast++;
+ Ring->PacketStatistics.UnicastBytes += Length;
+ break;
+
+ case ETHERNET_ADDRESS_MULTICAST:
+ Ring->PacketStatistics.Multicast++;
+ Ring->PacketStatistics.MulticastBytes += Length;
+ break;
+
+ case ETHERNET_ADDRESS_BROADCAST:
+ Ring->PacketStatistics.Broadcast++;
+ Ring->PacketStatistics.BroadcastBytes += Length;
+ break;
+
+ default:
+ ASSERT(FALSE);
+ break;
+ }
+ }
+
+ *Ring->Completed.TailPacket = Packet;
+ ASSERT3P(Packet->Next, ==, NULL);
+ Ring->Completed.TailPacket = &Packet->Next;
+
+ Ring->PacketsCompleted++;
+}
+
+#define TRANSMITTER_BATCH(_Ring) (RING_SIZE(&(_Ring)->Front) / 4)
+
+static DECLSPEC_NOINLINE VOID
+RingPoll(
+ IN PTRANSMITTER_RING Ring
+ )
+{
+ for (;;) {
+ RING_IDX rsp_prod;
+ RING_IDX rsp_cons;
+ ULONG Delta;
+
+ KeMemoryBarrier();
+
+ rsp_prod = Ring->Shared->rsp_prod;
+ rsp_cons = Ring->Front.rsp_cons;
+
+ KeMemoryBarrier();
+
+ if (rsp_cons == rsp_prod)
+ break;
+
+ while (rsp_cons != rsp_prod) {
+ netif_tx_response_t *rsp;
+ uint16_t id;
+ netif_tx_request_t *req;
+ PTRANSMITTER_TAG Tag;
+ PXENVIF_TRANSMITTER_PACKET Packet;
+
+ rsp = RING_GET_RESPONSE(&Ring->Front, rsp_cons);
+ rsp_cons++;
+ Ring->ResponsesProcessed++;
+
+ Ring->Stopped = FALSE;
+
+ if (rsp->status == NETIF_RSP_NULL)
+ continue;
+
+ ASSERT3U((rsp->id & REQ_ID_INTEGRITY_CHECK), ==, REQ_ID_INTEGRITY_CHECK);
+ id = rsp->id & ~REQ_ID_INTEGRITY_CHECK;
+
+ ASSERT3U(id, <, MAXIMUM_TAG_COUNT);
+ req = &Ring->Pending[id];
+
+ ASSERT3U(req->id, ==, rsp->id);
+ RtlZeroMemory(req, sizeof (netif_tx_request_t));
+
+ Tag = &Ring->Tag[id];
+
+ switch (Tag->Type) {
+ case TAG_BUFFER: {
+ PTRANSMITTER_BUFFER Buffer;
+
+ Buffer = Tag->Context;
+ Tag->Context = NULL;
+ Tag->Type = TAG_TYPE_INVALID;
+
+ Packet = Buffer->Context;
+ Buffer->Context = NULL;
+
+ ASSERT(Buffer->Reference != 0);
+ if (--Buffer->Reference == 0)
+ __TransmitterPutBuffer(Ring, Buffer);
+
+ break;
+ }
+ case TAG_PACKET:
+ Packet = Tag->Context;
+ Tag->Context = NULL;
+ Tag->Type = TAG_TYPE_INVALID;
+
+ break;
+
+ default:
+ Packet = NULL;
+ ASSERT(FALSE);
+ }
+
+ __RingReleaseTag(Ring, Tag);
+ Tag = NULL;
+
+ if (Packet == NULL) {
+ RtlZeroMemory(rsp, sizeof (netif_tx_response_t));
+ continue;
+ }
+
+ DECREMENT_PACKET_REFERENCE(Packet);
+
+ if (rsp->status != NETIF_RSP_OKAY &&
+ Packet->Completion.Status == PACKET_PENDING) {
+ switch (rsp->status) {
+ case NETIF_RSP_DROPPED:
+ Packet->Completion.Status = PACKET_DROPPED;
+ break;
+
+ case NETIF_RSP_ERROR:
+ Packet->Completion.Status = PACKET_ERROR;
+ break;
+
+ default:
+ ASSERT(FALSE);
+ break;
+ }
+ }
+
+ RtlZeroMemory(rsp, sizeof (netif_tx_response_t));
+
+ if (PACKET_REFERENCE(Packet) != 0)
+ continue;
+
+ if (Packet->Completion.Status == PACKET_PENDING)
+ Packet->Completion.Status = PACKET_OK;
+
+ __RingCompletePacket(Ring, Packet);
+ }
+
+ KeMemoryBarrier();
+
+ Ring->Front.rsp_cons = rsp_cons;
+
+ Delta = Ring->Front.req_prod_pvt - rsp_cons;
+ Delta = __min(Delta, TRANSMITTER_BATCH(Ring));
+ Delta = __max(Delta, 1);
+
+ Ring->Shared->rsp_event = rsp_cons + Delta;
+ }
+}
+
+static FORCEINLINE VOID
+__RingPushRequests(
+ IN PTRANSMITTER_RING Ring
+ )
+{
+ BOOLEAN Notify;
+
+ if (Ring->RequestsPosted == Ring->RequestsPushed)
+ return;
+
+#pragma warning (push)
+#pragma warning (disable:4244)
+
+ // Make the requests visible to the backend
+ RING_PUSH_REQUESTS_AND_CHECK_NOTIFY(&Ring->Front, Notify);
+
+#pragma warning (pop)
+
+ if (Notify) {
+ PXENVIF_TRANSMITTER Transmitter;
+ PXENVIF_FRONTEND Frontend;
+
+ Transmitter = Ring->Transmitter;
+ Frontend = Transmitter->Frontend;
+
+ NotifierSend(FrontendGetNotifier(Frontend));
+ }
+
+ Ring->RequestsPushed = Ring->RequestsPosted;
+}
+
+#define TRANSMITTER_ADVERTISEMENT_COUNT 3
+
+#define LOCK_BIT ((ULONG_PTR)1)
+
+static FORCEINLINE ULONG
+__RingReversePacketList(
+ IN PXENVIF_TRANSMITTER_PACKET *Packet
+ )
+{
+ PXENVIF_TRANSMITTER_PACKET HeadPacket;
+ ULONG Count;
+
+ HeadPacket = NULL;
+ Count = 0;
+
+ while (*Packet != NULL) {
+ PXENVIF_TRANSMITTER_PACKET Next;
+
+ ASSERT(((ULONG_PTR)*Packet & LOCK_BIT) == 0);
+
+ Next = (*Packet)->Next;
+
+ (*Packet)->Next = HeadPacket;
+ HeadPacket = *Packet;
+
+ *Packet = Next;
+ Count++;
+ }
+
+ *Packet = HeadPacket;
+
+ return Count;
+}
+
+static DECLSPEC_NOINLINE VOID
+RingSwizzle(
+ IN PTRANSMITTER_RING Ring
+ )
+{
+ ULONG_PTR Old;
+ ULONG_PTR New;
+ PXENVIF_TRANSMITTER_PACKET HeadPacket;
+ PXENVIF_TRANSMITTER_PACKET *TailPacket;
+ ULONG Count;
+
+ ASSERT3P(Ring->LockThread, ==, KeGetCurrentThread());
+
+ New = LOCK_BIT;
+ Old = (ULONG_PTR)InterlockedExchangePointer(&Ring->Lock, (PVOID)New);
+
+ ASSERT(Old & LOCK_BIT);
+ HeadPacket = (PVOID)(Old & ~LOCK_BIT);
+
+ if (HeadPacket == NULL)
+ return;
+
+ // Packets are held in the atomic packet list in reverse order
+ // so that the most recent is always head of the list. This is
+ // necessary to allow addition to the list to be done atomically.
+
+ TailPacket = &HeadPacket->Next;
+ Count = __RingReversePacketList(&HeadPacket);
+ ASSERT3P(*TailPacket, ==, NULL);
+
+ *(Ring->Queued.TailPacket) = HeadPacket;
+ Ring->Queued.TailPacket = TailPacket;
+ Ring->PacketsQueued += Count;
+}
+
+static DECLSPEC_NOINLINE VOID
+RingSchedule(
+ IN PTRANSMITTER_RING Ring
+ )
+{
+ PTRANSMITTER_STATE State;
+
+ if(!Ring->Enabled || Ring->Stopped)
+ return;
+
+ State = &Ring->State;
+
+ for (;;) {
+ PXENVIF_TRANSMITTER_PACKET Packet;
+ NTSTATUS status;
+
+ if (State->Count != 0) {
+ status = __RingPostTags(Ring);
+ if (!NT_SUCCESS(status)) {
+ Ring->Stopped = TRUE;
+ break;
+ }
+ }
+
+ if (Ring->RequestsPosted - Ring->RequestsPushed >=
+ RING_SIZE(&Ring->Front) / 4)
+ __RingPushRequests(Ring);
+
+ ASSERT3U(State->Count, ==, 0);
+
+ if (Ring->AddressIndex != 0) {
+ ULONG Index = (--Ring->AddressIndex) % Ring->AddressCount;
+
+ switch (Ring->AddressTable[Index].si_family) {
+ case AF_INET: {
+ IPV4_ADDRESS Address;
+
+ RtlCopyMemory(Address.Byte,
+ &Ring->AddressTable[Index].Ipv4.sin_addr.s_addr,
+ IPV4_ADDRESS_LENGTH);
+
+ (VOID) __RingPrepareGratuitousArp(Ring, &Address);
+
+ break;
+ }
+ case AF_INET6: {
+ IPV6_ADDRESS Address;
+
+ RtlCopyMemory(Address.Byte,
+ &Ring->AddressTable[Index].Ipv6.sin6_addr.s6_addr,
+ IPV6_ADDRESS_LENGTH);
+
+ (VOID) __RingPrepareNeighbourAdvertisement(Ring, &Address);
+
+ break;
+ }
+ default:
+ ASSERT(FALSE);
+ }
+
+ continue;
+ }
+
+ Packet = Ring->Queued.HeadPacket;
+
+ if (Packet == NULL)
+ break;
+
+ if (Packet->Next == NULL) {
+ Ring->Queued.HeadPacket = NULL;
+ Ring->Queued.TailPacket = &Ring->Queued.HeadPacket;
+ } else {
+ Ring->Queued.HeadPacket = Packet->Next;
+ Packet->Next = NULL;
+ }
+
+ status = __RingPreparePacket(Ring, Packet);
+ if (!NT_SUCCESS(status)) {
+ ASSERT(status != STATUS_BUFFER_OVERFLOW);
+
+ // Fake that we prapared and sent this packet
+ Ring->PacketsPrepared++;
+ Ring->PacketsSent++;
+ Ring->PacketsFaked++;
+
+ Packet->Completion.Status = PACKET_DROPPED;
+
+ Ring->PacketStatistics.FrontendError++;
+
+ __RingCompletePacket(Ring, Packet);
+ }
+
+ ASSERT3U(Ring->PacketsPrepared, ==, Ring->PacketsCopied + Ring->PacketsGranted + Ring->PacketsFaked);
+ }
+
+ ASSERT(IMPLY(Ring->Queued.HeadPacket == NULL, Ring->Queued.TailPacket == &Ring->Queued.HeadPacket));
+
+ __RingPushRequests(Ring);
+}
+
+static FORCEINLINE BOOLEAN
+__drv_requiresIRQL(DISPATCH_LEVEL)
+__RingTryAcquireLock(
+ IN PTRANSMITTER_RING Ring
+ )
+{
+ ULONG_PTR Old;
+ ULONG_PTR New;
+ BOOLEAN Acquired;
+
+ ASSERT3U(KeGetCurrentIrql(), ==, DISPATCH_LEVEL);
+
+ Old = (ULONG_PTR)Ring->Lock & ~LOCK_BIT;
+ New = Old | LOCK_BIT;
+
+ Acquired = ((ULONG_PTR)InterlockedCompareExchangePointer(&Ring->Lock, (PVOID)New, (PVOID)Old) == Old) ? TRUE : FALSE;
+ if (Acquired) {
+ ASSERT3P(Ring->LockThread, ==, NULL);
+ Ring->LockThread = KeGetCurrentThread();
+ }
+
+ return Acquired;
+}
+
+static FORCEINLINE VOID
+__drv_requiresIRQL(DISPATCH_LEVEL)
+__RingAcquireLock(
+ IN PTRANSMITTER_RING Ring
+ )
+{
+ ASSERT3U(KeGetCurrentIrql(), ==, DISPATCH_LEVEL);
+
+ for (;;) {
+ if (__RingTryAcquireLock(Ring))
+ break;
+ }
+}
+
+static DECLSPEC_NOINLINE VOID
+RingAcquireLock(
+ IN PTRANSMITTER_RING Ring
+ )
+{
+ __RingAcquireLock(Ring);
+}
+
+static FORCEINLINE BOOLEAN
+__drv_requiresIRQL(DISPATCH_LEVEL)
+__RingTryReleaseLock(
+ IN PTRANSMITTER_RING Ring
+ )
+{
+ ULONG_PTR Old;
+ ULONG_PTR New;
+ BOOLEAN Released;
+
+ ASSERT3U(KeGetCurrentIrql(), ==, DISPATCH_LEVEL);
+ ASSERT3P(KeGetCurrentThread(), ==, Ring->LockThread);
+
+ Old = LOCK_BIT;
+ New = 0;
+
+ Ring->LockThread = NULL;
+
+ Released = ((ULONG_PTR)InterlockedCompareExchangePointer(&Ring->Lock, (PVOID)New, (PVOID)Old) == Old) ? TRUE : FALSE;
+ if (!Released)
+ Ring->LockThread = KeGetCurrentThread();
+
+ return Released;
+}
+
+static FORCEINLINE VOID
+__drv_requiresIRQL(DISPATCH_LEVEL)
+__RingReleaseLock(
+ IN PTRANSMITTER_RING Ring
+ )
+{
+ PXENVIF_TRANSMITTER_PACKET HeadPacket;
+ PXENVIF_TRANSMITTER_PACKET *TailPacket;
+
+ HeadPacket = NULL;
+ TailPacket = &HeadPacket;
+
+ ASSERT3U(KeGetCurrentIrql(), ==, DISPATCH_LEVEL);
+
+ // As lock holder it is our responsibility to drain the atomic
+ // packet list into the transmit queue before we actually drop the
+ // lock. This may, of course, take a few attempts as another
+ // thread could be simuntaneously adding to the list.
+
+ do {
+ RingSwizzle(Ring);
+ RingSchedule(Ring);
+
+ *TailPacket = Ring->Completed.HeadPacket;
+ TailPacket = Ring->Completed.TailPacket;
+
+ Ring->Completed.HeadPacket = NULL;
+ Ring->Completed.TailPacket = &Ring->Completed.HeadPacket;
+ } while (!__RingTryReleaseLock(Ring));
+
+ if (HeadPacket != NULL) {
+ PXENVIF_TRANSMITTER Transmitter;
+
+ Transmitter = Ring->Transmitter;
+
+ VifCompletePackets(Transmitter->VifInterface, HeadPacket);
+ }
+}
+
+static DECLSPEC_NOINLINE VOID
+RingReleaseLock(
+ IN PTRANSMITTER_RING Ring
+ )
+{
+ __RingReleaseLock(Ring);
+}
+
+#define TIME_US(_us) ((_us) * 10)
+#define TIME_MS(_ms) (TIME_US((_ms) * 1000))
+#define TIME_RELATIVE(_t) (-(_t))
+
+#define RING_PERIOD 30000
+
+static NTSTATUS
+RingWatchdog(
+ IN PXENVIF_THREAD Self,
+ IN PVOID Context
+ )
+{
+ PTRANSMITTER_RING Ring = Context;
+ LARGE_INTEGER Timeout;
+ ULONG PacketsQueued;
+
+ Trace("====>\n");
+
+ Timeout.QuadPart = TIME_RELATIVE(TIME_MS(RING_PERIOD));
+ PacketsQueued = 0;
+
+ for (;;) {
+ PKEVENT Event;
+ KIRQL Irql;
+
+ Event = ThreadGetEvent(Self);
+
+ (VOID) KeWaitForSingleObject(Event,
+ Executive,
+ KernelMode,
+ FALSE,
+ &Timeout);
+ KeClearEvent(Event);
+
+ if (ThreadIsAlerted(Self))
+ break;
+
+ KeRaiseIrql(DISPATCH_LEVEL, &Irql);
+ __RingAcquireLock(Ring);
+
+ if (Ring->Enabled &&
+ Ring->PacketsQueued == PacketsQueued &&
+ Ring->PacketsCompleted != PacketsQueued) {
+ PXENVIF_TRANSMITTER Transmitter;
+ PXENVIF_FRONTEND Frontend;
+
+ Transmitter = Ring->Transmitter;
+ Frontend = Transmitter->Frontend;
+
+ Info("PATH: %s\n", FrontendGetPath(Frontend));
+
+ Info("FRONT: req_prod_pvt = %u rsp_cons = %u nr_ents = %u sring = %p\n",
+ Ring->Front.req_prod_pvt,
+ Ring->Front.rsp_cons,
+ Ring->Front.nr_ents,
+ Ring->Front.sring);
+
+ Info("SHARED: req_prod = %u req_event = %u rsp_prod = %u rsp_event = %u\n",
+ Ring->Shared->req_prod,
+ Ring->Shared->req_event,
+ Ring->Shared->rsp_prod,
+ Ring->Shared->rsp_event);
+
+ Info("RequestsPosted = %u RequestsPushed = %u ResponsesProcessed = %u\n",
+ Ring->RequestsPosted,
+ Ring->RequestsPushed,
+ Ring->ResponsesProcessed);
+
+ // Try to move things along
+ NotifierSend(FrontendGetNotifier(Frontend));
+ NotifierTrigger(FrontendGetNotifier(Frontend));
+ }
+
+ PacketsQueued = Ring->PacketsQueued;
+
+ __RingReleaseLock(Ring);
+ KeLowerIrql(Irql);
+ }
+
+ Trace("<====\n");
+
+ return STATUS_SUCCESS;
+}
+
+static FORCEINLINE VOID
+__RingDumpAddressTable(
+ IN PTRANSMITTER_RING Ring
+ )
+{
+ PXENVIF_TRANSMITTER Transmitter;
+ PXENVIF_FRONTEND Frontend;
+ ULONG Index;
+ ULONG IpVersion4Count;
+ ULONG IpVersion6Count;
+
+ __RingAcquireLock(Ring);
+ if (!Ring->Connected)
+ goto done;
+
+ Transmitter = Ring->Transmitter;
+ Frontend = Transmitter->Frontend;
+
+ STORE(Remove,
+ Transmitter->StoreInterface,
+ NULL,
+ FrontendGetPrefix(Frontend),
+ "ip");
+
+ STORE(Remove,
+ Transmitter->StoreInterface,
+ NULL,
+ FrontendGetPrefix(Frontend),
+ "ipv4");
+
+ STORE(Remove,
+ Transmitter->StoreInterface,
+ NULL,
+ FrontendGetPrefix(Frontend),
+ "ipv6");
+
+ IpVersion4Count = 0;
+ IpVersion6Count = 0;
+
+ for (Index = 0; Index < Ring->AddressCount; Index++) {
+ switch (Ring->AddressTable[Index].si_family) {
+ case AF_INET: {
+ IPV4_ADDRESS Address;
+ CHAR Node[sizeof ("ipv4/XX/addr")];
+
+ RtlCopyMemory(Address.Byte,
+ &Ring->AddressTable[Index].Ipv4.sin_addr.s_addr,
+ IPV4_ADDRESS_LENGTH);
+
+ (VOID) RtlStringCbPrintfA(Node,
+ sizeof (Node),
+ "ipv4/%u/addr",
+ IpVersion4Count);
+
+ STORE(Printf,
+ Transmitter->StoreInterface,
+ NULL,
+ FrontendGetPrefix(Frontend),
+ Node,
+ "%u.%u.%u.%u",
+ Address.Byte[0],
+ Address.Byte[1],
+ Address.Byte[2],
+ Address.Byte[3]);
+
+ if (IpVersion4Count == 0)
+ STORE(Printf,
+ Transmitter->StoreInterface,
+ NULL,
+ FrontendGetPrefix(Frontend),
+ "ip",
+ "%u.%u.%u.%u",
+ Address.Byte[0],
+ Address.Byte[1],
+ Address.Byte[2],
+ Address.Byte[3]);
+
+ IpVersion4Count++;
+ break;
+ }
+ case AF_INET6: {
+ IPV6_ADDRESS Address;
+ CHAR Node[sizeof ("ipv6/XX/addr")];
+
+ RtlCopyMemory(Address.Byte,
+ &Ring->AddressTable[Index].Ipv6.sin6_addr.s6_addr,
+ IPV6_ADDRESS_LENGTH);
+
+ RtlStringCbPrintfA(Node,
+ sizeof (Node),
+ "ipv6/%u/addr",
+ IpVersion6Count);
+
+ STORE(Printf,
+ Transmitter->StoreInterface,
+ NULL,
+ FrontendGetPrefix(Frontend),
+ Node,
+ "%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x",
+ NTOHS(Address.Word[0]),
+ NTOHS(Address.Word[1]),
+ NTOHS(Address.Word[2]),
+ NTOHS(Address.Word[3]),
+ NTOHS(Address.Word[4]),
+ NTOHS(Address.Word[5]),
+ NTOHS(Address.Word[6]),
+ NTOHS(Address.Word[7]));
+
+ IpVersion6Count++;
+ break;
+ }
+ default:
+ break;
+ }
+ }
+
+done:
+ __RingReleaseLock(Ring);
+}
+
+static FORCEINLINE VOID
+__RingUpdateAddressTable(
+ IN PTRANSMITTER_RING Ring,
+ IN PSOCKADDR_INET Table,
+ IN ULONG Count
+ )
+{
+ NTSTATUS status;
+
+ __RingAcquireLock(Ring);
+
+ if (Ring->AddressCount != 0) {
+ Ring->AddressCount = 0;
+
+ ASSERT(Ring->AddressTable != NULL);
+ __TransmitterFree(Ring->AddressTable);
+ Ring->AddressTable = NULL;
+ }
+
+ if (Count == 0)
+ goto done;
+
+ Ring->AddressTable = __TransmitterAllocate(sizeof (SOCKADDR_INET) * Count);
+
+ status = STATUS_NO_MEMORY;
+ if (Ring->AddressTable == NULL)
+ goto fail1;
+
+ RtlCopyMemory(Ring->AddressTable, Table, sizeof (SOCKADDR_INET) * Count);
+ Ring->AddressCount = Count;
+
+ // Re-advertize if we were part way through
+ if (Ring->AddressIndex != 0)
+ Ring->AddressIndex = Ring->AddressCount;
+
+done:
+ __RingReleaseLock(Ring);
+
+ __RingDumpAddressTable(Ring);
+
+ return;
+
+fail1:
+ Error("fail1 (%08x)\n", status);
+
+ __RingReleaseLock(Ring);
+}
+
+static FORCEINLINE VOID
+__RingAdvertizeAddresses(
+ IN PTRANSMITTER_RING Ring
+ )
+{
+ __RingAcquireLock(Ring);
+ Ring->AddressIndex = Ring->AddressCount;
+ __RingReleaseLock(Ring);
+}
+
+static FORCEINLINE NTSTATUS
+__RingInitialize(
+ IN PXENVIF_TRANSMITTER Transmitter,
+ OUT PTRANSMITTER_RING *Ring
+ )
+{
+ NTSTATUS status;
+
+ *Ring = __TransmitterAllocate(sizeof (TRANSMITTER_RING));
+
+ status = STATUS_NO_MEMORY;
+ if (*Ring == NULL)
+ goto fail1;
+
+ status = PoolInitialize("TransmitterBuffer",
+ sizeof (TRANSMITTER_BUFFER),
+ TransmitterBufferCtor,
+ TransmitterBufferDtor,
+ RingAcquireLock,
+ RingReleaseLock,
+ *Ring,
+ &(*Ring)->BufferPool);
+ if (!NT_SUCCESS(status))
+ goto fail2;
+
+ (*Ring)->Queued.TailPacket = &(*Ring)->Queued.HeadPacket;
+ (*Ring)->Completed.TailPacket = &(*Ring)->Completed.HeadPacket;
+
+ (*Ring)->Transmitter = Transmitter;
+
+ status = ThreadCreate(RingWatchdog,
+ *Ring,
+ &(*Ring)->Thread);
+ if (!NT_SUCCESS(status))
+ goto fail3;
+
+ return STATUS_SUCCESS;
+
+fail3:
+ Error("fail3\n");
+
+ (*Ring)->Transmitter = NULL;
+
+ (*Ring)->Queued.TailPacket = NULL;
+ (*Ring)->Completed.TailPacket = NULL;
+
+ PoolTeardown((*Ring)->BufferPool);
+ (*Ring)->BufferPool = NULL;
+
+fail2:
+ Error("fail2\n");
+
+ ASSERT(IsZeroMemory(*Ring, sizeof (TRANSMITTER_RING)));
+ __TransmitterFree(*Ring);
+ *Ring = NULL;
+
+fail1:
+ Error("fail1 (%08x)\n", status);
+
+ return status;
+}
+
+static FORCEINLINE NTSTATUS
+__RingConnect(
+ IN PTRANSMITTER_RING Ring
+ )
+{
+ PXENVIF_TRANSMITTER Transmitter;
+ PXENVIF_FRONTEND Frontend;
+ ULONG Index;
+ PFN_NUMBER Pfn;
+ NTSTATUS status;
+
+ ASSERT(!Ring->Connected);
+
+ Transmitter = Ring->Transmitter;
+ Frontend = Transmitter->Frontend;
+
+ Ring->Mdl = __AllocatePage();
+
+ status = STATUS_NO_MEMORY;
+ if (Ring->Mdl == NULL)
+ goto fail1;
+
+ Ring->Shared = MmGetSystemAddressForMdlSafe(Ring->Mdl, NormalPagePriority);
+ ASSERT(Ring->Shared != NULL);
+
+ SHARED_RING_INIT(Ring->Shared);
+ FRONT_RING_INIT(&Ring->Front, Ring->Shared, PAGE_SIZE);
+ ASSERT3P(Ring->Front.sring, ==, Ring->Shared);
+
+ Transmitter->GnttabInterface = FrontendGetGnttabInterface(Frontend);
+
+ GNTTAB(Acquire, Transmitter->GnttabInterface);
+
+ status = GNTTAB(Get,
+ Transmitter->GnttabInterface,
+ &Ring->Reference);
+ if (!NT_SUCCESS(status))
+ goto fail2;
+
+ Ring->HeadFreeTag = TAG_INDEX_INVALID;
+ for (Index = 0; Index < MAXIMUM_TAG_COUNT; Index++) {
+ PTRANSMITTER_TAG Tag = &Ring->Tag[Index];
+
+ status = GNTTAB(Get,
+ Transmitter->GnttabInterface,
+ &Tag->Reference);
+ if (!NT_SUCCESS(status))
+ goto fail3;
+
+ Tag->Next = Ring->HeadFreeTag;
+ Ring->HeadFreeTag = Index;
+ }
+
+ Pfn = MmGetMdlPfnArray(Ring->Mdl)[0];
+
+ GNTTAB(PermitForeignAccess,
+ Transmitter->GnttabInterface,
+ Ring->Reference,
+ FrontendGetBackendDomain(Frontend),
+ GNTTAB_ENTRY_FULL_PAGE,
+ Pfn,
+ FALSE);
+
+ Ring->Connected = TRUE;
+
+ return STATUS_SUCCESS;
+
+fail3:
+ Error("fail3\n");
+
+ while (Ring->HeadFreeTag != TAG_INDEX_INVALID) {
+ PTRANSMITTER_TAG Tag = &Ring->Tag[Ring->HeadFreeTag];
+
+ Ring->HeadFreeTag = Tag->Next;
+ Tag->Next = 0;
+
+ GNTTAB(Put,
+ Transmitter->GnttabInterface,
+ Tag->Reference);
+ Tag->Reference = 0;
+ }
+ Ring->HeadFreeTag = 0;
+
+ GNTTAB(Put,
+ Transmitter->GnttabInterface,
+ Ring->Reference);
+ Ring->Reference = 0;
+
+fail2:
+ Error("fail2\n");
+
+ GNTTAB(Release, Transmitter->GnttabInterface);
+ Transmitter->GnttabInterface = NULL;
+
+ RtlZeroMemory(&Ring->Front, sizeof (netif_tx_front_ring_t));
+ RtlZeroMemory(Ring->Shared, PAGE_SIZE);
+
+ Ring->Shared = NULL;
+ __FreePage(Ring->Mdl);
+ Ring->Mdl = NULL;
+
+fail1:
+ Error("fail1 (%08x)\n", status);
+
+ return status;
+}
+
+static FORCEINLINE NTSTATUS
+__RingStoreWrite(
+ IN PTRANSMITTER_RING Ring,
+ IN PXENBUS_STORE_TRANSACTION Transaction
+ )
+{
+ PXENVIF_TRANSMITTER Transmitter;
+ PXENVIF_FRONTEND Frontend;
+ NTSTATUS status;
+
+ Transmitter = Ring->Transmitter;
+ Frontend = Transmitter->Frontend;
+
+ status = STORE(Printf,
+ Transmitter->StoreInterface,
+ Transaction,
+ FrontendGetPath(Frontend),
+ "tx-ring-ref",
+ "%u",
+ Ring->Reference);
+
+ if (!NT_SUCCESS(status))
+ goto fail1;
+
+ return STATUS_SUCCESS;
+
+fail1:
+ Error("fail1 (%08x)\n", status);
+
+ return status;
+}
+
+static FORCEINLINE NTSTATUS
+__RingEnable(
+ IN PTRANSMITTER_RING Ring
+ )
+{
+ __RingAcquireLock(Ring);
+
+ ASSERT(!Ring->Enabled);
+ Ring->Enabled = TRUE;
+
+ __RingReleaseLock(Ring);
+
+ return STATUS_SUCCESS;
+}
+
+static FORCEINLINE VOID
+__RingDisable(
+ IN PTRANSMITTER_RING Ring
+ )
+{
+ PXENVIF_TRANSMITTER Transmitter;
+ PXENVIF_FRONTEND Frontend;
+ PXENVIF_TRANSMITTER_PACKET Packet;
+ PCHAR Buffer;
+ XenbusState State;
+ ULONG Attempt;
+ NTSTATUS status;
+
+ Transmitter = Ring->Transmitter;
+ Frontend = Transmitter->Frontend;
+
+ __RingAcquireLock(Ring);
+
+ ASSERT(Ring->Enabled);
+ Ring->Enabled = FALSE;
+
+ // Release any tags associated with a pending packet
+ Packet = __RingUnpreparePacket(Ring);
+
+ // Put any packet back on the head of the queue
+ if (Packet != NULL) {
+ ASSERT3P(Packet->Next, ==, NULL);
+
+ Packet->Next = Ring->Queued.HeadPacket;
+
+ if (Ring->Queued.TailPacket == &Ring->Queued.HeadPacket)
+ Ring->Queued.TailPacket = &Packet->Next;
+
+ Ring->Queued.HeadPacket = Packet;
+ }
+
+ Ring->AddressIndex = 0;
+
+ status = STORE(Read,
+ Transmitter->StoreInterface,
+ NULL,
+ FrontendGetBackendPath(Frontend),
+ "state",
+ &Buffer);
+ if (!NT_SUCCESS(status)) {
+ State = XenbusStateUnknown;
+ } else {
+ State = (XenbusState)strtol(Buffer, NULL, 10);
+
+ STORE(Free,
+ Transmitter->StoreInterface,
+ Buffer);
+ }
+
+ Attempt = 0;
+ ASSERT3U(Ring->RequestsPushed, ==, Ring->RequestsPosted);
+ while (Ring->ResponsesProcessed != Ring->RequestsPushed) {
+ Attempt++;
+ ASSERT(Attempt < 100);
+
+ RingPoll(Ring);
+
+ if (State != XenbusStateConnected)
+ __RingFakeResponses(Ring);
+
+ KeStallExecutionProcessor(10000); // 10 ms
+ }
+
+ __RingReleaseLock(Ring);
+}
+
+static FORCEINLINE VOID
+__RingDisconnect(
+ IN PTRANSMITTER_RING Ring
+ )
+{
+ PXENVIF_TRANSMITTER Transmitter;
+ PXENVIF_FRONTEND Frontend;
+ ULONG Count;
+
+ ASSERT(Ring->Connected);
+ Ring->Connected = FALSE;
+
+ Transmitter = Ring->Transmitter;
+ Frontend = Transmitter->Frontend;
+
+ ASSERT3U(Ring->ResponsesProcessed, ==, Ring->RequestsPushed);
+ ASSERT3U(Ring->RequestsPushed, ==, Ring->RequestsPosted);
+
+ Ring->ResponsesProcessed = 0;
+ Ring->RequestsPushed = 0;
+ Ring->RequestsPosted = 0;
+
+ GNTTAB(RevokeForeignAccess,
+ Transmitter->GnttabInterface,
+ Ring->Reference);
+
+ Count = 0;
+ while (Ring->HeadFreeTag != TAG_INDEX_INVALID) {
+ ULONG Index = Ring->HeadFreeTag;
+ PTRANSMITTER_TAG Tag = &Ring->Tag[Index];
+
+ Ring->HeadFreeTag = Tag->Next;
+ Tag->Next = 0;
+
+ GNTTAB(Put,
+ Transmitter->GnttabInterface,
+ Tag->Reference);
+ Tag->Reference = 0;
+
+ Count++;
+ }
+ ASSERT3U(Count, ==, MAXIMUM_TAG_COUNT);
+
+ Ring->HeadFreeTag = 0;
+
+ GNTTAB(Put,
+ Transmitter->GnttabInterface,
+ Ring->Reference);
+ Ring->Reference = 0;
+
+ GNTTAB(Release, Transmitter->GnttabInterface);
+ Transmitter->GnttabInterface = NULL;
+
+ RtlZeroMemory(&Ring->Front, sizeof (netif_tx_front_ring_t));
+ RtlZeroMemory(Ring->Shared, PAGE_SIZE);
+
+ Ring->Shared = NULL;
+ __FreePage(Ring->Mdl);
+ Ring->Mdl = NULL;
+}
+
+static FORCEINLINE VOID
+__RingTeardown(
+ IN PTRANSMITTER_RING Ring
+ )
+{
+ PXENVIF_TRANSMITTER Transmitter;
+ PXENVIF_FRONTEND Frontend;
+
+ Transmitter = Ring->Transmitter;
+ Frontend = Transmitter->Frontend;
+
+ ASSERT3U(Ring->PacketsCompleted, ==, Ring->PacketsSent);
+ ASSERT3U(Ring->PacketsSent, ==, Ring->PacketsPrepared - Ring->PacketsUnprepared);
+ ASSERT3U(Ring->PacketsPrepared, ==, Ring->PacketsCopied + Ring->PacketsGranted + Ring->PacketsFaked);
+ ASSERT3U(Ring->PacketsQueued, ==, Ring->PacketsPrepared - Ring->PacketsUnprepared);
+
+ Ring->PacketsCompleted = 0;
+ Ring->PacketsSent = 0;
+ Ring->PacketsCopied = 0;
+ Ring->PacketsGranted = 0;
+ Ring->PacketsFaked = 0;
+ Ring->PacketsUnprepared = 0;
+ Ring->PacketsPrepared = 0;
+ Ring->PacketsQueued = 0;
+
+ RtlZeroMemory(&Ring->HeaderStatistics, sizeof (XENVIF_HEADER_STATISTICS));
+ RtlZeroMemory(&Ring->OffloadStatistics, sizeof (TRANSMITTER_OFFLOAD_STATISTICS));
+ RtlZeroMemory(&Ring->PacketStatistics, sizeof (XENVIF_TRANSMITTER_PACKET_STATISTICS));
+
+ if (Ring->AddressCount != 0) {
+ ASSERT(Ring->AddressTable != NULL);
+ __TransmitterFree(Ring->AddressTable);
+ }
+
+ Ring->AddressTable = NULL;
+ Ring->AddressCount = 0;
+
+ ThreadAlert(Ring->Thread);
+ ThreadJoin(Ring->Thread);
+ Ring->Thread = NULL;
+
+ Ring->Transmitter = NULL;
+
+ ASSERT3P(Ring->Queued.TailPacket, ==, &Ring->Queued.HeadPacket);
+ Ring->Queued.TailPacket = NULL;
+
+ ASSERT3P(Ring->Completed.TailPacket, ==, &Ring->Completed.HeadPacket);
+ Ring->Completed.TailPacket = NULL;
+
+ PoolTeardown(Ring->BufferPool);
+ Ring->BufferPool = NULL;
+
+ ASSERT(IsZeroMemory(Ring, sizeof (TRANSMITTER_RING)));
+ __TransmitterFree(Ring);
+}
+
+static FORCEINLINE VOID
+__RingQueuePackets(
+ IN PTRANSMITTER_RING Ring,
+ IN PXENVIF_TRANSMITTER_PACKET HeadPacket
+ )
+{
+ PXENVIF_TRANSMITTER_PACKET *TailPacket;
+ ULONG_PTR Old;
+ ULONG_PTR LockBit;
+ ULONG_PTR New;
+
+ TailPacket = &HeadPacket->Next;
+ (VOID) __RingReversePacketList(&HeadPacket);
+ ASSERT3P(*TailPacket, ==, NULL);
+
+ do {
+ Old = (ULONG_PTR)Ring->Lock;
+ LockBit = Old & LOCK_BIT;
+
+ *TailPacket = (PVOID)(Old & ~LOCK_BIT);
+ New = (ULONG_PTR)HeadPacket;
+ ASSERT((New & LOCK_BIT) == 0);
+ New |= LockBit;
+ } while ((ULONG_PTR)InterlockedCompareExchangePointer(&Ring->Lock, (PVOID)New, (PVOID)Old) != Old);
+
+ // __RingReleaseLock() drains the atomic packet list into the transmit queue therefore,
+ // after adding to the list we need to attempt to grab and release the lock. If we can't
+ // grab it then that's ok because whichever thread is holding it will have to call
+ // __RingReleaseLock() and will therefore drain the atomic packet list.
+
+ if (!__RingTryAcquireLock(Ring))
+ return;
+
+ __RingReleaseLock(Ring);
+}
+
+static FORCEINLINE VOID
+__RingAbortPackets(
+ IN PTRANSMITTER_RING Ring
+ )
+{
+ PXENVIF_TRANSMITTER_PACKET Packet;
+
+ __RingAcquireLock(Ring);
+
+ RingSwizzle(Ring);
+
+ Packet = Ring->Queued.HeadPacket;
+
+ Ring->Queued.HeadPacket = NULL;
+ Ring->Queued.TailPacket = &Ring->Queued.HeadPacket;
+
+ while (Packet != NULL) {
+ PXENVIF_TRANSMITTER_PACKET Next;
+
+ Next = Packet->Next;
+ Packet->Next = NULL;
+
+ // Fake that we prapared and sent this packet
+ Ring->PacketsPrepared++;
+ Ring->PacketsSent++;
+ Ring->PacketsFaked++;
+
+ Packet->Completion.Status = PACKET_DROPPED;
+
+ __RingCompletePacket(Ring, Packet);
+
+ Packet = Next;
+ }
+
+ ASSERT3U(Ring->PacketsSent, ==, Ring->PacketsPrepared - Ring->PacketsUnprepared);
+ ASSERT3U(Ring->PacketsPrepared, ==, Ring->PacketsCopied + Ring->PacketsGranted + Ring->PacketsFaked);
+ ASSERT3U(Ring->PacketsQueued, ==, Ring->PacketsPrepared - Ring->PacketsUnprepared);
+
+ ASSERT3P((ULONG_PTR)Ring->Lock, ==, LOCK_BIT);
+ __RingReleaseLock(Ring);
+}
+
+static FORCEINLINE VOID
+__RingNotify(
+ IN PTRANSMITTER_RING Ring
+ )
+{
+ __RingAcquireLock(Ring);
+
+ RingPoll(Ring);
+
+ __RingReleaseLock(Ring);
+}
+
+static FORCEINLINE VOID
+__RingAddPacketStatistics(
+ IN PTRANSMITTER_RING Ring,
+ IN OUT PXENVIF_TRANSMITTER_PACKET_STATISTICS Statistics
+ )
+{
+ // Don't bother locking
+
+ Statistics->Drop += Ring->PacketStatistics.Drop;
+ Statistics->BackendError += Ring->PacketStatistics.BackendError;
+ Statistics->FrontendError += Ring->PacketStatistics.FrontendError;
+ Statistics->Unicast += Ring->PacketStatistics.Unicast;
+ Statistics->UnicastBytes += Ring->PacketStatistics.UnicastBytes;
+ Statistics->Multicast += Ring->PacketStatistics.Multicast;
+ Statistics->MulticastBytes += Ring->PacketStatistics.MulticastBytes;
+ Statistics->Broadcast += Ring->PacketStatistics.Broadcast;
+ Statistics->BroadcastBytes += Ring->PacketStatistics.BroadcastBytes;
+}
+
+static FORCEINLINE ULONG
+__RingGetSize(
+ IN PTRANSMITTER_RING Ring
+ )
+{
+ UNREFERENCED_PARAMETER(Ring);
+
+ return TRANSMITTER_RING_SIZE;
+}
+
+static VOID
+TransmitterDebugCallback(
+ IN PVOID Argument,
+ IN BOOLEAN Crashing
+ )
+{
+ PXENVIF_TRANSMITTER Transmitter = Argument;
+ PLIST_ENTRY ListEntry;
+
+ UNREFERENCED_PARAMETER(Crashing);
+
+ for (ListEntry = Transmitter->List.Flink;
+ ListEntry != &Transmitter->List;
+ ListEntry = ListEntry->Flink) {
+ PTRANSMITTER_RING Ring;
+
+ Ring = CONTAINING_RECORD(ListEntry, TRANSMITTER_RING, ListEntry);
+
+ __RingDebugCallback(Ring);
+ }
+
+ DEBUG(Printf,
+ Transmitter->DebugInterface,
+ Transmitter->DebugCallback,
+ "METADATA: Offset @ %ld Length @ %ld Mdl @ %ld\n",
+ Transmitter->Metadata.OffsetOffset,
+ Transmitter->Metadata.LengthOffset,
+ Transmitter->Metadata.MdlOffset);
+}
+
+NTSTATUS
+TransmitterInitialize(
+ IN PXENVIF_FRONTEND Frontend,
+ IN ULONG Count,
+ OUT PXENVIF_TRANSMITTER *Transmitter
+ )
+{
+ ULONG Done;
+ NTSTATUS status;
+
+ *Transmitter = __TransmitterAllocate(sizeof (XENVIF_TRANSMITTER));
+
+ status = STATUS_NO_MEMORY;
+ if (*Transmitter == NULL)
+ goto fail1;
+
+ InitializeListHead(&(*Transmitter)->List);
+
+ Done = 0;
+ while (Done < Count) {
+ PTRANSMITTER_RING Ring;
+
+ status = __RingInitialize(*Transmitter, &Ring);
+ if (!NT_SUCCESS(status))
+ goto fail2;
+
+ InsertTailList(&(*Transmitter)->List, &Ring->ListEntry);
+ Done++;
+ }
+
+ (*Transmitter)->Frontend = Frontend;
+
+ return STATUS_SUCCESS;
+
+fail2:
+ Error("fail2\n");
+
+ while (!IsListEmpty(&(*Transmitter)->List)) {
+ PLIST_ENTRY ListEntry;
+ PTRANSMITTER_RING Ring;
+
+ ListEntry = RemoveTailList(&(*Transmitter)->List);
+ ASSERT3P(ListEntry, !=, &(*Transmitter)->List);
+
+ RtlZeroMemory(ListEntry, sizeof (LIST_ENTRY));
+
+ Ring = CONTAINING_RECORD(ListEntry, TRANSMITTER_RING, ListEntry);
+
+ __RingTeardown(Ring);
+ --Done;
+ }
+ ASSERT3U(Done, ==, 0);
+
+ RtlZeroMemory(&(*Transmitter)->List, sizeof (LIST_ENTRY));
+
+ ASSERT(IsZeroMemory(*Transmitter, sizeof (XENVIF_TRANSMITTER)));
+ __TransmitterFree(*Transmitter);
+
+fail1:
+ Error("fail1 (%08x)\n", status);
+
+ return status;
+}
+
+NTSTATUS
+TransmitterConnect(
+ IN PXENVIF_TRANSMITTER Transmitter
+ )
+{
+ PXENVIF_FRONTEND Frontend;
+ PLIST_ENTRY ListEntry;
+ PTRANSMITTER_RING Ring;
+ NTSTATUS status;
+
+ Frontend = Transmitter->Frontend;
+
+ Transmitter->StoreInterface = FrontendGetStoreInterface(Frontend);
+
+ STORE(Acquire, Transmitter->StoreInterface);
+
+ for (ListEntry = Transmitter->List.Flink;
+ ListEntry != &Transmitter->List;
+ ListEntry = ListEntry->Flink) {
+
+ Ring = CONTAINING_RECORD(ListEntry, TRANSMITTER_RING, ListEntry);
+
+ status = __RingConnect(Ring);
+ if (!NT_SUCCESS(status))
+ goto fail1;
+ }
+
+ Transmitter->DebugInterface = FrontendGetDebugInterface(Frontend);
+
+ DEBUG(Acquire, Transmitter->DebugInterface);
+
+ status = DEBUG(Register,
+ Transmitter->DebugInterface,
+ __MODULE__ "|TRANSMITTER",
+ TransmitterDebugCallback,
+ Transmitter,
+ &Transmitter->DebugCallback);
+ if (!NT_SUCCESS(status))
+ goto fail2;
+
+ ListEntry = Transmitter->List.Flink;
+ Ring = CONTAINING_RECORD(ListEntry, TRANSMITTER_RING, ListEntry);
+
+ __RingDumpAddressTable(Ring);
+
+ Transmitter->VifInterface = FrontendGetVifInterface(Frontend);
+
+ return STATUS_SUCCESS;
+
+fail2:
+ Error("fail2\n");
+
+ DEBUG(Release, Transmitter->DebugInterface);
+ Transmitter->DebugInterface = NULL;
+
+ ListEntry = &Transmitter->List;
+
+fail1:
+ Error("fail1 (%08x)\n", status);
+
+ ListEntry = ListEntry->Blink;
+
+ while (ListEntry != &Transmitter->List) {
+ PLIST_ENTRY Prev = ListEntry->Blink;
+
+ Ring = CONTAINING_RECORD(ListEntry, TRANSMITTER_RING, ListEntry);
+
+ __RingDisconnect(Ring);
+
+ ListEntry = Prev;
+ }
+
+ STORE(Release, Transmitter->StoreInterface);
+ Transmitter->StoreInterface = NULL;
+
+ return status;
+}
+
+NTSTATUS
+TransmitterStoreWrite(
+ IN PXENVIF_TRANSMITTER Transmitter,
+ IN PXENBUS_STORE_TRANSACTION Transaction
+ )
+{
+ PLIST_ENTRY ListEntry;
+ NTSTATUS status;
+
+ for (ListEntry = Transmitter->List.Flink;
+ ListEntry != &Transmitter->List;
+ ListEntry = ListEntry->Flink) {
+ PTRANSMITTER_RING Ring;
+
+ Ring = CONTAINING_RECORD(ListEntry, TRANSMITTER_RING, ListEntry);
+
+ status = __RingStoreWrite(Ring, Transaction);
+ if (!NT_SUCCESS(status))
+ goto fail1;
+ }
+
+ return STATUS_SUCCESS;
+
+fail1:
+ Error("fail1 (%08x)\n", status);
+
+ return status;
+}
+
+NTSTATUS
+TransmitterEnable(
+ IN PXENVIF_TRANSMITTER Transmitter
+ )
+{
+ PLIST_ENTRY ListEntry;
+
+ for (ListEntry = Transmitter->List.Flink;
+ ListEntry != &Transmitter->List;
+ ListEntry = ListEntry->Flink) {
+ PTRANSMITTER_RING Ring;
+
+ Ring = CONTAINING_RECORD(ListEntry, TRANSMITTER_RING, ListEntry);
+
+ __RingEnable(Ring);
+ }
+
+ return STATUS_SUCCESS;
+}
+
+VOID
+TransmitterDisable(
+ IN PXENVIF_TRANSMITTER Transmitter
+ )
+{
+ PLIST_ENTRY ListEntry;
+
+ for (ListEntry = Transmitter->List.Blink;
+ ListEntry != &Transmitter->List;
+ ListEntry = ListEntry->Blink) {
+ PTRANSMITTER_RING Ring;
+
+ Ring = CONTAINING_RECORD(ListEntry, TRANSMITTER_RING, ListEntry);
+
+ __RingDisable(Ring);
+ }
+}
+
+VOID
+TransmitterDisconnect(
+ IN PXENVIF_TRANSMITTER Transmitter
+ )
+{
+ PXENVIF_FRONTEND Frontend;
+ PLIST_ENTRY ListEntry;
+
+ Frontend = Transmitter->Frontend;
+
+ Transmitter->VifInterface = NULL;
+
+ DEBUG(Deregister,
+ Transmitter->DebugInterface,
+ Transmitter->DebugCallback);
+ Transmitter->DebugCallback = NULL;
+
+ DEBUG(Release, Transmitter->DebugInterface);
+ Transmitter->DebugInterface = NULL;
+
+ for (ListEntry = Transmitter->List.Blink;
+ ListEntry != &Transmitter->List;
+ ListEntry = ListEntry->Blink) {
+ PTRANSMITTER_RING Ring;
+
+ Ring = CONTAINING_RECORD(ListEntry, TRANSMITTER_RING, ListEntry);
+
+ __RingDisconnect(Ring);
+ }
+
+ STORE(Remove,
+ Transmitter->StoreInterface,
+ NULL,
+ FrontendGetPrefix(Frontend),
+ "ip");
+
+ STORE(Remove,
+ Transmitter->StoreInterface,
+ NULL,
+ FrontendGetPrefix(Frontend),
+ "ipv4");
+
+ STORE(Remove,
+ Transmitter->StoreInterface,
+ NULL,
+ FrontendGetPrefix(Frontend),
+ "ipv6");
+
+ STORE(Release, Transmitter->StoreInterface);
+ Transmitter->StoreInterface = NULL;
+}
+
+VOID
+TransmitterTeardown(
+ IN PXENVIF_TRANSMITTER Transmitter
+ )
+{
+ RtlZeroMemory(&Transmitter->Metadata, sizeof (XENVIF_TRANSMITTER_PACKET_METADATA));
+
+ Transmitter->Frontend = NULL;
+
+ while (!IsListEmpty(&Transmitter->List)) {
+ PLIST_ENTRY ListEntry;
+ PTRANSMITTER_RING Ring;
+
+ ListEntry = RemoveHeadList(&Transmitter->List);
+ ASSERT3P(ListEntry, !=, &Transmitter->List);
+ RtlZeroMemory(ListEntry, sizeof (LIST_ENTRY));
+
+ Ring = CONTAINING_RECORD(ListEntry, TRANSMITTER_RING, ListEntry);
+
+ __RingTeardown(Ring);
+ }
+
+ RtlZeroMemory(&Transmitter->List, sizeof (LIST_ENTRY));
+
+ ASSERT(IsZeroMemory(Transmitter, sizeof (XENVIF_TRANSMITTER)));
+ __TransmitterFree(Transmitter);
+}
+
+VOID
+TransmitterUpdateAddressTable(
+ IN PXENVIF_TRANSMITTER Transmitter,
+ IN SOCKADDR_INET Table[],
+ IN ULONG Count
+ )
+{
+ KIRQL Irql;
+ PLIST_ENTRY ListEntry;
+ PTRANSMITTER_RING Ring;
+
+ // Make sure we don't suspend
+ KeRaiseIrql(DISPATCH_LEVEL, &Irql);
+
+ // Use the first ring for address advertisment
+ ListEntry = Transmitter->List.Flink;
+ Ring = CONTAINING_RECORD(ListEntry, TRANSMITTER_RING, ListEntry);
+
+ __RingUpdateAddressTable(Ring, Table, Count);
+
+ KeLowerIrql(Irql);
+}
+
+VOID
+TransmitterAdvertizeAddresses(
+ IN PXENVIF_TRANSMITTER Transmitter
+ )
+{
+ PLIST_ENTRY ListEntry;
+ PTRANSMITTER_RING Ring;
+
+ // Use the first ring for address advertisment
+ ListEntry = Transmitter->List.Flink;
+ Ring = CONTAINING_RECORD(ListEntry, TRANSMITTER_RING, ListEntry);
+
+ __RingAdvertizeAddresses(Ring);
+}
+
+VOID
+TransmitterSetPacketMetadata(
+ IN PXENVIF_TRANSMITTER Transmitter,
+ IN XENVIF_TRANSMITTER_PACKET_METADATA Metadata
+ )
+{
+ Transmitter->Metadata = Metadata;
+}
+
+VOID
+TransmitterQueuePackets(
+ IN PXENVIF_TRANSMITTER Transmitter,
+ IN PXENVIF_TRANSMITTER_PACKET HeadPacket
+ )
+{
+ PLIST_ENTRY ListEntry;
+ PTRANSMITTER_RING Ring;
+
+ // We need to hash for a ring eventually. Since there is only a
+ // single ring for now, we just use that.
+ ListEntry = Transmitter->List.Flink;
+ Ring = CONTAINING_RECORD(ListEntry, TRANSMITTER_RING, ListEntry);
+
+ __RingQueuePackets(Ring, HeadPacket);
+}
+
+VOID
+TransmitterAbortPackets(
+ IN PXENVIF_TRANSMITTER Transmitter
+ )
+{
+ PLIST_ENTRY ListEntry;
+ KIRQL Irql;
+
+ KeRaiseIrql(DISPATCH_LEVEL, &Irql);
+
+ for (ListEntry = Transmitter->List.Flink;
+ ListEntry != &Transmitter->List;
+ ListEntry = ListEntry->Flink) {
+ PTRANSMITTER_RING Ring;
+
+ Ring = CONTAINING_RECORD(ListEntry, TRANSMITTER_RING, ListEntry);
+
+ __RingAbortPackets(Ring);
+ }
+
+ KeLowerIrql(Irql);
+}
+
+VOID
+TransmitterGetPacketStatistics(
+ IN PXENVIF_TRANSMITTER Transmitter,
+ OUT PXENVIF_TRANSMITTER_PACKET_STATISTICS Statistics
+ )
+{
+ PLIST_ENTRY ListEntry;
+
+ RtlZeroMemory(Statistics, sizeof (XENVIF_TRANSMITTER_PACKET_STATISTICS));
+
+ for (ListEntry = Transmitter->List.Flink;
+ ListEntry != &Transmitter->List;
+ ListEntry = ListEntry->Flink) {
+ PTRANSMITTER_RING Ring;
+
+ Ring = CONTAINING_RECORD(ListEntry, TRANSMITTER_RING, ListEntry);
+
+ __RingAddPacketStatistics(Ring, Statistics);
+ }
+}
+
+ULONG
+TransmitterGetRingSize(
+ IN PXENVIF_TRANSMITTER Transitter
+ )
+{
+ PLIST_ENTRY ListEntry;
+ PTRANSMITTER_RING Ring;
+
+ // Use the first ring
+ ListEntry = Transitter->List.Flink;
+ Ring = CONTAINING_RECORD(ListEntry, TRANSMITTER_RING, ListEntry);
+
+ return __RingGetSize(Ring);
+}
+
+VOID
+TransmitterNotify(
+ IN PXENVIF_TRANSMITTER Transmitter
+ )
+{
+ PLIST_ENTRY ListEntry;
+
+ for (ListEntry = Transmitter->List.Flink;
+ ListEntry != &Transmitter->List;
+ ListEntry = ListEntry->Flink) {
+ PTRANSMITTER_RING Ring;
+
+ Ring = CONTAINING_RECORD(ListEntry, TRANSMITTER_RING, ListEntry);
+
+ __RingNotify(Ring);
+ }
+}
+
+VOID
+TransmitterGetOffloadOptions(
+ IN PXENVIF_TRANSMITTER Transmitter,
+ OUT PXENVIF_OFFLOAD_OPTIONS Options
+ )
+{
+ PXENVIF_FRONTEND Frontend;
+ PCHAR Buffer;
+ NTSTATUS status;
+
+ Frontend = Transmitter->Frontend;
+
+ Options->Value = 0;
+
+ Options->OffloadTagManipulation = 1;
+
+ status = STORE(Read,
+ Transmitter->StoreInterface,
+ NULL,
+ FrontendGetBackendPath(Frontend),
+ "feature-gso-tcpv4",
+ &Buffer);
+ if (!NT_SUCCESS(status)) {
+ Options->OffloadIpVersion4LargePacket = 0;
+ } else {
+ Options->OffloadIpVersion4LargePacket = (USHORT)strtol(Buffer, NULL, 2);
+
+ STORE(Free,
+ Transmitter->StoreInterface,
+ Buffer);
+ }
+
+ status = STORE(Read,
+ Transmitter->StoreInterface,
+ NULL,
+ FrontendGetBackendPath(Frontend),
+ "feature-gso-tcpv6",
+ &Buffer);
+ if (!NT_SUCCESS(status)) {
+ Options->OffloadIpVersion6LargePacket = 0;
+ } else {
+ Options->OffloadIpVersion6LargePacket = (USHORT)strtol(Buffer, NULL, 2);
+
+ STORE(Free,
+ Transmitter->StoreInterface,
+ Buffer);
+ }
+
+ Options->OffloadIpVersion4HeaderChecksum = 1;
+
+ status = STORE(Read,
+ Transmitter->StoreInterface,
+ NULL,
+ FrontendGetBackendPath(Frontend),
+ "feature-no-csum-offload",
+ &Buffer);
+ if (!NT_SUCCESS(status)) {
+ Options->OffloadIpVersion4TcpChecksum = 1;
+ Options->OffloadIpVersion4UdpChecksum = 1;
+ } else {
+ BOOLEAN Flag;
+
+ Flag = (BOOLEAN)strtol(Buffer, NULL, 2);
+
+ Options->OffloadIpVersion4TcpChecksum = (Flag) ? 0 : 1;
+ Options->OffloadIpVersion4UdpChecksum = (Flag) ? 0 : 1;
+
+ STORE(Free,
+ Transmitter->StoreInterface,
+ Buffer);
+ }
+
+ status = STORE(Read,
+ Transmitter->StoreInterface,
+ NULL,
+ FrontendGetBackendPath(Frontend),
+ "feature-ipv6-csum-offload",
+ &Buffer);
+ if (!NT_SUCCESS(status)) {
+ Options->OffloadIpVersion6TcpChecksum = 0;
+ Options->OffloadIpVersion6UdpChecksum = 0;
+ } else {
+ BOOLEAN Flag;
+
+ Flag = (BOOLEAN)strtol(Buffer, NULL, 2);
+
+ Options->OffloadIpVersion6TcpChecksum = (Flag) ? 1 : 0;
+ Options->OffloadIpVersion6UdpChecksum = (Flag) ? 1 : 0;
+
+ STORE(Free,
+ Transmitter->StoreInterface,
+ Buffer);
+ }
+}
+
+#define MAXIMUM_TX_REQ_SIZE ((1 << (RTL_FIELD_SIZE(netif_tx_request_t, size) * 8)) - 1)
+
+#define MAXIMUM_TCPV4_PAYLOAD_SIZE (MAXIMUM_TX_REQ_SIZE - \
+ sizeof (ETHERNET_HEADER) - \
+ MAXIMUM_IPV4_HEADER_LENGTH - \
+ MAXIMUM_TCP_HEADER_LENGTH)
+
+#define MAXIMUM_TCPV6_PAYLOAD_SIZE (MAXIMUM_TX_REQ_SIZE - \
+ sizeof (ETHERNET_HEADER) - \
+ MAXIMUM_IPV6_HEADER_LENGTH - \
+ MAXIMUM_TCP_HEADER_LENGTH)
+
+ULONG
+TransmitterGetLargePacketSize(
+ IN PXENVIF_TRANSMITTER Transmitter,
+ IN UCHAR Version
+ )
+{
+ PXENVIF_FRONTEND Frontend;
+ PCHAR Buffer;
+ ULONG OffloadIpLargePacket;
+ NTSTATUS status;
+
+ Frontend = Transmitter->Frontend;
+
+ if (Version == 4) {
+ status = STORE(Read,
+ Transmitter->StoreInterface,
+ NULL,
+ FrontendGetBackendPath(Frontend),
+ "feature-gso-tcpv4",
+ &Buffer);
+ } else if (Version == 6) {
+ status = STORE(Read,
+ Transmitter->StoreInterface,
+ NULL,
+ FrontendGetBackendPath(Frontend),
+ "feature-gso-tcpv6",
+ &Buffer);
+ } else {
+ Buffer = NULL;
+ status = STATUS_UNSUCCESSFUL;
+ }
+
+ if (!NT_SUCCESS(status)) {
+ OffloadIpLargePacket = 0;
+ } else {
+ OffloadIpLargePacket = (ULONG)strtol(Buffer, NULL, 2);
+
+ STORE(Free,
+ Transmitter->StoreInterface,
+ Buffer);
+ }
+
+ // The OffloadParity certification test requires that we have a single LSO size for IPv4 and IPv6 packets
+ return (OffloadIpLargePacket) ? __min(MAXIMUM_TCPV4_PAYLOAD_SIZE, MAXIMUM_TCPV6_PAYLOAD_SIZE) : 0;
+}
--- /dev/null
+/* Copyright (c) Citrix Systems Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms,
+ * with or without modification, are permitted provided
+ * that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer in the documentation and/or other
+ * materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef _XENVIF_TRANSMITTER_H
+#define _XENVIF_TRANSMITTER_H
+
+#include <ntddk.h>
+#include <netioapi.h>
+#include <vif_interface.h>
+
+#include "frontend.h"
+
+typedef struct _XENVIF_TRANSMITTER XENVIF_TRANSMITTER, *PXENVIF_TRANSMITTER;
+
+extern NTSTATUS
+TransmitterInitialize(
+ IN PXENVIF_FRONTEND Frontend,
+ IN ULONG Count,
+ OUT PXENVIF_TRANSMITTER *Transmitter
+ );
+
+extern NTSTATUS
+TransmitterConnect(
+ IN PXENVIF_TRANSMITTER Transmitter
+ );
+
+extern NTSTATUS
+TransmitterStoreWrite(
+ IN PXENVIF_TRANSMITTER Transmitter,
+ IN PXENBUS_STORE_TRANSACTION Transaction
+ );
+
+extern NTSTATUS
+TransmitterEnable(
+ IN PXENVIF_TRANSMITTER Transmitter
+ );
+
+extern VOID
+TransmitterDisable(
+ IN PXENVIF_TRANSMITTER Transmitter
+ );
+
+extern VOID
+TransmitterDisconnect(
+ IN PXENVIF_TRANSMITTER Transmitter
+ );
+
+extern VOID
+TransmitterTeardown(
+ IN PXENVIF_TRANSMITTER Transmitter
+ );
+
+extern VOID
+TransmitterNotify(
+ IN PXENVIF_TRANSMITTER Transmitter
+ );
+
+extern VOID
+TransmitterAbortPackets(
+ IN PXENVIF_TRANSMITTER Transmitter
+ );
+
+extern VOID
+TransmitterGetPacketStatistics(
+ IN PXENVIF_TRANSMITTER Transmitter,
+ OUT PXENVIF_TRANSMITTER_PACKET_STATISTICS Statistics
+ );
+
+extern ULONG
+TransmitterGetRingSize(
+ IN PXENVIF_TRANSMITTER Transmitter
+ );
+
+extern VOID
+TransmitterUpdateAddressTable(
+ IN PXENVIF_TRANSMITTER Transmitter,
+ IN SOCKADDR_INET Table[],
+ IN ULONG Count
+ );
+
+extern VOID
+TransmitterAdvertizeAddresses(
+ IN PXENVIF_TRANSMITTER Transmitter
+ );
+
+extern VOID
+TransmitterSetPacketMetadata(
+ IN PXENVIF_TRANSMITTER Transmitter,
+ IN XENVIF_TRANSMITTER_PACKET_METADATA Metadata
+ );
+
+extern VOID
+TransmitterQueuePackets(
+ IN PXENVIF_TRANSMITTER Transmitter,
+ IN PXENVIF_TRANSMITTER_PACKET HeadPacket
+ );
+
+extern VOID
+TransmitterGetOffloadOptions(
+ IN PXENVIF_TRANSMITTER Transmitter,
+ OUT PXENVIF_OFFLOAD_OPTIONS Options
+ );
+
+extern ULONG
+TransmitterGetLargePacketSize(
+ IN PXENVIF_TRANSMITTER Transmitter,
+ IN UCHAR Version
+ );
+
+#endif // _XENVIF_TRANSMITTER_H
--- /dev/null
+/* Copyright (c) Citrix Systems Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms,
+ * with or without modification, are permitted provided
+ * that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer in the documentation and/or other
+ * materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef _XENVIF_TYPES_H
+#define _XENVIF_TYPES_H
+
+typedef enum _DEVICE_OBJECT_TYPE {
+ PHYSICAL_DEVICE_OBJECT = 'ODP',
+ FUNCTION_DEVICE_OBJECT = 'ODF'
+} DEVICE_OBJECT_TYPE, *PDEVICE_OBJECT_TYPE;
+
+typedef enum _DEVICE_PNP_STATE {
+ Invalid = 0,
+ Present, // PDO only
+ Enumerated, // PDO only
+ Added, // FDO only
+ Started,
+ StopPending,
+ Stopped,
+ RemovePending,
+ SurpriseRemovePending,
+ Deleted
+} DEVICE_PNP_STATE, *PDEVICE_PNP_STATE;
+
+#endif // _XENVIF_TYPES_H
--- /dev/null
+/* Copyright (c) Citrix Systems Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms,
+ * with or without modification, are permitted provided
+ * that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer in the documentation and/or other
+ * materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <ntddk.h>
+#include <ntstrsafe.h>
+#include <stdarg.h>
+#include <xen.h>
+#include <util.h>
+
+#include "pdo.h"
+#include "vif.h"
+#include "mrsw.h"
+#include "thread.h"
+#include "log.h"
+#include "assert.h"
+
+#define VIF_POOL ' FIV'
+
+typedef struct _VIF_CALLBACK {
+ VOID (*Function)(PVOID, XENVIF_CALLBACK_TYPE, ...);
+ PVOID Argument;
+} VIF_CALLBACK, *PVIF_CALLBACK;
+
+struct _XENVIF_VIF_CONTEXT {
+ LONG References;
+ XENVIF_MRSW_LOCK Lock;
+ PXENVIF_PDO Pdo;
+ BOOLEAN Enabled;
+ PXENVIF_THREAD MonitorThread;
+ KEVENT MonitorEvent;
+ VIF_CALLBACK Callback;
+
+ PXENBUS_SUSPEND_INTERFACE SuspendInterface;
+ PXENBUS_SUSPEND_CALLBACK SuspendCallbackLate;
+};
+
+static FORCEINLINE PVOID
+__VifAllocate(
+ IN ULONG Length
+ )
+{
+ return __AllocateNonPagedPoolWithTag(Length, VIF_POOL);
+}
+
+static FORCEINLINE VOID
+__VifFree(
+ IN PVOID Buffer
+ )
+{
+ __FreePoolWithTag(Buffer, VIF_POOL);
+}
+
+VOID
+VifCompletePackets(
+ IN PXENVIF_VIF_INTERFACE Interface,
+ IN PXENVIF_TRANSMITTER_PACKET HeadPacket
+ )
+{
+ PXENVIF_VIF_CONTEXT Context = Interface->Context;
+ PVIF_CALLBACK Callback = &Context->Callback;
+
+ ASSERT(Callback != NULL);
+
+ Callback->Function(Callback->Argument,
+ XENVIF_CALLBACK_COMPLETE_PACKETS,
+ HeadPacket);
+}
+
+VOID
+VifReceivePackets(
+ IN PXENVIF_VIF_INTERFACE Interface,
+ IN PLIST_ENTRY List
+ )
+{
+ PXENVIF_VIF_CONTEXT Context = Interface->Context;
+ PVIF_CALLBACK Callback = &Context->Callback;
+
+ ASSERT(Callback != NULL);
+
+ Callback->Function(Callback->Argument,
+ XENVIF_CALLBACK_RECEIVE_PACKETS,
+ List);
+}
+
+enum {
+ THREAD_EVENT = 0,
+ MAC_EVENT,
+ EVENT_COUNT
+};
+
+C_ASSERT(EVENT_COUNT <= THREAD_WAIT_OBJECTS);
+
+#define STATUS_MASK ((1 << 6) - 1)
+
+static NTSTATUS
+VifMonitor(
+ IN PXENVIF_THREAD Self,
+ IN PVOID _Context
+ )
+{
+ PXENVIF_VIF_CONTEXT Context = _Context;
+ PXENVIF_FRONTEND Frontend = PdoGetFrontend(Context->Pdo);
+ PKEVENT Event[EVENT_COUNT];
+
+ Trace("====>\n");
+
+ Event[THREAD_EVENT] = ThreadGetEvent(Self);
+ Event[MAC_EVENT] = MacGetEvent(FrontendGetMac(Frontend));
+
+ for (;;) {
+ NTSTATUS status;
+
+ Trace("waiting...\n");
+
+ status = KeWaitForMultipleObjects(EVENT_COUNT,
+ Event,
+ WaitAny,
+ Executive,
+ KernelMode,
+ FALSE,
+ NULL,
+ NULL);
+
+ Trace("awake\n");
+
+ if (status >= STATUS_WAIT_0 &&
+ status < STATUS_WAIT_0 + EVENT_COUNT) {
+ switch (status & STATUS_MASK) {
+ case MAC_EVENT: {
+ KeClearEvent(Event[MAC_EVENT]);
+
+ Trace("MAC_EVENT\n");
+
+ if (Context->Enabled) {
+ PVIF_CALLBACK Callback = &Context->Callback;
+
+ Callback->Function(Callback->Argument,
+ XENVIF_CALLBACK_MEDIA_STATE_CHANGE);
+ }
+
+ break;
+ }
+ case THREAD_EVENT:
+ KeClearEvent(Event[THREAD_EVENT]);
+
+ Trace("THREAD_EVENT\n");
+
+ if (ThreadIsAlerted(Self))
+ goto done;
+
+ KeSetEvent(&Context->MonitorEvent, IO_NO_INCREMENT, FALSE);
+ break;
+
+ default:
+ ASSERT(FALSE);
+ break;
+ }
+ }
+ }
+
+done:
+ KeSetEvent(&Context->MonitorEvent, IO_NO_INCREMENT, FALSE);
+
+ Trace("<====\n");
+
+ return STATUS_SUCCESS;
+}
+
+static DECLSPEC_NOINLINE VOID
+VifSuspendCallbackLate(
+ IN PVOID Argument
+ )
+{
+ PXENVIF_VIF_CONTEXT Context = Argument;
+ PXENVIF_FRONTEND Frontend = PdoGetFrontend(Context->Pdo);
+ NTSTATUS status;
+
+ status = FrontendSetState(Frontend, FRONTEND_ENABLED);
+ ASSERT(NT_SUCCESS(status));
+
+ TransmitterAdvertizeAddresses(FrontendGetTransmitter(Frontend));
+}
+
+static NTSTATUS
+VifEnable(
+ IN PXENVIF_VIF_CONTEXT Context,
+ IN VOID (*Function)(PVOID, XENVIF_CALLBACK_TYPE, ...),
+ IN PVOID Argument
+ )
+{
+ PXENVIF_FRONTEND Frontend = PdoGetFrontend(Context->Pdo);
+ KIRQL Irql;
+ PVIF_CALLBACK Callback;
+ PKEVENT Event;
+ NTSTATUS status;
+
+ Trace("====>\n");
+
+ AcquireMrswLockExclusive(&Context->Lock, &Irql);
+
+ if (Context->Enabled)
+ goto done;
+
+ Callback = &Context->Callback;
+ Callback->Function = Function;
+ Callback->Argument = Argument;
+
+ status = FrontendSetState(Frontend, FRONTEND_ENABLED);
+ if (!NT_SUCCESS(status))
+ goto fail1;
+
+ Context->SuspendInterface = FrontendGetSuspendInterface(Frontend);
+
+ SUSPEND(Acquire, Context->SuspendInterface);
+
+ status = SUSPEND(Register,
+ Context->SuspendInterface,
+ SUSPEND_CALLBACK_LATE,
+ VifSuspendCallbackLate,
+ Context,
+ &Context->SuspendCallbackLate);
+ if (!NT_SUCCESS(status))
+ goto fail2;
+
+ Context->Enabled = TRUE;
+
+ Event = MacGetEvent(FrontendGetMac(Frontend));
+ KeSetEvent(Event, IO_NO_INCREMENT, FALSE);
+
+done:
+ ReleaseMrswLockExclusive(&Context->Lock, Irql, FALSE);
+
+ Trace("<====\n");
+
+ return STATUS_SUCCESS;
+
+fail2:
+ Error("fail2\n");
+
+ SUSPEND(Release, Context->SuspendInterface);
+ Context->SuspendInterface = NULL;
+
+fail1:
+ Error("fail1 (%08x)\n", status);
+
+ RtlZeroMemory(&Context->Callback, sizeof (VIF_CALLBACK));
+
+ ReleaseMrswLockExclusive(&Context->Lock, Irql, FALSE);
+
+ return status;
+}
+
+static VOID
+VifDisable(
+ IN PXENVIF_VIF_CONTEXT Context
+ )
+{
+ PXENVIF_FRONTEND Frontend = PdoGetFrontend(Context->Pdo);
+ KIRQL Irql;
+ PKEVENT Event;
+
+ Trace("====>\n");
+
+ AcquireMrswLockExclusive(&Context->Lock, &Irql);
+
+ if (!Context->Enabled) {
+ ReleaseMrswLockExclusive(&Context->Lock, Irql, FALSE);
+ goto done;
+ }
+
+ Context->Enabled = FALSE;
+
+ Event = MacGetEvent(FrontendGetMac(Frontend));
+ KeSetEvent(Event, IO_NO_INCREMENT, FALSE);
+
+ SUSPEND(Deregister,
+ Context->SuspendInterface,
+ Context->SuspendCallbackLate);
+ Context->SuspendCallbackLate = NULL;
+
+ SUSPEND(Release, Context->SuspendInterface);
+ Context->SuspendInterface = NULL;
+
+ (VOID) FrontendSetState(Frontend, FRONTEND_CONNECTED);
+
+ ReleaseMrswLockExclusive(&Context->Lock, Irql, TRUE);
+
+ ReceiverWaitForPackets(FrontendGetReceiver(Frontend));
+ TransmitterAbortPackets(FrontendGetTransmitter(Frontend));
+
+ KeClearEvent(&Context->MonitorEvent);
+ ThreadWake(Context->MonitorThread);
+
+ Trace("waiting for monitor thread\n");
+
+ (VOID) KeWaitForSingleObject(&Context->MonitorEvent,
+ Executive,
+ KernelMode,
+ FALSE,
+ NULL);
+
+ RtlZeroMemory(&Context->Callback, sizeof (VIF_CALLBACK));
+
+ ReleaseMrswLockShared(&Context->Lock);
+
+done:
+ Trace("<====\n");
+}
+
+static VOID
+VifQueryPacketStatistics(
+ IN PXENVIF_VIF_CONTEXT Context,
+ OUT PXENVIF_PACKET_STATISTICS Statistics
+ )
+{
+ PXENVIF_FRONTEND Frontend = PdoGetFrontend(Context->Pdo);
+
+ AcquireMrswLockShared(&Context->Lock);
+
+ ReceiverGetPacketStatistics(FrontendGetReceiver(Frontend),
+ &Statistics->Receiver);
+ TransmitterGetPacketStatistics(FrontendGetTransmitter(Frontend),
+ &Statistics->Transmitter);
+
+ ReleaseMrswLockShared(&Context->Lock);
+}
+
+static VOID
+VifUpdatePacketMetadata(
+ IN PXENVIF_VIF_CONTEXT Context,
+ IN PXENVIF_TRANSMITTER_PACKET_METADATA Metadata
+ )
+{
+ PXENVIF_FRONTEND Frontend = PdoGetFrontend(Context->Pdo);
+
+ AcquireMrswLockShared(&Context->Lock);
+
+ TransmitterSetPacketMetadata(FrontendGetTransmitter(Frontend),
+ *Metadata);
+
+ ReleaseMrswLockShared(&Context->Lock);
+}
+
+static VOID
+VifReturnPacket(
+ IN PXENVIF_VIF_CONTEXT Context,
+ IN PXENVIF_RECEIVER_PACKET Packet
+ )
+{
+ PXENVIF_FRONTEND Frontend = PdoGetFrontend(Context->Pdo);
+
+ AcquireMrswLockShared(&Context->Lock);
+
+ ReceiverReturnPacket(FrontendGetReceiver(Frontend),
+ Packet);
+
+ ReleaseMrswLockShared(&Context->Lock);
+}
+
+static NTSTATUS
+VifQueuePackets(
+ IN PXENVIF_VIF_CONTEXT Context,
+ IN PXENVIF_TRANSMITTER_PACKET HeadPacket
+ )
+{
+ PXENVIF_FRONTEND Frontend = PdoGetFrontend(Context->Pdo);
+ NTSTATUS status;
+
+ AcquireMrswLockShared(&Context->Lock);
+
+ status = STATUS_UNSUCCESSFUL;
+ if (Context->Enabled == FALSE) {
+ Trace("NOT ENABLED\n");
+
+ goto fail1;
+ }
+
+ TransmitterQueuePackets(FrontendGetTransmitter(Frontend),
+ HeadPacket);
+
+ ReleaseMrswLockShared(&Context->Lock);
+
+ return STATUS_SUCCESS;
+
+fail1:
+ ReleaseMrswLockShared(&Context->Lock);
+
+ return status;
+}
+
+static VOID
+VifQueryOffloadOptions(
+ IN PXENVIF_VIF_CONTEXT Context,
+ OUT PXENVIF_OFFLOAD_OPTIONS Options
+ )
+{
+ PXENVIF_FRONTEND Frontend = PdoGetFrontend(Context->Pdo);
+
+ AcquireMrswLockShared(&Context->Lock);
+
+ TransmitterGetOffloadOptions(FrontendGetTransmitter(Frontend),
+ Options);
+
+ ReleaseMrswLockShared(&Context->Lock);
+}
+
+static NTSTATUS
+VifUpdateOffloadOptions(
+ IN PXENVIF_VIF_CONTEXT Context,
+ IN XENVIF_OFFLOAD_OPTIONS Options
+ )
+{
+ PXENVIF_FRONTEND Frontend = PdoGetFrontend(Context->Pdo);
+ NTSTATUS status;
+
+ AcquireMrswLockShared(&Context->Lock);
+
+ status = ReceiverSetOffloadOptions(FrontendGetReceiver(Frontend),
+ Options);
+
+ ReleaseMrswLockShared(&Context->Lock);
+
+ return status;
+}
+
+static VOID
+VifQueryLargePacketSize(
+ IN PXENVIF_VIF_CONTEXT Context,
+ IN UCHAR Version,
+ OUT PULONG Size
+ )
+{
+ PXENVIF_FRONTEND Frontend = PdoGetFrontend(Context->Pdo);
+
+ AcquireMrswLockShared(&Context->Lock);
+
+ *Size = TransmitterGetLargePacketSize(FrontendGetTransmitter(Frontend), Version);
+
+ ReleaseMrswLockShared(&Context->Lock);
+}
+
+static VOID
+VifQueryMediaState(
+ IN PXENVIF_VIF_CONTEXT Context,
+ OUT PNET_IF_MEDIA_CONNECT_STATE MediaConnectState OPTIONAL,
+ OUT PULONG64 LinkSpeed OPTIONAL,
+ OUT PNET_IF_MEDIA_DUPLEX_STATE MediaDuplexState OPTIONAL
+ )
+{
+ PXENVIF_FRONTEND Frontend = PdoGetFrontend(Context->Pdo);
+
+ AcquireMrswLockShared(&Context->Lock);
+
+ if (Context->Enabled && MacGetLinkState(FrontendGetMac(Frontend))) {
+ if (MediaConnectState != NULL)
+ *MediaConnectState = MediaConnectStateConnected;
+
+ if (LinkSpeed != NULL)
+ *LinkSpeed = (ULONGLONG)MacGetLinkSpeed(FrontendGetMac(Frontend)) * 1000000000ull;
+
+ if (MediaDuplexState != NULL)
+ *MediaDuplexState = MediaDuplexStateFull;
+ } else {
+ if (MediaConnectState != NULL)
+ *MediaConnectState = MediaConnectStateDisconnected;
+
+ if (LinkSpeed != NULL)
+ *LinkSpeed = 0;
+
+ if (MediaDuplexState != NULL)
+ *MediaDuplexState = MediaDuplexStateUnknown;
+ }
+
+ ReleaseMrswLockShared(&Context->Lock);
+}
+
+static VOID
+VifQueryMaximumFrameSize(
+ IN PXENVIF_VIF_CONTEXT Context,
+ OUT PULONG Size
+ )
+{
+ PXENVIF_FRONTEND Frontend = PdoGetFrontend(Context->Pdo);
+
+ AcquireMrswLockShared(&Context->Lock);
+
+ *Size = MacGetMaximumFrameSize(FrontendGetMac(Frontend));
+
+ ReleaseMrswLockShared(&Context->Lock);
+}
+
+static VOID
+VifQueryPermanentAddress(
+ IN PXENVIF_VIF_CONTEXT Context,
+ OUT PETHERNET_ADDRESS Address
+ )
+{
+ PXENVIF_FRONTEND Frontend = PdoGetFrontend(Context->Pdo);
+
+ AcquireMrswLockShared(&Context->Lock);
+
+ *Address = *MacGetPermanentAddress(FrontendGetMac(Frontend));
+
+ ReleaseMrswLockShared(&Context->Lock);
+}
+
+static VOID
+VifQueryCurrentAddress(
+ IN PXENVIF_VIF_CONTEXT Context,
+ OUT PETHERNET_ADDRESS Address
+ )
+{
+ PXENVIF_FRONTEND Frontend = PdoGetFrontend(Context->Pdo);
+
+ AcquireMrswLockShared(&Context->Lock);
+
+ *Address = *MacGetCurrentAddress(FrontendGetMac(Frontend));
+
+ ReleaseMrswLockShared(&Context->Lock);
+}
+
+static NTSTATUS
+VifUpdateCurrentAddress(
+ IN PXENVIF_VIF_CONTEXT Context,
+ OUT PETHERNET_ADDRESS Address
+ )
+{
+ PXENVIF_FRONTEND Frontend = PdoGetFrontend(Context->Pdo);
+ NTSTATUS status;
+
+ AcquireMrswLockShared(&Context->Lock);
+
+ status = MacSetCurrentAddress(FrontendGetMac(Frontend), Address);
+
+ ReleaseMrswLockShared(&Context->Lock);
+
+ return status;
+}
+
+static NTSTATUS
+VifQueryMulticastAddresses(
+ IN PXENVIF_VIF_CONTEXT Context,
+ OUT ETHERNET_ADDRESS Address[] OPTIONAL,
+ OUT PULONG Count
+ )
+{
+ PXENVIF_FRONTEND Frontend = PdoGetFrontend(Context->Pdo);
+ ULONG Size;
+ NTSTATUS status;
+
+ AcquireMrswLockShared(&Context->Lock);
+
+ Size = sizeof (ETHERNET_ADDRESS) * *Count;
+
+ (VOID) MacGetMulticastAddresses(FrontendGetMac(Frontend),
+ Count);
+
+ status = STATUS_BUFFER_TOO_SMALL;
+ if (Size < sizeof (ETHERNET_ADDRESS) * *Count)
+ goto fail1;
+
+ RtlCopyMemory(Address,
+ MacGetMulticastAddresses(FrontendGetMac(Frontend), Count),
+ Size);
+
+ ReleaseMrswLockShared(&Context->Lock);
+
+ return STATUS_SUCCESS;
+
+fail1:
+ ReleaseMrswLockShared(&Context->Lock);
+
+ return status;
+}
+
+static NTSTATUS
+VifUpdateMulticastAddresses(
+ IN PXENVIF_VIF_CONTEXT Context,
+ IN ETHERNET_ADDRESS Address[],
+ IN ULONG Count
+ )
+{
+ PXENVIF_FRONTEND Frontend = PdoGetFrontend(Context->Pdo);
+ NTSTATUS status;
+
+ AcquireMrswLockShared(&Context->Lock);
+
+ status = MacSetMulticastAddresses(FrontendGetMac(Frontend),
+ Address,
+ Count);
+
+ ReleaseMrswLockShared(&Context->Lock);
+
+ return status;
+}
+
+static VOID
+VifQueryFilterLevel(
+ IN PXENVIF_VIF_CONTEXT Context,
+ IN ETHERNET_ADDRESS_TYPE Type,
+ OUT PXENVIF_MAC_FILTER_LEVEL Level
+ )
+{
+ PXENVIF_FRONTEND Frontend = PdoGetFrontend(Context->Pdo);
+
+ AcquireMrswLockShared(&Context->Lock);
+
+ *Level = MacGetFilterLevel(FrontendGetMac(Frontend),
+ Type);
+
+ ReleaseMrswLockShared(&Context->Lock);
+}
+
+static NTSTATUS
+VifUpdateFilterLevel(
+ IN PXENVIF_VIF_CONTEXT Context,
+ IN ETHERNET_ADDRESS_TYPE Type,
+ IN XENVIF_MAC_FILTER_LEVEL Level
+ )
+{
+ PXENVIF_FRONTEND Frontend = PdoGetFrontend(Context->Pdo);
+ NTSTATUS status;
+
+ AcquireMrswLockShared(&Context->Lock);
+
+ status = MacSetFilterLevel(FrontendGetMac(Frontend),
+ Type,
+ Level);
+
+ ReleaseMrswLockShared(&Context->Lock);
+
+ return status;
+}
+
+static VOID
+VifQueryReceiverRingSize(
+ IN PXENVIF_VIF_CONTEXT Context,
+ OUT PULONG Size
+ )
+{
+ PXENVIF_FRONTEND Frontend = PdoGetFrontend(Context->Pdo);
+
+ AcquireMrswLockShared(&Context->Lock);
+
+ *Size = ReceiverGetRingSize(FrontendGetReceiver(Frontend));
+
+ ReleaseMrswLockShared(&Context->Lock);
+}
+
+static VOID
+VifQueryTransmitterRingSize(
+ IN PXENVIF_VIF_CONTEXT Context,
+ OUT PULONG Size
+ )
+{
+ PXENVIF_FRONTEND Frontend = PdoGetFrontend(Context->Pdo);
+
+ AcquireMrswLockShared(&Context->Lock);
+
+ *Size = TransmitterGetRingSize(FrontendGetTransmitter(Frontend));
+
+ ReleaseMrswLockShared(&Context->Lock);
+}
+
+static VOID
+VifAcquire(
+ IN PXENVIF_VIF_CONTEXT Context
+ )
+{
+ InterlockedIncrement(&Context->References);
+}
+
+static VOID
+VifRelease(
+ IN PXENVIF_VIF_CONTEXT Context
+ )
+{
+ ASSERT(Context->References != 0);
+ InterlockedDecrement(&Context->References);
+}
+
+#define VIF_OPERATION(_Type, _Name, _Arguments) \
+ Vif ## _Name,
+
+static XENVIF_VIF_OPERATIONS Operations = {
+ DEFINE_VIF_OPERATIONS
+};
+
+#undef VIF_OPERATION
+
+NTSTATUS
+VifInitialize(
+ IN PXENVIF_PDO Pdo,
+ OUT PXENVIF_VIF_INTERFACE Interface
+ )
+{
+ PXENVIF_VIF_CONTEXT Context;
+ NTSTATUS status;
+
+ Context = __VifAllocate(sizeof (XENVIF_VIF_CONTEXT));
+
+ status = STATUS_NO_MEMORY;
+ if (Context == NULL)
+ goto fail1;
+
+ InitializeMrswLock(&Context->Lock);
+
+ Context->Pdo = Pdo;
+
+ KeInitializeEvent(&Context->MonitorEvent, NotificationEvent, FALSE);
+
+ status = ThreadCreate(VifMonitor, Context, &Context->MonitorThread);
+ if (!NT_SUCCESS(status))
+ goto fail2;
+
+ Interface->Context = Context;
+ Interface->Operations = &Operations;
+
+ return STATUS_SUCCESS;
+
+fail2:
+ Error("fail2\n");
+
+ RtlZeroMemory(&Context->MonitorEvent, sizeof (KEVENT));
+
+ Context->Pdo = NULL;
+ RtlZeroMemory(&Context->Lock, sizeof (XENVIF_MRSW_LOCK));
+
+ ASSERT(IsZeroMemory(Context, sizeof (XENVIF_VIF_CONTEXT)));
+ __VifFree(Context);
+
+fail1:
+ Error("fail1 (%08x)\n", status);
+
+ return status;
+}
+
+VOID
+VifTeardown(
+ IN OUT PXENVIF_VIF_INTERFACE Interface
+ )
+{
+ PXENVIF_VIF_CONTEXT Context = Interface->Context;
+
+ ThreadAlert(Context->MonitorThread);
+ ThreadJoin(Context->MonitorThread);
+ Context->MonitorThread = NULL;
+
+ RtlZeroMemory(&Context->MonitorEvent, sizeof (KEVENT));
+
+ Context->Pdo = NULL;
+ RtlZeroMemory(&Context->Lock, sizeof (XENVIF_MRSW_LOCK));
+
+ ASSERT(IsZeroMemory(Context, sizeof (XENVIF_VIF_CONTEXT)));
+ __VifFree(Context);
+
+ RtlZeroMemory(Interface, sizeof (XENVIF_VIF_INTERFACE));
+}
+
--- /dev/null
+/* Copyright (c) Citrix Systems Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms,
+ * with or without modification, are permitted provided
+ * that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer in the documentation and/or other
+ * materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef _XENVIF_VIF_H
+#define _XENVIF_VIF_H
+
+#include <ntddk.h>
+#include <vif_interface.h>
+
+#include "fdo.h"
+
+struct _XENVIF_VIF_INTERFACE {
+ PXENVIF_VIF_OPERATIONS Operations;
+ PXENVIF_VIF_CONTEXT Context;
+};
+
+C_ASSERT(FIELD_OFFSET(XENVIF_VIF_INTERFACE, Operations) == (ULONG_PTR)VIF_OPERATIONS(NULL));
+C_ASSERT(FIELD_OFFSET(XENVIF_VIF_INTERFACE, Context) == (ULONG_PTR)VIF_CONTEXT(NULL));
+
+extern NTSTATUS
+VifInitialize(
+ IN PXENVIF_PDO Pdo,
+ OUT PXENVIF_VIF_INTERFACE Interface
+ );
+
+extern VOID
+VifTeardown(
+ IN OUT PXENVIF_VIF_INTERFACE Interface
+ );
+
+extern VOID
+VifCompletePackets(
+ IN PXENVIF_VIF_INTERFACE Interface,
+ IN PXENVIF_TRANSMITTER_PACKET HeadPacket
+ );
+
+extern VOID
+VifReceivePackets(
+ IN PXENVIF_VIF_INTERFACE Interface,
+ IN PLIST_ENTRY List
+ );
+
+#endif // _XENVIF_VIF_H
+
--- /dev/null
+/* Copyright (c) Citrix Systems Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms,
+ * with or without modification, are permitted provided
+ * that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer in the documentation and/or other
+ * materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <windows.h>
+#include <ntverp.h>
+
+
+#undef VER_COMPANYNAME_STR
+#undef VER_PRODUCTNAME_STR
+#undef VER_PRODUCTVERSION
+#undef VER_PRODUCTVERSION_STR
+
+#define VER_COMPANYNAME_STR "Citrix Systems Inc."
+#define VER_LEGALCOPYRIGHT_STR "Copyright " YEAR_STR VER_COMPANYNAME_STR
+
+#include <version.h>
+
+#define VER_PRODUCTNAME_STR "Citrix PV Tools for Virtual Machines"
+#define VER_PRODUCTVERSION MAJOR_VERSION,MINOR_VERSION,MICRO_VERSION,BUILD_NUMBER
+#define VER_PRODUCTVERSION_STR MAJOR_VERSION_STR "." MINOR_VERSION_STR "." MICRO_VERSION_STR "." BUILD_NUMBER_STR
+
+#define VER_INTERNALNAME_STR "XENVIF.SYS"
+#define VER_FILEDESCRIPTION_STR "Citrix VIF Class Driver"
+
+#define VER_FILETYPE VFT_DRV
+#define VER_FILESUBTYPE VFT2_DRV_SYSTEM
+
+#include <common.ver>