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 a Coreboot Filesystem (CBFS) 6# 7 8from collections import OrderedDict 9 10from binman import cbfs_util 11from binman.cbfs_util import CbfsWriter 12from binman.entry import Entry 13from dtoc import fdt_util 14 15# This is imported if needed 16state = None 17 18class Entry_cbfs(Entry): 19 """Coreboot Filesystem (CBFS) 20 21 A CBFS provides a way to group files into a group. It has a simple directory 22 structure and allows the position of individual files to be set, since it is 23 designed to support execute-in-place in an x86 SPI-flash device. Where XIP 24 is not used, it supports compression and storing ELF files. 25 26 CBFS is used by coreboot as its way of orgnanising SPI-flash contents. 27 28 The contents of the CBFS are defined by subnodes of the cbfs entry, e.g.:: 29 30 cbfs { 31 size = <0x100000>; 32 u-boot { 33 cbfs-type = "raw"; 34 }; 35 u-boot-dtb { 36 cbfs-type = "raw"; 37 }; 38 }; 39 40 This creates a CBFS 1MB in size two files in it: u-boot.bin and u-boot.dtb. 41 Note that the size is required since binman does not support calculating it. 42 The contents of each entry is just what binman would normally provide if it 43 were not a CBFS node. A blob type can be used to import arbitrary files as 44 with the second subnode below:: 45 46 cbfs { 47 size = <0x100000>; 48 u-boot { 49 cbfs-name = "BOOT"; 50 cbfs-type = "raw"; 51 }; 52 53 dtb { 54 type = "blob"; 55 filename = "u-boot.dtb"; 56 cbfs-type = "raw"; 57 cbfs-compress = "lz4"; 58 cbfs-offset = <0x100000>; 59 }; 60 }; 61 62 This creates a CBFS 1MB in size with u-boot.bin (named "BOOT") and 63 u-boot.dtb (named "dtb") and compressed with the lz4 algorithm. 64 65 66 Properties supported in the top-level CBFS node: 67 68 cbfs-arch: 69 Defaults to "x86", but you can specify the architecture if needed. 70 71 72 Properties supported in the CBFS entry subnodes: 73 74 cbfs-name: 75 This is the name of the file created in CBFS. It defaults to the entry 76 name (which is the node name), but you can override it with this 77 property. 78 79 cbfs-type: 80 This is the CBFS file type. The following are supported: 81 82 raw: 83 This is a 'raw' file, although compression is supported. It can be 84 used to store any file in CBFS. 85 86 stage: 87 This is an ELF file that has been loaded (i.e. mapped to memory), so 88 appears in the CBFS as a flat binary. The input file must be an ELF 89 image, for example this puts "u-boot" (the ELF image) into a 'stage' 90 entry:: 91 92 cbfs { 93 size = <0x100000>; 94 u-boot-elf { 95 cbfs-name = "BOOT"; 96 cbfs-type = "stage"; 97 }; 98 }; 99 100 You can use your own ELF file with something like:: 101 102 cbfs { 103 size = <0x100000>; 104 something { 105 type = "blob"; 106 filename = "cbfs-stage.elf"; 107 cbfs-type = "stage"; 108 }; 109 }; 110 111 As mentioned, the file is converted to a flat binary, so it is 112 equivalent to adding "u-boot.bin", for example, but with the load and 113 start addresses specified by the ELF. At present there is no option 114 to add a flat binary with a load/start address, similar to the 115 'add-flat-binary' option in cbfstool. 116 117 cbfs-offset: 118 This is the offset of the file's data within the CBFS. It is used to 119 specify where the file should be placed in cases where a fixed position 120 is needed. Typical uses are for code which is not relocatable and must 121 execute in-place from a particular address. This works because SPI flash 122 is generally mapped into memory on x86 devices. The file header is 123 placed before this offset so that the data start lines up exactly with 124 the chosen offset. If this property is not provided, then the file is 125 placed in the next available spot. 126 127 The current implementation supports only a subset of CBFS features. It does 128 not support other file types (e.g. payload), adding multiple files (like the 129 'files' entry with a pattern supported by binman), putting files at a 130 particular offset in the CBFS and a few other things. 131 132 Of course binman can create images containing multiple CBFSs, simply by 133 defining these in the binman config:: 134 135 136 binman { 137 size = <0x800000>; 138 cbfs { 139 offset = <0x100000>; 140 size = <0x100000>; 141 u-boot { 142 cbfs-type = "raw"; 143 }; 144 u-boot-dtb { 145 cbfs-type = "raw"; 146 }; 147 }; 148 149 cbfs2 { 150 offset = <0x700000>; 151 size = <0x100000>; 152 u-boot { 153 cbfs-type = "raw"; 154 }; 155 u-boot-dtb { 156 cbfs-type = "raw"; 157 }; 158 image { 159 type = "blob"; 160 filename = "image.jpg"; 161 }; 162 }; 163 }; 164 165 This creates an 8MB image with two CBFSs, one at offset 1MB, one at 7MB, 166 both of size 1MB. 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._cbfs_arg = fdt_util.GetString(self._node, 'cbfs-arch', 'x86') 182 self.ReadEntries() 183 184 def ReadEntries(self): 185 """Read the subnodes to find out what should go in this CBFS""" 186 for node in self._node.subnodes: 187 entry = Entry.Create(self, node) 188 entry.ReadNode() 189 entry._cbfs_name = fdt_util.GetString(node, 'cbfs-name', entry.name) 190 entry._type = fdt_util.GetString(node, 'cbfs-type') 191 compress = fdt_util.GetString(node, 'cbfs-compress', 'none') 192 entry._cbfs_offset = fdt_util.GetInt(node, 'cbfs-offset') 193 entry._cbfs_compress = cbfs_util.find_compress(compress) 194 if entry._cbfs_compress is None: 195 self.Raise("Invalid compression in '%s': '%s'" % 196 (node.name, compress)) 197 self._entries[entry._cbfs_name] = entry 198 199 def ObtainCfile(self, cbfs, entry): 200 # First get the input data and put it in a file. If not available, 201 # try later. 202 data = entry.GetData() 203 cfile = None 204 if entry._type == 'raw': 205 cfile = cbfs.add_file_raw(entry._cbfs_name, data, 206 entry._cbfs_offset, 207 entry._cbfs_compress) 208 elif entry._type == 'stage': 209 cfile = cbfs.add_file_stage(entry._cbfs_name, data, 210 entry._cbfs_offset) 211 else: 212 entry.Raise("Unknown cbfs-type '%s' (use 'raw', 'stage')" % 213 entry._type) 214 return cfile 215 216 def ObtainContents(self, skip_entry=None): 217 arch = cbfs_util.find_arch(self._cbfs_arg) 218 if arch is None: 219 self.Raise("Invalid architecture '%s'" % self._cbfs_arg) 220 if self.size is None: 221 self.Raise("'cbfs' entry must have a size property") 222 cbfs = CbfsWriter(self.size, arch) 223 for entry in self._entries.values(): 224 if entry != skip_entry and not entry.ObtainContents(): 225 return False 226 cfile = self.ObtainCfile(cbfs, entry) 227 if cfile: 228 entry._cbfs_file = cfile 229 data = cbfs.get_data() 230 self.SetContents(data) 231 return True 232 233 def SetImagePos(self, image_pos): 234 """Override this function to set all the entry properties from CBFS 235 236 We can only do this once image_pos is known 237 238 Args: 239 image_pos: Position of this entry in the image 240 """ 241 super().SetImagePos(image_pos) 242 243 # Now update the entries with info from the CBFS entries 244 for entry in self._entries.values(): 245 cfile = entry._cbfs_file 246 entry.size = cfile.data_len 247 entry.offset = cfile.calced_cbfs_offset 248 entry.image_pos = self.image_pos + entry.offset 249 if entry._cbfs_compress: 250 entry.uncomp_size = cfile.memlen 251 252 def AddMissingProperties(self, have_image_pos): 253 super().AddMissingProperties(have_image_pos) 254 for entry in self._entries.values(): 255 entry.AddMissingProperties(have_image_pos) 256 if entry._cbfs_compress: 257 state.AddZeroProp(entry._node, 'uncomp-size') 258 # Store the 'compress' property, since we don't look at 259 # 'cbfs-compress' in Entry.ReadData() 260 state.AddString(entry._node, 'compress', 261 cbfs_util.compress_name(entry._cbfs_compress)) 262 263 def SetCalculatedProperties(self): 264 """Set the value of device-tree properties calculated by binman""" 265 super().SetCalculatedProperties() 266 for entry in self._entries.values(): 267 state.SetInt(entry._node, 'offset', entry.offset) 268 state.SetInt(entry._node, 'size', entry.size) 269 state.SetInt(entry._node, 'image-pos', entry.image_pos) 270 if entry.uncomp_size is not None: 271 state.SetInt(entry._node, 'uncomp-size', entry.uncomp_size) 272 273 def ListEntries(self, entries, indent): 274 """Override this method to list all files in the section""" 275 super().ListEntries(entries, indent) 276 for entry in self._entries.values(): 277 entry.ListEntries(entries, indent + 1) 278 279 def GetEntries(self): 280 return self._entries 281 282 def ReadData(self, decomp=True, alt_format=None): 283 data = super().ReadData(True, alt_format) 284 return data 285 286 def ReadChildData(self, child, decomp=True, alt_format=None): 287 if not self.reader: 288 data = super().ReadData(True, alt_format) 289 self.reader = cbfs_util.CbfsReader(data) 290 reader = self.reader 291 cfile = reader.files.get(child.name) 292 return cfile.data if decomp else cfile.orig_data 293 294 def WriteChildData(self, child): 295 # Recreate the data structure, leaving the data for this child alone, 296 # so that child.data is used to pack into the FIP. 297 self.ObtainContents(skip_entry=child) 298 return super().WriteChildData(child) 299 300 def AddBintools(self, btools): 301 super().AddBintools(btools) 302 for entry in self._entries.values(): 303 entry.AddBintools(btools) 304