Esempio n. 1
0
    def _ReadSubnodes(self):
        def _AddNode(base_node, depth, node):
            """Add a node to the FIT

            Args:
                base_node: Base Node of the FIT (with 'description' property)
                depth: Current node depth (0 is the base node)
                node: Current node to process

            There are two cases to deal with:
                - hash and signature nodes which become part of the FIT
                - binman entries which are used to define the 'data' for each
                  image
            """
            for pname, prop in node.props.items():
                if pname.startswith('fit,'):
                    self._fit_props[pname] = prop
                else:
                    fsw.property(pname, prop.bytes)

            rel_path = node.path[len(base_node.path):]
            has_images = depth == 2 and rel_path.startswith('/images/')
            for subnode in node.subnodes:
                if has_images and not (subnode.name.startswith('hash') or
                                       subnode.name.startswith('signature')):
                    # This is a content node. We collect all of these together
                    # and put them in the 'data' property. They do not appear
                    # in the FIT.
                    entry = Entry.Create(self.section, subnode)
                    entry.ReadNode()
                    self._fit_content[rel_path].append(entry)
                else:
                    with fsw.add_node(subnode.name):
                        _AddNode(base_node, depth + 1, subnode)

        # Build a new tree with all nodes and properties starting from the
        # entry node
        fsw = libfdt.FdtSw()
        fsw.finish_reservemap()
        with fsw.add_node(''):
            _AddNode(self._node, 0, self._node)
        fdt = fsw.as_fdt()

        # Pack this new FDT and scan it so we can add the data later
        fdt.pack()
        self._fdt = Fdt.FromData(fdt.as_bytearray())
        self._fdt.Scan()
Esempio n. 2
0
    def _GetFdtmap(self):
        """Build an FDT map from the entries in the current image

        Returns:
            FDT map binary data
        """
        def _AddNode(node):
            """Add a node to the FDT map"""
            for pname, prop in node.props.items():
                fsw.property(pname, prop.bytes)
            for subnode in node.subnodes:
                with fsw.add_node(subnode.name):
                    _AddNode(subnode)

        data = state.GetFdtContents('fdtmap')[1]
        # If we have an fdtmap it means that we are using this as the
        # fdtmap for this image.
        if data is None:
            # Get the FDT data into an Fdt object
            data = state.GetFdtContents()[1]
            infdt = Fdt.FromData(data)
            infdt.Scan()

            # Find the node for the image containing the Fdt-map entry
            path = self.section.GetPath()
            self.Detail("Fdtmap: Using section '%s' (path '%s')" %
                        (self.section.name, path))
            node = infdt.GetNode(path)
            if not node:
                self.Raise("Internal error: Cannot locate node for path '%s'" %
                           path)

            # Build a new tree with all nodes and properties starting from that
            # node
            fsw = libfdt.FdtSw()
            fsw.finish_reservemap()
            with fsw.add_node(''):
                fsw.property_string('image-node', node.name)
                _AddNode(node)
            fdt = fsw.as_fdt()

            # Pack this new FDT and return its contents
            fdt.pack()
            outfdt = Fdt.FromData(fdt.as_bytearray())
            data = outfdt.GetContents()
        data = FDTMAP_MAGIC + tools.GetBytes(0, 8) + data
        return data
