diff options
Diffstat (limited to 'tools')
32 files changed, 685 insertions, 37 deletions
diff --git a/tools/binman/README b/tools/binman/README index 4ef76c8f08..08c3e56bde 100644 --- a/tools/binman/README +++ b/tools/binman/README @@ -439,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. @@ -471,6 +473,33 @@ the 'warning' line in scripts/Makefile.lib to see what it has found: # 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 ------------- @@ -543,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 3ccf25f1f8..1c8e8dbff6 100755 --- a/tools/binman/binman.py +++ b/tools/binman/binman.py @@ -31,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 ftest + import image_test import test import doctest @@ -45,12 +47,15 @@ def RunTests(): suite.run(result) sys.argv = [sys.argv[0]] + 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): + for module in (ftest.TestFunctional, fdt_test.TestFdt, elf_test.TestElf, + image_test.TestImage): suite = unittest.TestLoader().loadTestsFromTestCase(module) suite.run(result) @@ -110,7 +115,7 @@ def RunBinman(options, args): sys.tracebacklimit = 0 if options.test: - ret_code = 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/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_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/ftest.py b/tools/binman/ftest.py index 9083143894..5812ab397c 100644 --- a/tools/binman/ftest.py +++ b/tools/binman/ftest.py @@ -20,6 +20,7 @@ import binman import cmdline import command import control +import elf import fdt import fdt_util import tools @@ -28,11 +29,12 @@ 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' +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' @@ -76,6 +78,7 @@ 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) @@ -134,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 @@ -142,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 @@ -302,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') @@ -362,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 @@ -563,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""" @@ -586,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): @@ -614,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""" @@ -835,6 +848,13 @@ class TestFunctional(unittest.TestCase): 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') @@ -862,6 +882,32 @@ class TestFunctional(unittest.TestCase): 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 24c4f6f578..741630f091 100644 --- a/tools/binman/image.py +++ b/tools/binman/image.py @@ -6,8 +6,12 @@ # 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 fdt_util import tools @@ -45,7 +49,7 @@ 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 @@ -64,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""" @@ -119,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) @@ -220,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) @@ -230,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/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 index 217d13c666..e58fc80775 100644 --- a/tools/binman/test/Makefile +++ b/tools/binman/test/Makefile @@ -10,8 +10,12 @@ 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 +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) @@ -24,6 +28,18 @@ 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) diff --git a/tools/binman/test/bss_data.c b/tools/binman/test/bss_data.c index f865a9d9f6..e0305c382c 100644 --- a/tools/binman/test/bss_data.c +++ b/tools/binman/test/bss_data.c @@ -4,7 +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. + * by binutils. This is used by binman tests. */ int bss_data[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_ucode_ptr.c b/tools/binman/test/u_boot_ucode_ptr.c index 24f349fb9e..734d54f0d4 100644 --- a/tools/binman/test/u_boot_ucode_ptr.c +++ b/tools/binman/test/u_boot_ucode_ptr.c @@ -4,7 +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. + * by binutils. This is used by binman tests. */ static unsigned long _dt_ucode_base_size[2] |