The information needed to determine if a machine can run jailhouse is located
entirely in sysfs.  To see if a machine can run jailhouse, the user must
currently (1) generate a configuration file using jailhouse-config-create (2)
run `make` (3) pass this file to jailhouse-hardware-check.

Prepare for enabling jailhouse-hardware-check to read this information directly
from sysfs by moving the relevant sysfs parsing functions in
jailhouse-config-create into a module in pyjailhouse.

Signed-off-by: Chris Goldsworthy <[email protected]>
---
 pyjailhouse/sysfs_parser.py    | 927 +++++++++++++++++++++++++++++++++++++++
 tools/jailhouse-config-create  | 959 ++---------------------------------------
 tools/jailhouse-hardware-check |   1 +
 3 files changed, 957 insertions(+), 930 deletions(-)
 create mode 100644 pyjailhouse/sysfs_parser.py

diff --git a/pyjailhouse/sysfs_parser.py b/pyjailhouse/sysfs_parser.py
new file mode 100644
index 0000000..c415473
--- /dev/null
+++ b/pyjailhouse/sysfs_parser.py
@@ -0,0 +1,927 @@
+#
+# Jailhouse, a Linux-based partitioning hypervisor
+#
+# Copyright (c) Siemens AG, 2014-2017
+# Copyright (c) Valentine Sinitsyn, 2014-2015
+#
+# Authors:
+#  Henning Schild <[email protected]>
+#  Jan Kiszka <[email protected]>
+#  Valentine Sinitsyn <[email protected]>
+#
+# This work is licensed under the terms of the GNU GPL, version 2.  See
+# the COPYING file in the top-level directory.
+#
+# This script should help to create a basic jailhouse configuration file.
+# It needs to be executed on the target machine, where it will gather
+# information about the system. For more advanced scenarios you will have
+# to change the generated C-code.
+
+
+import struct
+import os
+import fnmatch
+
+root_dir = "/"
+
+inputs = {
+    'files': set(),
+    'files_opt': set(),
+    'files_intel': set(),
+    'files_amd': set()
+}
+
+# required files
+inputs['files'].add('/proc/iomem')
+inputs['files'].add('/proc/cpuinfo')
+inputs['files'].add('/proc/cmdline')
+inputs['files'].add('/proc/ioports')
+inputs['files'].add('/sys/bus/pci/devices/*/config')
+inputs['files'].add('/sys/bus/pci/devices/*/resource')
+inputs['files'].add('/sys/devices/system/cpu/cpu*/uevent')
+inputs['files'].add('/sys/firmware/acpi/tables/APIC')
+inputs['files'].add('/sys/firmware/acpi/tables/MCFG')
+# optional files
+inputs['files_opt'].add('/sys/class/dmi/id/product_name')
+inputs['files_opt'].add('/sys/class/dmi/id/sys_vendor')
+inputs['files_opt'].add('/sys/class/tty/*/iomem_base')
+inputs['files_opt'].add('/sys/class/tty/*/iomem_reg_shift')
+inputs['files_opt'].add('/sys/class/tty/*/io_type')
+inputs['files_opt'].add('/sys/class/tty/*/port')
+inputs['files_opt'].add('/sys/devices/jailhouse/enabled')
+# platform specific files
+inputs['files_intel'].add('/sys/firmware/acpi/tables/DMAR')
+inputs['files_amd'].add('/sys/firmware/acpi/tables/IVRS')
+
+
+def check_input_listed(name, optional=False):
+    set = inputs['files_opt']
+    if optional is False:
+        set = inputs['files']
+        cpuvendor = get_cpu_vendor()
+        if cpuvendor == 'GenuineIntel':
+            set = set.union(inputs['files_intel'])
+        elif cpuvendor == 'AuthenticAMD':
+            set = set.union(inputs['files_amd'])
+
+    for file in set:
+        if fnmatch.fnmatch(name, file):
+            return True
+    raise RuntimeError('"' + name + '" is not a listed input file')
+
+
+def input_open(name, mode='r', optional=False):
+    check_input_listed(name, optional)
+    try:
+        f = open(os.path.join(root_dir, name), mode)
+    except Exception as e:
+        if optional:
+            return open("/dev/null", mode)
+        raise e
+    return f
+
+
+def input_listdir(dir, wildcards):
+    for w in wildcards:
+        check_input_listed(os.path.join(dir, w))
+    dirs = os.listdir(os.path.join(root_dir, dir))
+    dirs.sort()
+    return dirs
+
+
+def parse_iomem(pcidevices):
+    regions = IOMemRegionTree.parse_iomem_tree(
+        IOMemRegionTree.parse_iomem_file())
+
+    rom_region = MemRegion(0xc0000, 0xdffff, 'ROMs')
+    add_rom_region = False
+
+    ret = []
+    dmar_regions = []
+    for r in regions:
+        append_r = True
+        # filter the list for MSI-X pages
+        for d in pcidevices:
+            if d.msix_address >= r.start and d.msix_address <= r.stop:
+                if d.msix_address > r.start:
+                    head_r = MemRegion(r.start, d.msix_address - 1,
+                                       r.typestr, r.comments)
+                    ret.append(head_r)
+                if d.msix_address + d.msix_region_size < r.stop:
+                    tail_r = MemRegion(d.msix_address + d.msix_region_size,
+                                       r.stop, r.typestr, r.comments)
+                    ret.append(tail_r)
+                append_r = False
+                break
+        # filter out the ROMs
+        if (r.start >= rom_region.start and r.stop <= rom_region.stop):
+            add_rom_region = True
+            append_r = False
+        # filter out and save DMAR regions
+        if r.typestr.find('dmar') >= 0:
+            dmar_regions.append(r)
+            append_r = False
+        if append_r:
+            ret.append(r)
+
+    # add a region that covers all potential ROMs
+    if add_rom_region:
+        ret.append(rom_region)
+
+    # newer Linux kernels will report the first page as reserved
+    # it is needed for CPU init so include it anyways
+    if (ret[0].typestr == 'System RAM' and ret[0].start == 0x1000):
+        ret[0].start = 0
+
+    return ret, dmar_regions
+
+
+def parse_pcidevices():
+    int_src_cnt = 0
+    devices = []
+    caps = []
+    basedir = '/sys/bus/pci/devices'
+    list = input_listdir(basedir, ['*/config'])
+    for dir in list:
+        d = PCIDevice.parse_pcidevice_sysfsdir(basedir, dir)
+        if d is not None:
+            if len(d.caps) > 0:
+                duplicate = False
+                # look for duplicate capability patterns
+                for d2 in devices:
+                    if d2.caps == d.caps:
+                        # reused existing capability list, but record all users
+                        d2.caps[0].comments.append(str(d))
+                        d.caps_start = d2.caps_start
+                        duplicate = True
+                        break
+                if not duplicate:
+                    d.caps[0].comments.append(str(d))
+                    d.caps_start = len(caps)
+                    caps.extend(d.caps)
+            int_src_cnt += max(d.num_msi_vectors, d.num_msix_vectors)
+            devices.append(d)
+    return (devices, caps, int_src_cnt)
+
+
+def parse_madt():
+    f = input_open('/sys/firmware/acpi/tables/APIC', 'rb')
+    signature = f.read(4)
+    if signature != b'APIC':
+        raise RuntimeError('MADT: incorrect input file format %s' % signature)
+    (length,) = struct.unpack('<I', f.read(4))
+    f.seek(44)
+    length -= 44
+    ioapics = []
+
+    while length > 0:
+        offset = 0
+        (struct_type, struct_len) = struct.unpack('<BB', f.read(2))
+        offset += 2
+        length -= struct_len
+
+        if struct_type == 1:
+            (id, address, gsi_base) = struct.unpack('<BxII', f.read(10))
+            offset += 10
+            ioapics.append(IOAPIC(id, address, gsi_base))
+
+        f.seek(struct_len - offset, os.SEEK_CUR)
+
+    f.close()
+    return ioapics
+
+
+def parse_dmar_devscope(f):
+    (scope_type, scope_len, id, bus, dev, fn) = \
+        struct.unpack('<BBxxBBBB', f.read(8))
+    if scope_len != 8:
+        raise RuntimeError('Unsupported DMAR Device Scope Structure')
+    return (scope_type, scope_len, id, bus, dev, fn)
+
+
+# parsing of DMAR ACPI Table
+# see Intel VT-d Spec chapter 8
+def parse_dmar(pcidevices, ioapics, dmar_regions):
+    f = input_open('/sys/firmware/acpi/tables/DMAR', 'rb')
+    signature = f.read(4)
+    if signature != b'DMAR':
+        raise RuntimeError('DMAR: incorrect input file format %s' % signature)
+    (length,) = struct.unpack('<I', f.read(4))
+    f.seek(48)
+    length -= 48
+    units = []
+    regions = []
+
+    while length > 0:
+        offset = 0
+        (struct_type, struct_len) = struct.unpack('<HH', f.read(4))
+        offset += 4
+        length -= struct_len
+
+        # DMA Remapping Hardware Unit Definition
+        if struct_type == 0:
+            (flags, segment, base) = struct.unpack('<BxHQ', f.read(12))
+            if segment != 0:
+                raise RuntimeError('We do not support multiple PCI segments')
+            if len(units) >= 8:
+                raise RuntimeError('Too many DMAR units. '
+                                   'Raise JAILHOUSE_MAX_IOMMU_UNITS.')
+            size = 0
+            for r in dmar_regions:
+                if base == r.start:
+                    size = r.size()
+            if size == 0:
+                raise RuntimeError('DMAR region size cannot be identified.\n'
+                                   'Target Linux must run with Intel IOMMU '
+                                   'enabled.')
+            if size > 0x3000:
+                raise RuntimeError('Unexpectedly large DMAR region.')
+            units.append(IOMMUConfig({
+                'base_addr': base,
+                'mmio_size': size
+            }))
+            if flags & 1:
+                for d in pcidevices:
+                    if d.iommu is None:
+                        d.iommu = len(units) - 1
+            offset += 16 - offset
+            while offset < struct_len:
+                (scope_type, scope_len, id, bus, dev, fn) =\
+                    parse_dmar_devscope(f)
+                # PCI Endpoint Device
+                if scope_type == 1:
+                    assert not (flags & 1)
+                    for d in pcidevices:
+                        if d.bus == bus and d.dev == dev and d.fn == fn:
+                            d.iommu = len(units) - 1
+                            break
+                # PCI Sub-hierarchy
+                elif scope_type == 2:
+                    assert not (flags & 1)
+                    for d in pcidevices:
+                        if d.bus == bus and d.dev == dev and d.fn == fn:
+                            (secondbus, subordinate) = \
+                                PCIPCIBridge.get_2nd_busses(d)
+                            for d2 in pcidevices:
+                                if (
+                                    d2.bus >= secondbus and
+                                    d2.bus <= subordinate
+                                ):
+                                    d2.iommu = len(units) - 1
+                            break
+                # IOAPIC
+                elif scope_type == 3:
+                    ioapic = next(chip for chip in ioapics if chip.id == id)
+                    bdf = (bus << 8) | (dev << 3) | fn
+                    for chip in ioapics:
+                        if chip.bdf == bdf:
+                            raise RuntimeError('IOAPICs with identical BDF')
+                    ioapic.bdf = bdf
+                    ioapic.iommu = len(units) - 1
+                offset += scope_len
+
+        # Reserved Memory Region Reporting Structure
+        if struct_type == 1:
+            f.seek(8 - offset, os.SEEK_CUR)
+            offset += 8 - offset
+            (base, limit) = struct.unpack('<QQ', f.read(16))
+            offset += 16
+
+            comments = []
+            while offset < struct_len:
+                (scope_type, scope_len, id, bus, dev, fn) =\
+                    parse_dmar_devscope(f)
+                if scope_type == 1:
+                    comments.append('PCI device: %02x:%02x.%x' %
+                                    (bus, dev, fn))
+                else:
+                    comments.append('DMAR parser could not decode device path')
+                offset += scope_len
+
+            reg = MemRegion(base, limit, 'ACPI DMAR RMRR', comments)
+            regions.append(reg)
+
+        f.seek(struct_len - offset, os.SEEK_CUR)
+
+    f.close()
+
+    for d in pcidevices:
+        if d.iommu is None:
+            raise RuntimeError(
+                'PCI device %02x:%02x.%x outside the scope of an '
+                'IOMMU' % (d.bus, d.dev, d.fn))
+
+    return units, regions
+
+
+def parse_ivrs(pcidevices, ioapics):
+    def format_bdf(bdf):
+        bus, dev, fun = (bdf >> 8) & 0xff, (bdf >> 3) & 0x1f, bdf & 0x7
+        return '%02x:%02x.%x' % (bus, dev, fun)
+
+    f = input_open('/sys/firmware/acpi/tables/IVRS', 'rb')
+    signature = f.read(4)
+    if signature != b'IVRS':
+        raise RuntimeError('IVRS: incorrect input file format %s' % signature)
+
+    (length, revision) = struct.unpack('<IB', f.read(5))
+    if revision > 2:
+        raise RuntimeError('IVRS: unsupported Revision %02x' % revision)
+
+    f.seek(48, os.SEEK_SET)
+    length -= 48
+
+    units = []
+    regions = []
+    # BDF of devices that are permitted outside IOMMU: root complex
+    iommu_skiplist = set([0x0])
+    ivhd_blocks = 0
+    while length > 0:
+        (block_type, block_length) = struct.unpack('<BxH', f.read(4))
+        if block_type in [0x10, 0x11]:
+            ivhd_blocks += 1
+            if ivhd_blocks > 1:
+                raise RuntimeError('Jailhouse doesn\'t support more than one '
+                                   'AMD IOMMU per PCI function.')
+            # IVHD block
+            ivhd_fields = struct.unpack('<HHQHxxL', f.read(20))
+            (iommu_bdf, base_cap_ofs,
+             base_addr, pci_seg, iommu_feat) = ivhd_fields
+
+            length -= block_length
+            block_length -= 24
+
+            if pci_seg != 0:
+                raise RuntimeError('We do not support multiple PCI segments')
+
+            if len(units) > 8:
+                raise RuntimeError('Too many IOMMU units. '
+                                   'Raise JAILHOUSE_MAX_IOMMU_UNITS.')
+
+            msi_cap_ofs = None
+
+            for i, d in enumerate(pcidevices):
+                if d.bdf() == iommu_bdf:
+                    # Extract MSI capability offset
+                    for c in d.caps:
+                        if c.id == 0x05:
+                            msi_cap_ofs = c.start
+                    # We must not map IOMMU to the cells
+                    del pcidevices[i]
+
+            if msi_cap_ofs is None:
+                raise RuntimeError('AMD IOMMU lacks MSI support, and '
+                                   'Jailhouse doesn\'t support MSI-X yet.')
+
+            if (iommu_feat & (0xF << 13)) and (iommu_feat & (0x3F << 17)):
+                # Performance Counters are supported, allocate 512K
+                mmio_size = 524288
+            else:
+                # Allocate 16K
+                mmio_size = 16384
+
+            units.append(IOMMUConfig({
+                'base_addr': base_addr,
+                'mmio_size': mmio_size,
+                'amd_bdf': iommu_bdf,
+                'amd_base_cap': base_cap_ofs,
+                'amd_msi_cap': msi_cap_ofs,
+                # IVHD block type 0x11 has exact EFR copy but type 0x10 may
+                # overwrite what hardware reports. Set reserved bit 0 in that
+                # case to indicate that the value is in use.
+                'amd_features': (iommu_feat | 0x1) if block_type == 0x10 else 0
+            }))
+
+            bdf_start_range = None
+            while block_length > 0:
+                (entry_type, device_id) = struct.unpack('<BHx', f.read(4))
+                block_length -= 4
+
+                if entry_type == 0x01:
+                    # All
+                    for d in pcidevices:
+                        d.iommu = len(units) - 1
+                elif entry_type == 0x02:
+                    # Select
+                    for d in pcidevices:
+                        if d.bdf() == device_id:
+                            d.iommu = len(units) - 1
+                elif entry_type == 0x03:
+                    # Start of range
+                    bdf_start_range = device_id
+                elif entry_type == 0x04:
+                    # End of range
+                    if bdf_start_range is None:
+                        continue
+                    for d in pcidevices:
+                        if d.bdf() >= bdf_start_range and d.bdf() <= device_id:
+                            d.iommu = len(units) - 1
+                    bdf_start_range = None
+                elif entry_type == 0x42:
+                    # Alias select
+                    (device_id_b,) = struct.unpack('<xHx', f.read(4))
+                    block_length -= 4
+                    for d in pcidevices:
+                        if d.bdf() == device_id_b:
+                            d.iommu = len(units) - 1
+                elif entry_type == 0x43:
+                    # Alias start of range
+                    (device_id_b,) = struct.unpack('<xHx', f.read(4))
+                    block_length -= 4
+                    bdf_start_range = device_id_b
+                elif entry_type == 0x48:
+                    # Special device
+                    (handle, device_id_b, variety) = struct.unpack(
+                        '<BHB', f.read(4))
+                    block_length -= 4
+                    if variety == 0x01:  # IOAPIC
+                        for chip in ioapics:
+                            if chip.id == handle:
+                                chip.bdf = device_id
+                                chip.iommu = len(units) - 1
+                else:
+                    # Reserved or ignored entries
+                    if entry_type >= 0x40:
+                        f.seek(4, os.SEEK_CUR)
+                        block_length -= 4
+
+        elif type in [0x20, 0x21, 0x22]:
+            # IVMD block
+            ivmd_fields = struct.unpack('<BBHHHxxxxxxxxQQ', f.read(32))
+            (block_type, block_flags, block_length,
+             device_id, aux_data, mem_addr, mem_len) = ivmd_fields
+            length -= block_length
+
+            if int(block_flags):
+                bdf_str = format_bdf(device_id)
+                print(
+                    'WARNING: Jailhouse doesn\'t support configurable '
+                    '(eg. read-only) device memory. Device %s may not '
+                    'work properly, especially in non-root cell.' % bdf_str)
+
+            if block_type == 0x20:
+                # All devices
+                comment = None
+            elif block_type == 0x21:
+                # Selected device
+                comment = 'PCI Device: %s' % format_bdf(device_id)
+            elif block_type == 0x22:
+                # Device range
+                comment = 'PCI Device: %s - %s' % (
+                    format_bdf(device_id), format_bdf(aux_data))
+
+            if comment:
+                print('WARNING: Jailhouse doesn\'t support per-device memory '
+                      'regions. The memory at 0x%x will be mapped accessible '
+                      'to all devices.' % mem_addr)
+
+            regions.append(MemRegion(mem_addr, mem_len, 'ACPI IVRS', comment))
+        elif type == 0x40:
+            raise RuntimeError(
+                'You board uses IVRS Rev. 2 feature Jailhouse doesn\'t '
+                'support yet. Please report this to '
+                '[email protected].')
+        else:
+            print(
+                'WARNING: Skipping unknown IVRS '
+                'block type 0x%02x' % block_type)
+
+        for d in pcidevices:
+            if d.bdf() not in iommu_skiplist and d.iommu is None:
+                raise RuntimeError(
+                    'PCI device %02x:%02x.%x outside the scope of an '
+                    'IOMMU' % (d.bus, d.dev, d.fn))
+
+        f.close()
+        return units, regions
+
+
+def get_cpu_vendor():
+    with open('/proc/cpuinfo') as f:
+        for line in f:
+            if not line.strip():
+                continue
+            key, value = line.split(':')
+            if key.strip() == 'vendor_id':
+                return value.strip()
+
+
+class PCIBARs:
+    IORESOURCE_IO = 0x00000100
+    IORESOURCE_MEM = 0x00000200
+    IORESOURCE_MEM_64 = 0x00100000
+
+    def __init__(self, dir):
+        self.mask = []
+        f = input_open(os.path.join(dir, 'resource'), 'r')
+        n = 0
+        while (n < 6):
+            (start, end, flags) = f.readline().split()
+            n += 1
+            flags = int(flags, 16)
+            if flags & PCIBARs.IORESOURCE_IO:
+                mask = ~(int(end, 16) - int(start, 16))
+            elif flags & PCIBARs.IORESOURCE_MEM:
+                if flags & PCIBARs.IORESOURCE_MEM_64:
+                    mask = int(end, 16) - int(start, 16)
+                    (start, end, flags) = f.readline().split()
+                    mask |= (int(end, 16) - int(start, 16)) << 32
+                    mask = ~(mask)
+                    self.mask.append(mask & 0xffffffff)
+                    mask >>= 32
+                    n += 1
+                else:
+                    mask = ~(int(end, 16) - int(start, 16))
+            else:
+                mask = 0
+            self.mask.append(mask & 0xffffffff)
+        f.close()
+
+
+class PCICapability:
+    def __init__(self, id, start, len, flags, content, msix_address):
+        self.id = id
+        self.start = start
+        self.len = len
+        self.flags = flags
+        self.content = content
+        self.msix_address = msix_address
+        self.comments = []
+
+    def __eq__(self, other):
+        return self.id == other.id and self.start == other.start and \
+            self.len == other.len and self.flags == other.flags
+
+    RD = '0'
+    RW = 'JAILHOUSE_PCICAPS_WRITE'
+
+    JAILHOUSE_PCI_EXT_CAP = 0x8000
+
+    @staticmethod
+    def parse_pcicaps(dir):
+        caps = []
+        has_extended_caps = False
+        f = input_open(os.path.join(dir, 'config'), 'rb')
+        f.seek(0x06)
+        (status,) = struct.unpack('<H', f.read(2))
+        # capability list supported?
+        if (status & (1 << 4)) == 0:
+            f.close()
+            return caps
+        # walk capability list
+        f.seek(0x34)
+        (next,) = struct.unpack('B', f.read(1))
+        while next != 0:
+            cap = next
+            msix_address = 0
+            f.seek(cap)
+            (id, next) = struct.unpack('<BB', f.read(2))
+            if id == 0x01:  # Power Management
+                # this cap can be handed out completely
+                len = 8
+                flags = PCICapability.RW
+            elif id == 0x05:  # MSI
+                # access will be moderated by hypervisor
+                len = 10
+                (msgctl,) = struct.unpack('<H', f.read(2))
+                if (msgctl & (1 << 7)) != 0:  # 64-bit support
+                    len += 4
+                if (msgctl & (1 << 8)) != 0:  # per-vector masking support
+                    len += 10
+                flags = PCICapability.RW
+            elif id == 0x10:  # Express
+                len = 20
+                (cap_reg,) = struct.unpack('<H', f.read(2))
+                if (cap_reg & 0xf) >= 2:  # v2 capability
+                    len = 60
+                # access side effects still need to be analyzed
+                flags = PCICapability.RD
+                has_extended_caps = True
+            elif id == 0x11:  # MSI-X
+                # access will be moderated by hypervisor
+                len = 12
+                (table,) = struct.unpack('<xxI', f.read(6))
+                f.seek(0x10 + (table & 7) * 4)
+                (bar,) = struct.unpack('<I', f.read(4))
+                if (bar & 0x3) != 0:
+                    raise RuntimeError('Invalid MSI-X BAR found')
+                if (bar & 0x4) != 0:
+                    bar |= struct.unpack('<I', f.read(4))[0] << 32
+                msix_address = (bar & 0xfffffffffffffff0) + table & 0xfffffff8
+                flags = PCICapability.RW
+            else:
+                # unknown/unhandled cap, mark its existence
+                len = 2
+                flags = PCICapability.RD
+            f.seek(cap + 2)
+            content = f.read(len - 2)
+            caps.append(PCICapability(id, cap, len, flags, content,
+                                      msix_address))
+
+        if has_extended_caps:
+            # walk extended capability list
+            next = 0x100
+            while next != 0:
+                cap = next
+                f.seek(cap)
+                (id, version_next) = struct.unpack('<HH', f.read(4))
+                next = version_next >> 4
+                if id == 0xffff:
+                    break
+                elif id == 0x0010:  # SR-IOV
+                    len = 64
+                    # access side effects still need to be analyzed
+                    flags = PCICapability.RD
+                else:
+                    if (id & PCICapability.JAILHOUSE_PCI_EXT_CAP) != 0:
+                        print('WARNING: Ignoring unsupported PCI Express '
+                              'Extended Capability ID %x' % id)
+                        continue
+                    # unknown/unhandled cap, mark its existence
+                    len = 4
+                    flags = PCICapability.RD
+                f.seek(cap + 4)
+                content = f.read(len - 4)
+                id |= PCICapability.JAILHOUSE_PCI_EXT_CAP
+                caps.append(PCICapability(id, cap, len, flags, content, 0))
+
+        f.close()
+        return caps
+
+
+class PCIDevice:
+    def __init__(self, type, domain, bus, dev, fn, bars, caps, path):
+        self.type = type
+        self.iommu = None
+        self.domain = domain
+        self.bus = bus
+        self.dev = dev
+        self.fn = fn
+        self.bars = bars
+        self.caps = caps
+        self.path = path
+        self.caps_start = 0
+        self.num_caps = len(caps)
+        self.num_msi_vectors = 0
+        self.msi_64bits = 0
+        self.num_msix_vectors = 0
+        self.msix_region_size = 0
+        self.msix_address = 0
+        for c in caps:
+            if c.id in (0x05, 0x11):
+                msg_ctrl = struct.unpack('<H', c.content[:2])[0]
+                if c.id == 0x05:  # MSI
+                    self.num_msi_vectors = 1 << ((msg_ctrl >> 1) & 0x7)
+                    self.msi_64bits = (msg_ctrl >> 7) & 1
+                else:  # MSI-X
+                    if c.msix_address != 0:
+                        vectors = (msg_ctrl & 0x7ff) + 1
+                        self.num_msix_vectors = vectors
+                        self.msix_region_size = (vectors * 16 + 0xfff) & 0xf000
+                        self.msix_address = c.msix_address
+                    else:
+                        print('WARNING: Ignoring invalid MSI-X configuration'
+                              ' of device %02x:%02x.%x' % (bus, dev, fn))
+
+    def __str__(self):
+        return 'PCIDevice: %02x:%02x.%x' % (self.bus, self.dev, self.fn)
+
+    def bdf(self):
+        return self.bus << 8 | self.dev << 3 | self.fn
+
+    @staticmethod
+    def parse_pcidevice_sysfsdir(basedir, dir):
+        dpath = os.path.join(basedir, dir)
+        f = input_open(os.path.join(dpath, 'config'), 'rb')
+        (vendor_device,) = struct.unpack('<I', f.read(4))
+        if vendor_device == 0xffffffff:
+            print('WARNING: Ignoring apparently disabled PCI device %s' % dir)
+            return None
+        f.seek(0x0A)
+        (classcode,) = struct.unpack('<H', f.read(2))
+        f.close()
+        if classcode == 0x0604:
+            type = 'JAILHOUSE_PCI_TYPE_BRIDGE'
+        else:
+            type = 'JAILHOUSE_PCI_TYPE_DEVICE'
+        a = dir.split(':')
+        domain = int(a[0], 16)
+        bus = int(a[1], 16)
+        df = a[2].split('.')
+        bars = PCIBARs(dpath)
+        caps = PCICapability.parse_pcicaps(dpath)
+        return PCIDevice(type, domain, bus, int(df[0], 16), int(df[1], 16),
+                         bars, caps, dpath)
+
+
+class PCIPCIBridge(PCIDevice):
+    @staticmethod
+    def get_2nd_busses(dev):
+        assert dev.type == 'JAILHOUSE_PCI_TYPE_BRIDGE'
+        f = input_open(os.path.join(dev.path, 'config'), 'rb')
+        f.seek(0x19)
+        (secondbus, subordinate) = struct.unpack('<BB', f.read(2))
+        f.close()
+        return (secondbus, subordinate)
+
+
+class MemRegion:
+    def __init__(self, start, stop, typestr, comments=None):
+        self.start = start
+        self.stop = stop
+        self.typestr = typestr
+        self.comments = comments or []
+
+    def __str__(self):
+        return 'MemRegion: %08x-%08x : %s' % \
+            (self.start, self.stop, self.typestr)
+
+    def size(self):
+        # round up to full PAGE_SIZE
+        return int((self.stop - self.start + 0xfff) / 0x1000) * 0x1000
+
+    def flagstr(self, p=''):
+        if (
+            self.typestr == 'System RAM' or
+            self.typestr == 'Kernel' or
+            self.typestr == 'RAM buffer' or
+            self.typestr == 'ACPI DMAR RMRR' or
+            self.typestr == 'ACPI IVRS'
+        ):
+            s = 'JAILHOUSE_MEM_READ | JAILHOUSE_MEM_WRITE |\n'
+            s += p + '\t\tJAILHOUSE_MEM_EXECUTE | JAILHOUSE_MEM_DMA'
+            return s
+        return 'JAILHOUSE_MEM_READ | JAILHOUSE_MEM_WRITE'
+
+
+class IOAPIC:
+    def __init__(self, id, address, gsi_base, iommu=0, bdf=0):
+        self.id = id
+        self.address = address
+        self.gsi_base = gsi_base
+        self.iommu = iommu
+        self.bdf = bdf
+
+    def __str__(self):
+        return 'IOAPIC %d, GSI base %d' % (self.id, self.gsi_base)
+
+    def irqchip_id(self):
+        # encode the IOMMU number into the irqchip ID
+        return (self.iommu << 16) | self.bdf
+
+
+class IOMemRegionTree:
+    def __init__(self, region, level):
+        self.region = region
+        self.level = level
+        self.parent = None
+        self.children = []
+
+    def __str__(self):
+        s = ''
+        if (self.region):
+            s = (' ' * (self.level - 1)) + str(self.region)
+            if self.parent and self.parent.region:
+                s += ' --> ' + self.parent.region.typestr
+            s += '\n'
+        for c in self.children:
+            s += str(c)
+        return s
+
+    def regions_split_by_kernel(self):
+        kernel = [x for x in self.children if
+                  x.region.typestr.startswith('Kernel ')]
+
+        if (len(kernel) == 0):
+            return [self.region]
+
+        r = self.region
+        s = r.typestr
+
+        kernel_start = kernel[0].region.start
+        kernel_stop = kernel[len(kernel) - 1].region.stop
+
+        # align this for 16M, but only if we have enough space
+        kernel_stop = (kernel_stop & ~0xFFFFFF) + 0xFFFFFF
+        if (kernel_stop > r.stop):
+            kernel_stop = r.stop
+
+        before_kernel = None
+        after_kernel = None
+
+        # before Kernel if any
+        if (r.start < kernel_start):
+            before_kernel = MemRegion(r.start, kernel_start - 1, s)
+
+        kernel_region = MemRegion(kernel_start, kernel_stop, "Kernel")
+
+        # after Kernel if any
+        if (r.stop > kernel_stop):
+            after_kernel = MemRegion(kernel_stop + 1, r.stop, s)
+
+        return [before_kernel, kernel_region, after_kernel]
+
+    @staticmethod
+    def parse_iomem_line(line):
+        a = line.split(':', 1)
+        level = int(a[0].count(' ') / 2) + 1
+        region = a[0].split('-', 1)
+        a[1] = a[1].strip()
+        return level, MemRegion(int(region[0], 16), int(region[1], 16), a[1])
+
+    @staticmethod
+    def parse_iomem_file():
+        root = IOMemRegionTree(None, 0)
+        f = input_open('/proc/iomem')
+        lastlevel = 0
+        lastnode = root
+        for line in f:
+            (level, r) = IOMemRegionTree.parse_iomem_line(line)
+            t = IOMemRegionTree(r, level)
+            if (t.level > lastlevel):
+                t.parent = lastnode
+            if (t.level == lastlevel):
+                t.parent = lastnode.parent
+            if (t.level < lastlevel):
+                p = lastnode.parent
+                while(t.level < p.level):
+                    p = p.parent
+                t.parent = p.parent
+
+            t.parent.children.append(t)
+            lastnode = t
+            lastlevel = t.level
+        f.close()
+
+        return root
+
+    # find HPET regions in tree
+    @staticmethod
+    def find_hpet_regions(tree):
+        regions = []
+
+        for tree in tree.children:
+            r = tree.region
+            s = r.typestr
+
+            if (s.find('HPET') >= 0):
+                regions.append(r)
+
+            # if the tree continues recurse further down ...
+            if (len(tree.children) > 0):
+                regions.extend(IOMemRegionTree.find_hpet_regions(tree))
+
+        return regions
+
+    # recurse down the tree
+    @staticmethod
+    def parse_iomem_tree(tree):
+        regions = []
+
+        for tree in tree.children:
+            r = tree.region
+            s = r.typestr
+
+            # System RAM on the first level will be added completely,
+            # if they don't contain the kernel itself, if they do,
+            # we split them
+            if (tree.level == 1 and s == 'System RAM'):
+                regions.extend(tree.regions_split_by_kernel())
+                continue
+
+            # blacklisted on all levels
+            if (
+                (s.find('PCI MMCONFIG') >= 0) or
+                (s.find('APIC') >= 0)  # covers both APIC and IOAPIC
+            ):
+                continue
+
+            # generally blacklisted, unless we find an HPET behind it
+            if (s.lower() == 'reserved'):
+                regions.extend(IOMemRegionTree.find_hpet_regions(tree))
+                continue
+
+            # if the tree continues recurse further down ...
+            if (len(tree.children) > 0):
+                regions.extend(IOMemRegionTree.parse_iomem_tree(tree))
+                continue
+
+            # add all remaining leaves
+            regions.append(r)
+
+        return regions
+
+
+class IOMMUConfig:
+    def __init__(self, props):
+        self.base_addr = props['base_addr']
+        self.mmio_size = props['mmio_size']
+        if 'amd_bdf' in props:
+            self.amd_bdf = props['amd_bdf']
+            self.amd_base_cap = props['amd_base_cap']
+            self.amd_msi_cap = props['amd_msi_cap']
+            self.amd_features = props['amd_features']
+
+    @property
+    def is_amd_iommu(self):
+        return hasattr(self, 'amd_bdf')
diff --git a/tools/jailhouse-config-create b/tools/jailhouse-config-create
index df63b7a..297dee2 100755
--- a/tools/jailhouse-config-create
+++ b/tools/jailhouse-config-create
@@ -25,7 +25,6 @@ import math
 import re
 import argparse
 import struct
