--- /dev/null
+#!/usr/bin/env python
+#
+# Jailhouse, a Linux-based partitioning hypervisor
+#
+# Copyright (c) Siemens AG, 2014
+#
+# 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 sys
+import os
+import re
+import argparse
+import struct
+from mako.template import Template
+
+# pretend to be part of the jailhouse tool
+sys.argv[0] = sys.argv[0].replace('-', ' ')
+
+parser = argparse.ArgumentParser()
+parser.add_argument('-g', '--generate-collector',
+ help='generate a script to collect input files on '
+ 'a remote machine',
+ action='store_true')
+parser.add_argument('-r', '--root',
+ help='gather information in ROOT/, the default is "/" '
+ 'which means creating a config for localhost',
+ default='/',
+ action='store',
+ type=str)
+
+memargs = [['--mem-inmates', '2M', 'inmate'],
+ ['--mem-hv', '64M', 'hypervisor']]
+
+for entry in memargs:
+ parser.add_argument(entry[0],
+ help='the amount of ' + entry[2] +
+ ' memory, default is "' + entry[1] +
+ '", format "xxx[K|M|G]"',
+ default=entry[1],
+ action='store',
+ type=str)
+
+parser.add_argument('file', metavar='FILE',
+ help='name of file to write out',
+ type=str)
+
+options = parser.parse_args()
+
+inputs = {'files': set(), 'dirs': set()}
+
+
+class PCIDevice:
+ def __init__(self, type, domain, bus, dev, fn):
+ self.type = type
+ self.domain = domain
+ self.bus = bus
+ self.dev = dev
+ self.fn = fn
+
+ def __str__(self):
+ return 'PCIDevice: %02x:%02x.%x' % (self.bus, self.dev, self.fn)
+
+ def devfn(self):
+ return self.dev << 3 | self.fn
+
+ @staticmethod
+ def parse_pcidevice_sysfsdir(basedir, dir):
+ dclass = input_readline(basedir + '/' + dir + '/class', False, False)
+ if re.match(r'0x0604..', dclass):
+ 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('.')
+ return PCIDevice(type, domain, bus, int(df[0], 16), int(df[1], 16))
+
+
+class MemRegion:
+ def __init__(self, start, stop, typestr, comments=[]):
+ self.start = start
+ self.stop = stop
+ self.typestr = typestr
+ self.comments = comments
+
+ 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 == 'ACPI Tables' or
+ self.typestr == 'ACPI Non-volatile Storage'
+ ):
+ return 'JAILHOUSE_MEM_READ'
+ if (
+ self.typestr == 'System RAM' or
+ self.typestr == 'RAM buffer' or
+ self.typestr == 'ACPI DMAR RMRR'
+ ):
+ 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'
+
+ @staticmethod
+ # return the first region with the given typestr
+ def find_region(regions, typestr):
+ for r in regions:
+ if (r.typestr == typestr):
+ return r
+ return None
+
+ @staticmethod
+ def parse_iomem_line(line):
+ a = line.split(':', 1)
+ # HPET may be part of in reserved region
+ if a[0].startswith(' ') and a[1].find("HPET") < 0:
+ return None
+ region = a[0].split('-', 1)
+ a[1] = a[1].strip()
+ return MemRegion(int(region[0], 16), int(region[1], 16), a[1])
+
+
+def parse_iomem():
+ regions = []
+ f, e = input_open('/proc/iomem', True, False, 'r')
+ for line in f:
+ r = MemRegion.parse_iomem_line(line)
+ ## XXX what else to ignore??
+ if (
+ r is not None and
+ r.typestr != 'Local APIC' and
+ r.typestr != 'reserved'
+ ):
+ regions.append(r)
+ f.close()
+
+ # newer Linux kernels will report the first page as reserved
+ # it is needed for CPU init so include it anyways
+ if (
+ regions[0].typestr == 'System RAM' and
+ regions[0].start == 0x1000
+ ):
+ regions[0].start = 0
+
+ return regions
+
+
+def parse_pcidevices():
+ devices = []
+ basedir = '/sys/bus/pci/devices'
+ list = input_listdir(basedir, ['*/class'])
+ for dir in list:
+ d = PCIDevice.parse_pcidevice_sysfsdir(basedir, dir)
+ if d is not None:
+ devices.append(d)
+ return devices
+
+
+def kmg_multiply(value, kmg):
+ if (kmg == 'K' or kmg == 'k'):
+ return 1024 * value
+ if (kmg == 'M' or kmg == 'm'):
+ return 1024**2 * value
+ if (kmg == 'G' or kmg == 'g'):
+ return 1024**3 * value
+ return value
+
+
+def kmg_multiply_str(str):
+ m = re.match(r'([0-9a-fA-FxX]+)([KMG]?)', str)
+ if m is not None:
+ return kmg_multiply(int(m.group(1)), m.group(2))
+ raise RuntimeError('kmg_multiply_str can not parse input "' + str + '"')
+ return 0
+
+
+def input_open(name, record, optional, *args):
+ if record:
+ inputs['files'].add(name)
+ try:
+ f = open(options.root + name, *args)
+ except Exception as e:
+ if optional:
+ return None, e
+ raise e
+ return f, None
+
+
+def input_readline(name, record=True, optional=False):
+ f, e = input_open(name, record, optional, 'r')
+ if f is None and optional:
+ return ''
+
+ line = f.readline()
+ f.close()
+ return line
+
+
+def input_listdir(dir, wildcards):
+ for w in wildcards:
+ inputs['dirs'].add(dir + '/' + w)
+ return os.listdir(options.root + dir)
+
+
+def parse_cmdline():
+ line = input_readline('/proc/cmdline')
+ m = re.match(r'.*memmap=([0-9a-fA-FxX]+)([KMG]?)\$'
+ '([0-9a-fA-FxX]+)([KMG]?).*',
+ line)
+ if m is not None:
+ size = kmg_multiply(int(m.group(1), 0), m.group(2))
+ start = kmg_multiply(int(m.group(3), 0), m.group(4))
+ return [start, size]
+ return None
+
+
+def alloc_mem(regions, size):
+ mem = [0, size]
+ for r in reversed(regions):
+ if (r.typestr == 'System RAM' and r.size() >= mem[1]):
+ mem[0] = r.start
+ r.start += mem[1]
+ return mem
+ raise RuntimeError('failed to allocate memory')
+
+
+def count_cpus():
+ list = input_listdir('/sys/devices/system/cpu', ['cpu*/topology/core_id'])
+ count = 0
+ for f in list:
+ if re.match(r'cpu[0-9]+', f):
+ count += 1
+ return count
+
+
+def parse_dmar_devscope(f):
+ offset = 0
+ (scope_type, scope_len, bus, dev, fn) = \
+ struct.unpack('<BBxxxBBB', f.read(8))
+ offset += 8
+ return (offset, scope_type, scope_len, bus, dev, fn)
+
+
+# parsing of DMAR ACPI Table
+# see Intel VT-d Spec chapter 8
+def parse_dmar():
+ f, e = input_open('/sys/firmware/acpi/tables/DMAR', True, True, 'rb')
+ if not f:
+ if options.generate_collector:
+ return 0, []
+ if e:
+ raise e
+ raise RuntimeError('could not find DMAR ACPI table')
+
+ signature = f.read(4)
+ if signature != b'DMAR':
+ raise RuntimeError('incorrect input file format %s' % signature)
+ (length,) = struct.unpack('<I', f.read(4))
+ f.seek(48)
+ length -= 48
+ regions = []
+ ioapic_id = 0
+
+ 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:
+ f.seek(16 - offset, os.SEEK_CUR)
+ offset += 16 - offset
+ while offset < struct_len:
+ (off, scope_type, scope_len, bus, dev, fn) =\
+ parse_dmar_devscope(f)
+ offset += off
+ if scope_type == 3:
+ if ioapic_id != 0:
+ raise RuntimeError('We do not support more '
+ 'than 1 IOAPIC')
+ ioapic_id = (bus << 8) | (dev << 3) | fn
+ f.seek(scope_len - 8, os.SEEK_CUR)
+ offset += scope_len - 8
+
+ # 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:
+ (off, scope_type, scope_len, bus, dev, fn) =\
+ parse_dmar_devscope(f)
+ offset += off
+ npath = (scope_len - 6)/2
+ if scope_type == 1 and npath == 1:
+ comments.append('PCI device: %02x:%02x.%x' %
+ (bus, dev, fn))
+ else:
+ comments.append('DMAR parser could not decode device path')
+ f.seek(scope_len - off, os.SEEK_CUR)
+ offset += scope_len - off
+
+ reg = MemRegion(base, limit, 'ACPI DMAR RMRR', comments)
+ regions.append(reg)
+
+ f.seek(struct_len - offset, os.SEEK_CUR)
+
+ return ioapic_id, regions
+
+pcidevices = parse_pcidevices()
+
+product = [input_readline('/sys/class/dmi/id/sys_vendor',
+ True, True).rstrip(),
+ input_readline('/sys/class/dmi/id/product_name',
+ True, True).rstrip()
+ ]
+
+inmatemem = kmg_multiply_str(options.mem_inmates)
+hvmem = [0, kmg_multiply_str(options.mem_hv)]
+
+regions = parse_iomem()
+ourmem = parse_cmdline()
+total = hvmem[1] + inmatemem
+
+ioapic_id, rmrr_regs = parse_dmar()
+regions += rmrr_regs
+
+# kernel does not have memmap region, pick one
+if ourmem is None:
+ ourmem = alloc_mem(regions, total)
+elif (total > ourmem[1]):
+ raise RuntimeError('Your memmap reservation is too small you need >="' +
+ hex(total) + '"')
+
+hvmem[0] = ourmem[0]
+
+creg = MemRegion.find_region(regions, 'ACPI Tables')
+if creg is None:
+ raise RuntimeError('could not find "ACPI Tables" memory')
+confmem = [creg.start, creg.size()]
+inmatereg = MemRegion(ourmem[0] + hvmem[1],
+ ourmem[0] + hvmem[1] + inmatemem - 1,
+ 'JAILHOUSE Inmate Memory')
+regions.append(inmatereg)
+
+cpucount = count_cpus()
+
+f = open(options.file, 'w')
+
+if options.generate_collector:
+ filelist = ' '.join(inputs['files'].union(inputs['dirs']))
+
+ tmpl = Template(filename='jailhouse-config-collect.tmpl')
+ f.write(tmpl.render(filelist=filelist))
+else:
+ tmpl = Template(filename='root-cell-config.c.tmpl')
+ f.write(tmpl.render(regions=regions,
+ ourmem=ourmem,
+ argstr=' '.join(sys.argv),
+ hvmem=hvmem,
+ confmem=confmem,
+ product=product,
+ pcidevices=pcidevices,
+ cpucount=cpucount,
+ ioapic_id=ioapic_id))
--- /dev/null
+/*
+ * Jailhouse, a Linux-based partitioning hypervisor
+ *
+ * Copyright (c) Siemens AG, 2014
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2. See
+ * the COPYING file in the top-level directory.
+ *
+ * Configuration for ${product[0]} ${product[1]}
+ * created with '${argstr}'
+ *
+ * NOTE: This config expects the following to be appended to your kernel cmdline
+ * "memmap=${hex(ourmem[1])}$${hex(ourmem[0])}"
+ */
+
+#include <linux/types.h>
+#include <jailhouse/cell-config.h>
+
+#define ARRAY_SIZE(a) sizeof(a) / sizeof(a[0])
+
+struct {
+ struct jailhouse_system header;
+ __u64 cpus[1];
+ struct jailhouse_memory mem_regions[${len(regions)}];
+ struct jailhouse_irqchip irqchips[1];
+ __u8 pio_bitmap[0x2000];
+ struct jailhouse_pci_device pci_devices[${len(pcidevices)}];
+} __attribute__((packed)) config = {
+ .header = {
+ .hypervisor_memory = {
+ .phys_start = ${hex(hvmem[0])},
+ .size = ${hex(hvmem[1])},
+ },
+ .config_memory = {
+ .phys_start = ${hex(confmem[0])},
+ .size = ${hex(confmem[1])},
+ },
+ .root_cell = {
+ .name = "${product[1]}",
+ .cpu_set_size = sizeof(config.cpus),
+ .num_memory_regions = ARRAY_SIZE(config.mem_regions),
+ .num_irqchips = ARRAY_SIZE(config.irqchips),
+ .pio_bitmap_size = ARRAY_SIZE(config.pio_bitmap),
+ .num_pci_devices = ARRAY_SIZE(config.pci_devices),
+ },
+ },
+
+ .cpus = {
+ 0b${'1'*cpucount},
+ },
+
+ .mem_regions = {
+ % for r in regions:
+ /* ${str(r)} */
+ % for c in r.comments:
+ /* ${c} */
+ % endfor
+ {
+ .phys_start = ${hex(r.start)},
+ .virt_start = ${hex(r.start)},
+ .size = ${hex(r.size())},
+ .flags = ${r.flagstr('\t\t')},
+ },
+ % endfor
+ },
+
+ .irqchips = {
+ /* IOAPIC */ {
+ .address = 0xfec00000,
+ .id = ${hex(ioapic_id)},
+ .pin_bitmap = 0xffffff,
+ },
+ },
+
+ .pio_bitmap = {
+ [ 0/8 ... 0x1f/8] = -1,
+ [ 0x20/8 ... 0x27/8] = 0xfc, /* HACK: PIC */
+ [ 0x28/8 ... 0x3f/8] = -1,
+ [ 0x40/8 ... 0x47/8] = 0xf0, /* PIT */
+ [ 0x48/8 ... 0x5f/8] = -1,
+ [ 0x60/8 ... 0x67/8] = 0, /* HACK: 8042, NMI status/control */
+ [ 0x68/8 ... 0x6f/8] = -1,
+ [ 0x70/8 ... 0x77/8] = 0xfc, /* RTC */
+ [ 0x78/8 ... 0x3af/8] = -1,
+ [ 0x3b0/8 ... 0x3df/8] = 0x00, /* VGA */
+ [ 0x3e0/8 ... 0x3ff/8] = -1,
+ [ 0x400/8 ... 0x47f/8] = 0, /* HACK: ACPI */
+ [ 0x480/8 ... 0xcff/8] = -1,
+ [ 0xd00/8 ... 0xffff/8] = 0, /* HACK: PCI bus */
+ },
+
+ .pci_devices = {
+ % for d in pcidevices:
+ /* ${str(d)} */
+ {
+ .type = ${d.type},
+ .domain = ${hex(d.domain)},
+ .bus = ${hex(d.bus)},
+ .devfn = ${hex(d.devfn())},
+ },
+ % endfor
+ },
+};