]> rtime.felk.cvut.cz Git - jailhouse.git/blobdiff - tools/jailhouse-config-create
core/configs/tools: Report IOMMU association of devices via config file
[jailhouse.git] / tools / jailhouse-config-create
index b318116d3ed4a9d4685913966524c768c0d0f53b..7aedd5c5427298b74d95635028063159cae653b1 100755 (executable)
@@ -111,11 +111,13 @@ def input_listdir(dir, wildcards):
 
 
 class PCICapability:
-    def __init__(self, id, start, len, flags):
+    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):
@@ -140,6 +142,7 @@ class PCICapability:
         (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
@@ -158,18 +161,30 @@ class PCICapability:
             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
-            caps.append(PCICapability(id, cap, len, flags))
+            f.seek(cap + 2)
+            content = f.read(len - 2)
+            caps.append(PCICapability(id, cap, len, flags, content,
+                                      msix_address))
         return caps
 
 
 class PCIDevice:
     def __init__(self, type, domain, bus, dev, fn, caps):
         self.type = type
+        self.iommu = None
         self.domain = domain
         self.bus = bus
         self.dev = dev
@@ -177,6 +192,22 @@ class PCIDevice:
         self.caps = caps
         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
+                    vectors = (msg_ctrl & 0x7ff) + 1
+                    self.num_msix_vectors = vectors
+                    self.msix_region_size = (vectors * 16 + 0xfff) & 0xf000
+                    self.msix_address = c.msix_address
 
     def __str__(self):
         return 'PCIDevice: %02x:%02x.%x' % (self.bus, self.dev, self.fn)
@@ -336,7 +367,7 @@ class IOMemRegionTree:
         return linenumbers, regions
 
 
-def parse_iomem():
+def parse_iomem(pcidevices):
     (maxsz, tree) = IOMemRegionTree.parse_iomem_file()
 
     # create a spare array so we can easiely keep the order from the file
@@ -348,11 +379,25 @@ def parse_iomem():
         regions[l] = regs[i]
         i += 1
 
-    # now prepare a non-sparse array for a return value
+    # now prepare a non-sparse array for a return value,
+    # also filtering out MSI-X pages
     ret = []
     for r in regions:
         if r:
-            ret.append(r)
+            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)
+                    r = None
+                    break
+            if r:
+                ret.append(r)
 
     # newer Linux kernels will report the first page as reserved
     # it is needed for CPU init so include it anyways
@@ -429,7 +474,7 @@ def parse_dmar_devscope(f):
 
 # parsing of DMAR ACPI Table
 # see Intel VT-d Spec chapter 8
-def parse_dmar():
+def parse_dmar(pcidevices):
     f = input_open('/sys/firmware/acpi/tables/DMAR', 'rb', True)
     if get_cpu_vendor() == 'AuthenticAMD':
         print('WARNING: AMD IOMMU support is not implemented yet')
@@ -454,22 +499,34 @@ def parse_dmar():
 
         # DMA Remapping Hardware Unit Definition
         if struct_type == 0:
-            (segment, base) = struct.unpack('<xxHQ', f.read(12))
+            (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_DMAR_UNITS.')
             units.append(base)
+            if flags & 1:
+                for d in pcidevices:
+                    if d.iommu == None:
+                        d.iommu = len(units) - 1
             offset += 16 - offset
             while offset < struct_len:
                 (scope_type, scope_len, bus, dev, fn) =\
                     parse_dmar_devscope(f)
-                if scope_type == 3:
+                if scope_type == 1:
+                    for d in pcidevices:
+                        if d.bus == bus and d.dev == dev and d.fn == fn:
+                            d.iommu = len(units) - 1
+                elif scope_type == 2:
+                    raise RuntimeError('Unsupported DMAR Device Scope type')
+                elif scope_type == 3:
                     if ioapic_id != 0:
                         raise RuntimeError('We do not support more '
                                            'than 1 IOAPIC')
-                    ioapic_id = (bus << 8) | (dev << 3) | fn
+                    # encode the DMAR unit number into the device ID
+                    ioapic_id = ((len(units) - 1) << 16) | \
+                        (bus << 8) | (dev << 3) | fn
                 offset += scope_len
 
         # Reserved Memory Region Reporting Structure
@@ -563,15 +620,22 @@ 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 = parse_iomem()
+regions = parse_iomem(pcidevices)
 ourmem = parse_cmdline()
 total = hvmem[1] + inmatemem
 
 mmconfig = MMConfig.parse()
 
-(dmar_units, ioapic_id, rmrr_regs) = parse_dmar()
+(dmar_units, ioapic_id, rmrr_regs) = parse_dmar(pcidevices)
 regions += rmrr_regs
 
+for d in pcidevices:
+    if get_cpu_vendor() == 'AuthenticAMD':
+        d.iommu = 0  # temporary workaround
+    if d.iommu == None:
+        raise RuntimeError('PCI device %02x:%02x.%x outside the scope of an '
+                           'IOMMU' % (d.bus, d.dev, d.fn))
+
 # kernel does not have memmap region, pick one
 if ourmem is None:
     ourmem = alloc_mem(regions, total)