Beispiel #1
0
def pnp_uart(path, uid, ddn, port, irq):
    resources = []

    cls = rdt.SmallResourceItemIOPort
    length = ctypes.sizeof(cls)
    data = bytearray(length)
    res = cls.from_buffer(data)
    res.type = 0
    res.name = rdt.SMALL_RESOURCE_ITEM_IO_PORT
    res.length = 7
    res._DEC = 1
    res._MIN = port
    res._MAX = port
    res._ALN = 1
    res._LEN = 8
    resources.append(data)

    cls = rdt.SmallResourceItemIRQ_factory(2)
    length = ctypes.sizeof(cls)
    data = bytearray(length)
    res = cls.from_buffer(data)
    res.type = 0
    res.name = rdt.SMALL_RESOURCE_ITEM_IRQ_FORMAT
    res.length = 2
    res._INT = 1 << irq
    resources.append(data)

    resources.append(bytes([0x79, 0]))

    resource_buf = bytearray().join(resources)
    checksum = (256 - (sum(resource_buf) % 256)) % 256
    resource_buf[-1] = checksum
    uart = builder.DefDevice(
        builder.PkgLength(),
        path,
        builder.TermList(
            builder.DefName(
                "_HID",
                builder.DWordConst(encode_eisa_id("PNP0501"))),
            builder.DefName(
                "_UID",
                builder.build_value(uid)),
            builder.DefName(
                "_DDN",
                builder.String(ddn)),
            builder.DefName(
                "_CRS",
                builder.DefBuffer(
                    builder.PkgLength(),
                    builder.WordConst(len(resource_buf)),
                    builder.ByteList(resource_buf)))))

    return uart
Beispiel #2
0
 def __get_scope_contents(self, termlist, path):
     scopes = [i for i in path.lstrip("\\").split(".") if i]
     ret = termlist
     for scope in scopes:
         for term in ret:
             if term.label in ["DefScope", "DefDevice"] and term.NameString.value == scope:
                 ret = term.TermList.children
                 break
         else:
             tree = builder.DefScope(builder.PkgLength(), scope, builder.TermList())
             ret.append(tree)
             ret = tree.TermList.children
     return ret
Beispiel #3
0
def extract(args, board_etree):
    devices_node = get_node(board_etree, "//devices")

    try:
        namespace = parse_dsdt()
    except Exception as e:
        logging.warning(f"Parse ACPI DSDT/SSDT failed: {str(e)}")
        logging.warning(f"Will not extract information from ACPI DSDT/SSDT")
        return

    interpreter = ConcreteInterpreter(namespace)

    # With IOAPIC, Linux kernel will choose APIC mode as the IRQ model. Evalaute the \_PIC method (if exists) to inform the ACPI
    # namespace of this.
    try:
        interpreter.interpret_method_call("\\_PIC", 1)
    except:
        logging.info(f"\\_PIC is not evaluated.")

    for device in sorted(namespace.devices, key=lambda x:x.name):
        try:
            fetch_device_info(devices_node, interpreter, device.name, args)
        except Exception as e:
            logging.info(f"Fetch information about device object {device.name} failed: {str(e)}")

    visitor = GenerateBinaryVisitor()
    for dev, objs in device_objects.items():
        element = get_node(devices_node, f"//device[acpi_object='{dev}']")
        if element is not None:
            tree = builder.DefDevice(
                builder.PkgLength(),
                dev,
                builder.TermList(*list(objs.values())))
            add_child(element, "aml_template", visitor.generate(tree).hex())

    for dev, deps in device_deps.items():
        element = get_node(devices_node, f"//device[acpi_object='{dev}']")
        if element is not None:
            for kind, targets in deps.items():
                for target in targets:
                    if dev != target:
                        add_child(element, "dependency", target, type=kind)
Beispiel #4
0
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)
Beispiel #5
0
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