mirror of
https://github.com/brain-hackers/u-boot-brain
synced 2024-08-20 08:58:52 +09:00
![Simon Glass](/assets/img/avatar_default.png)
This comment references the wrong thing. Fix it. Signed-off-by: Simon Glass <sjg@chromium.org> Reviewed-by: Bin Meng <bmeng.cn@gmail.com>
285 lines
10 KiB
Python
285 lines
10 KiB
Python
# SPDX-License-Identifier: GPL-2.0+
|
|
# Copyright 2019 Google LLC
|
|
# Written by Simon Glass <sjg@chromium.org>
|
|
#
|
|
# Entry-type module for a Coreboot Filesystem (CBFS)
|
|
#
|
|
|
|
from collections import OrderedDict
|
|
|
|
from binman import cbfs_util
|
|
from binman.cbfs_util import CbfsWriter
|
|
from binman.entry import Entry
|
|
from dtoc import fdt_util
|
|
|
|
class Entry_cbfs(Entry):
|
|
"""Entry containing a Coreboot Filesystem (CBFS)
|
|
|
|
A CBFS provides a way to group files into a group. It has a simple directory
|
|
structure and allows the position of individual files to be set, since it is
|
|
designed to support execute-in-place in an x86 SPI-flash device. Where XIP
|
|
is not used, it supports compression and storing ELF files.
|
|
|
|
CBFS is used by coreboot as its way of orgnanising SPI-flash contents.
|
|
|
|
The contents of the CBFS are defined by subnodes of the cbfs entry, e.g.:
|
|
|
|
cbfs {
|
|
size = <0x100000>;
|
|
u-boot {
|
|
cbfs-type = "raw";
|
|
};
|
|
u-boot-dtb {
|
|
cbfs-type = "raw";
|
|
};
|
|
};
|
|
|
|
This creates a CBFS 1MB in size two files in it: u-boot.bin and u-boot.dtb.
|
|
Note that the size is required since binman does not support calculating it.
|
|
The contents of each entry is just what binman would normally provide if it
|
|
were not a CBFS node. A blob type can be used to import arbitrary files as
|
|
with the second subnode below:
|
|
|
|
cbfs {
|
|
size = <0x100000>;
|
|
u-boot {
|
|
cbfs-name = "BOOT";
|
|
cbfs-type = "raw";
|
|
};
|
|
|
|
dtb {
|
|
type = "blob";
|
|
filename = "u-boot.dtb";
|
|
cbfs-type = "raw";
|
|
cbfs-compress = "lz4";
|
|
cbfs-offset = <0x100000>;
|
|
};
|
|
};
|
|
|
|
This creates a CBFS 1MB in size with u-boot.bin (named "BOOT") and
|
|
u-boot.dtb (named "dtb") and compressed with the lz4 algorithm.
|
|
|
|
|
|
Properties supported in the top-level CBFS node:
|
|
|
|
cbfs-arch:
|
|
Defaults to "x86", but you can specify the architecture if needed.
|
|
|
|
|
|
Properties supported in the CBFS entry subnodes:
|
|
|
|
cbfs-name:
|
|
This is the name of the file created in CBFS. It defaults to the entry
|
|
name (which is the node name), but you can override it with this
|
|
property.
|
|
|
|
cbfs-type:
|
|
This is the CBFS file type. The following are supported:
|
|
|
|
raw:
|
|
This is a 'raw' file, although compression is supported. It can be
|
|
used to store any file in CBFS.
|
|
|
|
stage:
|
|
This is an ELF file that has been loaded (i.e. mapped to memory), so
|
|
appears in the CBFS as a flat binary. The input file must be an ELF
|
|
image, for example this puts "u-boot" (the ELF image) into a 'stage'
|
|
entry:
|
|
|
|
cbfs {
|
|
size = <0x100000>;
|
|
u-boot-elf {
|
|
cbfs-name = "BOOT";
|
|
cbfs-type = "stage";
|
|
};
|
|
};
|
|
|
|
You can use your own ELF file with something like:
|
|
|
|
cbfs {
|
|
size = <0x100000>;
|
|
something {
|
|
type = "blob";
|
|
filename = "cbfs-stage.elf";
|
|
cbfs-type = "stage";
|
|
};
|
|
};
|
|
|
|
As mentioned, the file is converted to a flat binary, so it is
|
|
equivalent to adding "u-boot.bin", for example, but with the load and
|
|
start addresses specified by the ELF. At present there is no option
|
|
to add a flat binary with a load/start address, similar to the
|
|
'add-flat-binary' option in cbfstool.
|
|
|
|
cbfs-offset:
|
|
This is the offset of the file's data within the CBFS. It is used to
|
|
specify where the file should be placed in cases where a fixed position
|
|
is needed. Typical uses are for code which is not relocatable and must
|
|
execute in-place from a particular address. This works because SPI flash
|
|
is generally mapped into memory on x86 devices. The file header is
|
|
placed before this offset so that the data start lines up exactly with
|
|
the chosen offset. If this property is not provided, then the file is
|
|
placed in the next available spot.
|
|
|
|
The current implementation supports only a subset of CBFS features. It does
|
|
not support other file types (e.g. payload), adding multiple files (like the
|
|
'files' entry with a pattern supported by binman), putting files at a
|
|
particular offset in the CBFS and a few other things.
|
|
|
|
Of course binman can create images containing multiple CBFSs, simply by
|
|
defining these in the binman config:
|
|
|
|
|
|
binman {
|
|
size = <0x800000>;
|
|
cbfs {
|
|
offset = <0x100000>;
|
|
size = <0x100000>;
|
|
u-boot {
|
|
cbfs-type = "raw";
|
|
};
|
|
u-boot-dtb {
|
|
cbfs-type = "raw";
|
|
};
|
|
};
|
|
|
|
cbfs2 {
|
|
offset = <0x700000>;
|
|
size = <0x100000>;
|
|
u-boot {
|
|
cbfs-type = "raw";
|
|
};
|
|
u-boot-dtb {
|
|
cbfs-type = "raw";
|
|
};
|
|
image {
|
|
type = "blob";
|
|
filename = "image.jpg";
|
|
};
|
|
};
|
|
};
|
|
|
|
This creates an 8MB image with two CBFSs, one at offset 1MB, one at 7MB,
|
|
both of size 1MB.
|
|
"""
|
|
def __init__(self, section, etype, node):
|
|
# Put this here to allow entry-docs and help to work without libfdt
|
|
global state
|
|
from binman import state
|
|
|
|
Entry.__init__(self, section, etype, node)
|
|
self._cbfs_arg = fdt_util.GetString(node, 'cbfs-arch', 'x86')
|
|
self._cbfs_entries = OrderedDict()
|
|
self._ReadSubnodes()
|
|
self.reader = None
|
|
|
|
def ObtainContents(self, skip=None):
|
|
arch = cbfs_util.find_arch(self._cbfs_arg)
|
|
if arch is None:
|
|
self.Raise("Invalid architecture '%s'" % self._cbfs_arg)
|
|
if self.size is None:
|
|
self.Raise("'cbfs' entry must have a size property")
|
|
cbfs = CbfsWriter(self.size, arch)
|
|
for entry in self._cbfs_entries.values():
|
|
# First get the input data and put it in a file. If not available,
|
|
# try later.
|
|
if entry != skip and not entry.ObtainContents():
|
|
return False
|
|
data = entry.GetData()
|
|
cfile = None
|
|
if entry._type == 'raw':
|
|
cfile = cbfs.add_file_raw(entry._cbfs_name, data,
|
|
entry._cbfs_offset,
|
|
entry._cbfs_compress)
|
|
elif entry._type == 'stage':
|
|
cfile = cbfs.add_file_stage(entry._cbfs_name, data,
|
|
entry._cbfs_offset)
|
|
else:
|
|
entry.Raise("Unknown cbfs-type '%s' (use 'raw', 'stage')" %
|
|
entry._type)
|
|
if cfile:
|
|
entry._cbfs_file = cfile
|
|
data = cbfs.get_data()
|
|
self.SetContents(data)
|
|
return True
|
|
|
|
def _ReadSubnodes(self):
|
|
"""Read the subnodes to find out what should go in this CBFS"""
|
|
for node in self._node.subnodes:
|
|
entry = Entry.Create(self, node)
|
|
entry.ReadNode()
|
|
entry._cbfs_name = fdt_util.GetString(node, 'cbfs-name', entry.name)
|
|
entry._type = fdt_util.GetString(node, 'cbfs-type')
|
|
compress = fdt_util.GetString(node, 'cbfs-compress', 'none')
|
|
entry._cbfs_offset = fdt_util.GetInt(node, 'cbfs-offset')
|
|
entry._cbfs_compress = cbfs_util.find_compress(compress)
|
|
if entry._cbfs_compress is None:
|
|
self.Raise("Invalid compression in '%s': '%s'" %
|
|
(node.name, compress))
|
|
self._cbfs_entries[entry._cbfs_name] = entry
|
|
|
|
def SetImagePos(self, image_pos):
|
|
"""Override this function to set all the entry properties from CBFS
|
|
|
|
We can only do this once image_pos is known
|
|
|
|
Args:
|
|
image_pos: Position of this entry in the image
|
|
"""
|
|
Entry.SetImagePos(self, image_pos)
|
|
|
|
# Now update the entries with info from the CBFS entries
|
|
for entry in self._cbfs_entries.values():
|
|
cfile = entry._cbfs_file
|
|
entry.size = cfile.data_len
|
|
entry.offset = cfile.calced_cbfs_offset
|
|
entry.image_pos = self.image_pos + entry.offset
|
|
if entry._cbfs_compress:
|
|
entry.uncomp_size = cfile.memlen
|
|
|
|
def AddMissingProperties(self):
|
|
Entry.AddMissingProperties(self)
|
|
for entry in self._cbfs_entries.values():
|
|
entry.AddMissingProperties()
|
|
if entry._cbfs_compress:
|
|
state.AddZeroProp(entry._node, 'uncomp-size')
|
|
# Store the 'compress' property, since we don't look at
|
|
# 'cbfs-compress' in Entry.ReadData()
|
|
state.AddString(entry._node, 'compress',
|
|
cbfs_util.compress_name(entry._cbfs_compress))
|
|
|
|
def SetCalculatedProperties(self):
|
|
"""Set the value of device-tree properties calculated by binman"""
|
|
Entry.SetCalculatedProperties(self)
|
|
for entry in self._cbfs_entries.values():
|
|
state.SetInt(entry._node, 'offset', entry.offset)
|
|
state.SetInt(entry._node, 'size', entry.size)
|
|
state.SetInt(entry._node, 'image-pos', entry.image_pos)
|
|
if entry.uncomp_size is not None:
|
|
state.SetInt(entry._node, 'uncomp-size', entry.uncomp_size)
|
|
|
|
def ListEntries(self, entries, indent):
|
|
"""Override this method to list all files in the section"""
|
|
Entry.ListEntries(self, entries, indent)
|
|
for entry in self._cbfs_entries.values():
|
|
entry.ListEntries(entries, indent + 1)
|
|
|
|
def GetEntries(self):
|
|
return self._cbfs_entries
|
|
|
|
def ReadData(self, decomp=True):
|
|
data = Entry.ReadData(self, True)
|
|
return data
|
|
|
|
def ReadChildData(self, child, decomp=True):
|
|
if not self.reader:
|
|
data = Entry.ReadData(self, True)
|
|
self.reader = cbfs_util.CbfsReader(data)
|
|
reader = self.reader
|
|
cfile = reader.files.get(child.name)
|
|
return cfile.data if decomp else cfile.orig_data
|
|
|
|
def WriteChildData(self, child):
|
|
self.ObtainContents(skip=child)
|
|
return True
|