diff options
Diffstat (limited to 'tools')
55 files changed, 1509 insertions, 201 deletions
diff --git a/tools/Makefile b/tools/Makefile index acbcd87af2..4d32fe5910 100644 --- a/tools/Makefile +++ b/tools/Makefile @@ -61,11 +61,11 @@ FIT_SIG_OBJS-$(CONFIG_FIT_SIGNATURE) := common/image-sig.o # The following files are synced with upstream DTC. # Use synced versions from scripts/dtc/libfdt/. -LIBFDT_SRCS_SYNCED := fdt.c fdt_sw.c fdt_strerror.c fdt_empty_tree.c \ +LIBFDT_SRCS_SYNCED := fdt.c fdt_wip.c fdt_sw.c fdt_strerror.c fdt_empty_tree.c \ fdt_addresses.c fdt_overlay.c # The following files are locally modified for U-Boot (unfotunately). # Use U-Boot own versions from lib/libfdt/. -LIBFDT_SRCS_UNSYNCED := fdt_ro.c fdt_wip.c fdt_rw.c fdt_region.c +LIBFDT_SRCS_UNSYNCED := fdt_ro.c fdt_rw.c fdt_region.c LIBFDT_OBJS := $(addprefix libfdt/, $(patsubst %.c, %.o, $(LIBFDT_SRCS_SYNCED))) \ $(addprefix lib/libfdt/, $(patsubst %.c, %.o, $(LIBFDT_SRCS_UNSYNCED))) diff --git a/tools/binman/README b/tools/binman/README index cb47e73599..08c3e56bde 100644 --- a/tools/binman/README +++ b/tools/binman/README @@ -206,6 +206,27 @@ for its instructions in the 'binman' node. Binman has a few other options which you can see by running 'binman -h'. +Enabling binman for a board +--------------------------- + +At present binman is invoked from a rule in the main Makefile. Typically you +will have a rule like: + +ifneq ($(CONFIG_ARCH_<something>),) +u-boot-<your_suffix>.bin: <input_file_1> <input_file_2> checkbinman FORCE + $(call if_changed,binman) +endif + +This assumes that u-boot-<your_suffix>.bin is a target, and is the final file +that you need to produce. You can make it a target by adding it to ALL-y +either in the main Makefile or in a config.mk file in your arch subdirectory. + +Once binman is executed it will pick up its instructions from a device-tree +file, typically <soc>-u-boot.dtsi, where <soc> is your CONFIG_SYS_SOC value. +You can use other, more specific CONFIG options - see 'Automatic .dtsi +inclusion' below. + + Image description format ------------------------ @@ -418,6 +439,8 @@ contents of an entry in some way. For example, it would be possible to create an entry containing a hash of the contents of some other entries. At this stage the position and size of entries should not be adjusted. +6. WriteEntryInfo() + 7. BuildImage() - builds the image and writes it to a file. This is the final step. @@ -446,7 +469,35 @@ If you are having trouble figuring out what is going on, you can uncomment the 'warning' line in scripts/Makefile.lib to see what it has found: # Uncomment for debugging - # $(warning binman_dtsi_options: $(binman_dtsi_options)) + # This shows all the files that were considered and the one that we chose. + # u_boot_dtsi_options_debug = $(u_boot_dtsi_options_raw) + + +Access to binman entry positions at run time +-------------------------------------------- + +Binman assembles images and determines where each entry is placed in the image. +This information may be useful to U-Boot at run time. For example, in SPL it +is useful to be able to find the location of U-Boot so that it can be executed +when SPL is finished. + +Binman allows you to declare symbols in the SPL image which are filled in +with their correct values during the build. For example: + + binman_sym_declare(ulong, u_boot_any, pos); + +declares a ulong value which will be assigned to the position of any U-Boot +image (u-boot.bin, u-boot.img, u-boot-nodtb.bin) that is present in the image. +You can access this value with something like: + + ulong u_boot_pos = binman_sym(ulong, u_boot_any, pos); + +Thus u_boot_pos will be set to the position of U-Boot in memory, assuming that +the whole image has been loaded, or is available in flash. You can then jump to +that address to start U-Boot. + +At present this feature is only supported in SPL. In principle it is possible +to fill in such symbols in U-Boot proper, as well. Code coverage @@ -521,7 +572,8 @@ To do Some ideas: - Fill out the device tree to include the final position and size of each - entry (since the input file may not always specify these) + entry (since the input file may not always specify these). See also + 'Access to binman entry positions at run time' above - Use of-platdata to make the information available to code that is unable to use device tree (such as a very small SPL image) - Write an image map to a text file diff --git a/tools/binman/binman.py b/tools/binman/binman.py index e75a59d951..1c8e8dbff6 100755 --- a/tools/binman/binman.py +++ b/tools/binman/binman.py @@ -10,6 +10,7 @@ """See README for more information""" +import glob import os import sys import traceback @@ -30,11 +31,13 @@ import cmdline import command import control -def RunTests(): +def RunTests(debug): """Run the functional tests and any embedded doctests""" + import elf_test import entry_test import fdt_test - import func_test + import ftest + import image_test import test import doctest @@ -44,8 +47,15 @@ def RunTests(): suite.run(result) sys.argv = [sys.argv[0]] - for module in (func_test.TestFunctional, fdt_test.TestFdt, - entry_test.TestEntry): + if debug: + sys.argv.append('-D') + + # Run the entry tests first ,since these need to be the first to import the + # 'entry' module. + suite = unittest.TestLoader().loadTestsFromTestCase(entry_test.TestEntry) + suite.run(result) + for module in (ftest.TestFunctional, fdt_test.TestFdt, elf_test.TestElf, + image_test.TestImage): suite = unittest.TestLoader().loadTestsFromTestCase(module) suite.run(result) @@ -53,22 +63,41 @@ def RunTests(): for test, err in result.errors: print test.id(), err for test, err in result.failures: - print err + print err, result.failures + if result.errors or result.failures: + print 'binman tests FAILED' + return 1 + return 0 def RunTestCoverage(): """Run the tests and check that we get 100% coverage""" # This uses the build output from sandbox_spl to get _libfdt.so - cmd = ('PYTHONPATH=%s/sandbox_spl/tools coverage run ' + cmd = ('PYTHONPATH=$PYTHONPATH:%s/sandbox_spl/tools coverage run ' '--include "tools/binman/*.py" --omit "*test*,*binman.py" ' 'tools/binman/binman.py -t' % options.build_dir) os.system(cmd) stdout = command.Output('coverage', 'report') - coverage = stdout.splitlines()[-1].split(' ')[-1] + lines = stdout.splitlines() + + test_set= set([os.path.basename(line.split()[0]) + for line in lines if '/etype/' in line]) + glob_list = glob.glob(os.path.join(our_path, 'etype/*.py')) + all_set = set([os.path.basename(item) for item in glob_list]) + missing_list = all_set + missing_list.difference_update(test_set) + missing_list.remove('_testing.py') + coverage = lines[-1].split(' ')[-1] + ok = True + if missing_list: + print 'Missing tests for %s' % (', '.join(missing_list)) + ok = False if coverage != '100%': print stdout print "Type 'coverage html' to get a report in htmlcov/index.html" - raise ValueError('Coverage error: %s, but should be 100%%' % coverage) - + print 'Coverage error: %s, but should be 100%%' % coverage + ok = False + if not ok: + raise ValueError('Test coverage failure') def RunBinman(options, args): """Main entry point to binman once arguments are parsed @@ -86,7 +115,7 @@ def RunBinman(options, args): sys.tracebacklimit = 0 if options.test: - RunTests() + ret_code = RunTests(options.debug) elif options.test_coverage: RunTestCoverage() diff --git a/tools/binman/control.py b/tools/binman/control.py index e9d48df030..ffa2bbd80f 100644 --- a/tools/binman/control.py +++ b/tools/binman/control.py @@ -12,6 +12,7 @@ import sys import tools import command +import elf import fdt import fdt_util from image import Image @@ -89,6 +90,7 @@ def Binman(options, args): try: tout.Init(options.verbosity) + elf.debug = options.debug try: tools.SetInputDirs(options.indir) tools.PrepareOutputDir(options.outdir, options.preserve) @@ -109,6 +111,7 @@ def Binman(options, args): image.CheckSize() image.CheckEntries() image.ProcessEntryContents() + image.WriteSymbols() image.BuildImage() finally: tools.FinaliseOutputDir() diff --git a/tools/binman/elf.py b/tools/binman/elf.py new file mode 100644 index 0000000000..80ff2253f0 --- /dev/null +++ b/tools/binman/elf.py @@ -0,0 +1,129 @@ +# Copyright (c) 2016 Google, Inc +# Written by Simon Glass <sjg@chromium.org> +# +# SPDX-License-Identifier: GPL-2.0+ +# +# Handle various things related to ELF images +# + +from collections import namedtuple, OrderedDict +import command +import os +import re +import struct + +import tools + +# This is enabled from control.py +debug = False + +Symbol = namedtuple('Symbol', ['section', 'address', 'size', 'weak']) + + +def GetSymbols(fname, patterns): + """Get the symbols from an ELF file + + Args: + fname: Filename of the ELF file to read + patterns: List of regex patterns to search for, each a string + + Returns: + None, if the file does not exist, or Dict: + key: Name of symbol + value: Hex value of symbol + """ + stdout = command.Output('objdump', '-t', fname, raise_on_error=False) + lines = stdout.splitlines() + if patterns: + re_syms = re.compile('|'.join(patterns)) + else: + re_syms = None + syms = {} + syms_started = False + for line in lines: + if not line or not syms_started: + if 'SYMBOL TABLE' in line: + syms_started = True + line = None # Otherwise code coverage complains about 'continue' + continue + if re_syms and not re_syms.search(line): + continue + + space_pos = line.find(' ') + value, rest = line[:space_pos], line[space_pos + 1:] + flags = rest[:7] + parts = rest[7:].split() + section, size = parts[:2] + if len(parts) > 2: + name = parts[2] + syms[name] = Symbol(section, int(value, 16), int(size,16), + flags[1] == 'w') + return syms + +def GetSymbolAddress(fname, sym_name): + """Get a value of a symbol from an ELF file + + Args: + fname: Filename of the ELF file to read + patterns: List of regex patterns to search for, each a string + + Returns: + Symbol value (as an integer) or None if not found + """ + syms = GetSymbols(fname, [sym_name]) + sym = syms.get(sym_name) + if not sym: + return None + return sym.address + +def LookupAndWriteSymbols(elf_fname, entry, image): + """Replace all symbols in an entry with their correct values + + The entry contents is updated so that values for referenced symbols will be + visible at run time. This is done by finding out the symbols positions in + the entry (using the ELF file) and replacing them with values from binman's + data structures. + + Args: + elf_fname: Filename of ELF image containing the symbol information for + entry + entry: Entry to process + image: Image which can be used to lookup symbol values + """ + fname = tools.GetInputFilename(elf_fname) + syms = GetSymbols(fname, ['image', 'binman']) + if not syms: + return + base = syms.get('__image_copy_start') + if not base: + return + for name, sym in syms.iteritems(): + if name.startswith('_binman'): + msg = ("Image '%s': Symbol '%s'\n in entry '%s'" % + (image.GetPath(), name, entry.GetPath())) + offset = sym.address - base.address + if offset < 0 or offset + sym.size > entry.contents_size: + raise ValueError('%s has offset %x (size %x) but the contents ' + 'size is %x' % (entry.GetPath(), offset, + sym.size, entry.contents_size)) + if sym.size == 4: + pack_string = '<I' + elif sym.size == 8: + pack_string = '<Q' + else: + raise ValueError('%s has size %d: only 4 and 8 are supported' % + (msg, sym.size)) + + # Look up the symbol in our entry tables. + value = image.LookupSymbol(name, sym.weak, msg) + if value is not None: + value += base.address + else: + value = -1 + pack_string = pack_string.lower() + value_bytes = struct.pack(pack_string, value) + if debug: + print('%s:\n insert %s, offset %x, value %x, length %d' % + (msg, name, offset, value, len(value_bytes))) + entry.data = (entry.data[:offset] + value_bytes + + entry.data[offset + sym.size:]) diff --git a/tools/binman/elf_test.py b/tools/binman/elf_test.py new file mode 100644 index 0000000000..e5fc28258d --- /dev/null +++ b/tools/binman/elf_test.py @@ -0,0 +1,122 @@ +# +# Copyright (c) 2017 Google, Inc +# Written by Simon Glass <sjg@chromium.org> +# +# SPDX-License-Identifier: GPL-2.0+ +# +# Test for the elf module + +from contextlib import contextmanager +import os +import sys +import unittest + +try: + from StringIO import StringIO +except ImportError: + from io import StringIO + +import elf + +binman_dir = os.path.dirname(os.path.realpath(sys.argv[0])) + +# Use this to suppress stdout/stderr output: +# with capture_sys_output() as (stdout, stderr) +# ...do something... +@contextmanager +def capture_sys_output(): + capture_out, capture_err = StringIO(), StringIO() + old_out, old_err = sys.stdout, sys.stderr + try: + sys.stdout, sys.stderr = capture_out, capture_err + yield capture_out, capture_err + finally: + sys.stdout, sys.stderr = old_out, old_err + + +class FakeEntry: + def __init__(self, contents_size): + self.contents_size = contents_size + self.data = 'a' * contents_size + + def GetPath(self): + return 'entry_path' + +class FakeImage: + def __init__(self, sym_value=1): + self.sym_value = sym_value + + def GetPath(self): + return 'image_path' + + def LookupSymbol(self, name, weak, msg): + return self.sym_value + +class TestElf(unittest.TestCase): + def testAllSymbols(self): + fname = os.path.join(binman_dir, 'test', 'u_boot_ucode_ptr') + syms = elf.GetSymbols(fname, []) + self.assertIn('.ucode', syms) + + def testRegexSymbols(self): + fname = os.path.join(binman_dir, 'test', 'u_boot_ucode_ptr') + syms = elf.GetSymbols(fname, ['ucode']) + self.assertIn('.ucode', syms) + syms = elf.GetSymbols(fname, ['missing']) + self.assertNotIn('.ucode', syms) + syms = elf.GetSymbols(fname, ['missing', 'ucode']) + self.assertIn('.ucode', syms) + + def testMissingFile(self): + entry = FakeEntry(10) + image = FakeImage() + with self.assertRaises(ValueError) as e: + syms = elf.LookupAndWriteSymbols('missing-file', entry, image) + self.assertIn("Filename 'missing-file' not found in input path", + str(e.exception)) + + def testOutsideFile(self): + entry = FakeEntry(10) + image = FakeImage() + elf_fname = os.path.join(binman_dir, 'test', 'u_boot_binman_syms') + with self.assertRaises(ValueError) as e: + syms = elf.LookupAndWriteSymbols(elf_fname, entry, image) + self.assertIn('entry_path has offset 4 (size 8) but the contents size ' + 'is a', str(e.exception)) + + def testMissingImageStart(self): + entry = FakeEntry(10) + image = FakeImage() + elf_fname = os.path.join(binman_dir, 'test', 'u_boot_binman_syms_bad') + self.assertEqual(elf.LookupAndWriteSymbols(elf_fname, entry, image), + None) + + def testBadSymbolSize(self): + entry = FakeEntry(10) + image = FakeImage() + elf_fname = os.path.join(binman_dir, 'test', 'u_boot_binman_syms_size') + with self.assertRaises(ValueError) as e: + syms = elf.LookupAndWriteSymbols(elf_fname, entry, image) + self.assertIn('has size 1: only 4 and 8 are supported', + str(e.exception)) + + def testNoValue(self): + entry = FakeEntry(20) + image = FakeImage(sym_value=None) + elf_fname = os.path.join(binman_dir, 'test', 'u_boot_binman_syms') + syms = elf.LookupAndWriteSymbols(elf_fname, entry, image) + self.assertEqual(chr(255) * 16 + 'a' * 4, entry.data) + + def testDebug(self): + elf.debug = True + entry = FakeEntry(20) + image = FakeImage() + elf_fname = os.path.join(binman_dir, 'test', 'u_boot_binman_syms') + with capture_sys_output() as (stdout, stderr): + syms = elf.LookupAndWriteSymbols(elf_fname, entry, image) + elf.debug = False + self.assertTrue(len(stdout.getvalue()) > 0) + + +if __name__ == '__main__': + unittest.main() diff --git a/tools/binman/entry_test.py b/tools/binman/entry_test.py index 8a9ae017f0..caa523ebf8 100644 --- a/tools/binman/entry_test.py +++ b/tools/binman/entry_test.py @@ -7,21 +7,55 @@ # Test for the Entry class import collections +import os +import sys import unittest -import entry +import fdt +import fdt_util +import tools class TestEntry(unittest.TestCase): + def GetNode(self): + binman_dir = os.path.dirname(os.path.realpath(sys.argv[0])) + tools.PrepareOutputDir(None) + fname = fdt_util.EnsureCompiled( + os.path.join(binman_dir,('test/05_simple.dts'))) + dtb = fdt.FdtScan(fname) + return dtb.GetNode('/binman/u-boot') + + def test1EntryNoImportLib(self): + """Test that we can import Entry subclassess successfully""" + + sys.modules['importlib'] = None + global entry + import entry + entry.Entry.Create(None, self.GetNode(), 'u-boot') + + def test2EntryImportLib(self): + del sys.modules['importlib'] + global entry + reload(entry) + entry.Entry.Create(None, self.GetNode(), 'u-boot-spl') + tools._RemoveOutputDir() + del entry + def testEntryContents(self): """Test the Entry bass class""" + import entry base_entry = entry.Entry(None, None, None, read_node=False) self.assertEqual(True, base_entry.ObtainContents()) def testUnknownEntry(self): """Test that unknown entry types are detected""" + import entry Node = collections.namedtuple('Node', ['name', 'path']) node = Node('invalid-name', 'invalid-path') with self.assertRaises(ValueError) as e: entry.Entry.Create(None, node, node.name) self.assertIn("Unknown entry type 'invalid-name' in node " "'invalid-path'", str(e.exception)) + + +if __name__ == "__main__": + unittest.main() diff --git a/tools/binman/etype/entry.py b/tools/binman/etype/entry.py index 67c57341ca..5541887d47 100644 --- a/tools/binman/etype/entry.py +++ b/tools/binman/etype/entry.py @@ -198,3 +198,11 @@ class Entry(object): def ProcessContents(self): pass + + def WriteSymbols(self, image): + """Write symbol values into binary files for access at run time + + Args: + image: Image containing the entry + """ + pass diff --git a/tools/binman/etype/u_boot_spl.py b/tools/binman/etype/u_boot_spl.py index 68b0148427..3720b47fef 100644 --- a/tools/binman/etype/u_boot_spl.py +++ b/tools/binman/etype/u_boot_spl.py @@ -6,12 +6,18 @@ # Entry-type module for spl/u-boot-spl.bin # +import elf + from entry import Entry from blob import Entry_blob class Entry_u_boot_spl(Entry_blob): def __init__(self, image, etype, node): Entry_blob.__init__(self, image, etype, node) + self.elf_fname = 'spl/u-boot-spl' def GetDefaultFilename(self): return 'spl/u-boot-spl.bin' + + def WriteSymbols(self, image): + elf.LookupAndWriteSymbols(self.elf_fname, self, image) diff --git a/tools/binman/etype/u_boot_spl_bss_pad.py b/tools/binman/etype/u_boot_spl_bss_pad.py index c005f28191..c37f61db23 100644 --- a/tools/binman/etype/u_boot_spl_bss_pad.py +++ b/tools/binman/etype/u_boot_spl_bss_pad.py @@ -9,6 +9,7 @@ # import command +import elf from entry import Entry from blob import Entry_blob import tools @@ -19,8 +20,8 @@ class Entry_u_boot_spl_bss_pad(Entry_blob): def ObtainContents(self): fname = tools.GetInputFilename('spl/u-boot-spl') - args = [['nm', fname], ['grep', '__bss_size']] - out = command.RunPipe(args, capture=True).stdout.splitlines() - bss_size = int(out[0].split()[0], 16) + bss_size = elf.GetSymbolAddress(fname, '__bss_size') + if not bss_size: + self.Raise('Expected __bss_size symbol in spl/u-boot-spl') self.data = chr(0) * bss_size self.contents_size = bss_size diff --git a/tools/binman/etype/u_boot_spl_dtb.py b/tools/binman/etype/u_boot_spl_dtb.py new file mode 100644 index 0000000000..6c5ce1e996 --- /dev/null +++ b/tools/binman/etype/u_boot_spl_dtb.py @@ -0,0 +1,17 @@ +# Copyright (c) 2016 Google, Inc +# Written by Simon Glass <sjg@chromium.org> +# +# SPDX-License-Identifier: GPL-2.0+ +# +# Entry-type module for U-Boot device tree +# + +from entry import Entry +from blob import Entry_blob + +class Entry_u_boot_spl_dtb(Entry_blob): + def __init__(self, image, etype, node): + Entry_blob.__init__(self, image, etype, node) + + def GetDefaultFilename(self): + return 'spl/u-boot-spl.dtb' diff --git a/tools/binman/etype/u_boot_spl_nodtb.py b/tools/binman/etype/u_boot_spl_nodtb.py new file mode 100644 index 0000000000..880e0c78fb --- /dev/null +++ b/tools/binman/etype/u_boot_spl_nodtb.py @@ -0,0 +1,17 @@ +# Copyright (c) 2016 Google, Inc +# Written by Simon Glass <sjg@chromium.org> +# +# SPDX-License-Identifier: GPL-2.0+ +# +# Entry-type module for 'u-boot-nodtb.bin' +# + +from entry import Entry +from blob import Entry_blob + +class Entry_u_boot_spl_nodtb(Entry_blob): + def __init__(self, image, etype, node): + Entry_blob.__init__(self, image, etype, node) + + def GetDefaultFilename(self): + return 'spl/u-boot-spl-nodtb.bin' diff --git a/tools/binman/etype/u_boot_spl_with_ucode_ptr.py b/tools/binman/etype/u_boot_spl_with_ucode_ptr.py index 1c6706df6d..7b25ccb048 100644 --- a/tools/binman/etype/u_boot_spl_with_ucode_ptr.py +++ b/tools/binman/etype/u_boot_spl_with_ucode_ptr.py @@ -25,4 +25,4 @@ class Entry_u_boot_spl_with_ucode_ptr(Entry_u_boot_with_ucode_ptr): self.elf_fname = 'spl/u-boot-spl' def GetDefaultFilename(self): - return 'spl/u-boot-spl.bin' + return 'spl/u-boot-spl-nodtb.bin' diff --git a/tools/binman/etype/u_boot_ucode.py b/tools/binman/etype/u_boot_ucode.py index 8e51e99a11..f9f434d2cc 100644 --- a/tools/binman/etype/u_boot_ucode.py +++ b/tools/binman/etype/u_boot_ucode.py @@ -58,13 +58,10 @@ class Entry_u_boot_ucode(Entry_blob): def ObtainContents(self): # If the image does not need microcode, there is nothing to do ucode_dest_entry = self.image.FindEntryType('u-boot-with-ucode-ptr') - if ucode_dest_entry and not ucode_dest_entry.target_pos: - self.data = '' - return True - - # Handle microcode in SPL image as well - ucode_dest_entry = self.image.FindEntryType('u-boot-spl-with-ucode-ptr') - if ucode_dest_entry and not ucode_dest_entry.target_pos: + ucode_dest_entry_spl = self.image.FindEntryType( + 'u-boot-spl-with-ucode-ptr') + if ((not ucode_dest_entry or not ucode_dest_entry.target_pos) and + (not ucode_dest_entry_spl or not ucode_dest_entry_spl.target_pos)): self.data = '' return True diff --git a/tools/binman/etype/u_boot_with_ucode_ptr.py b/tools/binman/etype/u_boot_with_ucode_ptr.py index 6f01adb970..99b437130d 100644 --- a/tools/binman/etype/u_boot_with_ucode_ptr.py +++ b/tools/binman/etype/u_boot_with_ucode_ptr.py @@ -9,6 +9,7 @@ import struct import command +import elf from entry import Entry from blob import Entry_blob import fdt_util @@ -31,11 +32,9 @@ class Entry_u_boot_with_ucode_ptr(Entry_blob): def ObtainContents(self): # Figure out where to put the microcode pointer fname = tools.GetInputFilename(self.elf_fname) - args = [['nm', fname], ['grep', '-w', '_dt_ucode_base_size']] - out = (command.RunPipe(args, capture=True, raise_on_error=False). - stdout.splitlines()) - if len(out) == 1: - self.target_pos = int(out[0].split()[0], 16) + sym = elf.GetSymbolAddress(fname, '_dt_ucode_base_size') + if sym: + self.target_pos = sym elif not fdt_util.GetBool(self._node, 'optional-ucode'): self.Raise('Cannot locate _dt_ucode_base_size symbol in u-boot') diff --git a/tools/binman/func_test.py b/tools/binman/ftest.py index c4207ce5d2..5812ab397c 100644 --- a/tools/binman/func_test.py +++ b/tools/binman/ftest.py @@ -20,25 +20,29 @@ import binman import cmdline import command import control -import entry +import elf import fdt import fdt_util import tools import tout # Contents of test files, corresponding to different entry types -U_BOOT_DATA = '1234' -U_BOOT_IMG_DATA = 'img' -U_BOOT_SPL_DATA = '567' -BLOB_DATA = '89' -ME_DATA = '0abcd' -VGA_DATA = 'vga' -U_BOOT_DTB_DATA = 'udtb' -X86_START16_DATA = 'start16' -U_BOOT_NODTB_DATA = 'nodtb with microcode pointer somewhere in here' -FSP_DATA = 'fsp' -CMC_DATA = 'cmc' -VBT_DATA = 'vbt' +U_BOOT_DATA = '1234' +U_BOOT_IMG_DATA = 'img' +U_BOOT_SPL_DATA = '56780123456789abcde' +BLOB_DATA = '89' +ME_DATA = '0abcd' +VGA_DATA = 'vga' +U_BOOT_DTB_DATA = 'udtb' +U_BOOT_SPL_DTB_DATA = 'spldtb' +X86_START16_DATA = 'start16' +X86_START16_SPL_DATA = 'start16spl' +U_BOOT_NODTB_DATA = 'nodtb with microcode pointer somewhere in here' +U_BOOT_SPL_NODTB_DATA = 'splnodtb with microcode pointer somewhere in here' +FSP_DATA = 'fsp' +CMC_DATA = 'cmc' +VBT_DATA = 'vbt' +MRC_DATA = 'mrc' class TestFunctional(unittest.TestCase): """Functional tests for binman @@ -56,6 +60,9 @@ class TestFunctional(unittest.TestCase): """ @classmethod def setUpClass(self): + global entry + import entry + # Handle the case where argv[0] is 'python' self._binman_dir = os.path.dirname(os.path.realpath(sys.argv[0])) self._binman_pathname = os.path.join(self._binman_dir, 'binman') @@ -71,11 +78,17 @@ class TestFunctional(unittest.TestCase): TestFunctional._MakeInputFile('me.bin', ME_DATA) TestFunctional._MakeInputFile('vga.bin', VGA_DATA) TestFunctional._MakeInputFile('u-boot.dtb', U_BOOT_DTB_DATA) + TestFunctional._MakeInputFile('spl/u-boot-spl.dtb', U_BOOT_SPL_DTB_DATA) TestFunctional._MakeInputFile('u-boot-x86-16bit.bin', X86_START16_DATA) + TestFunctional._MakeInputFile('spl/u-boot-x86-16bit-spl.bin', + X86_START16_SPL_DATA) TestFunctional._MakeInputFile('u-boot-nodtb.bin', U_BOOT_NODTB_DATA) + TestFunctional._MakeInputFile('spl/u-boot-spl-nodtb.bin', + U_BOOT_SPL_NODTB_DATA) TestFunctional._MakeInputFile('fsp.bin', FSP_DATA) TestFunctional._MakeInputFile('cmc.bin', CMC_DATA) TestFunctional._MakeInputFile('vbt.bin', VBT_DATA) + TestFunctional._MakeInputFile('mrc.bin', MRC_DATA) self._output_setup = False # ELF file with a '_dt_ucode_base_size' symbol @@ -124,7 +137,10 @@ class TestFunctional(unittest.TestCase): Returns: Return value (0 for success) """ - (options, args) = cmdline.ParseArgs(list(args)) + args = list(args) + if '-D' in sys.argv: + args = args + ['-D'] + (options, args) = cmdline.ParseArgs(args) options.pager = 'binman-invalid-pager' options.build_dir = self._indir @@ -132,14 +148,16 @@ class TestFunctional(unittest.TestCase): # options.verbosity = tout.DEBUG return control.Binman(options, args) - def _DoTestFile(self, fname): + def _DoTestFile(self, fname, debug=False): """Run binman with a given test file Args: fname: Device tree source filename to use (e.g. 05_simple.dts) """ - return self._DoBinman('-p', '-I', self._indir, - '-d', self.TestFile(fname)) + args = ['-p', '-I', self._indir, '-d', self.TestFile(fname)] + if debug: + args.append('-D') + return self._DoBinman(*args) def _SetupDtb(self, fname, outfile='u-boot.dtb'): """Set up a new test device-tree file @@ -292,7 +310,6 @@ class TestFunctional(unittest.TestCase): self.assertEqual(0, len(result.stderr)) self.assertEqual(0, result.return_code) - # Not yet available. def testBoard(self): """Test that we can run it with a specific board""" self._SetupDtb('05_simple.dts', 'sandbox/u-boot.dtb') @@ -352,6 +369,10 @@ class TestFunctional(unittest.TestCase): data = self._DoReadFile('05_simple.dts') self.assertEqual(U_BOOT_DATA, data) + def testSimpleDebug(self): + """Test a simple binman run with debugging enabled""" + data = self._DoTestFile('05_simple.dts', debug=True) + def testDual(self): """Test that we can handle creating two images @@ -553,8 +574,10 @@ class TestFunctional(unittest.TestCase): def testImagePadByte(self): """Test that the image pad byte can be specified""" + with open(self.TestFile('bss_data')) as fd: + TestFunctional._MakeInputFile('spl/u-boot-spl', fd.read()) data = self._DoReadFile('21_image_pad.dts') - self.assertEqual(U_BOOT_SPL_DATA + (chr(0xff) * 9) + U_BOOT_DATA, data) + self.assertEqual(U_BOOT_SPL_DATA + (chr(0xff) * 1) + U_BOOT_DATA, data) def testImageName(self): """Test that image files can be named""" @@ -576,7 +599,7 @@ class TestFunctional(unittest.TestCase): def testPackSorted(self): """Test that entries can be sorted""" data = self._DoReadFile('24_sorted.dts') - self.assertEqual(chr(0) * 5 + U_BOOT_SPL_DATA + chr(0) * 2 + + self.assertEqual(chr(0) * 1 + U_BOOT_SPL_DATA + chr(0) * 2 + U_BOOT_DATA, data) def testPackZeroPosition(self): @@ -604,14 +627,14 @@ class TestFunctional(unittest.TestCase): with self.assertRaises(ValueError) as e: self._DoTestFile('28_pack_4gb_outside.dts') self.assertIn("Node '/binman/u-boot': Position 0x0 (0) is outside " - "the image starting at 0xfffffff0 (4294967280)", + "the image starting at 0xffffffe0 (4294967264)", str(e.exception)) def testPackX86Rom(self): """Test that a basic x86 ROM can be created""" data = self._DoReadFile('29_x86-rom.dts') - self.assertEqual(U_BOOT_DATA + chr(0) * 3 + U_BOOT_SPL_DATA + - chr(0) * 6, data) + self.assertEqual(U_BOOT_DATA + chr(0) * 7 + U_BOOT_SPL_DATA + + chr(0) * 2, data) def testPackX86RomMeNoDesc(self): """Test that an invalid Intel descriptor entry is detected""" @@ -644,19 +667,11 @@ class TestFunctional(unittest.TestCase): data = self._DoReadFile('33_x86-start16.dts') self.assertEqual(X86_START16_DATA, data[:len(X86_START16_DATA)]) - def testPackUbootMicrocode(self): - """Test that x86 microcode can be handled correctly - - We expect to see the following in the image, in order: - u-boot-nodtb.bin with a microcode pointer inserted at the correct - place - u-boot.dtb with the microcode removed - the microcode - """ - data = self._DoReadFile('34_x86_ucode.dts', True) + def _RunMicrocodeTest(self, dts_fname, nodtb_data): + data = self._DoReadFile(dts_fname, True) # Now check the device tree has no microcode - second = data[len(U_BOOT_NODTB_DATA):] + second = data[len(nodtb_data):] fname = tools.GetOutputFilename('test.dtb') with open(fname, 'wb') as fd: fd.write(second) @@ -671,17 +686,30 @@ class TestFunctional(unittest.TestCase): # Check that the microcode appears immediately after the Fdt # This matches the concatenation of the data properties in - # the /microcode/update@xxx nodes in x86_ucode.dts. + # the /microcode/update@xxx nodes in 34_x86_ucode.dts. ucode_data = struct.pack('>4L', 0x12345678, 0x12345679, 0xabcd0000, 0x78235609) self.assertEqual(ucode_data, third[:len(ucode_data)]) - ucode_pos = len(U_BOOT_NODTB_DATA) + fdt_len + ucode_pos = len(nodtb_data) + fdt_len # Check that the microcode pointer was inserted. It should match the # expected position and size pos_and_size = struct.pack('<2L', 0xfffffe00 + ucode_pos, len(ucode_data)) - first = data[:len(U_BOOT_NODTB_DATA)] + first = data[:len(nodtb_data)] + return first, pos_and_size + + def testPackUbootMicrocode(self): + """Test that x86 microcode can be handled correctly + + We expect to see the following in the image, in order: + u-boot-nodtb.bin with a microcode pointer inserted at the correct + place + u-boot.dtb with the microcode removed + the microcode + """ + first, pos_and_size = self._RunMicrocodeTest('34_x86_ucode.dts', + U_BOOT_NODTB_DATA) self.assertEqual('nodtb with microcode' + pos_and_size + ' somewhere in here', first) @@ -811,3 +839,75 @@ class TestFunctional(unittest.TestCase): """Test that an image with a VBT binary can be created""" data = self._DoReadFile('46_intel-vbt.dts') self.assertEqual(VBT_DATA, data[:len(VBT_DATA)]) + + def testSplBssPad(self): + """Test that we can pad SPL's BSS with zeros""" + # ELF file with a '__bss_size' symbol + with open(self.TestFile('bss_data')) as fd: + TestFunctional._MakeInputFile('spl/u-boot-spl', fd.read()) + data = self._DoReadFile('47_spl_bss_pad.dts') + self.assertEqual(U_BOOT_SPL_DATA + (chr(0) * 10) + U_BOOT_DATA, data) + + with open(self.TestFile('u_boot_ucode_ptr')) as fd: + TestFunctional._MakeInputFile('spl/u-boot-spl', fd.read()) + with self.assertRaises(ValueError) as e: + data = self._DoReadFile('47_spl_bss_pad.dts') + self.assertIn('Expected __bss_size symbol in spl/u-boot-spl', + str(e.exception)) + + def testPackStart16Spl(self): + """Test that an image with an x86 start16 region can be created""" + data = self._DoReadFile('48_x86-start16-spl.dts') + self.assertEqual(X86_START16_SPL_DATA, data[:len(X86_START16_SPL_DATA)]) + + def testPackUbootSplMicrocode(self): + """Test that x86 microcode can be handled correctly in SPL + + We expect to see the following in the image, in order: + u-boot-spl-nodtb.bin with a microcode pointer inserted at the + correct place + u-boot.dtb with the microcode removed + the microcode + """ + # ELF file with a '_dt_ucode_base_size' symbol + with open(self.TestFile('u_boot_ucode_ptr')) as fd: + TestFunctional._MakeInputFile('spl/u-boot-spl', fd.read()) + first, pos_and_size = self._RunMicrocodeTest('49_x86_ucode_spl.dts', + U_BOOT_SPL_NODTB_DATA) + self.assertEqual('splnodtb with microc' + pos_and_size + + 'ter somewhere in here', first) + + def testPackMrc(self): + """Test that an image with an MRC binary can be created""" + data = self._DoReadFile('50_intel_mrc.dts') + self.assertEqual(MRC_DATA, data[:len(MRC_DATA)]) + + def testSplDtb(self): + """Test that an image with spl/u-boot-spl.dtb can be created""" + data = self._DoReadFile('51_u_boot_spl_dtb.dts') + self.assertEqual(U_BOOT_SPL_DTB_DATA, data[:len(U_BOOT_SPL_DTB_DATA)]) + + def testSplNoDtb(self): + """Test that an image with spl/u-boot-spl-nodtb.bin can be created""" + data = self._DoReadFile('52_u_boot_spl_nodtb.dts') + self.assertEqual(U_BOOT_SPL_NODTB_DATA, data[:len(U_BOOT_SPL_NODTB_DATA)]) + + def testSymbols(self): + """Test binman can assign symbols embedded in U-Boot""" + elf_fname = self.TestFile('u_boot_binman_syms') + syms = elf.GetSymbols(elf_fname, ['binman', 'image']) + addr = elf.GetSymbolAddress(elf_fname, '__image_copy_start') + self.assertEqual(syms['_binman_u_boot_spl_prop_pos'].address, addr) + + with open(self.TestFile('u_boot_binman_syms')) as fd: + TestFunctional._MakeInputFile('spl/u-boot-spl', fd.read()) + data = self._DoReadFile('53_symbols.dts') + sym_values = struct.pack('<LQL', 0x24 + 0, 0x24 + 24, 0x24 + 20) + expected = (sym_values + U_BOOT_SPL_DATA[16:] + chr(0xff) + + U_BOOT_DATA + + sym_values + U_BOOT_SPL_DATA[16:]) + self.assertEqual(expected, data) + + +if __name__ == "__main__": + unittest.main() diff --git a/tools/binman/image.py b/tools/binman/image.py index 07fc930665..741630f091 100644 --- a/tools/binman/image.py +++ b/tools/binman/image.py @@ -6,11 +6,13 @@ # Class for an image, the output of binman # +from __future__ import print_function + from collections import OrderedDict from operator import attrgetter +import re +import sys -import entry -from entry import Entry import fdt_util import tools @@ -47,7 +49,12 @@ class Image: address. _entries: OrderedDict() of entries """ - def __init__(self, name, node): + def __init__(self, name, node, test=False): + global entry + global Entry + import entry + from entry import Entry + self._node = node self._name = name self._size = None @@ -61,8 +68,9 @@ class Image: self._end_4gb = False self._entries = OrderedDict() - self._ReadNode() - self._ReadEntries() + if not test: + self._ReadNode() + self._ReadEntries() def _ReadNode(self): """Read properties from the image node""" @@ -116,6 +124,14 @@ class Image: """ raise ValueError("Image '%s': %s" % (self._node.path, msg)) + def GetPath(self): + """Get the path of an image (in the FDT) + + Returns: + Full path of the node for this image + """ + return self._node.path + def _ReadEntries(self): for node in self._node.subnodes: self._entries[node.name] = Entry.Create(self, node) @@ -217,6 +233,11 @@ class Image: for entry in self._entries.values(): entry.ProcessContents() + def WriteSymbols(self): + """Write symbol values into binary files for access at run time""" + for entry in self._entries.values(): + entry.WriteSymbols(self) + def BuildImage(self): """Write the image to a file""" fname = tools.GetOutputFilename(self._filename) @@ -227,3 +248,58 @@ class Image: data = entry.GetData() fd.seek(self._pad_before + entry.pos - self._skip_at_start) fd.write(data) + + def LookupSymbol(self, sym_name, optional, msg): + """Look up a symbol in an ELF file + + Looks up a symbol in an ELF file. Only entry types which come from an + ELF image can be used by this function. + + At present the only entry property supported is pos. + + Args: + sym_name: Symbol name in the ELF file to look up in the format + _binman_<entry>_prop_<property> where <entry> is the name of + the entry and <property> is the property to find (e.g. + _binman_u_boot_prop_pos). As a special case, you can append + _any to <entry> to have it search for any matching entry. E.g. + _binman_u_boot_any_prop_pos will match entries called u-boot, + u-boot-img and u-boot-nodtb) + optional: True if the symbol is optional. If False this function + will raise if the symbol is not found + msg: Message to display if an error occurs + + Returns: + Value that should be assigned to that symbol, or None if it was + optional and not found + + Raises: + ValueError if the symbol is invalid or not found, or references a + property which is not supported + """ + m = re.match(r'^_binman_(\w+)_prop_(\w+)$', sym_name) + if not m: + raise ValueError("%s: Symbol '%s' has invalid format" % + (msg, sym_name)) + entry_name, prop_name = m.groups() + entry_name = entry_name.replace('_', '-') + entry = self._entries.get(entry_name) + if not entry: + if entry_name.endswith('-any'): + root = entry_name[:-4] + for name in self._entries: + if name.startswith(root): + rest = name[len(root):] + if rest in ['', '-img', '-nodtb']: + entry = self._entries[name] + if not entry: + err = ("%s: Entry '%s' not found in list (%s)" % + (msg, entry_name, ','.join(self._entries.keys()))) + if optional: + print('Warning: %s' % err, file=sys.stderr) + return None + raise ValueError(err) + if prop_name == 'pos': + return entry.pos + else: + raise ValueError("%s: No such property '%s'" % (msg, prop_name)) diff --git a/tools/binman/image_test.py b/tools/binman/image_test.py new file mode 100644 index 0000000000..1b50dda4dc --- /dev/null +++ b/tools/binman/image_test.py @@ -0,0 +1,46 @@ +# +# Copyright (c) 2017 Google, Inc +# Written by Simon Glass <sjg@chromium.org> +# +# SPDX-License-Identifier: GPL-2.0+ +# +# Test for the image module + +import unittest + +from image import Image +from elf_test import capture_sys_output + +class TestImage(unittest.TestCase): + def testInvalidFormat(self): + image = Image('name', 'node', test=True) + with self.assertRaises(ValueError) as e: + image.LookupSymbol('_binman_something_prop_', False, 'msg') + self.assertIn( + "msg: Symbol '_binman_something_prop_' has invalid format", + str(e.exception)) + + def testMissingSymbol(self): + image = Image('name', 'node', test=True) + image._entries = {} + with self.assertRaises(ValueError) as e: + image.LookupSymbol('_binman_type_prop_pname', False, 'msg') + self.assertIn("msg: Entry 'type' not found in list ()", + str(e.exception)) + + def testMissingSymbolOptional(self): + image = Image('name', 'node', test=True) + image._entries = {} + with capture_sys_output() as (stdout, stderr): + val = image.LookupSymbol('_binman_type_prop_pname', True, 'msg') + self.assertEqual(val, None) + self.assertEqual("Warning: msg: Entry 'type' not found in list ()\n", + stderr.getvalue()) + self.assertEqual('', stdout.getvalue()) + + def testBadProperty(self): + image = Image('name', 'node', test=True) + image._entries = {'u-boot': 1} + with self.assertRaises(ValueError) as e: + image.LookupSymbol('_binman_u_boot_prop_bad', False, 'msg') + self.assertIn("msg: No such property 'bad", str(e.exception)) diff --git a/tools/binman/test/21_image_pad.dts b/tools/binman/test/21_image_pad.dts index daf8385f6d..bf39dc1b6f 100644 --- a/tools/binman/test/21_image_pad.dts +++ b/tools/binman/test/21_image_pad.dts @@ -10,7 +10,7 @@ }; u-boot { - pos = <12>; + pos = <20>; }; }; }; diff --git a/tools/binman/test/24_sorted.dts b/tools/binman/test/24_sorted.dts index 9f4151c932..43a7831341 100644 --- a/tools/binman/test/24_sorted.dts +++ b/tools/binman/test/24_sorted.dts @@ -7,11 +7,11 @@ binman { sort-by-pos; u-boot { - pos = <10>; + pos = <22>; }; u-boot-spl { - pos = <5>; + pos = <1>; }; }; }; diff --git a/tools/binman/test/28_pack_4gb_outside.dts b/tools/binman/test/28_pack_4gb_outside.dts index ff468c7d41..18d6bb5b8a 100644 --- a/tools/binman/test/28_pack_4gb_outside.dts +++ b/tools/binman/test/28_pack_4gb_outside.dts @@ -7,13 +7,13 @@ binman { sort-by-pos; end-at-4gb; - size = <16>; + size = <32>; u-boot { pos = <0>; }; u-boot-spl { - pos = <0xfffffff7>; + pos = <0xffffffeb>; }; }; }; diff --git a/tools/binman/test/29_x86-rom.dts b/tools/binman/test/29_x86-rom.dts index 075ede36ab..d49078e19e 100644 --- a/tools/binman/test/29_x86-rom.dts +++ b/tools/binman/test/29_x86-rom.dts @@ -7,13 +7,13 @@ binman { sort-by-pos; end-at-4gb; - size = <16>; + size = <32>; u-boot { - pos = <0xfffffff0>; + pos = <0xffffffe0>; }; u-boot-spl { - pos = <0xfffffff7>; + pos = <0xffffffeb>; }; }; }; diff --git a/tools/binman/test/47_spl_bss_pad.dts b/tools/binman/test/47_spl_bss_pad.dts new file mode 100644 index 0000000000..6bd88b83f9 --- /dev/null +++ b/tools/binman/test/47_spl_bss_pad.dts @@ -0,0 +1,17 @@ +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + u-boot-spl { + }; + + u-boot-spl-bss-pad { + }; + + u-boot { + }; + }; +}; diff --git a/tools/binman/test/48_x86-start16-spl.dts b/tools/binman/test/48_x86-start16-spl.dts new file mode 100644 index 0000000000..e2009f15f0 --- /dev/null +++ b/tools/binman/test/48_x86-start16-spl.dts @@ -0,0 +1,13 @@ +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + size = <16>; + + x86-start16-spl { + }; + }; +}; diff --git a/tools/binman/test/49_x86_ucode_spl.dts b/tools/binman/test/49_x86_ucode_spl.dts new file mode 100644 index 0000000000..67db93ad50 --- /dev/null +++ b/tools/binman/test/49_x86_ucode_spl.dts @@ -0,0 +1,29 @@ +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + sort-by-pos; + end-at-4gb; + size = <0x200>; + u-boot-spl-with-ucode-ptr { + }; + + u-boot-dtb-with-ucode { + }; + + u-boot-ucode { + }; + }; + + microcode { + update@0 { + data = <0x12345678 0x12345679>; + }; + update@1 { + data = <0xabcd0000 0x78235609>; + }; + }; +}; diff --git a/tools/binman/test/50_intel_mrc.dts b/tools/binman/test/50_intel_mrc.dts new file mode 100644 index 0000000000..54cd52a2b7 --- /dev/null +++ b/tools/binman/test/50_intel_mrc.dts @@ -0,0 +1,13 @@ +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + size = <16>; + + intel-mrc { + }; + }; +}; diff --git a/tools/binman/test/51_u_boot_spl_dtb.dts b/tools/binman/test/51_u_boot_spl_dtb.dts new file mode 100644 index 0000000000..3912f86b4c --- /dev/null +++ b/tools/binman/test/51_u_boot_spl_dtb.dts @@ -0,0 +1,13 @@ +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + size = <16>; + + u-boot-spl-dtb { + }; + }; +}; diff --git a/tools/binman/test/52_u_boot_spl_nodtb.dts b/tools/binman/test/52_u_boot_spl_nodtb.dts new file mode 100644 index 0000000000..7f4e27780f --- /dev/null +++ b/tools/binman/test/52_u_boot_spl_nodtb.dts @@ -0,0 +1,11 @@ +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + u-boot-spl-nodtb { + }; + }; +}; diff --git a/tools/binman/test/53_symbols.dts b/tools/binman/test/53_symbols.dts new file mode 100644 index 0000000000..980b066eb2 --- /dev/null +++ b/tools/binman/test/53_symbols.dts @@ -0,0 +1,20 @@ +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + pad-byte = <0xff>; + u-boot-spl { + }; + + u-boot { + pos = <20>; + }; + + u-boot-spl2 { + type = "u-boot-spl"; + }; + }; +}; diff --git a/tools/binman/test/Makefile b/tools/binman/test/Makefile new file mode 100644 index 0000000000..e58fc80775 --- /dev/null +++ b/tools/binman/test/Makefile @@ -0,0 +1,55 @@ +# +# Builds test programs +# +# Copyright (C) 2017 Google, Inc +# Written by Simon Glass <sjg@chromium.org> +# +# SPDX-License-Identifier: GPL-2.0+ +# + +CFLAGS := -march=i386 -m32 -nostdlib -I ../../../include + +LDS_UCODE := -T u_boot_ucode_ptr.lds +LDS_BINMAN := -T u_boot_binman_syms.lds +LDS_BINMAN_BAD := -T u_boot_binman_syms_bad.lds + +TARGETS = u_boot_ucode_ptr u_boot_no_ucode_ptr bss_data \ + u_boot_binman_syms u_boot_binman_syms.bin u_boot_binman_syms_bad \ + u_boot_binman_syms_size + +all: $(TARGETS) + +u_boot_no_ucode_ptr: CFLAGS += $(LDS_UCODE) +u_boot_no_ucode_ptr: u_boot_no_ucode_ptr.c + +u_boot_ucode_ptr: CFLAGS += $(LDS_UCODE) +u_boot_ucode_ptr: u_boot_ucode_ptr.c + +bss_data: CFLAGS += bss_data.lds +bss_data: bss_data.c + +u_boot_binman_syms.bin: u_boot_binman_syms + objcopy -O binary $< -R .note.gnu.build-id $@ + +u_boot_binman_syms: CFLAGS += $(LDS_BINMAN) +u_boot_binman_syms: u_boot_binman_syms.c + +u_boot_binman_syms_bad: CFLAGS += $(LDS_BINMAN_BAD) +u_boot_binman_syms_bad: u_boot_binman_syms_bad.c + +u_boot_binman_syms_size: CFLAGS += $(LDS_BINMAN) +u_boot_binman_syms_size: u_boot_binman_syms_size.c + +clean: + rm -f $(TARGETS) + +help: + @echo "Makefile for binman test programs" + @echo + @echo "Intended for use on x86 hosts" + @echo + @echo "Targets:" + @echo + @echo -e "\thelp - Print help (this is it!)" + @echo -e "\tall - Builds test programs (default targget)" + @echo -e "\tclean - Delete output files" diff --git a/tools/binman/test/bss_data b/tools/binman/test/bss_data Binary files differnew file mode 100755 index 0000000000..afa28282aa --- /dev/null +++ b/tools/binman/test/bss_data diff --git a/tools/binman/test/bss_data.c b/tools/binman/test/bss_data.c new file mode 100644 index 0000000000..e0305c382c --- /dev/null +++ b/tools/binman/test/bss_data.c @@ -0,0 +1,18 @@ +/* + * Copyright (c) 2016 Google, Inc + * + * SPDX-License-Identifier: GPL-2.0+ + * + * Simple program to create a _dt_ucode_base_size symbol which can be read + * by binutils. This is used by binman tests. + */ + +int bss_data[10]; +int __bss_size = sizeof(bss_data); + +int main() +{ + bss_data[2] = 2; + + return 0; +} diff --git a/tools/binman/test/bss_data.lds b/tools/binman/test/bss_data.lds new file mode 100644 index 0000000000..6b2fe09d35 --- /dev/null +++ b/tools/binman/test/bss_data.lds @@ -0,0 +1,16 @@ +/* + * Copyright (c) 2016 Google, Inc + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +OUTPUT_FORMAT("elf32-i386", "elf32-i386", "elf32-i386") +OUTPUT_ARCH(i386) +ENTRY(_start) + +SECTIONS +{ + . = 0xfffffdf0; + _start = .; + __bss_size = 10; +} diff --git a/tools/binman/test/u_boot_binman_syms b/tools/binman/test/u_boot_binman_syms Binary files differnew file mode 100755 index 0000000000..2e02dc0ca9 --- /dev/null +++ b/tools/binman/test/u_boot_binman_syms diff --git a/tools/binman/test/u_boot_binman_syms.c b/tools/binman/test/u_boot_binman_syms.c new file mode 100644 index 0000000000..a975476944 --- /dev/null +++ b/tools/binman/test/u_boot_binman_syms.c @@ -0,0 +1,14 @@ +/* + * Copyright (c) 2017 Google, Inc + * + * SPDX-License-Identifier: GPL-2.0+ + * + * Simple program to create some binman symbols. This is used by binman tests. + */ + +#define CONFIG_BINMAN +#include <binman_sym.h> + +binman_sym_declare(unsigned long, u_boot_spl, pos); +binman_sym_declare(unsigned long long, u_boot_spl2, pos); +binman_sym_declare(unsigned long, u_boot_any, pos); diff --git a/tools/binman/test/u_boot_binman_syms.lds b/tools/binman/test/u_boot_binman_syms.lds new file mode 100644 index 0000000000..d3130cdeb3 --- /dev/null +++ b/tools/binman/test/u_boot_binman_syms.lds @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2016 Google, Inc + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +OUTPUT_FORMAT("elf32-i386", "elf32-i386", "elf32-i386") +OUTPUT_ARCH(i386) +ENTRY(_start) + +SECTIONS +{ + . = 0x00000000; + _start = .; + + . = ALIGN(4); + .text : + { + __image_copy_start = .; + *(.text*) + } + + . = ALIGN(4); + .binman_sym_table : { + __binman_sym_start = .; + KEEP(*(SORT(.binman_sym*))); + __binman_sym_end = .; + } + +} diff --git a/tools/binman/test/u_boot_binman_syms_bad b/tools/binman/test/u_boot_binman_syms_bad Binary files differnew file mode 100755 index 0000000000..8da3d9d48f --- /dev/null +++ b/tools/binman/test/u_boot_binman_syms_bad diff --git a/tools/binman/test/u_boot_binman_syms_bad.c b/tools/binman/test/u_boot_binman_syms_bad.c new file mode 120000 index 0000000000..939b2e965f --- /dev/null +++ b/tools/binman/test/u_boot_binman_syms_bad.c @@ -0,0 +1 @@ +u_boot_binman_syms.c
\ No newline at end of file diff --git a/tools/binman/test/u_boot_binman_syms_bad.lds b/tools/binman/test/u_boot_binman_syms_bad.lds new file mode 100644 index 0000000000..0b474b5374 --- /dev/null +++ b/tools/binman/test/u_boot_binman_syms_bad.lds @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2016 Google, Inc + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +OUTPUT_FORMAT("elf32-i386", "elf32-i386", "elf32-i386") +OUTPUT_ARCH(i386) +ENTRY(_start) + +SECTIONS +{ + . = 0x00000000; + _start = .; + + . = ALIGN(4); + .text : + { + *(.text*) + } + + . = ALIGN(4); + .binman_sym_table : { + __binman_sym_start = .; + KEEP(*(SORT(.binman_sym*))); + __binman_sym_end = .; + } + +} diff --git a/tools/binman/test/u_boot_binman_syms_size b/tools/binman/test/u_boot_binman_syms_size Binary files differnew file mode 100755 index 0000000000..d691e897c0 --- /dev/null +++ b/tools/binman/test/u_boot_binman_syms_size diff --git a/tools/binman/test/u_boot_binman_syms_size.c b/tools/binman/test/u_boot_binman_syms_size.c new file mode 100644 index 0000000000..ee4c048b28 --- /dev/null +++ b/tools/binman/test/u_boot_binman_syms_size.c @@ -0,0 +1,12 @@ +/* + * Copyright (c) 2017 Google, Inc + * + * SPDX-License-Identifier: GPL-2.0+ + * + * Simple program to create some binman symbols. This is used by binman tests. + */ + +#define CONFIG_BINMAN +#include <binman_sym.h> + +binman_sym_declare(char, u_boot_spl, pos); diff --git a/tools/binman/test/u_boot_no_ucode_ptr.c b/tools/binman/test/u_boot_no_ucode_ptr.c index a17bb4c6c2..c4a2b85fc9 100644 --- a/tools/binman/test/u_boot_no_ucode_ptr.c +++ b/tools/binman/test/u_boot_no_ucode_ptr.c @@ -5,10 +5,6 @@ * * Simple program to create a bad _dt_ucode_base_size symbol to create an * error when it is used. This is used by binman tests. - * - * Build with: - * cc -march=i386 -m32 -o u_boot_no_ucode_ptr -T u_boot_ucode_ptr.lds \ - -nostdlib u_boot_no_ucode_ptr.c */ static unsigned long not__dt_ucode_base_size[2] diff --git a/tools/binman/test/u_boot_ucode_ptr.c b/tools/binman/test/u_boot_ucode_ptr.c index 434c9f4400..734d54f0d4 100644 --- a/tools/binman/test/u_boot_ucode_ptr.c +++ b/tools/binman/test/u_boot_ucode_ptr.c @@ -4,11 +4,7 @@ * SPDX-License-Identifier: GPL-2.0+ * * Simple program to create a _dt_ucode_base_size symbol which can be read - * by 'nm'. This is used by binman tests. - * - * Build with: - * cc -march=i386 -m32 -o u_boot_ucode_ptr -T u_boot_ucode_ptr.lds -nostdlib \ - u_boot_ucode_ptr.c + * by binutils. This is used by binman tests. */ static unsigned long _dt_ucode_base_size[2] diff --git a/tools/buildman/buildman.py b/tools/buildman/buildman.py index 607429df7b..11a4f162c5 100755 --- a/tools/buildman/buildman.py +++ b/tools/buildman/buildman.py @@ -30,7 +30,7 @@ import patchstream import terminal import toolchain -def RunTests(): +def RunTests(skip_net_tests): import func_test import test import doctest @@ -41,6 +41,8 @@ def RunTests(): suite.run(result) sys.argv = [sys.argv[0]] + if skip_net_tests: + test.use_network = False for module in (test.TestBuild, func_test.TestFunctional): suite = unittest.TestLoader().loadTestsFromTestCase(module) suite.run(result) @@ -56,7 +58,7 @@ options, args = cmdline.ParseArgs() # Run our meagre tests if options.test: - RunTests() + RunTests(options.skip_net_tests) # Build selected commits for selected boards else: diff --git a/tools/buildman/cmdline.py b/tools/buildman/cmdline.py index 0060e0317c..74247f0aff 100644 --- a/tools/buildman/cmdline.py +++ b/tools/buildman/cmdline.py @@ -82,6 +82,8 @@ def ParseArgs(): default=False, help='Show a build summary') parser.add_option('-S', '--show-sizes', action='store_true', default=False, help='Show image size variation in summary') + parser.add_option('--skip-net-tests', action='store_true', default=False, + help='Skip tests which need the network') parser.add_option('--step', type='int', default=1, help='Only build every n commits (0=just first and last)') parser.add_option('-t', '--test', action='store_true', dest='test', diff --git a/tools/buildman/test.py b/tools/buildman/test.py index 53ebc3756c..e564a8a142 100644 --- a/tools/buildman/test.py +++ b/tools/buildman/test.py @@ -24,6 +24,8 @@ import commit import terminal import toolchain +use_network = True + settings_data = ''' # Buildman settings file @@ -89,6 +91,7 @@ boards = [ ['Active', 'arm', 'armv7', '', 'Tester', 'ARM Board 1', 'board0', ''], ['Active', 'arm', 'armv7', '', 'Tester', 'ARM Board 2', 'board1', ''], ['Active', 'powerpc', 'powerpc', '', 'Tester', 'PowerPC board 1', 'board2', ''], + ['Active', 'powerpc', 'mpc83xx', '', 'Tester', 'PowerPC board 2', 'board3', ''], ['Active', 'sandbox', 'sandbox', '', 'Tester', 'Sandbox board', 'board4', ''], ] @@ -311,50 +314,60 @@ class TestBuild(unittest.TestCase): def testBoardSingle(self): """Test single board selection""" self.assertEqual(self.boards.SelectBoards(['sandbox']), - {'all': 1, 'sandbox': 1}) + {'all': ['board4'], 'sandbox': ['board4']}) def testBoardArch(self): """Test single board selection""" self.assertEqual(self.boards.SelectBoards(['arm']), - {'all': 2, 'arm': 2}) + {'all': ['board0', 'board1'], + 'arm': ['board0', 'board1']}) def testBoardArchSingle(self): """Test single board selection""" self.assertEqual(self.boards.SelectBoards(['arm sandbox']), - {'all': 3, 'arm': 2, 'sandbox' : 1}) + {'sandbox': ['board4'], + 'all': ['board0', 'board1', 'board4'], + 'arm': ['board0', 'board1']}) + def testBoardArchSingleMultiWord(self): """Test single board selection""" self.assertEqual(self.boards.SelectBoards(['arm', 'sandbox']), - {'all': 3, 'arm': 2, 'sandbox' : 1}) + {'sandbox': ['board4'], 'all': ['board0', 'board1', 'board4'], 'arm': ['board0', 'board1']}) def testBoardSingleAnd(self): """Test single board selection""" self.assertEqual(self.boards.SelectBoards(['Tester & arm']), - {'all': 2, 'Tester&arm': 2}) + {'Tester&arm': ['board0', 'board1'], 'all': ['board0', 'board1']}) def testBoardTwoAnd(self): """Test single board selection""" self.assertEqual(self.boards.SelectBoards(['Tester', '&', 'arm', 'Tester' '&', 'powerpc', 'sandbox']), - {'all': 5, 'Tester&powerpc': 2, 'Tester&arm': 2, - 'sandbox' : 1}) + {'sandbox': ['board4'], + 'all': ['board0', 'board1', 'board2', 'board3', + 'board4'], + 'Tester&powerpc': ['board2', 'board3'], + 'Tester&arm': ['board0', 'board1']}) def testBoardAll(self): """Test single board selection""" - self.assertEqual(self.boards.SelectBoards([]), {'all': 5}) + self.assertEqual(self.boards.SelectBoards([]), + {'all': ['board0', 'board1', 'board2', 'board3', + 'board4']}) def testBoardRegularExpression(self): """Test single board selection""" self.assertEqual(self.boards.SelectBoards(['T.*r&^Po']), - {'T.*r&^Po': 2, 'all': 2}) + {'all': ['board2', 'board3'], + 'T.*r&^Po': ['board2', 'board3']}) def testBoardDuplicate(self): """Test single board selection""" self.assertEqual(self.boards.SelectBoards(['sandbox sandbox', 'sandbox']), - {'all': 1, 'sandbox': 1}) + {'all': ['board4'], 'sandbox': ['board4']}) def CheckDirs(self, build, dirname): self.assertEqual('base%s' % dirname, build._GetOutputDir(1)) self.assertEqual('base%s/fred' % dirname, @@ -410,8 +423,9 @@ class TestBuild(unittest.TestCase): def testToolchainDownload(self): """Test that we can download toolchains""" - self.assertEqual('https://www.kernel.org/pub/tools/crosstool/files/bin/x86_64/4.9.0/x86_64-gcc-4.9.0-nolibc_arm-unknown-linux-gnueabi.tar.xz', - self.toolchains.LocateArchUrl('arm')) + if use_network: + self.assertEqual('https://www.kernel.org/pub/tools/crosstool/files/bin/x86_64/4.9.0/x86_64-gcc-4.9.0-nolibc_arm-unknown-linux-gnueabi.tar.xz', + self.toolchains.LocateArchUrl('arm')) if __name__ == "__main__": diff --git a/tools/dtoc/fdt_util.py b/tools/dtoc/fdt_util.py index 338d47a5e1..ba0b6cc381 100644 --- a/tools/dtoc/fdt_util.py +++ b/tools/dtoc/fdt_util.py @@ -75,7 +75,8 @@ def EnsureCompiled(fname): search_list = [] for path in search_paths: search_list.extend(['-i', path]) - args = ['-I', 'dts', '-o', dtb_output, '-O', 'dtb'] + args = ['-I', 'dts', '-o', dtb_output, '-O', 'dtb', + '-W', 'no-unit_address_vs_reg'] args.extend(search_list) args.append(dts_input) command.Run('dtc', *args) diff --git a/tools/dtoc/test_dtoc.py b/tools/dtoc/test_dtoc.py index cc009b2a25..41ed80e6da 100644 --- a/tools/dtoc/test_dtoc.py +++ b/tools/dtoc/test_dtoc.py @@ -26,6 +26,27 @@ import tools our_path = os.path.dirname(os.path.realpath(__file__)) +HEADER = '''/* + * DO NOT MODIFY + * + * This file was generated by dtoc from a .dtb (device tree binary) file. + */ + +#include <stdbool.h> +#include <libfdt.h>''' + +C_HEADER = '''/* + * DO NOT MODIFY + * + * This file was generated by dtoc from a .dtb (device tree binary) file. + */ + +#include <common.h> +#include <dm.h> +#include <dt-structs.h> +''' + + def get_dtb_file(dts_fname): """Compile a .dts file to a .dtb @@ -104,13 +125,12 @@ class TestDtoc(unittest.TestCase): dtb_platdata.run_steps(['struct'], dtb_file, False, output) with open(output) as infile: lines = infile.read().splitlines() - self.assertEqual(['#include <stdbool.h>', '#include <libfdt.h>'], lines) + self.assertEqual(HEADER.splitlines(), lines) dtb_platdata.run_steps(['platdata'], dtb_file, False, output) with open(output) as infile: lines = infile.read().splitlines() - self.assertEqual(['#include <common.h>', '#include <dm.h>', - '#include <dt-structs.h>', ''], lines) + self.assertEqual(C_HEADER.splitlines() + [''], lines) def test_simple(self): """Test output from some simple nodes with various types of data""" @@ -119,8 +139,7 @@ class TestDtoc(unittest.TestCase): dtb_platdata.run_steps(['struct'], dtb_file, False, output) with open(output) as infile: data = infile.read() - self.assertEqual('''#include <stdbool.h> -#include <libfdt.h> + self.assertEqual(HEADER + ''' struct dtd_sandbox_i2c_test { }; struct dtd_sandbox_pmic_test { @@ -144,10 +163,7 @@ struct dtd_sandbox_spl_test_2 { dtb_platdata.run_steps(['platdata'], dtb_file, False, output) with open(output) as infile: data = infile.read() - self.assertEqual('''#include <common.h> -#include <dm.h> -#include <dt-structs.h> - + self.assertEqual(C_HEADER + ''' static struct dtd_sandbox_spl_test dtv_spl_test = { \t.bytearray\t\t= {0x6, 0x0, 0x0}, \t.byteval\t\t= 0x5, @@ -225,8 +241,7 @@ U_BOOT_DEVICE(pmic_at_9) = { dtb_platdata.run_steps(['struct'], dtb_file, False, output) with open(output) as infile: data = infile.read() - self.assertEqual('''#include <stdbool.h> -#include <libfdt.h> + self.assertEqual(HEADER + ''' struct dtd_source { \tstruct phandle_2_arg clocks[4]; }; @@ -238,10 +253,7 @@ struct dtd_target { dtb_platdata.run_steps(['platdata'], dtb_file, False, output) with open(output) as infile: data = infile.read() - self.assertEqual('''#include <common.h> -#include <dm.h> -#include <dt-structs.h> - + self.assertEqual(C_HEADER + ''' static struct dtd_target dtv_phandle_target = { \t.intval\t\t\t= 0x0, }; @@ -291,8 +303,7 @@ U_BOOT_DEVICE(phandle_source) = { dtb_platdata.run_steps(['struct'], dtb_file, False, output) with open(output) as infile: data = infile.read() - self.assertEqual('''#include <stdbool.h> -#include <libfdt.h> + self.assertEqual(HEADER + ''' struct dtd_compat1 { \tfdt32_t\t\tintval; }; @@ -303,10 +314,7 @@ struct dtd_compat1 { dtb_platdata.run_steps(['platdata'], dtb_file, False, output) with open(output) as infile: data = infile.read() - self.assertEqual('''#include <common.h> -#include <dm.h> -#include <dt-structs.h> - + self.assertEqual(C_HEADER + ''' static struct dtd_compat1 dtv_spl_test = { \t.intval\t\t\t= 0x1, }; @@ -325,8 +333,7 @@ U_BOOT_DEVICE(spl_test) = { dtb_platdata.run_steps(['struct'], dtb_file, False, output) with open(output) as infile: data = infile.read() - self.assertEqual('''#include <stdbool.h> -#include <libfdt.h> + self.assertEqual(HEADER + ''' struct dtd_test1 { \tfdt64_t\t\treg[2]; }; @@ -341,10 +348,7 @@ struct dtd_test3 { dtb_platdata.run_steps(['platdata'], dtb_file, False, output) with open(output) as infile: data = infile.read() - self.assertEqual('''#include <common.h> -#include <dm.h> -#include <dt-structs.h> - + self.assertEqual(C_HEADER + ''' static struct dtd_test1 dtv_test1 = { \t.reg\t\t\t= {0x1234, 0x5678}, }; @@ -381,8 +385,7 @@ U_BOOT_DEVICE(test3) = { dtb_platdata.run_steps(['struct'], dtb_file, False, output) with open(output) as infile: data = infile.read() - self.assertEqual('''#include <stdbool.h> -#include <libfdt.h> + self.assertEqual(HEADER + ''' struct dtd_test1 { \tfdt32_t\t\treg[2]; }; @@ -394,10 +397,7 @@ struct dtd_test2 { dtb_platdata.run_steps(['platdata'], dtb_file, False, output) with open(output) as infile: data = infile.read() - self.assertEqual('''#include <common.h> -#include <dm.h> -#include <dt-structs.h> - + self.assertEqual(C_HEADER + ''' static struct dtd_test1 dtv_test1 = { \t.reg\t\t\t= {0x1234, 0x5678}, }; @@ -425,8 +425,7 @@ U_BOOT_DEVICE(test2) = { dtb_platdata.run_steps(['struct'], dtb_file, False, output) with open(output) as infile: data = infile.read() - self.assertEqual('''#include <stdbool.h> -#include <libfdt.h> + self.assertEqual(HEADER + ''' struct dtd_test1 { \tfdt64_t\t\treg[2]; }; @@ -441,10 +440,7 @@ struct dtd_test3 { dtb_platdata.run_steps(['platdata'], dtb_file, False, output) with open(output) as infile: data = infile.read() - self.assertEqual('''#include <common.h> -#include <dm.h> -#include <dt-structs.h> - + self.assertEqual(C_HEADER + ''' static struct dtd_test1 dtv_test1 = { \t.reg\t\t\t= {0x123400000000, 0x5678}, }; @@ -481,8 +477,7 @@ U_BOOT_DEVICE(test3) = { dtb_platdata.run_steps(['struct'], dtb_file, False, output) with open(output) as infile: data = infile.read() - self.assertEqual('''#include <stdbool.h> -#include <libfdt.h> + self.assertEqual(HEADER + ''' struct dtd_test1 { \tfdt64_t\t\treg[2]; }; @@ -497,10 +492,7 @@ struct dtd_test3 { dtb_platdata.run_steps(['platdata'], dtb_file, False, output) with open(output) as infile: data = infile.read() - self.assertEqual('''#include <common.h> -#include <dm.h> -#include <dt-structs.h> - + self.assertEqual(C_HEADER + ''' static struct dtd_test1 dtv_test1 = { \t.reg\t\t\t= {0x1234, 0x567800000000}, }; diff --git a/tools/env/fw_env.c b/tools/env/fw_env.c index 963a6152a5..18c2324d2f 100644 --- a/tools/env/fw_env.c +++ b/tools/env/fw_env.c @@ -25,6 +25,7 @@ #include <sys/ioctl.h> #include <sys/stat.h> #include <unistd.h> +#include <dirent.h> #ifdef MTD_OLD # include <stdint.h> @@ -34,6 +35,8 @@ # include <mtd/mtd-user.h> #endif +#include <mtd/ubi-user.h> + #include "fw_env_private.h" #include "fw_env.h" @@ -58,6 +61,7 @@ struct envdev_s { ulong erase_size; /* device erase size */ ulong env_sectors; /* number of environment sectors */ uint8_t mtd_type; /* type of the MTD device */ + int is_ubi; /* set if we use UBI volume */ }; static struct envdev_s envdevices[2] = @@ -76,6 +80,7 @@ static int dev_current; #define DEVESIZE(i) envdevices[(i)].erase_size #define ENVSECTORS(i) envdevices[(i)].env_sectors #define DEVTYPE(i) envdevices[(i)].mtd_type +#define IS_UBI(i) envdevices[(i)].is_ubi #define CUR_ENVSIZE ENVSIZE(dev_current) @@ -120,6 +125,228 @@ static unsigned char obsolete_flag = 0; #define DEFAULT_ENV_INSTANCE_STATIC #include <env_default.h> +#define UBI_DEV_START "/dev/ubi" +#define UBI_SYSFS "/sys/class/ubi" +#define UBI_VOL_NAME_PATT "ubi%d_%d" + +static int is_ubi_devname(const char *devname) +{ + return !strncmp(devname, UBI_DEV_START, sizeof(UBI_DEV_START) - 1); +} + +static int ubi_check_volume_sysfs_name(const char *volume_sysfs_name, + const char *volname) +{ + char path[256]; + FILE *file; + char *name; + int ret; + + strcpy(path, UBI_SYSFS "/"); + strcat(path, volume_sysfs_name); + strcat(path, "/name"); + + file = fopen(path, "r"); + if (!file) + return -1; + + ret = fscanf(file, "%ms", &name); + fclose(file); + if (ret <= 0 || !name) { + fprintf(stderr, + "Failed to read from file %s, ret = %d, name = %s\n", + path, ret, name); + return -1; + } + + if (!strcmp(name, volname)) { + free(name); + return 0; + } + free(name); + + return -1; +} + +static int ubi_get_volnum_by_name(int devnum, const char *volname) +{ + DIR *sysfs_ubi; + struct dirent *dirent; + int ret; + int tmp_devnum; + int volnum; + + sysfs_ubi = opendir(UBI_SYSFS); + if (!sysfs_ubi) + return -1; + +#ifdef DEBUG + fprintf(stderr, "Looking for volume name \"%s\"\n", volname); +#endif + + while (1) { + dirent = readdir(sysfs_ubi); + if (!dirent) + return -1; + + ret = sscanf(dirent->d_name, UBI_VOL_NAME_PATT, + &tmp_devnum, &volnum); + if (ret == 2 && devnum == tmp_devnum) { + if (ubi_check_volume_sysfs_name(dirent->d_name, + volname) == 0) + return volnum; + } + } + + return -1; +} + +static int ubi_get_devnum_by_devname(const char *devname) +{ + int devnum; + int ret; + + ret = sscanf(devname + sizeof(UBI_DEV_START) - 1, "%d", &devnum); + if (ret != 1) + return -1; + + return devnum; +} + +static const char *ubi_get_volume_devname(const char *devname, + const char *volname) +{ + char *volume_devname; + int volnum; + int devnum; + int ret; + + devnum = ubi_get_devnum_by_devname(devname); + if (devnum < 0) + return NULL; + + volnum = ubi_get_volnum_by_name(devnum, volname); + if (volnum < 0) + return NULL; + + ret = asprintf(&volume_devname, "%s_%d", devname, volnum); + if (ret < 0) + return NULL; + +#ifdef DEBUG + fprintf(stderr, "Found ubi volume \"%s:%s\" -> %s\n", + devname, volname, volume_devname); +#endif + + return volume_devname; +} + +static void ubi_check_dev(unsigned int dev_id) +{ + char *devname = (char *)DEVNAME(dev_id); + char *pname; + const char *volname = NULL; + const char *volume_devname; + + if (!is_ubi_devname(DEVNAME(dev_id))) + return; + + IS_UBI(dev_id) = 1; + + for (pname = devname; *pname != '\0'; pname++) { + if (*pname == ':') { + *pname = '\0'; + volname = pname + 1; + break; + } + } + + if (volname) { + /* Let's find real volume device name */ + volume_devname = ubi_get_volume_devname(devname, volname); + if (!volume_devname) { + fprintf(stderr, "Didn't found ubi volume \"%s\"\n", + volname); + return; + } + + free(devname); + DEVNAME(dev_id) = volume_devname; + } +} + +static int ubi_update_start(int fd, int64_t bytes) +{ + if (ioctl(fd, UBI_IOCVOLUP, &bytes)) + return -1; + return 0; +} + +static int ubi_read(int fd, void *buf, size_t count) +{ + ssize_t ret; + + while (count > 0) { + ret = read(fd, buf, count); + if (ret > 0) { + count -= ret; + buf += ret; + + continue; + } + + if (ret == 0) { + /* + * Happens in case of too short volume data size. If we + * return error status we will fail it will be treated + * as UBI device error. + * + * Leave catching this error to CRC check. + */ + fprintf(stderr, "Warning: end of data on ubi volume\n"); + return 0; + } else if (errno == EBADF) { + /* + * Happens in case of corrupted volume. The same as + * above, we cannot return error now, as we will still + * be able to successfully write environment later. + */ + fprintf(stderr, "Warning: corrupted volume?\n"); + return 0; + } else if (errno == EINTR) { + continue; + } + + fprintf(stderr, "Cannot read %u bytes from ubi volume, %s\n", + (unsigned int)count, strerror(errno)); + return -1; + } + + return 0; +} + +static int ubi_write(int fd, const void *buf, size_t count) +{ + ssize_t ret; + + while (count > 0) { + ret = write(fd, buf, count); + if (ret <= 0) { + if (ret < 0 && errno == EINTR) + continue; + + fprintf(stderr, "Cannot write %u bytes to ubi volume\n", + (unsigned int)count); + return -1; + } + + count -= ret; + buf += ret; + } + + return 0; +} + static int flash_io (int mode); static int parse_config(struct env_opts *opts); @@ -960,6 +1187,12 @@ static int flash_write (int fd_current, int fd_target, int dev_target) DEVOFFSET (dev_target), DEVNAME (dev_target)); #endif + if (IS_UBI(dev_target)) { + if (ubi_update_start(fd_target, CUR_ENVSIZE) < 0) + return 0; + return ubi_write(fd_target, environment.image, CUR_ENVSIZE); + } + rc = flash_write_buf(dev_target, fd_target, environment.image, CUR_ENVSIZE); if (rc < 0) @@ -984,6 +1217,12 @@ static int flash_read (int fd) { int rc; + if (IS_UBI(dev_current)) { + DEVTYPE(dev_current) = MTD_ABSENT; + + return ubi_read(fd, environment.image, CUR_ENVSIZE); + } + rc = flash_read_buf(dev_current, fd, environment.image, CUR_ENVSIZE, DEVOFFSET(dev_current)); if (rc != CUR_ENVSIZE) @@ -1165,7 +1404,8 @@ int fw_env_open(struct env_opts *opts) DEVTYPE(!dev_current) == MTD_UBIVOLUME) { environment.flag_scheme = FLAG_INCREMENTAL; } else if (DEVTYPE(dev_current) == MTD_ABSENT && - DEVTYPE(!dev_current) == MTD_ABSENT) { + DEVTYPE(!dev_current) == MTD_ABSENT && + IS_UBI(dev_current) == IS_UBI(!dev_current)) { environment.flag_scheme = FLAG_INCREMENTAL; } else { fprintf (stderr, "Incompatible flash types!\n"); @@ -1271,8 +1511,12 @@ int fw_env_close(struct env_opts *opts) static int check_device_config(int dev) { struct stat st; + int32_t lnum = 0; int fd, rc = 0; + /* Fills in IS_UBI(), converts DEVNAME() with ubi volume name */ + ubi_check_dev(dev); + fd = open(DEVNAME(dev), O_RDONLY); if (fd < 0) { fprintf(stderr, @@ -1288,7 +1532,14 @@ static int check_device_config(int dev) goto err; } - if (S_ISCHR(st.st_mode)) { + if (IS_UBI(dev)) { + rc = ioctl(fd, UBI_IOCEBISMAP, &lnum); + if (rc < 0) { + fprintf(stderr, "Cannot get UBI information for %s\n", + DEVNAME(dev)); + goto err; + } + } else if (S_ISCHR(st.st_mode)) { struct mtd_info_user mtdinfo; rc = ioctl(fd, MEMGETINFO, &mtdinfo); if (rc < 0) { diff --git a/tools/env/fw_env.config b/tools/env/fw_env.config index 7916ebdb1f..053895a2c0 100644 --- a/tools/env/fw_env.config +++ b/tools/env/fw_env.config @@ -28,3 +28,11 @@ # VFAT example #/boot/uboot.env 0x0000 0x4000 + +# UBI volume +#/dev/ubi0_0 0x0 0x1f000 0x1f000 +#/dev/ubi0_1 0x0 0x1f000 0x1f000 + +# UBI volume by name +#/dev/ubi0:env 0x0 0x1f000 0x1f000 +#/dev/ubi0:env-redund 0x0 0x1f000 0x1f000 diff --git a/tools/libfdt/fdt_wip.c b/tools/libfdt/fdt_wip.c new file mode 100644 index 0000000000..bad73ed9e7 --- /dev/null +++ b/tools/libfdt/fdt_wip.c @@ -0,0 +1,2 @@ +#include "fdt_host.h" +#include "../scripts/dtc/libfdt/fdt_wip.c" diff --git a/tools/omapimage.c b/tools/omapimage.c index e31b94ae4f..e7c46388f4 100644 --- a/tools/omapimage.c +++ b/tools/omapimage.c @@ -20,6 +20,8 @@ #include "gpheader.h" #include "omapimage.h" +#define DIV_ROUND_UP(n, d) (((n) + (d) - 1) / (d)) + /* Header size is CH header rounded up to 512 bytes plus GP header */ #define OMAP_CH_HDR_SIZE 512 #define OMAP_FILE_HDR_SIZE (OMAP_CH_HDR_SIZE + GPIMAGE_HDR_SIZE) @@ -150,8 +152,10 @@ static void omapimage_set_header(void *ptr, struct stat *sbuf, int ifd, do_swap32 = 1; int swapped = 0; uint32_t *data = (uint32_t *)ptr; + const off_t size_in_words = + DIV_ROUND_UP(sbuf->st_size, sizeof(uint32_t)); - while (swapped <= (sbuf->st_size / sizeof(uint32_t))) { + while (swapped < size_in_words) { *data = cpu_to_be32(*data); swapped++; data++; diff --git a/tools/patman/test.py b/tools/patman/test.py index 20dc9c1e0d..51145e8390 100644 --- a/tools/patman/test.py +++ b/tools/patman/test.py @@ -88,8 +88,7 @@ Signed-off-by: Simon Glass <sjg@chromium.org> os.remove(expname) def GetData(self, data_type): - data=''' -From 4924887af52713cabea78420eff03badea8f0035 Mon Sep 17 00:00:00 2001 + data='''From 4924887af52713cabea78420eff03badea8f0035 Mon Sep 17 00:00:00 2001 From: Simon Glass <sjg@chromium.org> Date: Thu, 7 Apr 2011 10:14:41 -0700 Subject: [PATCH 1/4] Add microsecond boot time measurement @@ -101,6 +100,7 @@ an available microsecond counter. %s --- README | 11 ++++++++ + MAINTAINERS | 3 ++ common/bootstage.c | 50 ++++++++++++++++++++++++++++++++++++ include/bootstage.h | 71 +++++++++++++++++++++++++++++++++++++++++++++++++++ include/common.h | 8 ++++++ @@ -130,19 +130,31 @@ index 6f3748d..f9e4e65 100644 - Standalone program support: CONFIG_STANDALONE_LOAD_ADDR +diff --git a/MAINTAINERS b/MAINTAINERS +index b167b028ec..beb7dc634f 100644 +--- a/MAINTAINERS ++++ b/MAINTAINERS +@@ -474,3 +474,8 @@ S: Maintained + T: git git://git.denx.de/u-boot.git + F: * + F: */ ++ ++BOOTSTAGE ++M: Simon Glass <sjg@chromium.org> ++L: u-boot@lists.denx.de ++F: common/bootstage.c diff --git a/common/bootstage.c b/common/bootstage.c new file mode 100644 index 0000000..2234c87 --- /dev/null +++ b/common/bootstage.c -@@ -0,0 +1,39 @@ +@@ -0,0 +1,37 @@ +/* + * Copyright (c) 2011, Google Inc. All rights reserved. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + -+ +/* + * This module records the progress of boot and arbitrary commands, and + * permits accurate timestamping of each. The records can optionally be @@ -151,26 +163,25 @@ index 0000000..2234c87 + +#include <common.h> + -+ +struct bootstage_record { -+ uint32_t time_us; ++ u32 time_us; + const char *name; +}; + +static struct bootstage_record record[BOOTSTAGE_COUNT]; + -+uint32_t bootstage_mark(enum bootstage_id id, const char *name) ++u32 bootstage_mark(enum bootstage_id id, const char *name) +{ + struct bootstage_record *rec = &record[id]; + + /* Only record the first event for each */ +%sif (!rec->name) { -+ rec->time_us = (uint32_t)timer_get_us(); ++ rec->time_us = (u32)timer_get_us(); + rec->name = name; + } + if (!rec->name && + %ssomething_else) { -+ rec->time_us = (uint32_t)timer_get_us(); ++ rec->time_us = (u32)timer_get_us(); + rec->name = name; + } +%sreturn rec->time_us; @@ -210,7 +221,7 @@ index 0000000..2234c87 self.assertEqual(result.errors, 0) self.assertEqual(result.warnings, 0) self.assertEqual(result.checks, 0) - self.assertEqual(result.lines, 56) + self.assertEqual(result.lines, 62) os.remove(inf) def testNoSignoff(self): @@ -221,18 +232,18 @@ index 0000000..2234c87 self.assertEqual(result.errors, 1) self.assertEqual(result.warnings, 0) self.assertEqual(result.checks, 0) - self.assertEqual(result.lines, 56) + self.assertEqual(result.lines, 62) os.remove(inf) def testSpaces(self): inf = self.SetupData('spaces') result = checkpatch.CheckPatch(inf) self.assertEqual(result.ok, False) - self.assertEqual(len(result.problems), 2) + self.assertEqual(len(result.problems), 3) self.assertEqual(result.errors, 0) - self.assertEqual(result.warnings, 2) + self.assertEqual(result.warnings, 3) self.assertEqual(result.checks, 0) - self.assertEqual(result.lines, 56) + self.assertEqual(result.lines, 62) os.remove(inf) def testIndent(self): @@ -243,7 +254,7 @@ index 0000000..2234c87 self.assertEqual(result.errors, 0) self.assertEqual(result.warnings, 0) self.assertEqual(result.checks, 1) - self.assertEqual(result.lines, 56) + self.assertEqual(result.lines, 62) os.remove(inf) diff --git a/tools/rkcommon.c b/tools/rkcommon.c index 1a24e16589..aed2b95fd6 100644 --- a/tools/rkcommon.c +++ b/tools/rkcommon.c @@ -58,9 +58,6 @@ struct header1_info { * @spl_hdr: Boot ROM requires a 4-bytes spl header * @spl_size: Spl size(include extra 4-bytes spl header) * @spl_rc4: RC4 encode the SPL binary (same key as header) - * @spl_boot0: A new-style (ARM_SOC_BOOT0_HOOK) image that should - * have the boot magic (e.g. 'RK33') written to its first - * word. */ struct spl_info { @@ -68,19 +65,18 @@ struct spl_info { const char *spl_hdr; const uint32_t spl_size; const bool spl_rc4; - const bool spl_boot0; }; static struct spl_info spl_infos[] = { - { "rk3036", "RK30", 0x1000, false, false }, - { "rk3128", "RK31", 0x1800, false, false }, - { "rk3188", "RK31", 0x8000 - 0x800, true, false }, - { "rk322x", "RK32", 0x8000 - 0x1000, false, false }, - { "rk3288", "RK32", 0x8000, false, false }, - { "rk3328", "RK32", 0x8000 - 0x1000, false, false }, - { "rk3368", "RK33", 0x8000 - 0x1000, false, true }, - { "rk3399", "RK33", 0x30000 - 0x2000, false, true }, - { "rv1108", "RK11", 0x1800, false, false}, + { "rk3036", "RK30", 0x1000, false }, + { "rk3128", "RK31", 0x1800, false }, + { "rk3188", "RK31", 0x8000 - 0x800, true }, + { "rk322x", "RK32", 0x8000 - 0x1000, false }, + { "rk3288", "RK32", 0x8000, false }, + { "rk3328", "RK32", 0x8000 - 0x1000, false }, + { "rk3368", "RK33", 0x8000 - 0x1000, false }, + { "rk3399", "RK33", 0x30000 - 0x2000, false }, + { "rv1108", "RK11", 0x1800, false }, }; static unsigned char rc4_key[16] = { @@ -158,16 +154,6 @@ bool rkcommon_need_rc4_spl(struct image_tool_params *params) return info->spl_rc4; } -bool rkcommon_spl_is_boot0(struct image_tool_params *params) -{ - struct spl_info *info = rkcommon_get_spl_info(params->imagename); - - /* - * info would not be NULL, because of we checked params before. - */ - return info->spl_boot0; -} - static void rkcommon_set_header0(void *buf, uint file_size, struct image_tool_params *params) { @@ -366,15 +352,12 @@ int rkcommon_vrec_header(struct image_tool_params *params, * have the first 4 bytes reserved for the spl_name). Reserving * these 4 bytes is done using the BOOT0_HOOK infrastructure. * - * Depending on this, the header is either 0x800 (if this is a - * 'boot0'-style payload, which has reserved 4 bytes at the - * beginning for the 'spl_name' and expects us to overwrite - * its first 4 bytes) or 0x804 bytes in length. + * The header is always at 0x800 (as we now use a payload + * prepadded using the boot0 hook for all targets): the first + * 4 bytes of these images can safely be overwritten using the + * boot magic. */ - if (rkcommon_spl_is_boot0(params)) - tparams->header_size = RK_SPL_HDR_START; - else - tparams->header_size = RK_SPL_HDR_START + 4; + tparams->header_size = RK_SPL_HDR_START; /* Allocate, clear and install the header */ tparams->hdr = malloc(tparams->header_size); diff --git a/tools/zynqmpimage.c b/tools/zynqmpimage.c index 0c9a3daddd..f48ac6dbe5 100644 --- a/tools/zynqmpimage.c +++ b/tools/zynqmpimage.c @@ -6,7 +6,7 @@ * * The following Boot Header format/structures and values are defined in the * following documents: - * * ug1085 ZynqMP TRM (Chapter 9, Table 9-3) + * * ug1085 ZynqMP TRM doc v1.4 (Chapter 11, Table 11-4) * * Expected Header Size = 0x9C0 * Forced as 'little' endian, 32-bit words @@ -99,6 +99,8 @@ struct zynqmp_header { }; static struct zynqmp_header zynqmpimage_header; +static void *dynamic_header; +static FILE *fpmu; static uint32_t zynqmpimage_checksum(struct zynqmp_header *ptr) { @@ -181,6 +183,13 @@ static void zynqmpimage_print_header(const void *ptr) printf("Image Size : %lu bytes (%lu bytes packed)\n", (unsigned long)le32_to_cpu(zynqhdr->image_size), (unsigned long)le32_to_cpu(zynqhdr->image_stored_size)); + + if (zynqhdr->pfw_image_length) + printf("PMUFW Size : %lu bytes (%lu bytes packed)\n", + (unsigned long)le32_to_cpu(zynqhdr->pfw_image_length), + (unsigned long)le32_to_cpu( + zynqhdr->total_pfw_image_length)); + printf("Image Load : 0x%08x\n", le32_to_cpu(zynqhdr->image_load)); printf("Checksum : 0x%08x\n", le32_to_cpu(zynqhdr->checksum)); @@ -203,6 +212,8 @@ static void zynqmpimage_print_header(const void *ptr) le32_to_cpu(zynqhdr->register_init[i].address), le32_to_cpu(zynqhdr->register_init[i].data)); } + + free(dynamic_header); } static int zynqmpimage_check_params(struct image_tool_params *params) @@ -234,6 +245,66 @@ static int zynqmpimage_check_image_types(uint8_t type) return EXIT_FAILURE; } +static uint32_t fsize(FILE *fp) +{ + int size, ret, origin; + + origin = ftell(fp); + if (origin < 0) { + fprintf(stderr, "Incorrect file size\n"); + fclose(fp); + exit(2); + } + + ret = fseek(fp, 0L, SEEK_END); + if (ret) { + fprintf(stderr, "Incorrect file SEEK_END\n"); + fclose(fp); + exit(3); + } + + size = ftell(fp); + if (size < 0) { + fprintf(stderr, "Incorrect file size\n"); + fclose(fp); + exit(4); + } + + /* going back */ + ret = fseek(fp, origin, SEEK_SET); + if (ret) { + fprintf(stderr, "Incorrect file SEEK_SET to %d\n", origin); + fclose(fp); + exit(3); + } + + return size; +} + +static void zynqmpimage_pmufw(struct zynqmp_header *zynqhdr, + const char *filename) +{ + uint32_t size; + + /* Setup PMU fw size */ + zynqhdr->pfw_image_length = fsize(fpmu); + zynqhdr->total_pfw_image_length = zynqhdr->pfw_image_length; + + zynqhdr->image_size -= zynqhdr->pfw_image_length; + zynqhdr->image_stored_size -= zynqhdr->total_pfw_image_length; + + /* Read the whole PMUFW to the header */ + size = fread(&zynqhdr->__reserved4[66], 1, + zynqhdr->pfw_image_length, fpmu); + if (size != zynqhdr->pfw_image_length) { + fprintf(stderr, "Cannot read PMUFW file: %s\n", filename); + fclose(fpmu); + exit(1); + } + + fclose(fpmu); +} + static void zynqmpimage_parse_initparams(struct zynqmp_header *zynqhdr, const char *filename) { @@ -288,6 +359,10 @@ static void zynqmpimage_set_header(void *ptr, struct stat *sbuf, int ifd, if (params->eflag) zynqhdr->image_load = cpu_to_le32((uint32_t)params->ep); + /* PMUFW */ + if (fpmu) + zynqmpimage_pmufw(zynqhdr, params->imagename); + /* User can pass in text file with init list */ if (strlen(params->imagename2)) zynqmpimage_parse_initparams(zynqhdr, params->imagename2); @@ -295,6 +370,50 @@ static void zynqmpimage_set_header(void *ptr, struct stat *sbuf, int ifd, zynqhdr->checksum = zynqmpimage_checksum(zynqhdr); } +static int zynqmpimage_vrec_header(struct image_tool_params *params, + struct image_type_params *tparams) +{ + struct stat path_stat; + char *filename = params->imagename; + int err; + + /* Handle static case without PMUFW */ + tparams->header_size = sizeof(struct zynqmp_header); + tparams->hdr = (void *)&zynqmpimage_header; + + /* PMUFW name is passed via params->imagename */ + if (strlen(filename) == 0) + return EXIT_SUCCESS; + + fpmu = fopen(filename, "r"); + if (!fpmu) { + fprintf(stderr, "Cannot open PMUFW file: %s\n", filename); + return EXIT_FAILURE; + } + + err = fstat(fileno(fpmu), &path_stat); + if (err) { + fclose(fpmu); + fpmu = NULL; + return EXIT_FAILURE; + } + + if (!S_ISREG(path_stat.st_mode)) { + fclose(fpmu); + fpmu = NULL; + return EXIT_FAILURE; + } + + /* Increase header size by PMUFW file size */ + tparams->header_size += fsize(fpmu); + + /* Allocate buffer with space for PMUFW */ + dynamic_header = calloc(1, tparams->header_size); + tparams->hdr = dynamic_header; + + return EXIT_SUCCESS; +} + U_BOOT_IMAGE_TYPE( zynqmpimage, "Xilinx ZynqMP Boot Image support", @@ -307,5 +426,5 @@ U_BOOT_IMAGE_TYPE( NULL, zynqmpimage_check_image_types, NULL, - NULL + zynqmpimage_vrec_header ); |