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
def SetImagePos(self, image_pos): """Set the position in the image This sets each subentry's offsets, sizes and positions-in-image according to where they ended up in the packed FIT file. Args: image_pos (int): Position of this entry in the image """ super().SetImagePos(image_pos) # If mkimage is missing we'll have empty data, # which will cause a FDT_ERR_BADMAGIC error if self.mkimage in self.missing_bintools: return fdt = Fdt.FromData(self.GetData()) fdt.Scan() for image_name, section in self._entries.items(): path = f"/images/{image_name}" node = fdt.GetNode(path) data_prop = node.props.get("data") data_pos = fdt_util.GetInt(node, "data-position") data_offset = fdt_util.GetInt(node, "data-offset") data_size = fdt_util.GetInt(node, "data-size") # Contents are inside the FIT if data_prop is not None: # GetOffset() returns offset of a fdt_property struct, # which has 3 fdt32_t members before the actual data. offset = data_prop.GetOffset() + 12 size = len(data_prop.bytes) # External offset from the base of the FIT elif data_pos is not None: offset = data_pos size = data_size # External offset from the end of the FIT, not used in binman elif data_offset is not None: # pragma: no cover offset = fdt.GetFdtObj().totalsize() + data_offset size = data_size # This should never happen else: # pragma: no cover self.Raise(f'{path}: missing data properties') section.SetOffsetSize(offset, size) section.SetImagePos(self.image_pos)
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()
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()
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()