1 # SPDX-License-Identifier: GPL-2.0+
2 # Copyright 2019 Google LLC
3 # Written by Simon Glass <sjg@chromium.org>
5 # Entry-type module for a Coreboot Filesystem (CBFS)
8 from collections import OrderedDict
10 from binman import cbfs_util
11 from binman.cbfs_util import CbfsWriter
12 from binman.entry import Entry
13 from dtoc import fdt_util
15 class Entry_cbfs(Entry):
16 """Entry containing a Coreboot Filesystem (CBFS)
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.
23 CBFS is used by coreboot as its way of orgnanising SPI-flash contents.
25 The contents of the CBFS are defined by subnodes of the cbfs entry, e.g.:
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:
52 filename = "u-boot.dtb";
54 cbfs-compress = "lz4";
55 cbfs-offset = <0x100000>;
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.
63 Properties supported in the top-level CBFS node:
66 Defaults to "x86", but you can specify the architecture if needed.
69 Properties supported in the CBFS entry subnodes:
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
77 This is the CBFS file type. The following are supported:
80 This is a 'raw' file, although compression is supported. It can be
81 used to store any file in CBFS.
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'
97 You can use your own ELF file with something like:
103 filename = "cbfs-stage.elf";
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.
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.
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.
129 Of course binman can create images containing multiple CBFSs, simply by
130 defining these in the binman config:
157 filename = "image.jpg";
162 This creates an 8MB image with two CBFSs, one at offset 1MB, one at 7MB,
165 def __init__(self, section, etype, node):
166 # Put this here to allow entry-docs and help to work without libfdt
168 from binman import state
170 Entry.__init__(self, section, etype, node)
171 self._cbfs_arg = fdt_util.GetString(node, 'cbfs-arch', 'x86')
172 self._cbfs_entries = OrderedDict()
176 def ObtainContents(self, skip=None):
177 arch = cbfs_util.find_arch(self._cbfs_arg)
179 self.Raise("Invalid architecture '%s'" % self._cbfs_arg)
180 if self.size is None:
181 self.Raise("'cbfs' entry must have a size property")
182 cbfs = CbfsWriter(self.size, arch)
183 for entry in self._cbfs_entries.values():
184 # First get the input data and put it in a file. If not available,
186 if entry != skip and not entry.ObtainContents():
188 data = entry.GetData()
190 if entry._type == 'raw':
191 cfile = cbfs.add_file_raw(entry._cbfs_name, data,
193 entry._cbfs_compress)
194 elif entry._type == 'stage':
195 cfile = cbfs.add_file_stage(entry._cbfs_name, data,
198 entry.Raise("Unknown cbfs-type '%s' (use 'raw', 'stage')" %
201 entry._cbfs_file = cfile
202 data = cbfs.get_data()
203 self.SetContents(data)
206 def _ReadSubnodes(self):
207 """Read the subnodes to find out what should go in this IFWI"""
208 for node in self._node.subnodes:
209 entry = Entry.Create(self, node)
211 entry._cbfs_name = fdt_util.GetString(node, 'cbfs-name', entry.name)
212 entry._type = fdt_util.GetString(node, 'cbfs-type')
213 compress = fdt_util.GetString(node, 'cbfs-compress', 'none')
214 entry._cbfs_offset = fdt_util.GetInt(node, 'cbfs-offset')
215 entry._cbfs_compress = cbfs_util.find_compress(compress)
216 if entry._cbfs_compress is None:
217 self.Raise("Invalid compression in '%s': '%s'" %
218 (node.name, compress))
219 self._cbfs_entries[entry._cbfs_name] = entry
221 def SetImagePos(self, image_pos):
222 """Override this function to set all the entry properties from CBFS
224 We can only do this once image_pos is known
227 image_pos: Position of this entry in the image
229 Entry.SetImagePos(self, image_pos)
231 # Now update the entries with info from the CBFS entries
232 for entry in self._cbfs_entries.values():
233 cfile = entry._cbfs_file
234 entry.size = cfile.data_len
235 entry.offset = cfile.calced_cbfs_offset
236 entry.image_pos = self.image_pos + entry.offset
237 if entry._cbfs_compress:
238 entry.uncomp_size = cfile.memlen
240 def AddMissingProperties(self):
241 Entry.AddMissingProperties(self)
242 for entry in self._cbfs_entries.values():
243 entry.AddMissingProperties()
244 if entry._cbfs_compress:
245 state.AddZeroProp(entry._node, 'uncomp-size')
246 # Store the 'compress' property, since we don't look at
247 # 'cbfs-compress' in Entry.ReadData()
248 state.AddString(entry._node, 'compress',
249 cbfs_util.compress_name(entry._cbfs_compress))
251 def SetCalculatedProperties(self):
252 """Set the value of device-tree properties calculated by binman"""
253 Entry.SetCalculatedProperties(self)
254 for entry in self._cbfs_entries.values():
255 state.SetInt(entry._node, 'offset', entry.offset)
256 state.SetInt(entry._node, 'size', entry.size)
257 state.SetInt(entry._node, 'image-pos', entry.image_pos)
258 if entry.uncomp_size is not None:
259 state.SetInt(entry._node, 'uncomp-size', entry.uncomp_size)
261 def ListEntries(self, entries, indent):
262 """Override this method to list all files in the section"""
263 Entry.ListEntries(self, entries, indent)
264 for entry in self._cbfs_entries.values():
265 entry.ListEntries(entries, indent + 1)
267 def GetEntries(self):
268 return self._cbfs_entries
270 def ReadData(self, decomp=True):
271 data = Entry.ReadData(self, True)
274 def ReadChildData(self, child, decomp=True):
276 data = Entry.ReadData(self, True)
277 self.reader = cbfs_util.CbfsReader(data)
279 cfile = reader.files.get(child.name)
280 return cfile.data if decomp else cfile.orig_data
282 def WriteChildData(self, child):
283 self.ObtainContents(skip=child)