def gen_dsdt(board_etree, scenario_etree, allocation_etree, vm_id, dest_path): interrupt_pin_ids = { "INTA#": 0, "INTB#": 1, "INTC#": 2, "INTD#": 3, } header = builder.DefBlockHeader( int.from_bytes(bytearray("DSDT", "ascii"), sys.byteorder), # Signature 0x0badbeef, # Length, will calculate later 3, # Revision 0, # Checksum, will calculate later int.from_bytes(bytearray("ACRN ", "ascii"), sys.byteorder), # OEM ID int.from_bytes(bytearray("ACRNDSDT", "ascii"), sys.byteorder), # OEM Table ID 1, # OEM Revision int.from_bytes(bytearray("INTL", "ascii"), sys.byteorder), # Compiler ID 0x20190703, # Compiler Version ) objects = ObjectCollector() prt_packages = [] pci_dev_regex = re.compile(r"([0-9a-f]{2}):([0-1][0-9a-f]{1}).([0-7]) .*") for pci_dev in scenario_etree.xpath( f"//vm[@id='{vm_id}']/pci_devs/pci_dev/text()"): m = pci_dev_regex.match(pci_dev) if m: device_number = int(m.group(2), 16) function_number = int(m.group(3)) bus_number = int(m.group(1), 16) bdf = f"{bus_number:02x}:{device_number:02x}.{function_number}" address = hex((device_number << 16) | (function_number)) device_node = common.get_node( f"//bus[@address='{hex(bus_number)}']/device[@address='{address}']", board_etree) alloc_node = common.get_node( f"/acrn-config/vm[@id='{vm_id}']/device[@name='PTDEV_{bdf}']", allocation_etree) if device_node is not None and alloc_node is not None: assert int(alloc_node.find("bus").text, 16) == 0, "Virtual PCI devices must be on bus 0." vdev = int(alloc_node.find("dev").text, 16) vfunc = int(alloc_node.find("func").text, 16) # The AML object template, with _ADR replaced to vBDF template = device_node.find("aml_template") if template is not None: tree = parse_tree("DefDevice", bytes.fromhex(template.text)) vaddr = (vdev << 16) | vfunc add_or_replace_object( tree, builder.DefName("_ADR", builder.build_value(vaddr))) objects.add_device_object(tree) # The _PRT remapping package, if necessary intr_pin_node = common.get_node( "resource[@type='interrupt_pin']", device_node) virq_node = common.get_node("pt_intx", alloc_node) if intr_pin_node is not None and virq_node is not None: pin_id = interrupt_pin_ids[intr_pin_node.get("pin")] vaddr = (vdev << 16) | 0xffff pirq = int(intr_pin_node.get("source", -1)) virq_mapping = dict( eval( f"[{virq_node.text.replace(' ','').replace(')(', '), (')}]" )) if pirq in virq_mapping.keys(): virq = virq_mapping[pirq] prt_packages.append( builder.DefPackage( builder.PkgLength(), builder.ByteData(4), builder.PackageElementList( builder.build_value(vaddr), builder.build_value(pin_id), builder.build_value(0), builder.build_value(virq)))) for peer_device_node in collect_dependent_devices( board_etree, device_node): template = peer_device_node.find("aml_template") if template is not None: tree = parse_tree("DefDevice", bytes.fromhex(template.text)) objects.add_device_object(tree) root_pci_bus = board_etree.xpath("//bus[@type='pci' and @address='0x0']") if root_pci_bus: acpi_object = root_pci_bus[0].find("acpi_object") if acpi_object is not None: path = acpi_object.text objects.add_device_object(gen_root_pci_bus(path, prt_packages)) # If TPM is assigned to the VM, copy the TPM2 device object to vACPI as well. # # FIXME: Today the TPM2 MMIO registers are always located at 0xFED40000 with length 0x5000. The same address is used # as the guest physical address of the passed through TPM2. Thus, it works for now to reuse the host TPM2 device # object without updating the addresses of operation regions or resource descriptors. It is, however, necessary to # introduce a pass to translate such address to arbitrary guest physical ones in the future. has_tpm2 = common.get_node(f"//vm[@id='{vm_id}']//TPM2/text()", scenario_etree) if has_tpm2 == "y": # TPM2 devices should have "MSFT0101" as hardware id or compatible ID template = common.get_node("//device[@id='MSFT0101']/aml_template", board_etree) if template is None: template = common.get_node( "//device[compatible_id='MSFT0101']/aml_template", board_etree) if template is not None: tree = parse_tree("DefDevice", bytes.fromhex(template.text)) objects.add_device_object(tree) s5_object = builder.DefName( "_S5_", builder.DefPackage( builder.PkgLength(), 2, builder.PackageElementList(builder.build_value(5), builder.build_value(0)))) objects.add_object("\\", s5_object) rtvm = False for guest_flag in scenario_etree.xpath( f"//vm[@id='{vm_id}']/guest_flags/guest_flag/text()"): if guest_flag == 'GUEST_FLAG_LAPIC_PASSTHROUGH': rtvm = True break # RTVM cannot set IRQ because no INTR is sent with LAPIC PT if rtvm is False: objects.add_object("\\_SB_", pnp_uart("UAR0", 0, "COM1", 0x3f8, 4)) objects.add_object("\\_SB_", pnp_uart("UAR1", 1, "COM2", 0x2f8, 3)) objects.add_object("\\_SB_", pnp_rtc("RTC0")) amlcode = builder.AMLCode(header, *objects.get_term_list()) with open(dest_path, "wb") as dest: visitor = GenerateBinaryVisitor() binary = bytearray(visitor.generate(amlcode)) # Overwrite length binary[4:8] = len(binary).to_bytes(4, sys.byteorder) # Overwrite checksum checksum = (256 - (sum(binary) % 256)) % 256 binary[9] = checksum dest.write(binary)
def gen_root_pci_bus(path, prt_packages): resources = [] # Bus number cls = rdt.LargeResourceItemWordAddressSpace_factory() length = ctypes.sizeof(cls) data = bytearray(length) res = cls.from_buffer(data) res.type = 1 # Large type res.name = rdt.LARGE_RESOURCE_ITEM_WORD_ADDRESS_SPACE res.length = length - 3 res._TYP = 2 # Bus number range res._DEC = 0 # Positive decoding res._MIF = 1 # Minimum address fixed res._MAF = 1 # Maximum address fixed res.flags = 0 res._MAX = 0xff res._LEN = 0x100 resources.append(data) # The PCI hole below 4G cls = rdt.LargeResourceItemDWordAddressSpace_factory() length = ctypes.sizeof(cls) data = bytearray(length) res = cls.from_buffer(data) res.type = 1 # Large type res.name = rdt.LARGE_RESOURCE_ITEM_ADDRESS_SPACE_RESOURCE res.length = length - 3 res._TYP = 0 # Memory range res._DEC = 0 # Positive decoding res._MIF = 1 # Minimum address fixed res._MAF = 1 # Maximum address fixed res.flags = 1 # read-write, non-cachable, TypeStatic res._MIN = 0x80000000 res._MAX = 0xdfffffff res._LEN = 0x60000000 resources.append(data) # The PCI hole above 4G cls = rdt.LargeResourceItemQWordAddressSpace_factory() length = ctypes.sizeof(cls) data = bytearray(length) res = cls.from_buffer(data) res.type = 1 # Large type res.name = rdt.LARGE_RESOURCE_ITEM_QWORD_ADDRESS_SPACE res.length = length - 3 res._TYP = 0 # Memory range res._DEC = 0 # Positive decoding res._MIF = 1 # Minimum address fixed res._MAF = 1 # Maximum address fixed res.flags = 1 # read-write, non-cachable, TypeStatic res._MIN = 0x4000000000 res._MAX = 0x7fffffffff res._LEN = 0x4000000000 resources.append(data) cls = rdt.LargeResourceItemDWordAddressSpace_factory() length = ctypes.sizeof(cls) data = bytearray(length) res = cls.from_buffer(data) res.type = 1 # Large type res.name = rdt.LARGE_RESOURCE_ITEM_ADDRESS_SPACE_RESOURCE res.length = length - 3 res._TYP = 1 # Memory range res._DEC = 0 # Positive decoding res._MIF = 1 # Minimum address fixed res._MAF = 1 # Maximum address fixed res.flags = 3 # Entire range, TypeStatic res._MIN = 0x0 res._MAX = 0xffff res._LEN = 0x10000 resources.append(data) # End tag resources.append(bytes([0x79, 0])) resource_buf = bytearray().join(resources) checksum = (256 - (sum(resource_buf) % 256)) % 256 resource_buf[-1] = checksum # Device object for the root PCI bus tree = builder.DefDevice( builder.PkgLength(), path, builder.TermList( builder.DefName("_HID", builder.DWordConst(encode_eisa_id("PNP0A08"))), builder.DefName("_CID", builder.DWordConst(encode_eisa_id("PNP0A03"))), builder.DefName("_BBN", builder.ZeroOp()), builder.DefName("_UID", builder.ZeroOp()), builder.DefName( "_CRS", builder.DefBuffer(builder.PkgLength(), builder.WordConst(len(resource_buf)), builder.ByteList(resource_buf))), builder.DefName( "_PRT", builder.DefPackage( builder.PkgLength(), builder.ByteData(len(prt_packages)), builder.PackageElementList(*prt_packages))))) return tree