-import fnmatch
 
 try:
     from mako.template import Template
@@ -33,6 +32,11 @@ except ImportError:
     print("This script requires the mako library to run.")
     sys.exit(1)
 
+sys.path[0] = os.path.dirname(os.path.abspath(__file__)) + "/.."
+import pyjailhouse.sysfs_parser as sysfs_parser
+# Imports from directory containing this must be done above
+# import statement, see python documentation on sys.path[0]
+
 datadir = None
 
 if datadir:
@@ -40,8 +44,6 @@ if datadir:
 else:
     template_default_dir = os.path.abspath(os.path.dirname(sys.argv[0]))
 
-cpuvendor = None
-
 # pretend to be part of the jailhouse tool
 sys.argv[0] = sys.argv[0].replace('-', ' ')
 
@@ -87,35 +89,6 @@ parser.add_argument('file', metavar='FILE',
 
 options = parser.parse_args()
 
-inputs = {
-    'files': set(),
-    'files_opt': set(),
-    'files_intel': set(),
-    'files_amd': set()
-}
-
-# required files
-inputs['files'].add('/proc/iomem')
-inputs['files'].add('/proc/cpuinfo')
-inputs['files'].add('/proc/cmdline')
-inputs['files'].add('/proc/ioports')
-inputs['files'].add('/sys/bus/pci/devices/*/config')
-inputs['files'].add('/sys/bus/pci/devices/*/resource')
-inputs['files'].add('/sys/devices/system/cpu/cpu*/uevent')
-inputs['files'].add('/sys/firmware/acpi/tables/APIC')
-inputs['files'].add('/sys/firmware/acpi/tables/MCFG')
-# optional files
-inputs['files_opt'].add('/sys/class/dmi/id/product_name')
-inputs['files_opt'].add('/sys/class/dmi/id/sys_vendor')
-inputs['files_opt'].add('/sys/class/tty/*/iomem_base')
-inputs['files_opt'].add('/sys/class/tty/*/iomem_reg_shift')
-inputs['files_opt'].add('/sys/class/tty/*/io_type')
-inputs['files_opt'].add('/sys/class/tty/*/port')
-inputs['files_opt'].add('/sys/devices/jailhouse/enabled')
-# platform specific files
-inputs['files_intel'].add('/sys/firmware/acpi/tables/DMAR')
-inputs['files_amd'].add('/sys/firmware/acpi/tables/IVRS')
-
 
 def kmg_multiply(value, kmg):
     if (kmg == 'K' or kmg == 'k'):
@@ -134,544 +107,13 @@ def kmg_multiply_str(str):
     raise RuntimeError('kmg_multiply_str can not parse input "' + str + '"')
 
 
-def check_input_listed(name, optional=False):
-    set = inputs['files_opt']
-    if optional is False:
-        set = inputs['files']
-        global cpuvendor
-        if cpuvendor == 'GenuineIntel':
-            set = set.union(inputs['files_intel'])
-        elif cpuvendor == 'AuthenticAMD':
-            set = set.union(inputs['files_amd'])
-
-    for file in set:
-        if fnmatch.fnmatch(name, file):
-            return True
-    raise RuntimeError('"' + name + '" is not a listed input file')
-
-
-def input_open(name, mode='r', optional=False):
-    check_input_listed(name, optional)
-    try:
-        f = open(options.root + name, mode)
-    except Exception as e:
-        if optional:
-            return open("/dev/null", mode)
-        raise e
-    return f
-
-
 def input_readline(name, optional=False):
-    f = input_open(name, optional=optional)
+    f = sysfs_parser.input_open(name, optional=optional)
     line = f.readline()
     f.close()
     return line
 
 
-def input_listdir(dir, wildcards):
-    for w in wildcards:
-        check_input_listed(os.path.join(dir, w))
-    dirs = os.listdir(options.root + dir)
-    dirs.sort()
-    return dirs
-
-
-class PCIBARs:
-    IORESOURCE_IO = 0x00000100
-    IORESOURCE_MEM = 0x00000200
-    IORESOURCE_MEM_64 = 0x00100000
-
-    def __init__(self, dir):
-        self.mask = []
-        f = input_open(os.path.join(dir, 'resource'), 'r')
-        n = 0
-        while (n < 6):
-            (start, end, flags) = f.readline().split()
-            n += 1
-            flags = int(flags, 16)
-            if flags & PCIBARs.IORESOURCE_IO:
-                mask = ~(int(end, 16) - int(start, 16))
-            elif flags & PCIBARs.IORESOURCE_MEM:
-                if flags & PCIBARs.IORESOURCE_MEM_64:
-                    mask = int(end, 16) - int(start, 16)
-                    (start, end, flags) = f.readline().split()
-                    mask |= (int(end, 16) - int(start, 16)) << 32
-                    mask = ~(mask)
-                    self.mask.append(mask & 0xffffffff)
-                    mask >>= 32
-                    n += 1
-                else:
-                    mask = ~(int(end, 16) - int(start, 16))
-            else:
-                mask = 0
-            self.mask.append(mask & 0xffffffff)
-        f.close()
-
-
-class PCICapability:
-    def __init__(self, id, start, len, flags, content, msix_address):
-        self.id = id
-        self.start = start
-        self.len = len
-        self.flags = flags
-        self.content = content
-        self.msix_address = msix_address
-        self.comments = []
-
-    def __eq__(self, other):
-        return self.id == other.id and self.start == other.start and \
-            self.len == other.len and self.flags == other.flags
-
-    RD = '0'
-    RW = 'JAILHOUSE_PCICAPS_WRITE'
-
-    JAILHOUSE_PCI_EXT_CAP = 0x8000
-
-    @staticmethod
-    def parse_pcicaps(dir):
-        caps = []
-        has_extended_caps = False
-        f = input_open(os.path.join(dir, 'config'), 'rb')
-        f.seek(0x06)
-        (status,) = struct.unpack('<H', f.read(2))
-        # capability list supported?
-        if (status & (1 << 4)) == 0:
-            f.close()
-            return caps
-        # walk capability list
-        f.seek(0x34)
-        (next,) = struct.unpack('B', f.read(1))
-        while next != 0:
-            cap = next
-            msix_address = 0
-            f.seek(cap)
-            (id, next) = struct.unpack('<BB', f.read(2))
-            if id == 0x01:  # Power Management
-                # this cap can be handed out completely
-                len = 8
-                flags = PCICapability.RW
-            elif id == 0x05:  # MSI
-                # access will be moderated by hypervisor
-                len = 10
-                (msgctl,) = struct.unpack('<H', f.read(2))
-                if (msgctl & (1 << 7)) != 0:  # 64-bit support
-                    len += 4
-                if (msgctl & (1 << 8)) != 0:  # per-vector masking support
-                    len += 10
-                flags = PCICapability.RW
-            elif id == 0x10:  # Express
-                len = 20
-                (cap_reg,) = struct.unpack('<H', f.read(2))
-                if (cap_reg & 0xf) >= 2:  # v2 capability
-                    len = 60
-                # access side effects still need to be analyzed
-                flags = PCICapability.RD
-                has_extended_caps = True
-            elif id == 0x11:  # MSI-X
-                # access will be moderated by hypervisor
-                len = 12
-                (table,) = struct.unpack('<xxI', f.read(6))
-                f.seek(0x10 + (table & 7) * 4)
-                (bar,) = struct.unpack('<I', f.read(4))
-                if (bar & 0x3) != 0:
-                    raise RuntimeError('Invalid MSI-X BAR found')
-                if (bar & 0x4) != 0:
-                    bar |= struct.unpack('<I', f.read(4))[0] << 32
-                msix_address = (bar & 0xfffffffffffffff0) + table & 0xfffffff8
-                flags = PCICapability.RW
-            else:
-                # unknown/unhandled cap, mark its existence
-                len = 2
-                flags = PCICapability.RD
-            f.seek(cap + 2)
-            content = f.read(len - 2)
-            caps.append(PCICapability(id, cap, len, flags, content,
-                                      msix_address))
-
-        if has_extended_caps:
-            # walk extended capability list
-            next = 0x100
-            while next != 0:
-                cap = next
-                f.seek(cap)
-                (id, version_next) = struct.unpack('<HH', f.read(4))
-                next = version_next >> 4
-                if id == 0xffff:
-                    break
-                elif id == 0x0010:  # SR-IOV
-                    len = 64
-                    # access side effects still need to be analyzed
-                    flags = PCICapability.RD
-                else:
-                    if (id & PCICapability.JAILHOUSE_PCI_EXT_CAP) != 0:
-                        print('WARNING: Ignoring unsupported PCI Express '
-                              'Extended Capability ID %x' % id)
-                        continue
-                    # unknown/unhandled cap, mark its existence
-                    len = 4
-                    flags = PCICapability.RD
-                f.seek(cap + 4)
-                content = f.read(len - 4)
-                id |= PCICapability.JAILHOUSE_PCI_EXT_CAP
-                caps.append(PCICapability(id, cap, len, flags, content, 0))
-
-        f.close()
-        return caps
-
-
-class PCIDevice:
-    def __init__(self, type, domain, bus, dev, fn, bars, caps, path):
-        self.type = type
-        self.iommu = None
-        self.domain = domain
-        self.bus = bus
-        self.dev = dev
-        self.fn = fn
-        self.bars = bars
-        self.caps = caps
-        self.path = path
-        self.caps_start = 0
-        self.num_caps = len(caps)
-        self.num_msi_vectors = 0
-        self.msi_64bits = 0
-        self.num_msix_vectors = 0
-        self.msix_region_size = 0
-        self.msix_address = 0
-        for c in caps:
-            if c.id in (0x05, 0x11):
-                msg_ctrl = struct.unpack('<H', c.content[:2])[0]
-                if c.id == 0x05:  # MSI
-                    self.num_msi_vectors = 1 << ((msg_ctrl >> 1) & 0x7)
-                    self.msi_64bits = (msg_ctrl >> 7) & 1
-                else:  # MSI-X
-                    if c.msix_address != 0:
-                        vectors = (msg_ctrl & 0x7ff) + 1
-                        self.num_msix_vectors = vectors
-                        self.msix_region_size = (vectors * 16 + 0xfff) & 0xf000
-                        self.msix_address = c.msix_address
-                    else:
-                        print('WARNING: Ignoring invalid MSI-X configuration'
-                              ' of device %02x:%02x.%x' % (bus, dev, fn))
-
-    def __str__(self):
-        return 'PCIDevice: %02x:%02x.%x' % (self.bus, self.dev, self.fn)
-
-    def bdf(self):
-        return self.bus << 8 | self.dev << 3 | self.fn
-
-    @staticmethod
-    def parse_pcidevice_sysfsdir(basedir, dir):
-        dpath = os.path.join(basedir, dir)
-        f = input_open(os.path.join(dpath, 'config'), 'rb')
-        (vendor_device,) = struct.unpack('<I', f.read(4))
-        if vendor_device == 0xffffffff:
-            print('WARNING: Ignoring apparently disabled PCI device %s' % dir)
-            return None
-        f.seek(0x0A)
-        (classcode,) = struct.unpack('<H', f.read(2))
-        f.close()
-        if classcode == 0x0604:
-            type = 'JAILHOUSE_PCI_TYPE_BRIDGE'
-        else:
-            type = 'JAILHOUSE_PCI_TYPE_DEVICE'
-        a = dir.split(':')
-        domain = int(a[0], 16)
-        bus = int(a[1], 16)
-        df = a[2].split('.')
-        bars = PCIBARs(dpath)
-        caps = PCICapability.parse_pcicaps(dpath)
-        return PCIDevice(type, domain, bus, int(df[0], 16), int(df[1], 16),
-                         bars, caps, dpath)
-
-
-class PCIPCIBridge(PCIDevice):
-    @staticmethod
-    def get_2nd_busses(dev):
-        assert dev.type == 'JAILHOUSE_PCI_TYPE_BRIDGE'
-        f = input_open(os.path.join(dev.path, 'config'), 'rb')
-        f.seek(0x19)
-        (secondbus, subordinate) = struct.unpack('<BB', f.read(2))
-        f.close()
-        return (secondbus, subordinate)
-
-
-class MemRegion:
-    def __init__(self, start, stop, typestr, comments=None):
-        self.start = start
-        self.stop = stop
-        self.typestr = typestr
-        self.comments = comments or []
-
-    def __str__(self):
-        return 'MemRegion: %08x-%08x : %s' % \
-            (self.start, self.stop, self.typestr)
-
-    def size(self):
-        # round up to full PAGE_SIZE
-        return int((self.stop - self.start + 0xfff) / 0x1000) * 0x1000
-
-    def flagstr(self, p=''):
-        if (
-            self.typestr == 'System RAM' or
-            self.typestr == 'Kernel' or
-            self.typestr == 'RAM buffer' or
-            self.typestr == 'ACPI DMAR RMRR' or
-            self.typestr == 'ACPI IVRS'
-        ):
-            s = 'JAILHOUSE_MEM_READ | JAILHOUSE_MEM_WRITE |\n'
-            s += p + '\t\tJAILHOUSE_MEM_EXECUTE | JAILHOUSE_MEM_DMA'
-            return s
-        return 'JAILHOUSE_MEM_READ | JAILHOUSE_MEM_WRITE'
-
-
-class IOAPIC:
-    def __init__(self, id, address, gsi_base, iommu=0, bdf=0):
-        self.id = id
-        self.address = address
-        self.gsi_base = gsi_base
-        self.iommu = iommu
-        self.bdf = bdf
-
-    def __str__(self):
-        return 'IOAPIC %d, GSI base %d' % (self.id, self.gsi_base)
-
-    def irqchip_id(self):
-        # encode the IOMMU number into the irqchip ID
-        return (self.iommu << 16) | self.bdf
-
-
-class IOMemRegionTree:
-    def __init__(self, region, level):
-        self.region = region
-        self.level = level
-        self.parent = None
-        self.children = []
-
-    def __str__(self):
-        s = ''
-        if (self.region):
-            s = (' ' * (self.level - 1)) + str(self.region)
-            if self.parent and self.parent.region:
-                s += ' --> ' + self.parent.region.typestr
-            s += '\n'
-        for c in self.children:
-            s += str(c)
-        return s
-
-    def regions_split_by_kernel(self):
-        kernel = [x for x in self.children if
-                  x.region.typestr.startswith('Kernel ')]
-
-        if (len(kernel) == 0):
-            return [self.region]
-
-        r = self.region
-        s = r.typestr
-
-        kernel_start = kernel[0].region.start
-        kernel_stop = kernel[len(kernel) - 1].region.stop
-
-        # align this for 16M, but only if we have enough space
-        kernel_stop = (kernel_stop & ~0xFFFFFF) + 0xFFFFFF
-        if (kernel_stop > r.stop):
-            kernel_stop = r.stop
-
-        before_kernel = None
-        after_kernel = None
-
-        # before Kernel if any
-        if (r.start < kernel_start):
-            before_kernel = MemRegion(r.start, kernel_start - 1, s)
-
-        kernel_region = MemRegion(kernel_start, kernel_stop, "Kernel")
-
-        # after Kernel if any
-        if (r.stop > kernel_stop):
-            after_kernel = MemRegion(kernel_stop + 1, r.stop, s)
-
-        return [before_kernel, kernel_region, after_kernel]
-
-    @staticmethod
-    def parse_iomem_line(line):
-        a = line.split(':', 1)
-        level = int(a[0].count(' ') / 2) + 1
-        region = a[0].split('-', 1)
-        a[1] = a[1].strip()
-        return level, MemRegion(int(region[0], 16), int(region[1], 16), a[1])
-
-    @staticmethod
-    def parse_iomem_file():
-        root = IOMemRegionTree(None, 0)
-        f = input_open('/proc/iomem')
-        lastlevel = 0
-        lastnode = root
-        for line in f:
-            (level, r) = IOMemRegionTree.parse_iomem_line(line)
-            t = IOMemRegionTree(r, level)
-            if (t.level > lastlevel):
-                t.parent = lastnode
-            if (t.level == lastlevel):
-                t.parent = lastnode.parent
-            if (t.level < lastlevel):
-                p = lastnode.parent
-                while(t.level < p.level):
-                    p = p.parent
-                t.parent = p.parent
-
-            t.parent.children.append(t)
-            lastnode = t
-            lastlevel = t.level
-        f.close()
-
-        return root
-
-    # find HPET regions in tree
-    @staticmethod
-    def find_hpet_regions(tree):
-        regions = []
-
-        for tree in tree.children:
-            r = tree.region
-            s = r.typestr
-
-            if (s.find('HPET') >= 0):
-                regions.append(r)
-
-            # if the tree continues recurse further down ...
-            if (len(tree.children) > 0):
-                regions.extend(IOMemRegionTree.find_hpet_regions(tree))
-
-        return regions
-
-    # recurse down the tree
-    @staticmethod
-    def parse_iomem_tree(tree):
-        regions = []
-
-        for tree in tree.children:
-            r = tree.region
-            s = r.typestr
-
-            # System RAM on the first level will be added completely,
-            # if they don't contain the kernel itself, if they do,
-            # we split them
-            if (tree.level == 1 and s == 'System RAM'):
-                regions.extend(tree.regions_split_by_kernel())
-                continue
-
-            # blacklisted on all levels
-            if (
-                (s.find('PCI MMCONFIG') >= 0) or
-                (s.find('APIC') >= 0)  # covers both APIC and IOAPIC
-            ):
-                continue
-
-            # generally blacklisted, unless we find an HPET behind it
-            if (s.lower() == 'reserved'):
-                regions.extend(IOMemRegionTree.find_hpet_regions(tree))
-                continue
-
-            # if the tree continues recurse further down ...
-            if (len(tree.children) > 0):
-                regions.extend(IOMemRegionTree.parse_iomem_tree(tree))
-                continue
-
-            # add all remaining leaves
-            regions.append(r)
-
-        return regions
-
-
-class IOMMUConfig:
-    def __init__(self, props):
-        self.base_addr = props['base_addr']
-        self.mmio_size = props['mmio_size']
-        if 'amd_bdf' in props:
-            self.amd_bdf = props['amd_bdf']
-            self.amd_base_cap = props['amd_base_cap']
-            self.amd_msi_cap = props['amd_msi_cap']
-            self.amd_features = props['amd_features']
-
-    @property
-    def is_amd_iommu(self):
-        return hasattr(self, 'amd_bdf')
-
-
-def parse_iomem(pcidevices):
-    regions = IOMemRegionTree.parse_iomem_tree(
-        IOMemRegionTree.parse_iomem_file())
-
-    rom_region = MemRegion(0xc0000, 0xdffff, 'ROMs')
-    add_rom_region = False
-
-    ret = []
-    dmar_regions = []
-    for r in regions:
-        append_r = True
-        # filter the list for MSI-X pages
-        for d in pcidevices:
-            if d.msix_address >= r.start and d.msix_address <= r.stop:
-                if d.msix_address > r.start:
-                    head_r = MemRegion(r.start, d.msix_address - 1,
-                                       r.typestr, r.comments)
-                    ret.append(head_r)
-                if d.msix_address + d.msix_region_size < r.stop:
-                    tail_r = MemRegion(d.msix_address + d.msix_region_size,
-                                       r.stop, r.typestr, r.comments)
-                    ret.append(tail_r)
-                append_r = False
-                break
-        # filter out the ROMs
-        if (r.start >= rom_region.start and r.stop <= rom_region.stop):
-            add_rom_region = True
-            append_r = False
-        # filter out and save DMAR regions
-        if r.typestr.find('dmar') >= 0:
-            dmar_regions.append(r)
-            append_r = False
-        if append_r:
-            ret.append(r)
-
-    # add a region that covers all potential ROMs
-    if add_rom_region:
-        ret.append(rom_region)
-
-    # newer Linux kernels will report the first page as reserved
-    # it is needed for CPU init so include it anyways
-    if (ret[0].typestr == 'System RAM' and ret[0].start == 0x1000):
-        ret[0].start = 0
-
-    return ret, dmar_regions
-
-
-def parse_pcidevices():
-    int_src_cnt = 0
-    devices = []
-    caps = []
-    basedir = '/sys/bus/pci/devices'
-    list = input_listdir(basedir, ['*/config'])
-    for dir in list:
-        d = PCIDevice.parse_pcidevice_sysfsdir(basedir, dir)
-        if d is not None:
-            if len(d.caps) > 0:
-                duplicate = False
-                # look for duplicate capability patterns
-                for d2 in devices:
-                    if d2.caps == d.caps:
-                        # reused existing capability list, but record all users
-                        d2.caps[0].comments.append(str(d))
-                        d.caps_start = d2.caps_start
-                        duplicate = True
-                        break
-                if not duplicate:
-                    d.caps[0].comments.append(str(d))
-                    d.caps_start = len(caps)
-                    caps.extend(d.caps)
-            int_src_cnt += max(d.num_msi_vectors, d.num_msix_vectors)
-            devices.append(d)
-    return (devices, caps, int_src_cnt)
-
-
 def parse_kernel_cmdline():
     line = input_readline('/proc/cmdline')
     ma = re.findall(r'memmap=([0-9a-fA-FxX]+)([KMG]?)\$'
@@ -696,11 +138,12 @@ def alloc_mem(regions, size):
             r.stop + 1 >= mem[0] + mem[1]
         ):
             if r.start < mem[0]:
-                head_r = MemRegion(r.start, mem[0] - 1, r.typestr, r.comments)
+                head_r = sysfs_parser.MemRegion(r.start, mem[0] - 1, r.typestr,
+                                                r.comments)
                 regions.insert(regions.index(r), head_r)
             if r.stop + 1 > mem[0] + mem[1]:
-                tail_r = MemRegion(mem[0] + mem[1], r.stop, r.typestr,
-                                   r.comments)
+                tail_r = sysfs_parser.MemRegion(mem[0] + mem[1], r.stop,
+                                                r.typestr, r.comments)
                 regions.insert(regions.index(r), tail_r)
             regions.remove(r)
             return mem
@@ -713,7 +156,7 @@ def alloc_mem(regions, size):
 
 
 def count_cpus():
-    list = input_listdir('/sys/devices/system/cpu', ['cpu*/uevent'])
+    list = sysfs_parser.input_listdir('/sys/devices/system/cpu', 
['cpu*/uevent'])
     count = 0
     for f in list:
         if re.match(r'cpu[0-9]+', f):
@@ -721,341 +164,9 @@ def count_cpus():
     return count
 
 
-def parse_madt():
-    f = input_open('/sys/firmware/acpi/tables/APIC', 'rb')
-    signature = f.read(4)
-    if signature != b'APIC':
-        raise RuntimeError('MADT: incorrect input file format %s' % signature)
-    (length,) = struct.unpack('<I', f.read(4))
-    f.seek(44)
-    length -= 44
-    ioapics = []
-
-    while length > 0:
-        offset = 0
-        (struct_type, struct_len) = struct.unpack('<BB', f.read(2))
-        offset += 2
-        length -= struct_len
-
-        if struct_type == 1:
-            (id, address, gsi_base) = struct.unpack('<BxII', f.read(10))
-            offset += 10
-            ioapics.append(IOAPIC(id, address, gsi_base))
-
-        f.seek(struct_len - offset, os.SEEK_CUR)
-
-    f.close()
-    return ioapics
-
-
-def parse_dmar_devscope(f):
-    (scope_type, scope_len, id, bus, dev, fn) = \
-        struct.unpack('<BBxxBBBB', f.read(8))
-    if scope_len != 8:
-        raise RuntimeError('Unsupported DMAR Device Scope Structure')
-    return (scope_type, scope_len, id, bus, dev, fn)
-
-
-# parsing of DMAR ACPI Table
-# see Intel VT-d Spec chapter 8
-def parse_dmar(pcidevices, ioapics, dmar_regions):
-    f = input_open('/sys/firmware/acpi/tables/DMAR', 'rb')
-    signature = f.read(4)
-    if signature != b'DMAR':
-        raise RuntimeError('DMAR: incorrect input file format %s' % signature)
-    (length,) = struct.unpack('<I', f.read(4))
-    f.seek(48)
-    length -= 48
-    units = []
-    regions = []
-
-    while length > 0:
-        offset = 0
-        (struct_type, struct_len) = struct.unpack('<HH', f.read(4))
-        offset += 4
-        length -= struct_len
-
-        # DMA Remapping Hardware Unit Definition
-        if struct_type == 0:
-            (flags, segment, base) = struct.unpack('<BxHQ', f.read(12))
-            if segment != 0:
-                raise RuntimeError('We do not support multiple PCI segments')
-            if len(units) >= 8:
-                raise RuntimeError('Too many DMAR units. '
-                                   'Raise JAILHOUSE_MAX_IOMMU_UNITS.')
-            size = 0
-            for r in dmar_regions:
-                if base == r.start:
-                    size = r.size()
-            if size == 0:
-                raise RuntimeError('DMAR region size cannot be identified.\n'
-                                   'Target Linux must run with Intel IOMMU '
-                                   'enabled.')
-            if size > 0x3000:
-                raise RuntimeError('Unexpectedly large DMAR region.')
-            units.append(IOMMUConfig({
-                'base_addr': base,
-                'mmio_size': size
-            }))
-            if flags & 1:
-                for d in pcidevices:
-                    if d.iommu is None:
-                        d.iommu = len(units) - 1
-            offset += 16 - offset
-            while offset < struct_len:
-                (scope_type, scope_len, id, bus, dev, fn) =\
-                    parse_dmar_devscope(f)
-                # PCI Endpoint Device
-                if scope_type == 1:
-                    assert not (flags & 1)
-                    for d in pcidevices:
-                        if d.bus == bus and d.dev == dev and d.fn == fn:
-                            d.iommu = len(units) - 1
-                            break
-                # PCI Sub-hierarchy
-                elif scope_type == 2:
-                    assert not (flags & 1)
-                    for d in pcidevices:
-                        if d.bus == bus and d.dev == dev and d.fn == fn:
-                            (secondbus, subordinate) = \
-                                PCIPCIBridge.get_2nd_busses(d)
-                            for d2 in pcidevices:
-                                if (
-                                    d2.bus >= secondbus and
-                                    d2.bus <= subordinate
-                                ):
-                                    d2.iommu = len(units) - 1
-                            break
-                # IOAPIC
-                elif scope_type == 3:
-                    ioapic = next(chip for chip in ioapics if chip.id == id)
-                    bdf = (bus << 8) | (dev << 3) | fn
-                    for chip in ioapics:
-                        if chip.bdf == bdf:
-                            raise RuntimeError('IOAPICs with identical BDF')
-                    ioapic.bdf = bdf
-                    ioapic.iommu = len(units) - 1
-                offset += scope_len
-
-        # Reserved Memory Region Reporting Structure
-        if struct_type == 1:
-            f.seek(8 - offset, os.SEEK_CUR)
-            offset += 8 - offset
-            (base, limit) = struct.unpack('<QQ', f.read(16))
-            offset += 16
-
-            comments = []
-            while offset < struct_len:
-                (scope_type, scope_len, id, bus, dev, fn) =\
-                    parse_dmar_devscope(f)
-                if scope_type == 1:
-                    comments.append('PCI device: %02x:%02x.%x' %
-                                    (bus, dev, fn))
-                else:
-                    comments.append('DMAR parser could not decode device path')
-                offset += scope_len
-
-            reg = MemRegion(base, limit, 'ACPI DMAR RMRR', comments)
-            regions.append(reg)
-
-        f.seek(struct_len - offset, os.SEEK_CUR)
-
-    f.close()
-
-    for d in pcidevices:
-        if d.iommu is None:
-            raise RuntimeError(
-                'PCI device %02x:%02x.%x outside the scope of an '
-                'IOMMU' % (d.bus, d.dev, d.fn))
-
-    return units, regions
-
-
-def parse_ivrs(pcidevices, ioapics):
-    def format_bdf(bdf):
-        bus, dev, fun = (bdf >> 8) & 0xff, (bdf >> 3) & 0x1f, bdf & 0x7
-        return '%02x:%02x.%x' % (bus, dev, fun)
-
-    f = input_open('/sys/firmware/acpi/tables/IVRS', 'rb')
-    signature = f.read(4)
-    if signature != b'IVRS':
-        raise RuntimeError('IVRS: incorrect input file format %s' % signature)
-
-    (length, revision) = struct.unpack('<IB', f.read(5))
-    if revision > 2:
-        raise RuntimeError('IVRS: unsupported Revision %02x' % revision)
-
-    f.seek(48, os.SEEK_SET)
-    length -= 48
-
-    units = []
-    regions = []
-    # BDF of devices that are permitted outside IOMMU: root complex
-    iommu_skiplist = set([0x0])
-    ivhd_blocks = 0
-    while length > 0:
-        (block_type, block_length) = struct.unpack('<BxH', f.read(4))
-        if block_type in [0x10, 0x11]:
-            ivhd_blocks += 1
-            if ivhd_blocks > 1:
-                raise RuntimeError('Jailhouse doesn\'t support more than one '
-                                   'AMD IOMMU per PCI function.')
-            # IVHD block
-            ivhd_fields = struct.unpack('<HHQHxxL', f.read(20))
-            (iommu_bdf, base_cap_ofs,
-             base_addr, pci_seg, iommu_feat) = ivhd_fields
-
-            length -= block_length
-            block_length -= 24
-
-            if pci_seg != 0:
-                raise RuntimeError('We do not support multiple PCI segments')
-
-            if len(units) > 8:
-                raise RuntimeError('Too many IOMMU units. '
-                                   'Raise JAILHOUSE_MAX_IOMMU_UNITS.')
-
-            msi_cap_ofs = None
-
-            for i, d in enumerate(pcidevices):
-                if d.bdf() == iommu_bdf:
-                    # Extract MSI capability offset
-                    for c in d.caps:
-                        if c.id == 0x05:
-                            msi_cap_ofs = c.start
-                    # We must not map IOMMU to the cells
-                    del pcidevices[i]
-
-            if msi_cap_ofs is None:
-                raise RuntimeError('AMD IOMMU lacks MSI support, and '
-                                   'Jailhouse doesn\'t support MSI-X yet.')
-
-            if (iommu_feat & (0xF << 13)) and (iommu_feat & (0x3F << 17)):
-                # Performance Counters are supported, allocate 512K
-                mmio_size = 524288
-            else:
-                # Allocate 16K
-                mmio_size = 16384
-
-            units.append(IOMMUConfig({
-                'base_addr': base_addr,
-                'mmio_size': mmio_size,
-                'amd_bdf': iommu_bdf,
-                'amd_base_cap': base_cap_ofs,
-                'amd_msi_cap': msi_cap_ofs,
-                # IVHD block type 0x11 has exact EFR copy but type 0x10 may
-                # overwrite what hardware reports. Set reserved bit 0 in that
-                # case to indicate that the value is in use.
-                'amd_features': (iommu_feat | 0x1) if block_type == 0x10 else 0
-            }))
-
-            bdf_start_range = None
-            while block_length > 0:
-                (entry_type, device_id) = struct.unpack('<BHx', f.read(4))
-                block_length -= 4
-
-                if entry_type == 0x01:
-                    # All
-                    for d in pcidevices:
-                        d.iommu = len(units) - 1
-                elif entry_type == 0x02:
-                    # Select
-                    for d in pcidevices:
-                        if d.bdf() == device_id:
-                            d.iommu = len(units) - 1
-                elif entry_type == 0x03:
-                    # Start of range
-                    bdf_start_range = device_id
-                elif entry_type == 0x04:
-                    # End of range
-                    if bdf_start_range is None:
-                        continue
-                    for d in pcidevices:
-                        if d.bdf() >= bdf_start_range and d.bdf() <= device_id:
-                            d.iommu = len(units) - 1
-                    bdf_start_range = None
-                elif entry_type == 0x42:
-                    # Alias select
-                    (device_id_b,) = struct.unpack('<xHx', f.read(4))
-                    block_length -= 4
-                    for d in pcidevices:
-                        if d.bdf() == device_id_b:
-                            d.iommu = len(units) - 1
-                elif entry_type == 0x43:
-                    # Alias start of range
-                    (device_id_b,) = struct.unpack('<xHx', f.read(4))
-                    block_length -= 4
-                    bdf_start_range = device_id_b
-                elif entry_type == 0x48:
-                    # Special device
-                    (handle, device_id_b, variety) = struct.unpack(
-                        '<BHB', f.read(4))
-                    block_length -= 4
-                    if variety == 0x01:  # IOAPIC
-                        for chip in ioapics:
-                            if chip.id == handle:
-                                chip.bdf = device_id
-                                chip.iommu = len(units) - 1
-                else:
-                    # Reserved or ignored entries
-                    if entry_type >= 0x40:
-                        f.seek(4, os.SEEK_CUR)
-                        block_length -= 4
-
-        elif type in [0x20, 0x21, 0x22]:
-            # IVMD block
-            ivmd_fields = struct.unpack('<BBHHHxxxxxxxxQQ', f.read(32))
-            (block_type, block_flags, block_length,
-             device_id, aux_data, mem_addr, mem_len) = ivmd_fields
-            length -= block_length
-
-            if int(block_flags):
-                bdf_str = format_bdf(device_id)
-                print(
-                    'WARNING: Jailhouse doesn\'t support configurable '
-                    '(eg. read-only) device memory. Device %s may not '
-                    'work properly, especially in non-root cell.' % bdf_str)
-
-            if block_type == 0x20:
-                # All devices
-                comment = None
-            elif block_type == 0x21:
-                # Selected device
-                comment = 'PCI Device: %s' % format_bdf(device_id)
-            elif block_type == 0x22:
-                # Device range
-                comment = 'PCI Device: %s - %s' % (
-                    format_bdf(device_id), format_bdf(aux_data))
-
-            if comment:
-                print('WARNING: Jailhouse doesn\'t support per-device memory '
-                      'regions. The memory at 0x%x will be mapped accessible '
-                      'to all devices.' % mem_addr)
-
-            regions.append(MemRegion(mem_addr, mem_len, 'ACPI IVRS', comment))
-        elif type == 0x40:
-            raise RuntimeError(
-                'You board uses IVRS Rev. 2 feature Jailhouse doesn\'t '
-                'support yet. Please report this to '
-                '[email protected].')
-        else:
-            print(
-                'WARNING: Skipping unknown IVRS '
-                'block type 0x%02x' % block_type)
-
-        for d in pcidevices:
-            if d.bdf() not in iommu_skiplist and d.iommu is None:
-                raise RuntimeError(
-                    'PCI device %02x:%02x.%x outside the scope of an '
-                    'IOMMU' % (d.bus, d.dev, d.fn))
-
-        f.close()
-        return units, regions
-
-
 def parse_ioports():
     pm_timer_base = None
-    f = input_open('/proc/ioports')
+    f = sysfs_parser.input_open('/proc/ioports')
     for line in f:
         if line.endswith('ACPI PM_TMR\n'):
             pm_timer_base = int(line.split('-')[0], 16)
@@ -1071,7 +182,7 @@ class MMConfig:
 
     @staticmethod
     def parse():
-        f = input_open('/sys/firmware/acpi/tables/MCFG', 'rb')
+        f = sysfs_parser.input_open('/sys/firmware/acpi/tables/MCFG', 'rb')
         signature = f.read(4)
         if signature != b'MCFG':
             raise RuntimeError('MCFG: incorrect input file format %s' %
@@ -1088,20 +199,6 @@ class MMConfig:
         return MMConfig(base, end_bus)
 
 
-def get_cpu_vendor():
-    global cpuvendor
-    if cpuvendor is not None:
-        return cpuvendor
-    with input_open('/proc/cpuinfo', 'r') as f:
-        for line in f:
-            if not line.strip():
-                continue
-            key, value = line.split(':')
-            if key.strip() == 'vendor_id':
-                cpuvendor = value.strip()
-                return cpuvendor
-
-
 class DebugConsole:
     def __init__(self, console):
         self.address = 0
@@ -1131,10 +228,10 @@ class DebugConsole:
 
 if options.generate_collector:
     f = open(options.file, 'w')
-    filelist = ' '.join(inputs['files'])
-    filelist_opt = ' '.join(inputs['files_opt'])
-    filelist_intel = ' '.join(inputs['files_intel'])
-    filelist_amd = ' '.join(inputs['files_amd'])
+    filelist = ' '.join(sysfs_parser.inputs['files'])
+    filelist_opt = ' '.join(sysfs_parser.inputs['files_opt'])
+    filelist_intel = ' '.join(sysfs_parser.inputs['files_intel'])
+    filelist_amd = ' '.join(sysfs_parser.inputs['files_amd'])
 
     tmpl = Template(filename=os.path.join(options.template_dir,
                                           'jailhouse-config-collect.tmpl'))
@@ -1157,7 +254,9 @@ if jh_enabled == '1':
 IOAPIC_MAX_PINS = 120
 int_src_count = IOAPIC_MAX_PINS
 
-(pcidevices, pcicaps, cnt) = parse_pcidevices()
+sysfs_parser.root_dir = options.root
+
+(pcidevices, pcicaps, cnt) = sysfs_parser.parse_pcidevices()
 
 int_src_count += cnt
 vtd_interrupt_limit = 2**math.ceil(math.log(int_src_count, 2))
@@ -1171,20 +270,20 @@ product = [input_readline('/sys/class/dmi/id/sys_vendor',
 inmatemem = kmg_multiply_str(options.mem_inmates)
 hvmem = [0, kmg_multiply_str(options.mem_hv)]
 
-(regions, dmar_regions) = parse_iomem(pcidevices)
+(regions, dmar_regions) = sysfs_parser.parse_iomem(pcidevices)
 ourmem = parse_kernel_cmdline()
 total = hvmem[1] + inmatemem
 
 mmconfig = MMConfig.parse()
 
-ioapics = parse_madt()
+ioapics = sysfs_parser.parse_madt()
 
-vendor = get_cpu_vendor()
+vendor = sysfs_parser.get_cpu_vendor()
 if vendor == 'GenuineIntel':
-    (iommu_units, extra_memregs) = parse_dmar(pcidevices, ioapics,
-                                              dmar_regions)
+    (iommu_units, extra_memregs) = sysfs_parser.parse_dmar(pcidevices, ioapics,
+                                                           dmar_regions)
 else:
-    (iommu_units, extra_memregs) = parse_ivrs(pcidevices, ioapics)
+    (iommu_units, extra_memregs) = sysfs_parser.parse_ivrs(pcidevices, ioapics)
 regions += extra_memregs
 
 # kernel does not have memmap region, pick one
@@ -1197,9 +296,9 @@ elif (total > ourmem[1]):
 
 hvmem[0] = ourmem[0]
 
-inmatereg = MemRegion(ourmem[0] + hvmem[1],
-                      ourmem[0] + hvmem[1] + inmatemem - 1,
-                      'JAILHOUSE Inmate Memory')
+inmatereg = sysfs_parser.MemRegion(ourmem[0] + hvmem[1],
+                                   ourmem[0] + hvmem[1] + inmatemem - 1,
+                                   'JAILHOUSE Inmate Memory')
 regions.append(inmatereg)
 
 cpucount = count_cpus()
diff --git a/tools/jailhouse-hardware-check b/tools/jailhouse-hardware-check
index ae4879d..1c7d1b9 100755
--- a/tools/jailhouse-hardware-check
+++ b/tools/jailhouse-hardware-check
@@ -25,6 +25,7 @@ if sys.version_info[0] < 3:
 check_passed = True
 ran_all = True
 
+
 def check_feature(msg, ok, optional=False):
     if not (ok or optional):
         global check_passed
-- 
2.7.4

-- 
You received this message because you are subscribed to the Google Groups 
"Jailhouse" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
For more options, visit https://groups.google.com/d/optout.

Reply via email to