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