Exemple #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
Exemple #2
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)
Exemple #3
0
    def aux(device_path, obj_name, result):
        # This is the main function that recursively scans dependent object definitions and include either their
        # original definition or calculated values into the AML template. The algorithm is as follows:
        #
        #   1. Collect the objects that are used (either directly or indirectly) by the given object.
        #
        #   2. Determine how this object should go to the AML template by the following rules.
        #
        #      a. If the object depends on any global object (i.e. not in the scope of any device), the object will not
        #         be put to the AML template at all. Up to now we are not aware of any safe way to expose the object to
        #         VMs as global objects can be operation fields within arbitrary memory-mapped regions.
        #
        #      b. If the object depends on any operation field that is exposed to a VM, the object will be copied as is
        #         in the AML template.
        #
        #      c. Otherwise, it will be replaced with the current evaluated value as it is unlikely to change due to
        #         guest software activities.
        #
        # Operation regions and its fields, when necessary, are always copied as is.
        #
        # Dependency among devices are also collected along the way.
        if not obj_name in device_objects[device_path].keys():
            visitor = CollectDependencyVisitor(interpreter)
            deps = visitor.analyze(device_path, obj_name)
            copy_object = False

            if deps.all:
                # If the object refers to any operation region directly or indirectly, it is generally necessary to copy
                # the original definition of the object.
                for dev, decls in deps.all.items():
                    if next(filter(lambda x: isinstance(x, context.OperationFieldDecl) and visitor.is_exposed_field(x), decls), None):
                        copy_object = True
                        break

            evaluated = (result != None)
            need_global = ("global" in deps.all.keys())
            formatter = lambda x: '+' if x else '-'
            logging.info(f"{device_path}.{obj_name}: Evaluated{formatter(evaluated)} Copy{formatter(copy_object)} NeedGlobal{formatter(need_global)}")
            if result == None or copy_object:
                if need_global:
                    global_objs = ', '.join(map(lambda x: x.name, deps.all["global"]))
                    raise NotImplementedError(f"{device_path}.{obj_name}: references to global objects: {global_objs}")

                # Add directly referred objects first
                for peer_device, peer_decls in deps.direct.items():
                    if peer_device == "global":
                        peer_device = device_path

                    for peer_decl in peer_decls:
                        peer_obj_name = peer_decl.name[-4:]
                        if isinstance(peer_decl, context.OperationRegionDecl):
                            aux(peer_device, peer_obj_name, None)
                        elif isinstance(peer_decl, context.OperationFieldDecl):
                            op_region_name = peer_decl.region
                            # Assume an operation region has at most one DefField object defining its fields
                            device_objects[peer_device][f"{op_region_name}_fields"] = peer_decl.parent_tree
                        else:
                            if isinstance(peer_decl, context.MethodDecl) and peer_decl.nargs > 0:
                                raise NotImplementedError(f"{peer_decl.name}: copy of methods with arguments is not supported")
                            value = interpreter.interpret_method_call(peer_decl.name)
                            aux(peer_device, peer_obj_name, value)

                        # If decl is of another device, declare decl as an external symbol in the template of
                        # device_path so that the template can be parsed on its own
                        if peer_device != device_path:
                            device_objects[device_path][peer_decl.name] = builder.DefExternal(
                                peer_decl.name,
                                peer_decl.object_type(),
                                peer_decl.nargs if isinstance(peer_decl, context.MethodDecl) else 0)
                            device_deps[device_path][DEP_TYPE_USES].add(peer_device)
                            device_deps[peer_device][DEP_TYPE_USED_BY].add(device_path)

                decl = interpreter.context.lookup_symbol(obj_name, device_path)
                device_objects[device_path][obj_name] = decl.tree
            else:
                tree = builder.build_value(result)
                if tree:
                    device_objects[device_path][obj_name] = builder.DefName(obj_name, tree)
                else:
                    raise NotImplementedError(f"{device_path}.{obj_name}: unrecognized type: {result.__class__.__name__}")