Esempio n. 3
0
    def _ReadSubnodes(self):
        def _AddNode(base_node, depth, node):
            """Add a node to the FIT

            Args:
                base_node: Base Node of the FIT (with 'description' property)
                depth: Current node depth (0 is the base node)
                node: Current node to process

            There are two cases to deal with:
                - hash and signature nodes which become part of the FIT
                - binman entries which are used to define the 'data' for each
                  image
            """
            for pname, prop in node.props.items():
                if not pname.startswith('fit,'):
                    if pname == 'default':
                        val = prop.value
                        # Handle the 'default' property
                        if val.startswith('@'):
                            if not self._fdts:
                                continue
                            if not self._fit_default_dt:
                                self.Raise(
                                    "Generated 'default' node requires default-dt entry argument"
                                )
                            if self._fit_default_dt not in self._fdts:
                                self.Raise(
                                    "default-dt entry argument '%s' not found in fdt list: %s"
                                    % (self._fit_default_dt, ', '.join(
                                        self._fdts)))
                            seq = self._fdts.index(self._fit_default_dt)
                            val = val[1:].replace('DEFAULT-SEQ', str(seq + 1))
                            fsw.property_string(pname, val)
                            continue
                    fsw.property(pname, prop.bytes)

            rel_path = node.path[len(base_node.path):]
            in_images = rel_path.startswith('/images')
            has_images = depth == 2 and in_images
            if has_images:
                # This node is a FIT subimage node (e.g. "/images/kernel")
                # containing content nodes. We collect the subimage nodes and
                # section entries for them here to merge the content subnodes
                # together and put the merged contents in the subimage node's
                # 'data' property later.
                entry = Entry.Create(self.section, node, etype='section')
                entry.ReadNode()
                self._fit_sections[rel_path] = entry

            for subnode in node.subnodes:
                if has_images and not (subnode.name.startswith('hash') or
                                       subnode.name.startswith('signature')):
                    # This subnode is a content node not meant to appear in
                    # the FIT (e.g. "/images/kernel/u-boot"), so don't call
                    # fsw.add_node() or _AddNode() for it.
                    pass
                elif subnode.name.startswith('@'):
                    if self._fdts:
                        # Generate notes for each FDT
                        for seq, fdt_fname in enumerate(self._fdts):
                            node_name = subnode.name[1:].replace(
                                'SEQ', str(seq + 1))
                            fname = tools.GetInputFilename(fdt_fname + '.dtb')
                            with fsw.add_node(node_name):
                                for pname, prop in subnode.props.items():
                                    val = prop.bytes.replace(
                                        b'NAME', tools.ToBytes(fdt_fname))
                                    val = val.replace(
                                        b'SEQ', tools.ToBytes(str(seq + 1)))
                                    fsw.property(pname, val)

                                    # Add data for 'fdt' nodes (but not 'config')
                                    if depth == 1 and in_images:
                                        fsw.property('data',
                                                     tools.ReadFile(fname))
                    else:
                        if self._fdts is None:
                            if self._fit_list_prop:
                                self.Raise(
                                    "Generator node requires '%s' entry argument"
                                    % self._fit_list_prop.value)
                            else:
                                self.Raise(
                                    "Generator node requires 'fit,fdt-list' property"
                                )
                else:
                    with fsw.add_node(subnode.name):
                        _AddNode(base_node, depth + 1, subnode)

        # Build a new tree with all nodes and properties starting from the
        # entry node
        fsw = libfdt.FdtSw()
        fsw.finish_reservemap()
        with fsw.add_node(''):
            _AddNode(self._node, 0, self._node)
        fdt = fsw.as_fdt()

        # Pack this new FDT and scan it so we can add the data later
        fdt.pack()
        self._fdt = Fdt.FromData(fdt.as_bytearray())
        self._fdt.Scan()
