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):
(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
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
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)
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
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
# 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')
# 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
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)