From ed16b12576ad386d3b22257b7399cd9eb01c69b4 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Tue, 23 Nov 2021 21:08:58 -0700 Subject: [PATCH 1/8] binman: Add a utility module for ATF FIP Add support for this format which is used by ARM Trusted Firmware to find firmware binaries to load. FIP is like a simpler version of FMAP but uses a UUID instead of a name, for each entry. It supports reading a FIP, writing a FIP and parsing the ATF source code to get a list of supported UUIDs. Signed-off-by: Simon Glass --- tools/binman/fip_util.py | 653 ++++++++++++++++++++++++++++++++++ tools/binman/fip_util_test.py | 405 +++++++++++++++++++++ tools/binman/main.py | 4 +- 3 files changed, 1061 insertions(+), 1 deletion(-) create mode 100755 tools/binman/fip_util.py create mode 100755 tools/binman/fip_util_test.py diff --git a/tools/binman/fip_util.py b/tools/binman/fip_util.py new file mode 100755 index 0000000000..5f7dbc04d5 --- /dev/null +++ b/tools/binman/fip_util.py @@ -0,0 +1,653 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-2.0+ +# Copyright 2021 Google LLC +# Written by Simon Glass + +"""Support for ARM's Firmware Image Package (FIP) format + +FIP is a format similar to FMAP[1] but with fewer features and an obscure UUID +instead of the region name. + +It consists of a header and a table of entries, each pointing to a place in the +firmware image where something can be found. + +[1] https://chromium.googlesource.com/chromiumos/third_party/flashmap/+/refs/heads/master/lib/fmap.h + +If ATF updates, run this program to update the FIT_TYPE_LIST. + +ARM Trusted Firmware is available at: + +https://github.com/ARM-software/arm-trusted-firmware.git +""" + +from argparse import ArgumentParser +import collections +import io +import os +import re +import struct +import sys +from uuid import UUID + +OUR_FILE = os.path.realpath(__file__) +OUR_PATH = os.path.dirname(OUR_FILE) + +# Bring in the patman and dtoc libraries (but don't override the first path +# in PYTHONPATH) +sys.path.insert(2, os.path.join(OUR_PATH, '..')) + +# pylint: disable=C0413 +from patman import command +from patman import tools + +# The TOC header, at the start of the FIP +HEADER_FORMAT = ' + +"""Tests for fip_util + +This tests a few features of fip_util which are not covered by binman's ftest.py +""" + +import os +import shutil +import sys +import tempfile +import unittest + +# Bring in the patman and dtoc libraries (but don't override the first path +# in PYTHONPATH) +OUR_PATH = os.path.dirname(os.path.realpath(__file__)) +sys.path.insert(2, os.path.join(OUR_PATH, '..')) + +# pylint: disable=C0413 +from patman import test_util +from patman import tools +import fip_util + +HAVE_FIPTOOL = True +try: + tools.Run('which', 'fiptool') +except ValueError: + HAVE_FIPTOOL = False + +# pylint: disable=R0902,R0904 +class TestFip(unittest.TestCase): + """Test of fip_util classes""" + #pylint: disable=W0212 + def setUp(self): + # Create a temporary directory for test files + self._indir = tempfile.mkdtemp(prefix='fip_util.') + tools.SetInputDirs([self._indir]) + + # Set up a temporary output directory, used by the tools library when + # compressing files + tools.PrepareOutputDir(None) + + self.src_file = os.path.join(self._indir, 'orig.py') + self.outname = tools.GetOutputFilename('out.py') + self.args = ['-D', '-s', self._indir, '-o', self.outname] + self.readme = os.path.join(self._indir, 'readme.rst') + self.macro_dir = os.path.join(self._indir, 'include/tools_share') + self.macro_fname = os.path.join(self.macro_dir, + 'firmware_image_package.h') + self.name_dir = os.path.join(self._indir, 'tools/fiptool') + self.name_fname = os.path.join(self.name_dir, 'tbbr_config.c') + + macro_contents = ''' + +/* ToC Entry UUIDs */ +#define UUID_TRUSTED_UPDATE_FIRMWARE_SCP_BL2U \\ + {{0x65, 0x92, 0x27, 0x03}, {0x2f, 0x74}, {0xe6, 0x44}, 0x8d, 0xff, {0x57, 0x9a, 0xc1, 0xff, 0x06, 0x10} } +#define UUID_TRUSTED_UPDATE_FIRMWARE_BL2U \\ + {{0x60, 0xb3, 0xeb, 0x37}, {0xc1, 0xe5}, {0xea, 0x41}, 0x9d, 0xf3, {0x19, 0xed, 0xa1, 0x1f, 0x68, 0x01} } + +''' + + name_contents = ''' + +toc_entry_t toc_entries[] = { + { + .name = "SCP Firmware Updater Configuration FWU SCP_BL2U", + .uuid = UUID_TRUSTED_UPDATE_FIRMWARE_SCP_BL2U, + .cmdline_name = "scp-fwu-cfg" + }, + { + .name = "AP Firmware Updater Configuration BL2U", + .uuid = UUID_TRUSTED_UPDATE_FIRMWARE_BL2U, + .cmdline_name = "ap-fwu-cfg" + }, +''' + + def setup_readme(self): + """Set up the readme.txt file""" + tools.WriteFile(self.readme, 'Trusted Firmware-A\n==================', + binary=False) + + def setup_macro(self, data=macro_contents): + """Set up the tbbr_config.c file""" + os.makedirs(self.macro_dir) + tools.WriteFile(self.macro_fname, data, binary=False) + + def setup_name(self, data=name_contents): + """Set up the firmware_image_package.h file""" + os.makedirs(self.name_dir) + tools.WriteFile(self.name_fname, data, binary=False) + + def tearDown(self): + """Remove the temporary input directory and its contents""" + if self._indir: + shutil.rmtree(self._indir) + self._indir = None + tools.FinaliseOutputDir() + + def test_no_readme(self): + """Test handling of a missing readme.rst""" + with self.assertRaises(Exception) as err: + fip_util.main(self.args, self.src_file) + self.assertIn('Expected file', str(err.exception)) + + def test_invalid_readme(self): + """Test that an invalid readme.rst is detected""" + tools.WriteFile(self.readme, 'blah', binary=False) + with self.assertRaises(Exception) as err: + fip_util.main(self.args, self.src_file) + self.assertIn('does not start with', str(err.exception)) + + def test_no_fip_h(self): + """Check handling of missing firmware_image_package.h""" + self.setup_readme() + with self.assertRaises(Exception) as err: + fip_util.main(self.args, self.src_file) + self.assertIn('No such file or directory', str(err.exception)) + + def test_invalid_fip_h(self): + """Check failure to parse firmware_image_package.h""" + self.setup_readme() + self.setup_macro('blah') + with self.assertRaises(Exception) as err: + fip_util.main(self.args, self.src_file) + self.assertIn('Cannot parse file', str(err.exception)) + + def test_parse_fip_h(self): + """Check parsing of firmware_image_package.h""" + self.setup_readme() + # Check parsing the header file + self.setup_macro() + macros = fip_util.parse_macros(self._indir) + expected_macros = { + 'UUID_TRUSTED_UPDATE_FIRMWARE_SCP_BL2U': + ('ToC Entry UUIDs', 'UUID_TRUSTED_UPDATE_FIRMWARE_SCP_BL2U', + bytes([0x65, 0x92, 0x27, 0x03, 0x2f, 0x74, 0xe6, 0x44, + 0x8d, 0xff, 0x57, 0x9a, 0xc1, 0xff, 0x06, 0x10])), + 'UUID_TRUSTED_UPDATE_FIRMWARE_BL2U': + ('ToC Entry UUIDs', 'UUID_TRUSTED_UPDATE_FIRMWARE_BL2U', + bytes([0x60, 0xb3, 0xeb, 0x37, 0xc1, 0xe5, 0xea, 0x41, + 0x9d, 0xf3, 0x19, 0xed, 0xa1, 0x1f, 0x68, 0x01])), + } + self.assertEqual(expected_macros, macros) + + def test_missing_tbbr_c(self): + """Check handlinh of missing tbbr_config.c""" + self.setup_readme() + self.setup_macro() + + # Still need the .c file + with self.assertRaises(Exception) as err: + fip_util.main(self.args, self.src_file) + self.assertIn('tbbr_config.c', str(err.exception)) + + def test_invalid_tbbr_c(self): + """Check failure to parse tbbr_config.c""" + self.setup_readme() + self.setup_macro() + # Check invalid format for C file + self.setup_name('blah') + with self.assertRaises(Exception) as err: + fip_util.main(self.args, self.src_file) + self.assertIn('Cannot parse file', str(err.exception)) + + def test_inconsistent_tbbr_c(self): + """Check tbbr_config.c in a format we don't expect""" + self.setup_readme() + # This is missing a hex value + self.setup_macro(''' + +/* ToC Entry UUIDs */ +#define UUID_TRUSTED_UPDATE_FIRMWARE_SCP_BL2U \\ + {{0x65, 0x92, 0x27,}, {0x2f, 0x74}, {0xe6, 0x44}, 0x8d, 0xff, {0x57, 0x9a, 0xc1, 0xff, 0x06, 0x10} } +#define UUID_TRUSTED_UPDATE_FIRMWARE_BL2U \\ + {{0x60, 0xb3, 0xeb, 0x37}, {0xc1, 0xe5}, {0xea, 0x41}, 0x9d, 0xf3, {0x19, 0xed, 0xa1, 0x1f, 0x68, 0x01} } + +''') + # Check invalid format for C file + self.setup_name('blah') + with self.assertRaises(Exception) as err: + fip_util.main(self.args, self.src_file) + self.assertIn('Cannot parse UUID line 5', str(err.exception)) + + def test_parse_tbbr_c(self): + """Check parsing tbbr_config.c""" + self.setup_readme() + self.setup_macro() + self.setup_name() + + names = fip_util.parse_names(self._indir) + + expected_names = { + 'UUID_TRUSTED_UPDATE_FIRMWARE_SCP_BL2U': ( + 'SCP Firmware Updater Configuration FWU SCP_BL2U', + 'UUID_TRUSTED_UPDATE_FIRMWARE_SCP_BL2U', + 'scp-fwu-cfg'), + 'UUID_TRUSTED_UPDATE_FIRMWARE_BL2U': ( + 'AP Firmware Updater Configuration BL2U', + 'UUID_TRUSTED_UPDATE_FIRMWARE_BL2U', + 'ap-fwu-cfg'), + } + self.assertEqual(expected_names, names) + + def test_uuid_not_in_tbbr_config_c(self): + """Check handling a UUID in the header file that's not in the .c file""" + self.setup_readme() + self.setup_macro(self.macro_contents + ''' +#define UUID_TRUSTED_OS_FW_KEY_CERT \\ + {{0x94, 0x77, 0xd6, 0x03}, {0xfb, 0x60}, {0xe4, 0x11}, 0x85, 0xdd, {0xb7, 0x10, 0x5b, 0x8c, 0xee, 0x04} } + +''') + self.setup_name() + + macros = fip_util.parse_macros(self._indir) + names = fip_util.parse_names(self._indir) + with test_util.capture_sys_output() as (stdout, _): + fip_util.create_code_output(macros, names) + self.assertIn( + "UUID 'UUID_TRUSTED_OS_FW_KEY_CERT' is not mentioned in tbbr_config.c file", + stdout.getvalue()) + + def test_changes(self): + """Check handling of a source file that does/doesn't need changes""" + self.setup_readme() + self.setup_macro() + self.setup_name() + + # Check generating the file when changes are needed + tools.WriteFile(self.src_file, ''' + +# This is taken from tbbr_config.c in ARM Trusted Firmware +FIP_TYPE_LIST = [ + # ToC Entry UUIDs + FipType('scp-fwu-cfg', 'SCP Firmware Updater Configuration FWU SCP_BL2U', + [0x65, 0x92, 0x27, 0x03, 0x2f, 0x74, 0xe6, 0x44, + 0x8d, 0xff, 0x57, 0x9a, 0xc1, 0xff, 0x06, 0x10]), + ] # end +blah de blah + ''', binary=False) + with test_util.capture_sys_output() as (stdout, _): + fip_util.main(self.args, self.src_file) + self.assertIn('Needs update', stdout.getvalue()) + + # Check generating the file when no changes are needed + tools.WriteFile(self.src_file, ''' +# This is taken from tbbr_config.c in ARM Trusted Firmware +FIP_TYPE_LIST = [ + # ToC Entry UUIDs + FipType('scp-fwu-cfg', 'SCP Firmware Updater Configuration FWU SCP_BL2U', + [0x65, 0x92, 0x27, 0x03, 0x2f, 0x74, 0xe6, 0x44, + 0x8d, 0xff, 0x57, 0x9a, 0xc1, 0xff, 0x06, 0x10]), + FipType('ap-fwu-cfg', 'AP Firmware Updater Configuration BL2U', + [0x60, 0xb3, 0xeb, 0x37, 0xc1, 0xe5, 0xea, 0x41, + 0x9d, 0xf3, 0x19, 0xed, 0xa1, 0x1f, 0x68, 0x01]), + ] # end +blah blah''', binary=False) + with test_util.capture_sys_output() as (stdout, _): + fip_util.main(self.args, self.src_file) + self.assertIn('is up-to-date', stdout.getvalue()) + + def test_no_debug(self): + """Test running without the -D flag""" + self.setup_readme() + self.setup_macro() + self.setup_name() + + args = self.args.copy() + args.remove('-D') + tools.WriteFile(self.src_file, '', binary=False) + with test_util.capture_sys_output(): + fip_util.main(args, self.src_file) + + @unittest.skipIf(not HAVE_FIPTOOL, 'No fiptool available') + def test_fiptool_list(self): + """Create a FIP and check that fiptool can read it""" + fwu = b'my data' + tb_fw = b'some more data' + fip = fip_util.FipWriter(0x123, 0x10) + fip.add_entry('fwu', fwu, 0x456) + fip.add_entry('tb-fw', tb_fw, 0) + fip.add_entry(bytes(range(16)), tb_fw, 0) + data = fip.get_data() + fname = tools.GetOutputFilename('data.fip') + tools.WriteFile(fname, data) + result = fip_util.fiptool('info', fname) + self.assertEqual( + '''Firmware Updater NS_BL2U: offset=0xB0, size=0x7, cmdline="--fwu" +Trusted Boot Firmware BL2: offset=0xC0, size=0xE, cmdline="--tb-fw" +00010203-0405-0607-0809-0A0B0C0D0E0F: offset=0xD0, size=0xE, cmdline="--blob" +''', + result.stdout) + + fwu_data = b'my data' + tb_fw_data = b'some more data' + other_fw_data = b'even more' + + def create_fiptool_image(self): + """Create an image with fiptool which we can use for testing + + Returns: + FipReader: reader for the image + """ + fwu = os.path.join(self._indir, 'fwu') + tools.WriteFile(fwu, self.fwu_data) + + tb_fw = os.path.join(self._indir, 'tb_fw') + tools.WriteFile(tb_fw, self.tb_fw_data) + + other_fw = os.path.join(self._indir, 'other_fw') + tools.WriteFile(other_fw, self.other_fw_data) + + fname = tools.GetOutputFilename('data.fip') + uuid = 'e3b78d9e-4a64-11ec-b45c-fba2b9b49788' + fip_util.fiptool('create', '--align', '8', '--plat-toc-flags', '0x123', + '--fwu', fwu, + '--tb-fw', tb_fw, + '--blob', f'uuid={uuid},file={other_fw}', + fname) + + return fip_util.FipReader(tools.ReadFile(fname)) + + @unittest.skipIf(not HAVE_FIPTOOL, 'No fiptool available') + def test_fiptool_create(self): + """Create a FIP with fiptool and check that fip_util can read it""" + reader = self.create_fiptool_image() + + header = reader.header + fents = reader.fents + + self.assertEqual(0x123 << 32, header.flags) + self.assertEqual(fip_util.HEADER_MAGIC, header.name) + self.assertEqual(fip_util.HEADER_SERIAL, header.serial) + + self.assertEqual(3, len(fents)) + fent = fents[0] + self.assertEqual( + bytes([0x4f, 0x51, 0x1d, 0x11, 0x2b, 0xe5, 0x4e, 0x49, + 0xb4, 0xc5, 0x83, 0xc2, 0xf7, 0x15, 0x84, 0x0a]), fent.uuid) + self.assertEqual(0xb0, fent.offset) + self.assertEqual(len(self.fwu_data), fent.size) + self.assertEqual(0, fent.flags) + self.assertEqual(self.fwu_data, fent.data) + + fent = fents[1] + self.assertEqual( + bytes([0x5f, 0xf9, 0xec, 0x0b, 0x4d, 0x22, 0x3e, 0x4d, + 0xa5, 0x44, 0xc3, 0x9d, 0x81, 0xc7, 0x3f, 0x0a]), fent.uuid) + self.assertEqual(0xb8, fent.offset) + self.assertEqual(len(self.tb_fw_data), fent.size) + self.assertEqual(0, fent.flags) + self.assertEqual(self.tb_fw_data, fent.data) + + fent = fents[2] + self.assertEqual( + bytes([0xe3, 0xb7, 0x8d, 0x9e, 0x4a, 0x64, 0x11, 0xec, + 0xb4, 0x5c, 0xfb, 0xa2, 0xb9, 0xb4, 0x97, 0x88]), fent.uuid) + self.assertEqual(0xc8, fent.offset) + self.assertEqual(len(self.other_fw_data), fent.size) + self.assertEqual(0, fent.flags) + self.assertEqual(self.other_fw_data, fent.data) + + @unittest.skipIf(not HAVE_FIPTOOL, 'No fiptool available') + def test_reader_get_entry(self): + """Test get_entry() by name and UUID""" + reader = self.create_fiptool_image() + fents = reader.fents + fent = reader.get_entry('fwu') + self.assertEqual(fent, fents[0]) + + fent = reader.get_entry( + bytes([0x5f, 0xf9, 0xec, 0x0b, 0x4d, 0x22, 0x3e, 0x4d, + 0xa5, 0x44, 0xc3, 0x9d, 0x81, 0xc7, 0x3f, 0x0a])) + self.assertEqual(fent, fents[1]) + + # Try finding entries that don't exist + with self.assertRaises(Exception) as err: + fent = reader.get_entry('scp-fwu-cfg') + self.assertIn("Cannot find FIP entry 'scp-fwu-cfg'", str(err.exception)) + + with self.assertRaises(Exception) as err: + fent = reader.get_entry(bytes(list(range(16)))) + self.assertIn( + "Cannot find FIP entry '00010203-0405-0607-0809-0a0b0c0d0e0f'", + str(err.exception)) + + with self.assertRaises(Exception) as err: + fent = reader.get_entry('blah') + self.assertIn("Unknown FIP entry type 'blah'", str(err.exception)) + + @unittest.skipIf(not HAVE_FIPTOOL, 'No fiptool available') + def test_fiptool_errors(self): + """Check some error reporting from fiptool""" + with self.assertRaises(Exception) as err: + with test_util.capture_sys_output(): + fip_util.fiptool('create', '--fred') + self.assertIn("Failed to run (error 1): 'fiptool create --fred'", + str(err.exception)) + + +if __name__ == '__main__': + unittest.main() diff --git a/tools/binman/main.py b/tools/binman/main.py index 8c1e478d54..1a639f43e9 100755 --- a/tools/binman/main.py +++ b/tools/binman/main.py @@ -59,6 +59,7 @@ def RunTests(debug, verbosity, processes, test_preserve_dirs, args, toolpath): from binman import elf_test from binman import entry_test from binman import fdt_test + from binman import fip_util_test from binman import ftest from binman import image_test import doctest @@ -72,7 +73,8 @@ def RunTests(debug, verbosity, processes, test_preserve_dirs, args, toolpath): result, debug, verbosity, test_preserve_dirs, processes, test_name, toolpath, [entry_test.TestEntry, ftest.TestFunctional, fdt_test.TestFdt, - elf_test.TestElf, image_test.TestImage, cbfs_util_test.TestCbfs]) + elf_test.TestElf, image_test.TestImage, cbfs_util_test.TestCbfs, + fip_util_test.TestFip]) return test_util.ReportResult('binman', test_name, result) From 75989727601b8fe6244314f90bc6f9ef3503b590 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Tue, 23 Nov 2021 21:08:59 -0700 Subject: [PATCH 2/8] binman: Add support for ATF FIP This format is used in firmware binaries so we may as well supported it. With this patch binman supports creating, listing and updating FIPs, as well as extracting files from one, provided that an FDTMAP is also present somewhere in the image. Signed-off-by: Simon Glass --- tools/binman/entries.rst | 154 +++++++++++++ tools/binman/etype/atf_fip.py | 273 +++++++++++++++++++++++ tools/binman/ftest.py | 217 ++++++++++++++++++ tools/binman/test/203_fip.dts | 21 ++ tools/binman/test/204_fip_other.dts | 22 ++ tools/binman/test/205_fip_no_type.dts | 15 ++ tools/binman/test/206_fip_uuid.dts | 22 ++ tools/binman/test/207_fip_ls.dts | 25 +++ tools/binman/test/208_fip_replace.dts | 33 +++ tools/binman/test/209_fip_missing.dts | 19 ++ tools/binman/test/210_fip_size.dts | 19 ++ tools/binman/test/211_fip_bad_align.dts | 18 ++ tools/binman/test/212_fip_collection.dts | 24 ++ 13 files changed, 862 insertions(+) create mode 100644 tools/binman/etype/atf_fip.py create mode 100644 tools/binman/test/203_fip.dts create mode 100644 tools/binman/test/204_fip_other.dts create mode 100644 tools/binman/test/205_fip_no_type.dts create mode 100644 tools/binman/test/206_fip_uuid.dts create mode 100644 tools/binman/test/207_fip_ls.dts create mode 100644 tools/binman/test/208_fip_replace.dts create mode 100644 tools/binman/test/209_fip_missing.dts create mode 100644 tools/binman/test/210_fip_size.dts create mode 100644 tools/binman/test/211_fip_bad_align.dts create mode 100644 tools/binman/test/212_fip_collection.dts diff --git a/tools/binman/entries.rst b/tools/binman/entries.rst index d5aa3b0f4a..c47f7df098 100644 --- a/tools/binman/entries.rst +++ b/tools/binman/entries.rst @@ -25,6 +25,160 @@ about ATF. +Entry: atf-fip: ARM Trusted Firmware's Firmware Image Package (FIP) +------------------------------------------------------------------- + +A FIP_ provides a way to group binaries in a firmware image, used by ARM's +Trusted Firmware A (TF-A) code. It is a simple format consisting of a +table of contents with information about the type, offset and size of the +binaries in the FIP. It is quite similar to FMAP, with the major difference +that it uses UUIDs to indicate the type of each entry. + +Note: It is recommended to always add an fdtmap to every image, as well as +any FIPs so that binman and other tools can access the entire image +correctly. + +The UUIDs correspond to useful names in `fiptool`, provided by ATF to +operate on FIPs. Binman uses these names to make it easier to understand +what is going on, although it is possible to provide a UUID if needed. + +The contents of the FIP are defined by subnodes of the atf-fip entry, e.g.:: + + atf-fip { + soc-fw { + filename = "bl31.bin"; + }; + + scp-fwu-cfg { + filename = "bl2u.bin"; + }; + + u-boot { + fip-type = "nt-fw"; + }; + }; + +This describes a FIP with three entries: soc-fw, scp-fwu-cfg and nt-fw. +You can use normal (non-external) binaries like U-Boot simply by adding a +FIP type, with the `fip-type` property, as above. + +Since FIP exists to bring blobs together, Binman assumes that all FIP +entries are external binaries. If a binary may not exist, you can use the +`--allow-missing` flag to Binman, in which case the image is still created, +even though it will not actually work. + +The size of the FIP depends on the size of the binaries. There is currently +no way to specify a fixed size. If the `atf-fip` node has a `size` entry, +this affects the space taken up by the `atf-fip` entry, but the FIP itself +does not expand to use that space. + +Some other FIP features are available with Binman. The header and the +entries have 64-bit flag works. The flag flags do not seem to be defined +anywhere, but you can use `fip-hdr-flags` and fip-flags` to set the values +of the header and entries respectively. + +FIP entries can be aligned to a particular power-of-two boundary. Use +fip-align for this. + +Binman only understands the entry types that are included in its +implementation. It is possible to specify a 16-byte UUID instead, using the +fip-uuid property. In this case Binman doesn't know what its type is, so +just uses the UUID. See the `u-boot` node in this example:: + + binman { + atf-fip { + fip-hdr-flags = /bits/ 64 <0x123>; + fip-align = <16>; + soc-fw { + fip-flags = /bits/ 64 <0x456>; + filename = "bl31.bin"; + }; + + scp-fwu-cfg { + filename = "bl2u.bin"; + }; + + u-boot { + fip-uuid = [fc 65 13 92 4a 5b 11 ec + 94 35 ff 2d 1c fc 79 9c]; + }; + }; + fdtmap { + }; + }; + +Binman allows reading and updating FIP entries after the image is created, +provided that an FDPMAP is present too. Updates which change the size of a +FIP entry will cause it to be expanded or contracted as needed. + +Properties for top-level atf-fip node +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +fip-hdr-flags (64 bits) + Sets the flags for the FIP header. + +Properties for subnodes +~~~~~~~~~~~~~~~~~~~~~~~ + +fip-type (str) + FIP type to use for this entry. This is needed if the entry + name is not a valid type. Value types are defined in `fip_util.py`. + The FIP type defines the UUID that is used (they map 1:1). + +fip-uuid (16 bytes) + If there is no FIP-type name defined, or it is not supported by Binman, + this property sets the UUID. It should be a 16-byte value, following the + hex digits of the UUID. + +fip-flags (64 bits) + Set the flags for a FIP entry. Use in one of the subnodes of the + 7atf-fip entry. + +fip-align + Set the alignment for a FIP entry, FIP entries can be aligned to a + particular power-of-two boundary. The default is 1. + +Adding new FIP-entry types +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +When new FIP entries are defined by TF-A they appear in the +`TF-A source tree`_. You can use `fip_util.py` to update Binman to support +new types, then `send a patch`_ to the U-Boot mailing list. There are two +source files that the tool examples: + +- `include/tools_share/firmware_image_package.h` has the UUIDs +- `tools/fiptool/tbbr_config.c` has the name and descripion for each UUID + +To run the tool:: + + $ tools/binman/fip_util.py -s /path/to/arm-trusted-firmware + Warning: UUID 'UUID_NON_TRUSTED_WORLD_KEY_CERT' is not mentioned in tbbr_config.c file + Existing code in 'tools/binman/fip_util.py' is up-to-date + +If it shows there is an update, it writes a new version of `fip_util.py` +to `fip_util.py.out`. You can change the output file using the `-i` flag. +If you have a problem, use `-D` to enable traceback debugging. + +FIP commentary +~~~~~~~~~~~~~~ + +As a side effect of use of UUIDs, FIP does not support multiple +entries of the same type, such as might be used to store fonts or graphics +icons, for example. For verified boot it could be used for each part of the +image (e.g. separate FIPs for A and B) but cannot describe the whole +firmware image. As with FMAP there is no hierarchy defined, although FMAP +works around this by having 'section' areas which encompass others. A +similar workaround would be possible with FIP but is not currently defined. + +It is recommended to always add an fdtmap to every image, as well as any +FIPs so that binman and other tools can access the entire image correctly. + +.. _FIP: https://trustedfirmware-a.readthedocs.io/en/latest/design/firmware-design.html#firmware-image-package-fip +.. _`TF-A source tree`: https://git.trustedfirmware.org/TF-A/trusted-firmware-a.git +.. _`send a patch`: https://www.denx.de/wiki/U-Boot/Patches + + + Entry: blob: Arbitrary binary blob ---------------------------------- diff --git a/tools/binman/etype/atf_fip.py b/tools/binman/etype/atf_fip.py new file mode 100644 index 0000000000..07e6c645b0 --- /dev/null +++ b/tools/binman/etype/atf_fip.py @@ -0,0 +1,273 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright 2019 Google LLC +# Written by Simon Glass +# +# Entry-type module for the ARM Trusted Firmware's Firmware Image Package (FIP) +# format + +from collections import OrderedDict + +from binman.entry import Entry +from binman.etype.section import Entry_section +from binman.fip_util import FIP_TYPES, FipReader, FipWriter, UUID_LEN +from dtoc import fdt_util +from patman import tools + +class Entry_atf_fip(Entry_section): + """ARM Trusted Firmware's Firmware Image Package (FIP) + + A FIP_ provides a way to group binaries in a firmware image, used by ARM's + Trusted Firmware A (TF-A) code. It is a simple format consisting of a + table of contents with information about the type, offset and size of the + binaries in the FIP. It is quite similar to FMAP, with the major difference + that it uses UUIDs to indicate the type of each entry. + + Note: It is recommended to always add an fdtmap to every image, as well as + any FIPs so that binman and other tools can access the entire image + correctly. + + The UUIDs correspond to useful names in `fiptool`, provided by ATF to + operate on FIPs. Binman uses these names to make it easier to understand + what is going on, although it is possible to provide a UUID if needed. + + The contents of the FIP are defined by subnodes of the atf-fip entry, e.g.:: + + atf-fip { + soc-fw { + filename = "bl31.bin"; + }; + + scp-fwu-cfg { + filename = "bl2u.bin"; + }; + + u-boot { + fip-type = "nt-fw"; + }; + }; + + This describes a FIP with three entries: soc-fw, scp-fwu-cfg and nt-fw. + You can use normal (non-external) binaries like U-Boot simply by adding a + FIP type, with the `fip-type` property, as above. + + Since FIP exists to bring blobs together, Binman assumes that all FIP + entries are external binaries. If a binary may not exist, you can use the + `--allow-missing` flag to Binman, in which case the image is still created, + even though it will not actually work. + + The size of the FIP depends on the size of the binaries. There is currently + no way to specify a fixed size. If the `atf-fip` node has a `size` entry, + this affects the space taken up by the `atf-fip` entry, but the FIP itself + does not expand to use that space. + + Some other FIP features are available with Binman. The header and the + entries have 64-bit flag works. The flag flags do not seem to be defined + anywhere, but you can use `fip-hdr-flags` and fip-flags` to set the values + of the header and entries respectively. + + FIP entries can be aligned to a particular power-of-two boundary. Use + fip-align for this. + + Binman only understands the entry types that are included in its + implementation. It is possible to specify a 16-byte UUID instead, using the + fip-uuid property. In this case Binman doesn't know what its type is, so + just uses the UUID. See the `u-boot` node in this example:: + + binman { + atf-fip { + fip-hdr-flags = /bits/ 64 <0x123>; + fip-align = <16>; + soc-fw { + fip-flags = /bits/ 64 <0x456>; + filename = "bl31.bin"; + }; + + scp-fwu-cfg { + filename = "bl2u.bin"; + }; + + u-boot { + fip-uuid = [fc 65 13 92 4a 5b 11 ec + 94 35 ff 2d 1c fc 79 9c]; + }; + }; + fdtmap { + }; + }; + + Binman allows reading and updating FIP entries after the image is created, + provided that an FDPMAP is present too. Updates which change the size of a + FIP entry will cause it to be expanded or contracted as needed. + + Properties for top-level atf-fip node + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + fip-hdr-flags (64 bits) + Sets the flags for the FIP header. + + Properties for subnodes + ~~~~~~~~~~~~~~~~~~~~~~~ + + fip-type (str) + FIP type to use for this entry. This is needed if the entry + name is not a valid type. Value types are defined in `fip_util.py`. + The FIP type defines the UUID that is used (they map 1:1). + + fip-uuid (16 bytes) + If there is no FIP-type name defined, or it is not supported by Binman, + this property sets the UUID. It should be a 16-byte value, following the + hex digits of the UUID. + + fip-flags (64 bits) + Set the flags for a FIP entry. Use in one of the subnodes of the + 7atf-fip entry. + + fip-align + Set the alignment for a FIP entry, FIP entries can be aligned to a + particular power-of-two boundary. The default is 1. + + Adding new FIP-entry types + ~~~~~~~~~~~~~~~~~~~~~~~~~~ + + When new FIP entries are defined by TF-A they appear in the + `TF-A source tree`_. You can use `fip_util.py` to update Binman to support + new types, then `send a patch`_ to the U-Boot mailing list. There are two + source files that the tool examples: + + - `include/tools_share/firmware_image_package.h` has the UUIDs + - `tools/fiptool/tbbr_config.c` has the name and descripion for each UUID + + To run the tool:: + + $ tools/binman/fip_util.py -s /path/to/arm-trusted-firmware + Warning: UUID 'UUID_NON_TRUSTED_WORLD_KEY_CERT' is not mentioned in tbbr_config.c file + Existing code in 'tools/binman/fip_util.py' is up-to-date + + If it shows there is an update, it writes a new version of `fip_util.py` + to `fip_util.py.out`. You can change the output file using the `-i` flag. + If you have a problem, use `-D` to enable traceback debugging. + + FIP commentary + ~~~~~~~~~~~~~~ + + As a side effect of use of UUIDs, FIP does not support multiple + entries of the same type, such as might be used to store fonts or graphics + icons, for example. For verified boot it could be used for each part of the + image (e.g. separate FIPs for A and B) but cannot describe the whole + firmware image. As with FMAP there is no hierarchy defined, although FMAP + works around this by having 'section' areas which encompass others. A + similar workaround would be possible with FIP but is not currently defined. + + It is recommended to always add an fdtmap to every image, as well as any + FIPs so that binman and other tools can access the entire image correctly. + + .. _FIP: https://trustedfirmware-a.readthedocs.io/en/latest/design/firmware-design.html#firmware-image-package-fip + .. _`TF-A source tree`: https://git.trustedfirmware.org/TF-A/trusted-firmware-a.git + .. _`send a patch`: https://www.denx.de/wiki/U-Boot/Patches + """ + 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 + + super().__init__(section, etype, node) + self.align_default = None + self._entries = OrderedDict() + self.reader = None + + def ReadNode(self): + """Read properties from the atf-fip node""" + super().ReadNode() + self._pad_byte = fdt_util.GetInt(self._node, 'pad-byte', 0) + self._fip_flags = fdt_util.GetInt64(self._node, 'fip-hdr-flags', 0) + self._fip_align = fdt_util.GetInt(self._node, 'fip-align', 1) + if tools.NotPowerOfTwo(self._fip_align): + raise ValueError("Node '%s': FIP alignment %s must be a power of two" % + (self._node.path, self._fip_align)) + self.ReadEntries() + + def ReadEntries(self): + """Read the subnodes to find out what should go in this FIP""" + for node in self._node.subnodes: + fip_type = None + etype = None + if node.name in FIP_TYPES: + fip_type = node.name + etype = 'blob-ext' + + entry = Entry.Create(self, node, etype) + entry._fip_uuid = fdt_util.GetBytes(node, 'fip-uuid', UUID_LEN) + if not fip_type and not entry._fip_uuid: + fip_type = fdt_util.GetString(node, 'fip-type') + if not fip_type: + self.Raise("Must provide a fip-type (node name '%s' is not a known FIP type)" % + node.name) + + entry._fip_type = fip_type + entry._fip_flags = fdt_util.GetInt64(node, 'fip-flags', 0) + entry.ReadNode() + entry._fip_name = node.name + self._entries[entry._fip_name] = entry + + def BuildSectionData(self, required): + """Override this function to create a custom format for the entries + + Arguments: + required (bool): True if the data must be valid, False if it may + be missing (entry.GetData() returns None + + Returns: + bytes: Data obtained, or None if None + """ + fip = FipWriter(self._fip_flags, self._fip_align) + for entry in self._entries.values(): + # First get the input data and put it in an entry. If not available, + # try later. + entry_data = entry.GetData(required) + if not required and entry_data is None: + return None + fent = fip.add_entry(entry._fip_type or entry._fip_uuid, entry_data, + entry._fip_flags) + if fent: + entry._fip_entry = fent + data = fip.get_data() + return data + + def SetImagePos(self, image_pos): + """Override this function to set all the entry properties from FIP + + We can only do this once image_pos is known + + Args: + image_pos: Position of this entry in the image + """ + super().SetImagePos(image_pos) + + # Now update the entries with info from the FIP entries + for entry in self._entries.values(): + fent = entry._fip_entry + entry.size = fent.size + entry.offset = fent.offset + entry.image_pos = self.image_pos + entry.offset + + def ReadChildData(self, child, decomp=True, alt_format=None): + if not self.reader: + self.fip_data = super().ReadData(True) + self.reader = FipReader(self.fip_data) + reader = self.reader + + # It is tricky to obtain the data from a FIP entry since it is indexed + # by its UUID. + fent = reader.get_entry(child._fip_type or child._fip_uuid) + return fent.data + + # Note: + # It is also possible to extract it using the offsets directly, but this + # seems less FIP_friendly: + # return self.fip_data[child.offset:child.offset + child.size] + + def WriteChildData(self, child): + # Recreate the data structure, leaving the data for this child alone, + # so that child.data is used to pack into the FIP. + self.ObtainContents(skip_entry=child) + return True diff --git a/tools/binman/ftest.py b/tools/binman/ftest.py index f5ceb9fb17..2f3ec69709 100644 --- a/tools/binman/ftest.py +++ b/tools/binman/ftest.py @@ -23,6 +23,7 @@ from binman import cmdline from binman import control from binman import elf from binman import elf_test +from binman import fip_util from binman import fmap_util from binman import state from dtoc import fdt @@ -76,6 +77,7 @@ FSP_M_DATA = b'fsp_m' FSP_S_DATA = b'fsp_s' FSP_T_DATA = b'fsp_t' ATF_BL31_DATA = b'bl31' +ATF_BL2U_DATA = b'bl2u' OPENSBI_DATA = b'opensbi' SCP_DATA = b'scp' TEST_FDT1_DATA = b'fdt1' @@ -179,6 +181,7 @@ class TestFunctional(unittest.TestCase): TestFunctional._MakeInputFile('compress', COMPRESS_DATA) TestFunctional._MakeInputFile('compress_big', COMPRESS_DATA_BIG) TestFunctional._MakeInputFile('bl31.bin', ATF_BL31_DATA) + TestFunctional._MakeInputFile('bl2u.bin', ATF_BL2U_DATA) TestFunctional._MakeInputFile('fw_dynamic.bin', OPENSBI_DATA) TestFunctional._MakeInputFile('scp.bin', SCP_DATA) @@ -4735,6 +4738,220 @@ fdt fdtmap Extract the devicetree blob from the fdtmap err = stderr.getvalue() self.assertRegex(err, "Image 'main-section'.*missing.*: blob-ext") + def testFip(self): + """Basic test of generation of an ARM Firmware Image Package (FIP)""" + data = self._DoReadFile('203_fip.dts') + hdr, fents = fip_util.decode_fip(data) + self.assertEqual(fip_util.HEADER_MAGIC, hdr.name) + self.assertEqual(fip_util.HEADER_SERIAL, hdr.serial) + self.assertEqual(0x123, hdr.flags) + + self.assertEqual(2, len(fents)) + + fent = fents[0] + self.assertEqual( + bytes([0x47, 0xd4, 0x08, 0x6d, 0x4c, 0xfe, 0x98, 0x46, + 0x9b, 0x95, 0x29, 0x50, 0xcb, 0xbd, 0x5a, 0x0]), fent.uuid) + self.assertEqual('soc-fw', fent.fip_type) + self.assertEqual(0x88, fent.offset) + self.assertEqual(len(ATF_BL31_DATA), fent.size) + self.assertEqual(0x123456789abcdef, fent.flags) + self.assertEqual(ATF_BL31_DATA, fent.data) + self.assertEqual(True, fent.valid) + + fent = fents[1] + self.assertEqual( + bytes([0x65, 0x92, 0x27, 0x03, 0x2f, 0x74, 0xe6, 0x44, + 0x8d, 0xff, 0x57, 0x9a, 0xc1, 0xff, 0x06, 0x10]), fent.uuid) + self.assertEqual('scp-fwu-cfg', fent.fip_type) + self.assertEqual(0x8c, fent.offset) + self.assertEqual(len(ATF_BL31_DATA), fent.size) + self.assertEqual(0, fent.flags) + self.assertEqual(ATF_BL2U_DATA, fent.data) + self.assertEqual(True, fent.valid) + + def testFipOther(self): + """Basic FIP with something that isn't a external blob""" + data = self._DoReadFile('204_fip_other.dts') + hdr, fents = fip_util.decode_fip(data) + + self.assertEqual(2, len(fents)) + fent = fents[1] + self.assertEqual('rot-cert', fent.fip_type) + self.assertEqual(b'aa', fent.data) + + def testFipOther(self): + """Basic FIP with something that isn't a external blob""" + data = self._DoReadFile('204_fip_other.dts') + hdr, fents = fip_util.decode_fip(data) + + self.assertEqual(2, len(fents)) + fent = fents[1] + self.assertEqual('rot-cert', fent.fip_type) + self.assertEqual(b'aa', fent.data) + + def testFipNoType(self): + """FIP with an entry of an unknown type""" + with self.assertRaises(ValueError) as e: + self._DoReadFile('205_fip_no_type.dts') + self.assertIn("Must provide a fip-type (node name 'u-boot' is not a known FIP type)", + str(e.exception)) + + def testFipUuid(self): + """Basic FIP with a manual uuid""" + data = self._DoReadFile('206_fip_uuid.dts') + hdr, fents = fip_util.decode_fip(data) + + self.assertEqual(2, len(fents)) + fent = fents[1] + self.assertEqual(None, fent.fip_type) + self.assertEqual( + bytes([0xfc, 0x65, 0x13, 0x92, 0x4a, 0x5b, 0x11, 0xec, + 0x94, 0x35, 0xff, 0x2d, 0x1c, 0xfc, 0x79, 0x9c]), + fent.uuid) + self.assertEqual(U_BOOT_DATA, fent.data) + + def testFipLs(self): + """Test listing a FIP""" + data = self._DoReadFileRealDtb('207_fip_ls.dts') + hdr, fents = fip_util.decode_fip(data) + + try: + tmpdir, updated_fname = self._SetupImageInTmpdir() + with test_util.capture_sys_output() as (stdout, stderr): + self._DoBinman('ls', '-i', updated_fname) + finally: + shutil.rmtree(tmpdir) + lines = stdout.getvalue().splitlines() + expected = [ +'Name Image-pos Size Entry-type Offset Uncomp-size', +'----------------------------------------------------------------', +'main-section 0 2d3 section 0', +' atf-fip 0 90 atf-fip 0', +' soc-fw 88 4 blob-ext 88', +' u-boot 8c 4 u-boot 8c', +' fdtmap 90 243 fdtmap 90', +] + self.assertEqual(expected, lines) + + image = control.images['image'] + entries = image.GetEntries() + fdtmap = entries['fdtmap'] + + fdtmap_data = data[fdtmap.image_pos:fdtmap.image_pos + fdtmap.size] + magic = fdtmap_data[:8] + self.assertEqual(b'_FDTMAP_', magic) + self.assertEqual(tools.GetBytes(0, 8), fdtmap_data[8:16]) + + fdt_data = fdtmap_data[16:] + dtb = fdt.Fdt.FromData(fdt_data) + dtb.Scan() + props = self._GetPropTree(dtb, BASE_DTB_PROPS, prefix='/') + self.assertEqual({ + 'atf-fip/soc-fw:image-pos': 136, + 'atf-fip/soc-fw:offset': 136, + 'atf-fip/soc-fw:size': 4, + 'atf-fip/u-boot:image-pos': 140, + 'atf-fip/u-boot:offset': 140, + 'atf-fip/u-boot:size': 4, + 'atf-fip:image-pos': 0, + 'atf-fip:offset': 0, + 'atf-fip:size': 144, + 'image-pos': 0, + 'offset': 0, + 'fdtmap:image-pos': fdtmap.image_pos, + 'fdtmap:offset': fdtmap.offset, + 'fdtmap:size': len(fdtmap_data), + 'size': len(data), + }, props) + + def testFipExtractOneEntry(self): + """Test extracting a single entry fron an FIP""" + self._DoReadFileRealDtb('207_fip_ls.dts') + image_fname = tools.GetOutputFilename('image.bin') + fname = os.path.join(self._indir, 'output.extact') + control.ExtractEntries(image_fname, fname, None, ['atf-fip/u-boot']) + data = tools.ReadFile(fname) + self.assertEqual(U_BOOT_DATA, data) + + def testFipReplace(self): + """Test replacing a single file in a FIP""" + expected = U_BOOT_DATA + tools.GetBytes(0x78, 50) + data = self._DoReadFileRealDtb('208_fip_replace.dts') + updated_fname = tools.GetOutputFilename('image-updated.bin') + tools.WriteFile(updated_fname, data) + entry_name = 'atf-fip/u-boot' + control.WriteEntry(updated_fname, entry_name, expected, + allow_resize=True) + actual = control.ReadEntry(updated_fname, entry_name) + self.assertEqual(expected, actual) + + new_data = tools.ReadFile(updated_fname) + hdr, fents = fip_util.decode_fip(new_data) + + self.assertEqual(2, len(fents)) + + # Check that the FIP entry is updated + fent = fents[1] + self.assertEqual(0x8c, fent.offset) + self.assertEqual(len(expected), fent.size) + self.assertEqual(0, fent.flags) + self.assertEqual(expected, fent.data) + self.assertEqual(True, fent.valid) + + def testFipMissing(self): + with test_util.capture_sys_output() as (stdout, stderr): + self._DoTestFile('209_fip_missing.dts', allow_missing=True) + err = stderr.getvalue() + self.assertRegex(err, "Image 'main-section'.*missing.*: rmm-fw") + + def testFipSize(self): + """Test a FIP with a size property""" + data = self._DoReadFile('210_fip_size.dts') + self.assertEqual(0x100 + len(U_BOOT_DATA), len(data)) + hdr, fents = fip_util.decode_fip(data) + self.assertEqual(fip_util.HEADER_MAGIC, hdr.name) + self.assertEqual(fip_util.HEADER_SERIAL, hdr.serial) + + self.assertEqual(1, len(fents)) + + fent = fents[0] + self.assertEqual('soc-fw', fent.fip_type) + self.assertEqual(0x60, fent.offset) + self.assertEqual(len(ATF_BL31_DATA), fent.size) + self.assertEqual(ATF_BL31_DATA, fent.data) + self.assertEqual(True, fent.valid) + + rest = data[0x60 + len(ATF_BL31_DATA):0x100] + self.assertEqual(tools.GetBytes(0xff, len(rest)), rest) + + def testFipBadAlign(self): + """Test that an invalid alignment value in a FIP is detected""" + with self.assertRaises(ValueError) as e: + self._DoTestFile('211_fip_bad_align.dts') + self.assertIn( + "Node \'/binman/atf-fip\': FIP alignment 31 must be a power of two", + str(e.exception)) + + def testFipCollection(self): + """Test using a FIP in a collection""" + data = self._DoReadFile('212_fip_collection.dts') + entry1 = control.images['image'].GetEntries()['collection'] + data1 = data[:entry1.size] + hdr1, fents2 = fip_util.decode_fip(data1) + + entry2 = control.images['image'].GetEntries()['atf-fip'] + data2 = data[entry2.offset:entry2.offset + entry2.size] + hdr1, fents2 = fip_util.decode_fip(data2) + + # The 'collection' entry should have U-Boot included at the end + self.assertEqual(entry1.size - len(U_BOOT_DATA), entry2.size) + self.assertEqual(data1, data2 + U_BOOT_DATA) + self.assertEqual(U_BOOT_DATA, data1[-4:]) + + # There should be a U-Boot after the final FIP + self.assertEqual(U_BOOT_DATA, data[-4:]) + if __name__ == "__main__": unittest.main() diff --git a/tools/binman/test/203_fip.dts b/tools/binman/test/203_fip.dts new file mode 100644 index 0000000000..0897337324 --- /dev/null +++ b/tools/binman/test/203_fip.dts @@ -0,0 +1,21 @@ +// SPDX-License-Identifier: GPL-2.0+ +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + atf-fip { + fip-hdr-flags = /bits/ 64 <0x123>; + soc-fw { + fip-flags = /bits/ 64 <0x123456789abcdef>; + filename = "bl31.bin"; + }; + + scp-fwu-cfg { + filename = "bl2u.bin"; + }; + }; + }; +}; diff --git a/tools/binman/test/204_fip_other.dts b/tools/binman/test/204_fip_other.dts new file mode 100644 index 0000000000..6503941098 --- /dev/null +++ b/tools/binman/test/204_fip_other.dts @@ -0,0 +1,22 @@ +// SPDX-License-Identifier: GPL-2.0+ +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + atf-fip { + fip-hdr-flags = /bits/ 64 <0x123>; + soc-fw { + fip-flags = /bits/ 64 <0x123456789abcdef>; + filename = "bl31.bin"; + }; + + _testing { + fip-type = "rot-cert"; + return-contents-later; + }; + }; + }; +}; diff --git a/tools/binman/test/205_fip_no_type.dts b/tools/binman/test/205_fip_no_type.dts new file mode 100644 index 0000000000..23c8c3bc37 --- /dev/null +++ b/tools/binman/test/205_fip_no_type.dts @@ -0,0 +1,15 @@ +// SPDX-License-Identifier: GPL-2.0+ +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + atf-fip { + fip-hdr-flags = /bits/ 64 <0x123>; + u-boot { + }; + }; + }; +}; diff --git a/tools/binman/test/206_fip_uuid.dts b/tools/binman/test/206_fip_uuid.dts new file mode 100644 index 0000000000..c9bd44f9c3 --- /dev/null +++ b/tools/binman/test/206_fip_uuid.dts @@ -0,0 +1,22 @@ +// SPDX-License-Identifier: GPL-2.0+ +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + atf-fip { + fip-hdr-flags = /bits/ 64 <0x123>; + soc-fw { + fip-flags = /bits/ 64 <0x123456789abcdef>; + filename = "bl31.bin"; + }; + + u-boot { + fip-uuid = [fc 65 13 92 4a 5b 11 ec + 94 35 ff 2d 1c fc 79 9c]; + }; + }; + }; +}; diff --git a/tools/binman/test/207_fip_ls.dts b/tools/binman/test/207_fip_ls.dts new file mode 100644 index 0000000000..630fca1502 --- /dev/null +++ b/tools/binman/test/207_fip_ls.dts @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: GPL-2.0+ +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + atf-fip { + fip-hdr-flags = /bits/ 64 <0x123>; + soc-fw { + fip-flags = /bits/ 64 <0x123456789abcdef>; + filename = "bl31.bin"; + }; + + u-boot { + fip-uuid = [fc 65 13 92 4a 5b 11 ec + 94 35 ff 2d 1c fc 79 9c]; + }; + }; + + fdtmap { + }; + }; +}; diff --git a/tools/binman/test/208_fip_replace.dts b/tools/binman/test/208_fip_replace.dts new file mode 100644 index 0000000000..432c12474d --- /dev/null +++ b/tools/binman/test/208_fip_replace.dts @@ -0,0 +1,33 @@ +// SPDX-License-Identifier: GPL-2.0+ +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + allow-repack; + atf-fip { + fip-hdr-flags = /bits/ 64 <0x123>; + soc-fw { + fip-flags = /bits/ 64 <0x123456789abcdef>; + filename = "bl31.bin"; + }; + + u-boot { + fip-uuid = [fc 65 13 92 4a 5b 11 ec + 94 35 ff 2d 1c fc 79 9c]; + }; + + }; + + u-boot { + }; + + u-boot-dtb { + }; + + fdtmap { + }; + }; +}; diff --git a/tools/binman/test/209_fip_missing.dts b/tools/binman/test/209_fip_missing.dts new file mode 100644 index 0000000000..43bb600d04 --- /dev/null +++ b/tools/binman/test/209_fip_missing.dts @@ -0,0 +1,19 @@ +// SPDX-License-Identifier: GPL-2.0+ +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + atf-fip { + soc-fw { + filename = "bl31.bin"; + }; + + rmm-fw { + filename = "rmm.bin"; + }; + }; + }; +}; diff --git a/tools/binman/test/210_fip_size.dts b/tools/binman/test/210_fip_size.dts new file mode 100644 index 0000000000..9dfee79645 --- /dev/null +++ b/tools/binman/test/210_fip_size.dts @@ -0,0 +1,19 @@ +// SPDX-License-Identifier: GPL-2.0+ +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + atf-fip { + size = <0x100>; + pad-byte = <0xff>; + soc-fw { + filename = "bl31.bin"; + }; + }; + u-boot { + }; + }; +}; diff --git a/tools/binman/test/211_fip_bad_align.dts b/tools/binman/test/211_fip_bad_align.dts new file mode 100644 index 0000000000..a0901496d8 --- /dev/null +++ b/tools/binman/test/211_fip_bad_align.dts @@ -0,0 +1,18 @@ +// SPDX-License-Identifier: GPL-2.0+ +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + atf-fip { + fip-align = <31>; + size = <0x100>; + pad-byte = <0xff>; + soc-fw { + filename = "bl31.bin"; + }; + }; + }; +}; diff --git a/tools/binman/test/212_fip_collection.dts b/tools/binman/test/212_fip_collection.dts new file mode 100644 index 0000000000..332c023af8 --- /dev/null +++ b/tools/binman/test/212_fip_collection.dts @@ -0,0 +1,24 @@ +// SPDX-License-Identifier: GPL-2.0+ +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + collection { + content = <&fip &u_boot>; + }; + fip: atf-fip { + soc-fw { + filename = "bl31.bin"; + }; + + scp-fwu-cfg { + filename = "bl2u.bin"; + }; + }; + u_boot: u-boot { + }; + }; +}; From 6405ab7ad5898966136cf9044fef1bc0c4fe0ea9 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Wed, 24 Nov 2021 09:26:42 -0700 Subject: [PATCH 3/8] Convert CONFIG_PHYSMEM to Kconfig This converts the following to Kconfig: CONFIG_PHYSMEM Signed-off-by: Simon Glass Suggested-by: Sean Anderson --- README | 8 -------- arch/Kconfig | 2 ++ include/configs/edison.h | 3 --- include/configs/sandbox.h | 2 -- include/configs/x86-common.h | 2 -- lib/Kconfig | 10 ++++++++++ scripts/config_whitelist.txt | 1 - 7 files changed, 12 insertions(+), 16 deletions(-) diff --git a/README b/README index 5dee3eea5b..056b717557 100644 --- a/README +++ b/README @@ -1692,14 +1692,6 @@ The following options need to be configured: HERMES, IP860, RPXlite, LWMON, FLAGADM -- Access to physical memory region (> 4GB) - Some basic support is provided for operations on memory not - normally accessible to U-Boot - e.g. some architectures - support access to more than 4GB of memory on 32-bit - machines using physical address extension or similar. - Define CONFIG_PHYSMEM to access this basic support, which - currently only supports clearing the memory. - - Error Recovery: CONFIG_NET_RETRY_COUNT diff --git a/arch/Kconfig b/arch/Kconfig index fffddac04c..39156067b2 100644 --- a/arch/Kconfig +++ b/arch/Kconfig @@ -195,6 +195,7 @@ config SANDBOX imply DM_DSA imply CMD_EXTENSION imply KEYBOARD + imply PHYSMEM config SH bool "SuperH architecture" @@ -246,6 +247,7 @@ config X86 imply USB_ETHER_SMSC95XX imply USB_HOST_ETHER imply PCH + imply PHYSMEM imply RTC_MC146818 imply ACPIGEN if !QEMU imply SYSINFO if GENERATE_SMBIOS_TABLE diff --git a/include/configs/edison.h b/include/configs/edison.h index 3ec35db4bc..02f33f3c29 100644 --- a/include/configs/edison.h +++ b/include/configs/edison.h @@ -14,9 +14,6 @@ #define CONFIG_SYS_MAXARGS 128 #define CONFIG_SYS_BARGSIZE CONFIG_SYS_CBSIZE -/* Memory */ -#define CONFIG_PHYSMEM - #define CONFIG_SYS_STACK_SIZE (32 * 1024) #define CONFIG_SYS_MONITOR_BASE CONFIG_SYS_TEXT_BASE diff --git a/include/configs/sandbox.h b/include/configs/sandbox.h index 0458c72360..cd7d99ec69 100644 --- a/include/configs/sandbox.h +++ b/include/configs/sandbox.h @@ -14,8 +14,6 @@ #define CONFIG_SYS_CBSIZE 1024 /* Console I/O Buffer Size */ -#define CONFIG_PHYSMEM - /* Size of our emulated memory */ #define SB_CONCAT(x, y) x ## y #define SB_TO_UL(s) SB_CONCAT(s, UL) diff --git a/include/configs/x86-common.h b/include/configs/x86-common.h index ca90902c71..a03913d5e7 100644 --- a/include/configs/x86-common.h +++ b/include/configs/x86-common.h @@ -14,8 +14,6 @@ * High Level Configuration Options * (easy to change) */ -#define CONFIG_PHYSMEM - #define CONFIG_SYS_BOOTM_LEN (16 << 20) /* SATA AHCI storage */ diff --git a/lib/Kconfig b/lib/Kconfig index 807a4c6ade..10ba086b39 100644 --- a/lib/Kconfig +++ b/lib/Kconfig @@ -15,6 +15,16 @@ config SYS_NUM_ADDR_MAP help Sets the number of entries in the virtual-physical mapping table. +config PHYSMEM + bool "Access to physical memory region (> 4G)" + help + Some basic support is provided for operations on memory not + normally accessible to 32-bit U-Boot - e.g. some architectures + support access to more than 4G of memory on 32-bit + machines using physical address extension or similar. + Enable this to access this basic support, which only supports clearing + the memory. + config BCH bool "Enable Software based BCH ECC" help diff --git a/scripts/config_whitelist.txt b/scripts/config_whitelist.txt index 207d1ac7af..6d961ccb3e 100644 --- a/scripts/config_whitelist.txt +++ b/scripts/config_whitelist.txt @@ -911,7 +911,6 @@ CONFIG_PCI_SYS_BUS CONFIG_PCI_SYS_PHYS CONFIG_PCI_SYS_SIZE CONFIG_PEN_ADDR_BIG_ENDIAN -CONFIG_PHYSMEM CONFIG_PHY_BASE_ADR CONFIG_PHY_ET1011C_TX_CLK_FIX CONFIG_PHY_ID From 022f6b0643ee7cf6f9474b768ba2aa802762980b Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 6 Dec 2021 14:44:12 +0300 Subject: [PATCH 4/8] binman: Do not pollute source tree when build with `make O=...` Importing libraries in Python caches the bytecode by default. Since we run scripts in source tree it ignores the current directory settings, which is $(srctree), and creates cache just in the middle of the source tree. Move cache to the current directory. Signed-off-by: Andy Shevchenko --- tools/binman/main.py | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/tools/binman/main.py b/tools/binman/main.py index 1a639f43e9..2fa55ff549 100755 --- a/tools/binman/main.py +++ b/tools/binman/main.py @@ -16,9 +16,20 @@ import sys import traceback import unittest +# Get the absolute path to this file at run-time +our_path = os.path.dirname(os.path.realpath(__file__)) +our1_path = os.path.dirname(our_path) +our2_path = os.path.dirname(our1_path) + +# +# Do not pollute source tree with cache files: +# https://stackoverflow.com/a/60024195/2511795 +# https://bugs.python.org/issue33499 +# +sys.pycache_prefix = os.path.relpath(our_path, os.environ.get('srctree', our2_path)) + # Bring in the patman and dtoc libraries (but don't override the first path # in PYTHONPATH) -our_path = os.path.dirname(os.path.realpath(__file__)) sys.path.insert(2, os.path.join(our_path, '..')) from patman import test_util From 33f27f4fadfaca3735f1ae35a20757aa21472b7c Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 6 Dec 2021 14:44:13 +0300 Subject: [PATCH 5/8] binman: Use less hard coded magic when inserting new PATH Instead of joining hard coded '..' to the run-time path of the executable, take just a dirname out of it. Besides that, use $(srctree) where it makes sense. Signed-off-by: Andy Shevchenko --- tools/binman/main.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/tools/binman/main.py b/tools/binman/main.py index 2fa55ff549..35944f314a 100755 --- a/tools/binman/main.py +++ b/tools/binman/main.py @@ -21,24 +21,26 @@ our_path = os.path.dirname(os.path.realpath(__file__)) our1_path = os.path.dirname(our_path) our2_path = os.path.dirname(our1_path) +# Extract $(srctree) from Kbuild environment, or use relative paths below +srctree = os.environ.get('srctree', our2_path) + # # Do not pollute source tree with cache files: # https://stackoverflow.com/a/60024195/2511795 # https://bugs.python.org/issue33499 # -sys.pycache_prefix = os.path.relpath(our_path, os.environ.get('srctree', our2_path)) +sys.pycache_prefix = os.path.relpath(our_path, srctree) # Bring in the patman and dtoc libraries (but don't override the first path # in PYTHONPATH) -sys.path.insert(2, os.path.join(our_path, '..')) +sys.path.insert(2, our1_path) from patman import test_util # Bring in the libfdt module sys.path.insert(2, 'scripts/dtc/pylibfdt') -sys.path.insert(2, os.path.join(our_path, '../../scripts/dtc/pylibfdt')) -sys.path.insert(2, os.path.join(our_path, - '../../build-sandbox_spl/scripts/dtc/pylibfdt')) +sys.path.insert(2, os.path.join(srctree, 'scripts/dtc/pylibfdt')) +sys.path.insert(2, os.path.join(srctree, 'build-sandbox_spl/scripts/dtc/pylibfdt')) # When running under python-coverage on Ubuntu 16.04, the dist-packages # directories are dropped from the python path. Add them in so that we can find From 1c11b5e6f67ca5aeb2d61e198298ed655311070e Mon Sep 17 00:00:00 2001 From: Ivan Mikhaylov Date: Thu, 9 Dec 2021 16:10:53 +0000 Subject: [PATCH 6/8] iot2050: binman: add missing-msg for blobs Add the 'missing-msg' for blobs for more detailed output on missing system firmware and SEBoot blobs. Signed-off-by: Ivan Mikhaylov Reviewed-by: Simon Glass Fix minor typos: Signed-off-by: Simon Glass --- arch/arm/dts/k3-am65-iot2050-boot-image.dtsi | 5 +++++ tools/binman/missing-blob-help | 11 +++++++++++ 2 files changed, 16 insertions(+) diff --git a/arch/arm/dts/k3-am65-iot2050-boot-image.dtsi b/arch/arm/dts/k3-am65-iot2050-boot-image.dtsi index 69479d7b18..27058370cc 100644 --- a/arch/arm/dts/k3-am65-iot2050-boot-image.dtsi +++ b/arch/arm/dts/k3-am65-iot2050-boot-image.dtsi @@ -18,6 +18,7 @@ blob-ext@0x000000 { offset = <0x000000>; filename = "tiboot3.bin"; + missing-msg = "iot2050-seboot"; }; blob@0x080000 { @@ -153,21 +154,25 @@ blob-ext@0x6c0000 { offset = <0x6c0000>; filename = "sysfw.itb"; + missing-msg = "iot2050-sysfw"; }; /* PG1 sysfw, advanced variant */ blob-ext@0x740000 { offset = <0x740000>; filename = "sysfw.itb_HS"; + missing-msg = "iot2050-sysfw"; }; /* PG2 sysfw, basic variant */ blob-ext@0x7c0000 { offset = <0x7c0000>; filename = "sysfw_sr2.itb"; + missing-msg = "iot2050-sysfw"; }; /* PG2 sysfw, advanced variant */ blob-ext@0x840000 { offset = <0x840000>; filename = "sysfw_sr2.itb_HS"; + missing-msg = "iot2050-sysfw"; }; }; }; diff --git a/tools/binman/missing-blob-help b/tools/binman/missing-blob-help index dc2d9c9811..551ca87f6c 100644 --- a/tools/binman/missing-blob-help +++ b/tools/binman/missing-blob-help @@ -18,6 +18,17 @@ scp-sunxi: SCP firmware is required for system suspend, but is otherwise optional. Please read the section on SCP firmware in board/sunxi/README.sunxi64 +iot2050-seboot: +See the documentation for IOT2050 board. Your image is missing SEBoot +which is mandatory for board startup. Prebuilt SEBoot located at +meta-iot2050/tree/master/recipes-bsp/u-boot/files/prebuild/tiboot3.bin. + +iot2050-sysfw: +See the documentation for IOT2050 board. Your image is missing system +firmware which is mandatory for board startup. Prebuilt system firmware +located at meta-iot2050/tree/master/recipes-bsp/u-boot/files/prebuild/ +with sysfw prefix. + k3-rti-wdt-firmware: If CONFIG_WDT_K3_RTI_LOAD_FW is enabled, a firmware image is needed for the R5F core(s) to trigger the system reset. One possible source is From 70ab7b1799112398047be63cc113f93955f0f352 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Wed, 8 Dec 2021 09:55:34 -0700 Subject: [PATCH 7/8] fdtgrep: Correct alignment of struct section When outputting a devicetree we should not align the struct section to a 16-byte boundary. The normal position is fine, which is 8-byte aligned. This avoids leaving adding 8 extra zero bytes in the output tree in the case where the reserved section is empty (i.e has 16 zero bytes). Signed-off-by: Simon Glass --- tools/fdtgrep.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tools/fdtgrep.c b/tools/fdtgrep.c index db512465db..641d6a2e3e 100644 --- a/tools/fdtgrep.c +++ b/tools/fdtgrep.c @@ -438,8 +438,7 @@ static int dump_fdt_regions(struct display_info *disp, const void *blob, fdt = (struct fdt_header *)out; memset(fdt, '\0', sizeof(*fdt)); fdt_set_magic(fdt, FDT_MAGIC); - struct_start = FDT_ALIGN(sizeof(struct fdt_header), - sizeof(struct fdt_reserve_entry)); + struct_start = sizeof(struct fdt_header); fdt_set_off_mem_rsvmap(fdt, struct_start); fdt_set_version(fdt, FDT_LAST_SUPPORTED_VERSION); fdt_set_last_comp_version(fdt, FDT_FIRST_SUPPORTED_VERSION); From 121cfe5a84d463893bd01da16417ab138f98666a Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Wed, 8 Dec 2021 09:55:35 -0700 Subject: [PATCH 8/8] fdtgrep: Handle an empty output tree In strange cases it is possible for fdtgrep to find nothing to output. Typically this means that the resulting SPL device tree is not going to allow anything to boot, but at present the tree is actually invalid, since it only has an END tag in the struct region. The FDT spec requires at least a root node. So add a special case to include at least this, if the FDT_REG_SUPERNODES flag is set. This ensures that grepping an empty tree still produces a valid tree. Also add comments to the enum since it is not completely obvious from the names now. The typical symptom of this problem is a message from binman: pylibfdt error -11: FDT_ERR_BADSTRUCTURE Signed-off-by: Simon Glass --- boot/fdt_region.c | 43 +++++++++++++++++++++++++++++++++++++------ include/fdt_region.h | 1 + 2 files changed, 38 insertions(+), 6 deletions(-) diff --git a/boot/fdt_region.c b/boot/fdt_region.c index e4ef0ca770..bac5559329 100644 --- a/boot/fdt_region.c +++ b/boot/fdt_region.c @@ -185,6 +185,8 @@ static int fdt_add_region(struct fdt_region_state *info, int offset, int size) reg++; reg->offset = offset; reg->size = size; + if (!(offset - fdt_off_dt_struct(info->fdt))) + info->have_node = true; } } else { return -1; @@ -342,13 +344,19 @@ static int fdt_include_supernodes(struct fdt_region_state *info, int depth) return 0; } +/* + * Tracks the progress through the device tree. Everything fdt_next_region() is + * called it picks up at the same state as last time, looks at info->start and + * decides what region to add next + */ enum { - FDT_DONE_NOTHING, - FDT_DONE_MEM_RSVMAP, - FDT_DONE_STRUCT, - FDT_DONE_END, - FDT_DONE_STRINGS, - FDT_DONE_ALL, + FDT_DONE_NOTHING, /* Starting */ + FDT_DONE_MEM_RSVMAP, /* Completed mem_rsvmap region */ + FDT_DONE_STRUCT, /* Completed struct region */ + FDT_DONE_EMPTY, /* Completed checking for empty struct region */ + FDT_DONE_END, /* Completed the FDT_END tag */ + FDT_DONE_STRINGS, /* Completed the strings */ + FDT_DONE_ALL, /* All done */ }; int fdt_first_region(const void *fdt, @@ -365,6 +373,7 @@ int fdt_first_region(const void *fdt, info->can_merge = 1; info->max_regions = 1; info->start = -1; + info->have_node = false; p->want = WANT_NOTHING; p->end = path; *p->end = '\0'; @@ -633,6 +642,8 @@ int fdt_next_region(const void *fdt, * region. */ if (!include && info->start != -1) { + if (!info->start) + info->have_node = true; if (fdt_add_region(info, base + info->start, stop_at - info->start)) return 0; @@ -644,11 +655,31 @@ int fdt_next_region(const void *fdt, info->ptrs = p; } + if (info->ptrs.done < FDT_DONE_EMPTY) { + /* + * Handle a special case where no nodes have been written. Write + * the first { so we have at least something, since + * FDT_REG_SUPERNODES means that a valid tree is required. A + * tree with no nodes is not valid + */ + if ((flags & FDT_REG_SUPERNODES) && !info->have_node && + info->start) { + /* Output the FDT_BEGIN_NODE and the empty name */ + if (fdt_add_region(info, base, 8)) + return 0; + } + info->ptrs.done++; + } + /* Add a region for the END tag and a separate one for string table */ if (info->ptrs.done < FDT_DONE_END) { if (info->ptrs.nextoffset != fdt_size_dt_struct(fdt)) return -FDT_ERR_BADSTRUCTURE; + /* Output the } before the end tag to finish it off */ + if (info->start == fdt_size_dt_struct(fdt) - 4) + info->start -= 4; + if (fdt_add_region(info, base + info->start, info->ptrs.nextoffset - info->start)) return 0; diff --git a/include/fdt_region.h b/include/fdt_region.h index ff7a1ccb9a..d0c68760f7 100644 --- a/include/fdt_region.h +++ b/include/fdt_region.h @@ -77,6 +77,7 @@ struct fdt_region_state { int max_regions; /* Maximum regions to find */ int can_merge; /* 1 if we can merge with previous region */ int start; /* Start position of current region */ + bool have_node; /* True if any node is included */ struct fdt_region_ptrs ptrs; /* Pointers for what we are up to */ };