Esempio n. 4
0
    def _build_input(self):
        """Finish the FIT by adding the 'data' properties to it

        Arguments:
            fdt: FIT to update

        Returns:
            bytes: New fdt contents
        """
        def _process_prop(pname, prop):
            """Process special properties

            Handles properties with generated values. At present the only
            supported property is 'default', i.e. the default device tree in
            the configurations node.

            Args:
                pname (str): Name of property
                prop (Prop): Property to process
            """
            if pname == 'default':
                val = prop.value
                # Handle the 'default' property
                if val.startswith('@'):
                    if not self._fdts:
                        return
                    if not self._fit_default_dt:
                        self.Raise(
                            "Generated 'default' node requires default-dt entry argument"
                        )
                    if self._fit_default_dt not in self._fdts:
                        self.Raise(
                            f"default-dt entry argument '{self._fit_default_dt}' "
                            f"not found in fdt list: {', '.join(self._fdts)}")
                    seq = self._fdts.index(self._fit_default_dt)
                    val = val[1:].replace('DEFAULT-SEQ', str(seq + 1))
                    fsw.property_string(pname, val)
                    return
            elif pname.startswith('fit,'):
                # Ignore these, which are commands for binman to process
                return
            elif pname in ['offset', 'size', 'image-pos']:
                # Don't add binman's calculated properties
                return
            fsw.property(pname, prop.bytes)

        def _gen_fdt_nodes(base_node, node, depth, in_images):
            """Generate FDT nodes

            This creates one node for each member of self._fdts using the
            provided template. If a property value contains 'NAME' it is
            replaced with the filename of the FDT. If a property value contains
            SEQ it is replaced with the node sequence number, where 1 is the
            first.

            Args:
                node (None): Generator node to process
                depth: Current node depth (0 is the base 'fit' node)
                in_images: True if this is inside the 'images' node, so that
                    'data' properties should be generated
            """
            if self._fdts:
                # Generate nodes for each FDT
                for seq, fdt_fname in enumerate(self._fdts):
                    node_name = node.name[1:].replace('SEQ', str(seq + 1))
                    fname = tools.get_input_filename(fdt_fname + '.dtb')
                    with fsw.add_node(node_name):
                        for pname, prop in node.props.items():
                            if pname == 'fit,loadables':
                                val = '\0'.join(self._loadables) + '\0'
                                fsw.property('loadables', val.encode('utf-8'))
                            elif pname == 'fit,operation':
                                pass
                            elif pname.startswith('fit,'):
                                self._raise_subnode(
                                    node, f"Unknown directive '{pname}'")
                            else:
                                val = prop.bytes.replace(
                                    b'NAME', tools.to_bytes(fdt_fname))
                                val = val.replace(b'SEQ',
                                                  tools.to_bytes(str(seq + 1)))
                                fsw.property(pname, val)

                        # Add data for 'images' nodes (but not 'config')
                        if depth == 1 and in_images:
                            fsw.property('data', tools.read_file(fname))

                        for subnode in node.subnodes:
                            with fsw.add_node(subnode.name):
                                _add_node(node, depth + 1, subnode)
            else:
                if self._fdts is None:
                    if self._fit_list_prop:
                        self.Raise(
                            'Generator node requires '
                            f"'{self._fit_list_prop.value}' entry argument")
                    else:
                        self.Raise(
                            "Generator node requires 'fit,fdt-list' property")

        def _gen_split_elf(base_node, node, elf_data, missing):
            """Add nodes for the ELF file, one per group of contiguous segments

            Args:
                base_node (Node): Template node from the binman definition
                node (Node): Node to replace (in the FIT being built)
                data (bytes): ELF-format data to process (may be empty)
                missing (bool): True if any of the data is missing

            """
            # If any pieces are missing, skip this. The missing entries will
            # show an error
            if not missing:
                try:
                    segments, entry = elf.read_loadable_segments(elf_data)
                except ValueError as exc:
                    self._raise_subnode(
                        node, f'Failed to read ELF file: {str(exc)}')
                for (seq, start, data) in segments:
                    node_name = node.name[1:].replace('SEQ', str(seq + 1))
                    with fsw.add_node(node_name):
                        loadables.append(node_name)
                        for pname, prop in node.props.items():
                            if not pname.startswith('fit,'):
                                fsw.property(pname, prop.bytes)
                            elif pname == 'fit,load':
                                fsw.property_u32('load', start)
                            elif pname == 'fit,entry':
                                if seq == 0:
                                    fsw.property_u32('entry', entry)
                            elif pname == 'fit,data':
                                fsw.property('data', bytes(data))
                            elif pname != 'fit,operation':
                                self._raise_subnode(
                                    node, f"Unknown directive '{pname}'")

        def _gen_node(base_node, node, depth, in_images, entry):
            """Generate nodes from a template

            This creates one node for each member of self._fdts using the
            provided template. If a property value contains 'NAME' it is
            replaced with the filename of the FDT. If a property value contains
            SEQ it is replaced with the node sequence number, where 1 is the
            first.

            Args:
                base_node (Node): Base Node of the FIT (with 'description'
                    property)
                node (Node): Generator node to process
                depth (int): Current node depth (0 is the base 'fit' node)
                in_images (bool): True if this is inside the 'images' node, so
                    that 'data' properties should be generated
            """
            oper = self._get_operation(base_node, node)
            if oper == OP_GEN_FDT_NODES:
                _gen_fdt_nodes(base_node, node, depth, in_images)
            elif oper == OP_SPLIT_ELF:
                # Entry_section.ObtainContents() either returns True or
                # raises an exception.
                data = None
                missing_list = []
                entry.ObtainContents()
                entry.Pack(0)
                data = entry.GetData()
                entry.CheckMissing(missing_list)

                _gen_split_elf(base_node, node, data, bool(missing_list))

        def _add_node(base_node, depth, node):
            """Add nodes to the output FIT

            Args:
                base_node (Node): Base Node of the FIT (with 'description'
                    property)
                depth (int): Current node depth (0 is the base 'fit' node)
                node (Node): Current node to process

            There are two cases to deal with:
                - hash and signature nodes which become part of the FIT
                - binman entries which are used to define the 'data' for each
                  image, so don't appear in the FIT
            """
            # Copy over all the relevant properties
            for pname, prop in node.props.items():
                _process_prop(pname, prop)

            rel_path = node.path[len(base_node.path):]
            in_images = rel_path.startswith('/images')

            has_images = depth == 2 and in_images
            if has_images:
                entry = self._priv_entries[rel_path]
                data = entry.GetData()
                fsw.property('data', bytes(data))

            for subnode in node.subnodes:
                subnode_path = f'{rel_path}/{subnode.name}'
                if has_images and not (subnode.name.startswith('hash') or
                                       subnode.name.startswith('signature')):
                    # This subnode is a content node not meant to appear in
                    # the FIT (e.g. "/images/kernel/u-boot"), so don't call
                    # fsw.add_node() or _add_node() for it.
                    pass
                elif self.GetImage().generate and subnode.name.startswith('@'):
                    entry = self._priv_entries.get(subnode_path)
                    _gen_node(base_node, subnode, depth, in_images, entry)
                    # This is a generator (template) entry, so remove it from
                    # the list of entries used by PackEntries(), etc. Otherwise
                    # it will appear in the binman output
                    to_remove.append(subnode_path)
                else:
                    with fsw.add_node(subnode.name):
                        _add_node(base_node, depth + 1, subnode)

        # Build a new tree with all nodes and properties starting from the
        # entry node
        fsw = libfdt.FdtSw()
        fsw.finish_reservemap()
        to_remove = []
        loadables = []
        with fsw.add_node(''):
            _add_node(self._node, 0, self._node)
        self._loadables = loadables
        fdt = fsw.as_fdt()

        # Remove generator entries from the main list
        for path in to_remove:
            if path in self._entries:
                del self._entries[path]

        # Pack this new FDT and scan it so we can add the data later
        fdt.pack()
        data = fdt.as_bytearray()
        return data
