1# SPDX-License-Identifier: GPL-2.0+ 2# Copyright 2019 Google LLC 3# Written by Simon Glass <sjg@chromium.org> 4# 5# Entry-type module for the ARM Trusted Firmware's Firmware Image Package (FIP) 6# format 7 8from collections import OrderedDict 9 10from binman.entry import Entry 11from binman.etype.section import Entry_section 12from binman.fip_util import FIP_TYPES, FipReader, FipWriter, UUID_LEN 13from dtoc import fdt_util 14from u_boot_pylib import tools 15 16class Entry_atf_fip(Entry_section): 17 """ARM Trusted Firmware's Firmware Image Package (FIP) 18 19 A FIP_ provides a way to group binaries in a firmware image, used by ARM's 20 Trusted Firmware A (TF-A) code. It is a simple format consisting of a 21 table of contents with information about the type, offset and size of the 22 binaries in the FIP. It is quite similar to FMAP, with the major difference 23 that it uses UUIDs to indicate the type of each entry. 24 25 Note: It is recommended to always add an fdtmap to every image, as well as 26 any FIPs so that binman and other tools can access the entire image 27 correctly. 28 29 The UUIDs correspond to useful names in `fiptool`, provided by ATF to 30 operate on FIPs. Binman uses these names to make it easier to understand 31 what is going on, although it is possible to provide a UUID if needed. 32 33 The contents of the FIP are defined by subnodes of the atf-fip entry, e.g.:: 34 35 atf-fip { 36 soc-fw { 37 filename = "bl31.bin"; 38 }; 39 40 scp-fwu-cfg { 41 filename = "bl2u.bin"; 42 }; 43 44 u-boot { 45 fip-type = "nt-fw"; 46 }; 47 }; 48 49 This describes a FIP with three entries: soc-fw, scp-fwu-cfg and nt-fw. 50 You can use normal (non-external) binaries like U-Boot simply by adding a 51 FIP type, with the `fip-type` property, as above. 52 53 Since FIP exists to bring blobs together, Binman assumes that all FIP 54 entries are external binaries. If a binary may not exist, you can use the 55 `--allow-missing` flag to Binman, in which case the image is still created, 56 even though it will not actually work. 57 58 The size of the FIP depends on the size of the binaries. There is currently 59 no way to specify a fixed size. If the `atf-fip` node has a `size` entry, 60 this affects the space taken up by the `atf-fip` entry, but the FIP itself 61 does not expand to use that space. 62 63 Some other FIP features are available with Binman. The header and the 64 entries have 64-bit flag works. The flag flags do not seem to be defined 65 anywhere, but you can use `fip-hdr-flags` and fip-flags` to set the values 66 of the header and entries respectively. 67 68 FIP entries can be aligned to a particular power-of-two boundary. Use 69 fip-align for this. 70 71 Binman only understands the entry types that are included in its 72 implementation. It is possible to specify a 16-byte UUID instead, using the 73 fip-uuid property. In this case Binman doesn't know what its type is, so 74 just uses the UUID. See the `u-boot` node in this example:: 75 76 binman { 77 atf-fip { 78 fip-hdr-flags = /bits/ 64 <0x123>; 79 fip-align = <16>; 80 soc-fw { 81 fip-flags = /bits/ 64 <0x456>; 82 filename = "bl31.bin"; 83 }; 84 85 scp-fwu-cfg { 86 filename = "bl2u.bin"; 87 }; 88 89 u-boot { 90 fip-uuid = [fc 65 13 92 4a 5b 11 ec 91 94 35 ff 2d 1c fc 79 9c]; 92 }; 93 }; 94 fdtmap { 95 }; 96 }; 97 98 Binman allows reading and updating FIP entries after the image is created, 99 provided that an FDPMAP is present too. Updates which change the size of a 100 FIP entry will cause it to be expanded or contracted as needed. 101 102 Properties for top-level atf-fip node 103 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 104 105 fip-hdr-flags (64 bits) 106 Sets the flags for the FIP header. 107 108 Properties for subnodes 109 ~~~~~~~~~~~~~~~~~~~~~~~ 110 111 fip-type (str) 112 FIP type to use for this entry. This is needed if the entry 113 name is not a valid type. Value types are defined in `fip_util.py`. 114 The FIP type defines the UUID that is used (they map 1:1). 115 116 fip-uuid (16 bytes) 117 If there is no FIP-type name defined, or it is not supported by Binman, 118 this property sets the UUID. It should be a 16-byte value, following the 119 hex digits of the UUID. 120 121 fip-flags (64 bits) 122 Set the flags for a FIP entry. Use in one of the subnodes of the 123 7atf-fip entry. 124 125 fip-align 126 Set the alignment for a FIP entry, FIP entries can be aligned to a 127 particular power-of-two boundary. The default is 1. 128 129 Adding new FIP-entry types 130 ~~~~~~~~~~~~~~~~~~~~~~~~~~ 131 132 When new FIP entries are defined by TF-A they appear in the 133 `TF-A source tree`_. You can use `fip_util.py` to update Binman to support 134 new types, then `send a patch`_ to the U-Boot mailing list. There are two 135 source files that the tool examples: 136 137 - `include/tools_share/firmware_image_package.h` has the UUIDs 138 - `tools/fiptool/tbbr_config.c` has the name and descripion for each UUID 139 140 To run the tool:: 141 142 $ tools/binman/fip_util.py -s /path/to/arm-trusted-firmware 143 Warning: UUID 'UUID_NON_TRUSTED_WORLD_KEY_CERT' is not mentioned in tbbr_config.c file 144 Existing code in 'tools/binman/fip_util.py' is up-to-date 145 146 If it shows there is an update, it writes a new version of `fip_util.py` 147 to `fip_util.py.out`. You can change the output file using the `-i` flag. 148 If you have a problem, use `-D` to enable traceback debugging. 149 150 FIP commentary 151 ~~~~~~~~~~~~~~ 152 153 As a side effect of use of UUIDs, FIP does not support multiple 154 entries of the same type, such as might be used to store fonts or graphics 155 icons, for example. For verified boot it could be used for each part of the 156 image (e.g. separate FIPs for A and B) but cannot describe the whole 157 firmware image. As with FMAP there is no hierarchy defined, although FMAP 158 works around this by having 'section' areas which encompass others. A 159 similar workaround would be possible with FIP but is not currently defined. 160 161 It is recommended to always add an fdtmap to every image, as well as any 162 FIPs so that binman and other tools can access the entire image correctly. 163 164 .. _FIP: https://trustedfirmware-a.readthedocs.io/en/latest/design/firmware-design.html#firmware-image-package-fip 165 .. _`TF-A source tree`: https://git.trustedfirmware.org/TF-A/trusted-firmware-a.git 166 .. _`send a patch`: https://www.denx.de/wiki/U-Boot/Patches 167 """ 168 def __init__(self, section, etype, node): 169 # Put this here to allow entry-docs and help to work without libfdt 170 global state 171 from binman import state 172 173 super().__init__(section, etype, node) 174 self.align_default = None 175 self._entries = OrderedDict() 176 self.reader = None 177 178 def ReadNode(self): 179 """Read properties from the atf-fip node""" 180 super().ReadNode() 181 self._pad_byte = fdt_util.GetInt(self._node, 'pad-byte', 0) 182 self._fip_flags = fdt_util.GetInt64(self._node, 'fip-hdr-flags', 0) 183 self._fip_align = fdt_util.GetInt(self._node, 'fip-align', 1) 184 if tools.not_power_of_two(self._fip_align): 185 raise ValueError("Node '%s': FIP alignment %s must be a power of two" % 186 (self._node.path, self._fip_align)) 187 self.ReadEntries() 188 189 def ReadEntries(self): 190 """Read the subnodes to find out what should go in this FIP""" 191 for node in self._node.subnodes: 192 fip_type = None 193 etype = None 194 if node.name in FIP_TYPES: 195 fip_type = node.name 196 etype = 'blob-ext' 197 198 entry = Entry.Create(self, node, etype) 199 entry._fip_uuid = fdt_util.GetBytes(node, 'fip-uuid', UUID_LEN) 200 if not fip_type and not entry._fip_uuid: 201 fip_type = fdt_util.GetString(node, 'fip-type') 202 if not fip_type: 203 self.Raise("Must provide a fip-type (node name '%s' is not a known FIP type)" % 204 node.name) 205 206 entry._fip_type = fip_type 207 entry._fip_flags = fdt_util.GetInt64(node, 'fip-flags', 0) 208 entry.ReadNode() 209 entry._fip_name = node.name 210 self._entries[entry._fip_name] = entry 211 212 def BuildSectionData(self, required): 213 """Override this function to create a custom format for the entries 214 215 Arguments: 216 required (bool): True if the data must be valid, False if it may 217 be missing (entry.GetData() returns None 218 219 Returns: 220 bytes: Data obtained, or None if None 221 """ 222 fip = FipWriter(self._fip_flags, self._fip_align) 223 for entry in self._entries.values(): 224 # First get the input data and put it in an entry. If not available, 225 # try later. 226 entry_data = entry.GetData(required) 227 if not required and entry_data is None: 228 return None 229 fent = fip.add_entry(entry._fip_type or entry._fip_uuid, entry_data, 230 entry._fip_flags) 231 if fent: 232 entry._fip_entry = fent 233 data = fip.get_data() 234 return data 235 236 def SetImagePos(self, image_pos): 237 """Override this function to set all the entry properties from FIP 238 239 We can only do this once image_pos is known 240 241 Args: 242 image_pos: Position of this entry in the image 243 """ 244 super().SetImagePos(image_pos) 245 246 # Now update the entries with info from the FIP entries 247 for entry in self._entries.values(): 248 fent = entry._fip_entry 249 entry.size = fent.size 250 entry.offset = fent.offset 251 entry.image_pos = self.image_pos + entry.offset 252 253 def ReadChildData(self, child, decomp=True, alt_format=None): 254 if not self.reader: 255 self.fip_data = super().ReadData(True) 256 self.reader = FipReader(self.fip_data) 257 reader = self.reader 258 259 # It is tricky to obtain the data from a FIP entry since it is indexed 260 # by its UUID. 261 fent = reader.get_entry(child._fip_type or child._fip_uuid) 262 return fent.data 263 264 # Note: 265 # It is also possible to extract it using the offsets directly, but this 266 # seems less FIP_friendly: 267 # return self.fip_data[child.offset:child.offset + child.size] 268 269 def WriteChildData(self, child): 270 # Recreate the data structure, leaving the data for this child alone, 271 # so that child.data is used to pack into the FIP. 272 self.ObtainContents(skip_entry=child) 273 return super().WriteChildData(child) 274