Esempio n. 5
0
    def ReadEntries(self):
        def _process_prop(pname, prop):
            """Process special properties

            Handles properties with generated values. At present the only
            supported property is 'default', i.e. the default device tree in
            the configurations node.

            Args:
                pname (str): Name of property
                prop (Prop): Property to process
            """
            if pname == 'default':
                val = prop.value
                # Handle the 'default' property
                if val.startswith('@'):
                    if not self._fdts:
                        return
                    if not self._fit_default_dt:
                        self.Raise(
                            "Generated 'default' node requires default-dt entry argument"
                        )
                    if self._fit_default_dt not in self._fdts:
                        self.Raise(
                            "default-dt entry argument '%s' not found in fdt list: %s"
                            % (self._fit_default_dt, ', '.join(self._fdts)))
                    seq = self._fdts.index(self._fit_default_dt)
                    val = val[1:].replace('DEFAULT-SEQ', str(seq + 1))
                    fsw.property_string(pname, val)
                    return
            fsw.property(pname, prop.bytes)

        def _scan_gen_fdt_nodes(subnode, depth, in_images):
            """Generate FDT nodes

            This creates one node for each member of self._fdts using the
            provided template. If a property value contains 'NAME' it is
            replaced with the filename of the FDT. If a property value contains
            SEQ it is replaced with the node sequence number, where 1 is the
            first.

            Args:
                subnode (None): Generator node to process
                depth: Current node depth (0 is the base 'fit' node)
                in_images: True if this is inside the 'images' node, so that
                    'data' properties should be generated
            """
            if self._fdts:
                # Generate nodes for each FDT
                for seq, fdt_fname in enumerate(self._fdts):
                    node_name = subnode.name[1:].replace('SEQ', str(seq + 1))
                    fname = tools.get_input_filename(fdt_fname + '.dtb')
                    with fsw.add_node(node_name):
                        for pname, prop in subnode.props.items():
                            val = prop.bytes.replace(b'NAME',
                                                     tools.to_bytes(fdt_fname))
                            val = val.replace(b'SEQ',
                                              tools.to_bytes(str(seq + 1)))
                            fsw.property(pname, val)

                        # Add data for 'images' nodes (but not 'config')
                        if depth == 1 and in_images:
                            fsw.property('data', tools.read_file(fname))
            else:
                if self._fdts is None:
                    if self._fit_list_prop:
                        self.Raise(
                            "Generator node requires '%s' entry argument" %
                            self._fit_list_prop.value)
                    else:
                        self.Raise(
                            "Generator node requires 'fit,fdt-list' property")

        def _scan_node(subnode, depth, in_images):
            """Generate nodes from a template

            This creates one node for each member of self._fdts using the
            provided template. If a property value contains 'NAME' it is
            replaced with the filename of the FDT. If a property value contains
            SEQ it is replaced with the node sequence number, where 1 is the
            first.

            Args:
                subnode (None): Generator node to process
                depth: Current node depth (0 is the base 'fit' node)
                in_images: True if this is inside the 'images' node, so that
                    'data' properties should be generated
            """
            oper = self._get_operation(subnode)
            if oper == OP_GEN_FDT_NODES:
                _scan_gen_fdt_nodes(subnode, depth, in_images)

        def _AddNode(base_node, depth, node):
            """Add a node to the FIT

            Args:
                base_node: Base Node of the FIT (with 'description' property)
                depth: Current node depth (0 is the base 'fit' node)
                node: Current node to process

            There are two cases to deal with:
                - hash and signature nodes which become part of the FIT
                - binman entries which are used to define the 'data' for each
                  image
            """
            for pname, prop in node.props.items():
                if not pname.startswith('fit,'):
                    _process_prop(pname, prop)

            rel_path = node.path[len(base_node.path):]
            in_images = rel_path.startswith('/images')
            has_images = depth == 2 and in_images
            if has_images:
                # This node is a FIT subimage node (e.g. "/images/kernel")
                # containing content nodes. We collect the subimage nodes and
                # section entries for them here to merge the content subnodes
                # together and put the merged contents in the subimage node's
                # 'data' property later.
                entry = Entry.Create(self.section, node, etype='section')
                entry.ReadNode()
                # The hash subnodes here are for mkimage, not binman.
                entry.SetUpdateHash(False)
                self._entries[rel_path] = entry

            for subnode in node.subnodes:
                if has_images and not (subnode.name.startswith('hash') or
                                       subnode.name.startswith('signature')):
                    # This subnode is a content node not meant to appear in
                    # the FIT (e.g. "/images/kernel/u-boot"), so don't call
                    # fsw.add_node() or _AddNode() for it.
                    pass
                elif self.GetImage().generate and subnode.name.startswith('@'):
                    _scan_node(subnode, depth, in_images)
                else:
                    with fsw.add_node(subnode.name):
                        _AddNode(base_node, depth + 1, subnode)

        # Build a new tree with all nodes and properties starting from the
        # entry node
        fsw = libfdt.FdtSw()
        fsw.finish_reservemap()
        with fsw.add_node(''):
            _AddNode(self._node, 0, self._node)
        fdt = fsw.as_fdt()

        # Pack this new FDT and scan it so we can add the data later
        fdt.pack()
        self._fdt = Fdt.FromData(fdt.as_bytearray())
        self._fdt.Scan()