Merge git://git.denx.de/u-boot-dm

This commit is contained in:
Tom Rini 2018-06-08 07:20:08 -04:00
commit 8f48cf9f17
46 changed files with 975 additions and 389 deletions

View File

@ -11,8 +11,7 @@ result=0
run_test ./test/py/test.py --bd sandbox --build run_test ./test/py/test.py --bd sandbox --build
# Run tests which require sandbox_spl # Run tests which require sandbox_spl
run_test ./test/py/test.py --bd sandbox_spl --build -k \ run_test ./test/py/test.py --bd sandbox_spl --build -k test_ofplatdata.py
test/py/tests/test_ofplatdata.py
# Run tests for the flat DT version of sandbox # Run tests for the flat DT version of sandbox
./test/py/test.py --bd sandbox_flattree --build ./test/py/test.py --bd sandbox_flattree --build

View File

@ -302,9 +302,9 @@ align-size:
align-end: align-end:
This sets the alignment of the end of an entry. Some entries require This sets the alignment of the end of an entry. Some entries require
that they end on an alignment boundary, regardless of where they that they end on an alignment boundary, regardless of where they
start. If 'align-end' is not provided, no alignment is performed. start. This does not move the start of the entry, so the contents of
the entry will still start at the beginning. But there may be padding
Note: This is not yet implemented in binman. at the end. If 'align-end' is not provided, no alignment is performed.
filename: filename:
For 'blob' types this provides the filename containing the binary to For 'blob' types this provides the filename containing the binary to
@ -316,6 +316,13 @@ type:
possible to use any name, and then add (for example) 'type = "u-boot"' possible to use any name, and then add (for example) 'type = "u-boot"'
to specify the type. to specify the type.
pos-unset:
Indicates that the position of this entry should not be set by placing
it immediately after the entry before. Instead, is set by another
entry which knows where this entry should go. When this boolean
property is present, binman will give an error if another entry does
not set the position (with the GetPositions() method).
The attributes supported for images are described below. Several are similar The attributes supported for images are described below. Several are similar
to those for entries. to those for entries.
@ -387,6 +394,57 @@ end-at-4gb:
Examples of the above options can be found in the tests. See the Examples of the above options can be found in the tests. See the
tools/binman/test directory. tools/binman/test directory.
It is possible to have the same binary appear multiple times in the image,
either by using a unit number suffix (u-boot@0, u-boot@1) or by using a
different name for each and specifying the type with the 'type' attribute.
Sections and hiearchical images
-------------------------------
Sometimes it is convenient to split an image into several pieces, each of which
contains its own set of binaries. An example is a flash device where part of
the image is read-only and part is read-write. We can set up sections for each
of these, and place binaries in them independently. The image is still produced
as a single output file.
This feature provides a way of creating hierarchical images. For example here
is an example image with two copies of U-Boot. One is read-only (ro), intended
to be written only in the factory. Another is read-write (rw), so that it can be
upgraded in the field. The sizes are fixed so that the ro/rw boundary is known
and can be programmed:
binman {
section@0 {
read-only;
name-prefix = "ro-";
size = <0x100000>;
u-boot {
};
};
section@1 {
name-prefix = "rw-";
size = <0x100000>;
u-boot {
};
};
};
This image could be placed into a SPI flash chip, with the protection boundary
set at 1MB.
A few special properties are provided for sections:
read-only:
Indicates that this section is read-only. This has no impact on binman's
operation, but his property can be read at run time.
name-prefix:
This string is prepended to all the names of the binaries in the
section. In the example above, the 'u-boot' binaries which actually be
renamed to 'ro-u-boot' and 'rw-u-boot'. This can be useful to
distinguish binaries with otherwise identical names.
Special properties Special properties
------------------ ------------------
@ -498,6 +556,25 @@ 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. to fill in such symbols in U-Boot proper, as well.
Map files
---------
The -m option causes binman to output a .map file for each image that it
generates. This shows the position and size of each entry. For example:
Position Size Name
00000000 00000010 section@0
00000000 00000004 u-boot
00000010 00000010 section@1
00000000 00000004 u-boot
This shows a hierarchical image with two sections, each with a single entry. The
positions of the sections are absolute hex byte offsets within the image. The
positions of the entries are relative to their respective sections. The size of
each entry is also shown, in bytes (hex). The indentation shows the entries
nested inside their sections.
Code coverage Code coverage
------------- -------------
@ -547,7 +624,7 @@ Binman takes a lot of inspiration from a Chrome OS tool called
a reasonably simple and sound design but has expanded greatly over the a reasonably simple and sound design but has expanded greatly over the
years. In particular its handling of x86 images is convoluted. years. In particular its handling of x86 images is convoluted.
Quite a few lessons have been learned which are hopefully be applied here. Quite a few lessons have been learned which are hopefully applied here.
Design notes Design notes
@ -578,17 +655,13 @@ Some ideas:
'Access to binman entry positions at run time' above 'Access to binman entry positions at run time' above
- Use of-platdata to make the information available to code that is unable - Use of-platdata to make the information available to code that is unable
to use device tree (such as a very small SPL image) to use device tree (such as a very small SPL image)
- Write an image map to a text file
- Allow easy building of images by specifying just the board name - Allow easy building of images by specifying just the board name
- Produce a full Python binding for libfdt (for upstream) - Produce a full Python binding for libfdt (for upstream)
- Add an option to decode an image into the constituent binaries - Add an option to decode an image into the constituent binaries
- Suppoort hierarchical images (packing of binaries into another binary
which is then placed in the image)
- Support building an image for a board (-b) more completely, with a - Support building an image for a board (-b) more completely, with a
configurable build directory configurable build directory
- Consider making binman work with buildman, although if it is used in the - Consider making binman work with buildman, although if it is used in the
Makefile, this will be automatic Makefile, this will be automatic
- Implement align-end
-- --
Simon Glass <sjg@chromium.org> Simon Glass <sjg@chromium.org>

View File

@ -23,15 +23,18 @@ for dirname in ['../patman', '../dtoc', '..']:
# Bring in the libfdt module # Bring in the libfdt module
sys.path.insert(0, 'scripts/dtc/pylibfdt') sys.path.insert(0, 'scripts/dtc/pylibfdt')
# Also allow entry-type modules to be brought in from the etype directory.
sys.path.insert(0, os.path.join(our_path, 'etype'))
import cmdline import cmdline
import command import command
import control import control
def RunTests(debug): def RunTests(debug, args):
"""Run the functional tests and any embedded doctests""" """Run the functional tests and any embedded doctests
Args:
debug: True to enable debugging, which shows a full stack trace on error
args: List of positional args provided to binman. This can hold a test
name to execute (as in 'binman -t testSections', for example)
"""
import elf_test import elf_test
import entry_test import entry_test
import fdt_test import fdt_test
@ -53,9 +56,16 @@ def RunTests(debug):
# 'entry' module. # 'entry' module.
suite = unittest.TestLoader().loadTestsFromTestCase(entry_test.TestEntry) suite = unittest.TestLoader().loadTestsFromTestCase(entry_test.TestEntry)
suite.run(result) suite.run(result)
test_name = args and args[0] or None
for module in (ftest.TestFunctional, fdt_test.TestFdt, elf_test.TestElf, for module in (ftest.TestFunctional, fdt_test.TestFdt, elf_test.TestElf,
image_test.TestImage): image_test.TestImage):
suite = unittest.TestLoader().loadTestsFromTestCase(module) if test_name:
try:
suite = unittest.TestLoader().loadTestsFromName(args[0], module)
except AttributeError:
continue
else:
suite = unittest.TestLoader().loadTestsFromTestCase(module)
suite.run(result) suite.run(result)
print result print result
@ -114,7 +124,7 @@ def RunBinman(options, args):
sys.tracebacklimit = 0 sys.tracebacklimit = 0
if options.test: if options.test:
ret_code = RunTests(options.debug) ret_code = RunTests(options.debug, args[1:])
elif options.test_coverage: elif options.test_coverage:
RunTestCoverage() RunTestCoverage()

318
tools/binman/bsection.py Normal file
View File

@ -0,0 +1,318 @@
# SPDX-License-Identifier: GPL-2.0+
# Copyright (c) 2018 Google, Inc
# Written by Simon Glass <sjg@chromium.org>
#
# Base class for sections (collections of entries)
#
from __future__ import print_function
from collections import OrderedDict
import sys
import fdt_util
import re
import tools
class Section(object):
"""A section which contains multiple entries
A section represents a collection of entries. There must be one or more
sections in an image. Sections are used to group entries together.
Attributes:
_node: Node object that contains the section definition in device tree
_size: Section size in bytes, or None if not known yet
_align_size: Section size alignment, or None
_pad_before: Number of bytes before the first entry starts. This
effectively changes the place where entry position 0 starts
_pad_after: Number of bytes after the last entry ends. The last
entry will finish on or before this boundary
_pad_byte: Byte to use to pad the section where there is no entry
_sort: True if entries should be sorted by position, False if they
must be in-order in the device tree description
_skip_at_start: Number of bytes before the first entry starts. These
effectively adjust the starting position of entries. For example,
if _pad_before is 16, then the first entry would start at 16.
An entry with pos = 20 would in fact be written at position 4
in the image file.
_end_4gb: Indicates that the section ends at the 4GB boundary. This is
used for x86 images, which want to use positions such that a
memory address (like 0xff800000) is the first entry position.
This causes _skip_at_start to be set to the starting memory
address.
_name_prefix: Prefix to add to the name of all entries within this
section
_entries: OrderedDict() of entries
"""
def __init__(self, name, node, test=False):
global entry
global Entry
import entry
from entry import Entry
self._node = node
self._size = None
self._align_size = None
self._pad_before = 0
self._pad_after = 0
self._pad_byte = 0
self._sort = False
self._skip_at_start = 0
self._end_4gb = False
self._name_prefix = ''
self._entries = OrderedDict()
if not test:
self._ReadNode()
self._ReadEntries()
def _ReadNode(self):
"""Read properties from the section node"""
self._size = fdt_util.GetInt(self._node, 'size')
self._align_size = fdt_util.GetInt(self._node, 'align-size')
if tools.NotPowerOfTwo(self._align_size):
self._Raise("Alignment size %s must be a power of two" %
self._align_size)
self._pad_before = fdt_util.GetInt(self._node, 'pad-before', 0)
self._pad_after = fdt_util.GetInt(self._node, 'pad-after', 0)
self._pad_byte = fdt_util.GetInt(self._node, 'pad-byte', 0)
self._sort = fdt_util.GetBool(self._node, 'sort-by-pos')
self._end_4gb = fdt_util.GetBool(self._node, 'end-at-4gb')
if self._end_4gb and not self._size:
self._Raise("Section size must be provided when using end-at-4gb")
if self._end_4gb:
self._skip_at_start = 0x100000000 - self._size
self._name_prefix = fdt_util.GetString(self._node, 'name-prefix')
def _ReadEntries(self):
for node in self._node.subnodes:
entry = Entry.Create(self, node)
entry.SetPrefix(self._name_prefix)
self._entries[node.name] = entry
def CheckSize(self):
"""Check that the section contents does not exceed its size, etc."""
contents_size = 0
for entry in self._entries.values():
contents_size = max(contents_size, entry.pos + entry.size)
contents_size -= self._skip_at_start
size = self._size
if not size:
size = self._pad_before + contents_size + self._pad_after
size = tools.Align(size, self._align_size)
if self._size and contents_size > self._size:
self._Raise("contents size %#x (%d) exceeds section size %#x (%d)" %
(contents_size, contents_size, self._size, self._size))
if not self._size:
self._size = size
if self._size != tools.Align(self._size, self._align_size):
self._Raise("Size %#x (%d) does not match align-size %#x (%d)" %
(self._size, self._size, self._align_size, self._align_size))
return size
def _Raise(self, msg):
"""Raises an error for this section
Args:
msg: Error message to use in the raise string
Raises:
ValueError()
"""
raise ValueError("Section '%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 FindEntryType(self, etype):
"""Find an entry type in the section
Args:
etype: Entry type to find
Returns:
entry matching that type, or None if not found
"""
for entry in self._entries.values():
if entry.etype == etype:
return entry
return None
def GetEntryContents(self):
"""Call ObtainContents() for each entry
This calls each entry's ObtainContents() a few times until they all
return True. We stop calling an entry's function once it returns
True. This allows the contents of one entry to depend on another.
After 3 rounds we give up since it's likely an error.
"""
todo = self._entries.values()
for passnum in range(3):
next_todo = []
for entry in todo:
if not entry.ObtainContents():
next_todo.append(entry)
todo = next_todo
if not todo:
break
def _SetEntryPosSize(self, name, pos, size):
"""Set the position and size of an entry
Args:
name: Entry name to update
pos: New position
size: New size
"""
entry = self._entries.get(name)
if not entry:
self._Raise("Unable to set pos/size for unknown entry '%s'" % name)
entry.SetPositionSize(self._skip_at_start + pos, size)
def GetEntryPositions(self):
"""Handle entries that want to set the position/size of other entries
This calls each entry's GetPositions() method. If it returns a list
of entries to update, it updates them.
"""
for entry in self._entries.values():
pos_dict = entry.GetPositions()
for name, info in pos_dict.iteritems():
self._SetEntryPosSize(name, *info)
def PackEntries(self):
"""Pack all entries into the section"""
pos = self._skip_at_start
for entry in self._entries.values():
pos = entry.Pack(pos)
def _SortEntries(self):
"""Sort entries by position"""
entries = sorted(self._entries.values(), key=lambda entry: entry.pos)
self._entries.clear()
for entry in entries:
self._entries[entry._node.name] = entry
def CheckEntries(self):
"""Check that entries do not overlap or extend outside the section"""
if self._sort:
self._SortEntries()
pos = 0
prev_name = 'None'
for entry in self._entries.values():
entry.CheckPosition()
if (entry.pos < self._skip_at_start or
entry.pos >= self._skip_at_start + self._size):
entry.Raise("Position %#x (%d) is outside the section starting "
"at %#x (%d)" %
(entry.pos, entry.pos, self._skip_at_start,
self._skip_at_start))
if entry.pos < pos:
entry.Raise("Position %#x (%d) overlaps with previous entry '%s' "
"ending at %#x (%d)" %
(entry.pos, entry.pos, prev_name, pos, pos))
pos = entry.pos + entry.size
prev_name = entry.GetPath()
def ProcessEntryContents(self):
"""Call the ProcessContents() method for each entry
This is intended to adjust the contents as needed by the entry type.
"""
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 BuildSection(self, fd, base_pos):
"""Write the section to a file"""
fd.seek(base_pos)
fd.write(self.GetData())
def GetData(self):
"""Write the section to a file"""
section_data = chr(self._pad_byte) * self._size
for entry in self._entries.values():
data = entry.GetData()
base = self._pad_before + entry.pos - self._skip_at_start
section_data = (section_data[:base] + data +
section_data[base + len(data):])
return section_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))
def GetEntries(self):
return self._entries
def WriteMap(self, fd, indent):
"""Write a map of the section to a .map file
Args:
fd: File to write the map to
"""
for entry in self._entries.values():
entry.WriteMap(fd, indent)

View File

@ -30,6 +30,8 @@ def ParseArgs(argv):
help='Add a path to a directory to use for input files') help='Add a path to a directory to use for input files')
parser.add_option('-H', '--full-help', action='store_true', parser.add_option('-H', '--full-help', action='store_true',
default=False, help='Display the README file') default=False, help='Display the README file')
parser.add_option('-m', '--map', action='store_true',
default=False, help='Output a map file for each image')
parser.add_option('-O', '--outdir', type='string', parser.add_option('-O', '--outdir', type='string',
action='store', help='Path to directory to use for intermediate and ' action='store', help='Path to directory to use for intermediate and '
'output files') 'output files')

View File

@ -112,6 +112,8 @@ def Binman(options, args):
image.ProcessEntryContents() image.ProcessEntryContents()
image.WriteSymbols() image.WriteSymbols()
image.BuildImage() image.BuildImage()
if options.map:
image.WriteMap()
finally: finally:
tools.FinaliseOutputDir() tools.FinaliseOutputDir()
finally: finally:

View File

@ -75,7 +75,7 @@ def GetSymbolAddress(fname, sym_name):
return None return None
return sym.address return sym.address
def LookupAndWriteSymbols(elf_fname, entry, image): def LookupAndWriteSymbols(elf_fname, entry, section):
"""Replace all symbols in an entry with their correct values """Replace all symbols in an entry with their correct values
The entry contents is updated so that values for referenced symbols will be The entry contents is updated so that values for referenced symbols will be
@ -87,7 +87,7 @@ def LookupAndWriteSymbols(elf_fname, entry, image):
elf_fname: Filename of ELF image containing the symbol information for elf_fname: Filename of ELF image containing the symbol information for
entry entry
entry: Entry to process entry: Entry to process
image: Image which can be used to lookup symbol values section: Section which can be used to lookup symbol values
""" """
fname = tools.GetInputFilename(elf_fname) fname = tools.GetInputFilename(elf_fname)
syms = GetSymbols(fname, ['image', 'binman']) syms = GetSymbols(fname, ['image', 'binman'])
@ -98,8 +98,8 @@ def LookupAndWriteSymbols(elf_fname, entry, image):
return return
for name, sym in syms.iteritems(): for name, sym in syms.iteritems():
if name.startswith('_binman'): if name.startswith('_binman'):
msg = ("Image '%s': Symbol '%s'\n in entry '%s'" % msg = ("Section '%s': Symbol '%s'\n in entry '%s'" %
(image.GetPath(), name, entry.GetPath())) (section.GetPath(), name, entry.GetPath()))
offset = sym.address - base.address offset = sym.address - base.address
if offset < 0 or offset + sym.size > entry.contents_size: if offset < 0 or offset + sym.size > entry.contents_size:
raise ValueError('%s has offset %x (size %x) but the contents ' raise ValueError('%s has offset %x (size %x) but the contents '
@ -114,7 +114,7 @@ def LookupAndWriteSymbols(elf_fname, entry, image):
(msg, sym.size)) (msg, sym.size))
# Look up the symbol in our entry tables. # Look up the symbol in our entry tables.
value = image.LookupSymbol(name, sym.weak, msg) value = section.LookupSymbol(name, sym.weak, msg)
if value is not None: if value is not None:
value += base.address value += base.address
else: else:

View File

@ -40,12 +40,12 @@ class FakeEntry:
def GetPath(self): def GetPath(self):
return 'entry_path' return 'entry_path'
class FakeImage: class FakeSection:
def __init__(self, sym_value=1): def __init__(self, sym_value=1):
self.sym_value = sym_value self.sym_value = sym_value
def GetPath(self): def GetPath(self):
return 'image_path' return 'section_path'
def LookupSymbol(self, name, weak, msg): def LookupSymbol(self, name, weak, msg):
return self.sym_value return self.sym_value
@ -67,51 +67,51 @@ class TestElf(unittest.TestCase):
def testMissingFile(self): def testMissingFile(self):
entry = FakeEntry(10) entry = FakeEntry(10)
image = FakeImage() section = FakeSection()
with self.assertRaises(ValueError) as e: with self.assertRaises(ValueError) as e:
syms = elf.LookupAndWriteSymbols('missing-file', entry, image) syms = elf.LookupAndWriteSymbols('missing-file', entry, section)
self.assertIn("Filename 'missing-file' not found in input path", self.assertIn("Filename 'missing-file' not found in input path",
str(e.exception)) str(e.exception))
def testOutsideFile(self): def testOutsideFile(self):
entry = FakeEntry(10) entry = FakeEntry(10)
image = FakeImage() section = FakeSection()
elf_fname = os.path.join(binman_dir, 'test', 'u_boot_binman_syms') elf_fname = os.path.join(binman_dir, 'test', 'u_boot_binman_syms')
with self.assertRaises(ValueError) as e: with self.assertRaises(ValueError) as e:
syms = elf.LookupAndWriteSymbols(elf_fname, entry, image) syms = elf.LookupAndWriteSymbols(elf_fname, entry, section)
self.assertIn('entry_path has offset 4 (size 8) but the contents size ' self.assertIn('entry_path has offset 4 (size 8) but the contents size '
'is a', str(e.exception)) 'is a', str(e.exception))
def testMissingImageStart(self): def testMissingImageStart(self):
entry = FakeEntry(10) entry = FakeEntry(10)
image = FakeImage() section = FakeSection()
elf_fname = os.path.join(binman_dir, 'test', 'u_boot_binman_syms_bad') elf_fname = os.path.join(binman_dir, 'test', 'u_boot_binman_syms_bad')
self.assertEqual(elf.LookupAndWriteSymbols(elf_fname, entry, image), self.assertEqual(elf.LookupAndWriteSymbols(elf_fname, entry, section),
None) None)
def testBadSymbolSize(self): def testBadSymbolSize(self):
entry = FakeEntry(10) entry = FakeEntry(10)
image = FakeImage() section = FakeSection()
elf_fname = os.path.join(binman_dir, 'test', 'u_boot_binman_syms_size') elf_fname = os.path.join(binman_dir, 'test', 'u_boot_binman_syms_size')
with self.assertRaises(ValueError) as e: with self.assertRaises(ValueError) as e:
syms = elf.LookupAndWriteSymbols(elf_fname, entry, image) syms = elf.LookupAndWriteSymbols(elf_fname, entry, section)
self.assertIn('has size 1: only 4 and 8 are supported', self.assertIn('has size 1: only 4 and 8 are supported',
str(e.exception)) str(e.exception))
def testNoValue(self): def testNoValue(self):
entry = FakeEntry(20) entry = FakeEntry(20)
image = FakeImage(sym_value=None) section = FakeSection(sym_value=None)
elf_fname = os.path.join(binman_dir, 'test', 'u_boot_binman_syms') elf_fname = os.path.join(binman_dir, 'test', 'u_boot_binman_syms')
syms = elf.LookupAndWriteSymbols(elf_fname, entry, image) syms = elf.LookupAndWriteSymbols(elf_fname, entry, section)
self.assertEqual(chr(255) * 16 + 'a' * 4, entry.data) self.assertEqual(chr(255) * 16 + 'a' * 4, entry.data)
def testDebug(self): def testDebug(self):
elf.debug = True elf.debug = True
entry = FakeEntry(20) entry = FakeEntry(20)
image = FakeImage() section = FakeSection()
elf_fname = os.path.join(binman_dir, 'test', 'u_boot_binman_syms') elf_fname = os.path.join(binman_dir, 'test', 'u_boot_binman_syms')
with capture_sys_output() as (stdout, stderr): with capture_sys_output() as (stdout, stderr):
syms = elf.LookupAndWriteSymbols(elf_fname, entry, image) syms = elf.LookupAndWriteSymbols(elf_fname, entry, section)
elf.debug = False elf.debug = False
self.assertTrue(len(stdout.getvalue()) > 0) self.assertTrue(len(stdout.getvalue()) > 0)

View File

@ -4,6 +4,8 @@
# Base class for all entries # Base class for all entries
# #
from __future__ import print_function
# importlib was introduced in Python 2.7 but there was a report of it not # importlib was introduced in Python 2.7 but there was a report of it not
# working in 2.7.12, so we work around this: # working in 2.7.12, so we work around this:
# http://lists.denx.de/pipermail/u-boot/2016-October/269729.html # http://lists.denx.de/pipermail/u-boot/2016-October/269729.html
@ -14,15 +16,19 @@ except:
have_importlib = False have_importlib = False
import fdt_util import fdt_util
import os
import sys
import tools import tools
modules = {} modules = {}
our_path = os.path.dirname(os.path.realpath(__file__))
class Entry(object): class Entry(object):
"""An Entry in the image """An Entry in the section
An entry corresponds to a single node in the device-tree description An entry corresponds to a single node in the device-tree description
of the image. Each entry ends up being a part of the final image. of the section. Each entry ends up being a part of the final section.
Entries can be placed either right next to each other, or with padding Entries can be placed either right next to each other, or with padding
between them. The type of the entry determines the data that is in it. between them. The type of the entry determines the data that is in it.
@ -30,9 +36,9 @@ class Entry(object):
Entry. Entry.
Attributes: Attributes:
image: The image containing this entry section: The section containing this entry
node: The node that created this entry node: The node that created this entry
pos: Absolute position of entry within the image, None if not known pos: Absolute position of entry within the section, None if not known
size: Entry size in bytes, None if not known size: Entry size in bytes, None if not known
contents_size: Size of contents in bytes, 0 by default contents_size: Size of contents in bytes, 0 by default
align: Entry start position alignment, or None align: Entry start position alignment, or None
@ -42,10 +48,11 @@ class Entry(object):
pad_after: Number of pad bytes after the contents, 0 if none pad_after: Number of pad bytes after the contents, 0 if none
data: Contents of entry (string of bytes) data: Contents of entry (string of bytes)
""" """
def __init__(self, image, etype, node, read_node=True): def __init__(self, section, etype, node, read_node=True, name_prefix=''):
self.image = image self.section = section
self.etype = etype self.etype = etype
self._node = node self._node = node
self.name = node and (name_prefix + node.name) or 'none'
self.pos = None self.pos = None
self.size = None self.size = None
self.contents_size = 0 self.contents_size = 0
@ -59,11 +66,11 @@ class Entry(object):
self.ReadNode() self.ReadNode()
@staticmethod @staticmethod
def Create(image, node, etype=None): def Create(section, node, etype=None):
"""Create a new entry for a node. """Create a new entry for a node.
Args: Args:
image: Image object containing this node section: Image object containing this node
node: Node object containing information about the entry to create node: Node object containing information about the entry to create
etype: Entry type to use, or None to work it out (used for tests) etype: Entry type to use, or None to work it out (used for tests)
@ -72,11 +79,20 @@ class Entry(object):
""" """
if not etype: if not etype:
etype = fdt_util.GetString(node, 'type', node.name) etype = fdt_util.GetString(node, 'type', node.name)
# Convert something like 'u-boot@0' to 'u_boot' since we are only
# interested in the type.
module_name = etype.replace('-', '_') module_name = etype.replace('-', '_')
if '@' in module_name:
module_name = module_name.split('@')[0]
module = modules.get(module_name) module = modules.get(module_name)
# Also allow entry-type modules to be brought in from the etype directory.
# Import the module if we have not already done so. # Import the module if we have not already done so.
if not module: if not module:
old_path = sys.path
sys.path.insert(0, os.path.join(our_path, 'etype'))
try: try:
if have_importlib: if have_importlib:
module = importlib.import_module(module_name) module = importlib.import_module(module_name)
@ -85,11 +101,13 @@ class Entry(object):
except ImportError: except ImportError:
raise ValueError("Unknown entry type '%s' in node '%s'" % raise ValueError("Unknown entry type '%s' in node '%s'" %
(etype, node.path)) (etype, node.path))
finally:
sys.path = old_path
modules[module_name] = module modules[module_name] = module
# Call its constructor to get the object we want. # Call its constructor to get the object we want.
obj = getattr(module, 'Entry_%s' % module_name) obj = getattr(module, 'Entry_%s' % module_name)
return obj(image, etype, node) return obj(section, etype, node)
def ReadNode(self): def ReadNode(self):
"""Read entry information from the node """Read entry information from the node
@ -111,6 +129,15 @@ class Entry(object):
self.align_end = fdt_util.GetInt(self._node, 'align-end') self.align_end = fdt_util.GetInt(self._node, 'align-end')
self.pos_unset = fdt_util.GetBool(self._node, 'pos-unset') self.pos_unset = fdt_util.GetBool(self._node, 'pos-unset')
def SetPrefix(self, prefix):
"""Set the name prefix for a node
Args:
prefix: Prefix to set, or '' to not use a prefix
"""
if prefix:
self.name = prefix + self.name
def ObtainContents(self): def ObtainContents(self):
"""Figure out the contents of an entry. """Figure out the contents of an entry.
@ -122,7 +149,7 @@ class Entry(object):
return True return True
def Pack(self, pos): def Pack(self, pos):
"""Figure out how to pack the entry into the image """Figure out how to pack the entry into the section
Most of the time the entries are not fully specified. There may be Most of the time the entries are not fully specified. There may be
an alignment but no size. In that case we take the size from the an alignment but no size. In that case we take the size from the
@ -134,10 +161,10 @@ class Entry(object):
entry will be know. entry will be know.
Args: Args:
Current image position pointer Current section position pointer
Returns: Returns:
New image position pointer (after this entry) New section position pointer (after this entry)
""" """
if self.pos is None: if self.pos is None:
if self.pos_unset: if self.pos_unset:
@ -198,10 +225,29 @@ class Entry(object):
def ProcessContents(self): def ProcessContents(self):
pass pass
def WriteSymbols(self, image): def WriteSymbols(self, section):
"""Write symbol values into binary files for access at run time """Write symbol values into binary files for access at run time
Args: Args:
image: Image containing the entry section: Section containing the entry
""" """
pass pass
def CheckPosition(self):
"""Check that the entry positions are correct
This is used for entries which have extra position requirements (other
than having to be fully inside their section). Sub-classes can implement
this function and raise if there is a problem.
"""
pass
def WriteMap(self, fd, indent):
"""Write a map of the entry to a .map file
Args:
fd: File to write the map to
indent: Curent indent level of map (0=none, 1=one level, etc.)
"""
print('%s%08x %08x %s' % (' ' * indent, self.pos, self.size,
self.name), file=fd)

View File

@ -10,8 +10,8 @@ import fdt_util
import tools import tools
class Entry__testing(Entry): class Entry__testing(Entry):
def __init__(self, image, etype, node): def __init__(self, section, etype, node):
Entry.__init__(self, image, etype, node) Entry.__init__(self, section, etype, node)
def ObtainContents(self): def ObtainContents(self):
self.data = 'a' self.data = 'a'

View File

@ -10,8 +10,8 @@ import fdt_util
import tools import tools
class Entry_blob(Entry): class Entry_blob(Entry):
def __init__(self, image, etype, node): def __init__(self, section, etype, node):
Entry.__init__(self, image, etype, node) Entry.__init__(self, section, etype, node)
self._filename = fdt_util.GetString(self._node, "filename", self.etype) self._filename = fdt_util.GetString(self._node, "filename", self.etype)
def ObtainContents(self): def ObtainContents(self):

View File

@ -9,5 +9,5 @@ from entry import Entry
from blob import Entry_blob from blob import Entry_blob
class Entry_intel_cmc(Entry_blob): class Entry_intel_cmc(Entry_blob):
def __init__(self, image, etype, node): def __init__(self, section, etype, node):
Entry_blob.__init__(self, image, etype, node) Entry_blob.__init__(self, section, etype, node)

View File

@ -32,8 +32,8 @@ class Entry_intel_descriptor(Entry_blob):
size of the ME region, allowing us to place the ME binary in the right size of the ME region, allowing us to place the ME binary in the right
place. place.
""" """
def __init__(self, image, etype, node): def __init__(self, section, etype, node):
Entry_blob.__init__(self, image, etype, node) Entry_blob.__init__(self, section, etype, node)
self._regions = [] self._regions = []
def GetPositions(self): def GetPositions(self):

View File

@ -9,5 +9,5 @@ from entry import Entry
from blob import Entry_blob from blob import Entry_blob
class Entry_intel_fsp(Entry_blob): class Entry_intel_fsp(Entry_blob):
def __init__(self, image, etype, node): def __init__(self, section, etype, node):
Entry_blob.__init__(self, image, etype, node) Entry_blob.__init__(self, section, etype, node)

View File

@ -9,5 +9,5 @@ from entry import Entry
from blob import Entry_blob from blob import Entry_blob
class Entry_intel_me(Entry_blob): class Entry_intel_me(Entry_blob):
def __init__(self, image, etype, node): def __init__(self, section, etype, node):
Entry_blob.__init__(self, image, etype, node) Entry_blob.__init__(self, section, etype, node)

View File

@ -9,8 +9,8 @@ from entry import Entry
from blob import Entry_blob from blob import Entry_blob
class Entry_intel_mrc(Entry_blob): class Entry_intel_mrc(Entry_blob):
def __init__(self, image, etype, node): def __init__(self, section, etype, node):
Entry_blob.__init__(self, image, etype, node) Entry_blob.__init__(self, section, etype, node)
def GetDefaultFilename(self): def GetDefaultFilename(self):
return 'mrc.bin' return 'mrc.bin'

View File

@ -8,5 +8,5 @@ from entry import Entry
from blob import Entry_blob from blob import Entry_blob
class Entry_intel_vbt(Entry_blob): class Entry_intel_vbt(Entry_blob):
def __init__(self, image, etype, node): def __init__(self, section, etype, node):
Entry_blob.__init__(self, image, etype, node) Entry_blob.__init__(self, section, etype, node)

View File

@ -9,5 +9,5 @@ from entry import Entry
from blob import Entry_blob from blob import Entry_blob
class Entry_intel_vga(Entry_blob): class Entry_intel_vga(Entry_blob):
def __init__(self, image, etype, node): def __init__(self, section, etype, node):
Entry_blob.__init__(self, image, etype, node) Entry_blob.__init__(self, section, etype, node)

View File

@ -0,0 +1,59 @@
# SPDX-License-Identifier: GPL-2.0+
# Copyright (c) 2018 Google, Inc
# Written by Simon Glass <sjg@chromium.org>
#
# Entry-type module for sections, which are entries which can contain other
# entries.
#
from entry import Entry
import fdt_util
import tools
import bsection
class Entry_section(Entry):
def __init__(self, image, etype, node):
Entry.__init__(self, image, etype, node)
self._section = bsection.Section(node.name, node)
def ObtainContents(self):
self._section.GetEntryContents()
def GetData(self):
return self._section.GetData()
def GetPositions(self):
"""Handle entries that want to set the position/size of other entries
This calls each entry's GetPositions() method. If it returns a list
of entries to update, it updates them.
"""
self._section.GetEntryPositions()
return {}
def Pack(self, pos):
"""Pack all entries into the section"""
self._section.PackEntries()
self.size = self._section.CheckSize()
return super(Entry_section, self).Pack(pos)
def WriteSymbols(self, section):
"""Write symbol values into binary files for access at run time"""
self._section.WriteSymbols()
def ProcessContents(self):
self._section.ProcessEntryContents()
super(Entry_section, self).ProcessContents()
def CheckPosition(self):
self._section.CheckEntries()
def WriteMap(self, fd, indent):
"""Write a map of the section to a .map file
Args:
fd: File to write the map to
"""
super(Entry_section, self).WriteMap(fd, indent)
self._section.WriteMap(fd, indent + 1)

View File

@ -9,8 +9,8 @@ from entry import Entry
from blob import Entry_blob from blob import Entry_blob
class Entry_u_boot(Entry_blob): class Entry_u_boot(Entry_blob):
def __init__(self, image, etype, node): def __init__(self, section, etype, node):
Entry_blob.__init__(self, image, etype, node) Entry_blob.__init__(self, section, etype, node)
def GetDefaultFilename(self): def GetDefaultFilename(self):
return 'u-boot.bin' return 'u-boot.bin'

View File

@ -9,8 +9,8 @@ from entry import Entry
from blob import Entry_blob from blob import Entry_blob
class Entry_u_boot_dtb(Entry_blob): class Entry_u_boot_dtb(Entry_blob):
def __init__(self, image, etype, node): def __init__(self, section, etype, node):
Entry_blob.__init__(self, image, etype, node) Entry_blob.__init__(self, section, etype, node)
def GetDefaultFilename(self): def GetDefaultFilename(self):
return 'u-boot.dtb' return 'u-boot.dtb'

View File

@ -16,8 +16,8 @@ class Entry_u_boot_dtb_with_ucode(Entry_blob):
See Entry_u_boot_ucode for full details of the 3 entries involved in this See Entry_u_boot_ucode for full details of the 3 entries involved in this
process. process.
""" """
def __init__(self, image, etype, node): def __init__(self, section, etype, node):
Entry_blob.__init__(self, image, etype, node) Entry_blob.__init__(self, section, etype, node)
self.ucode_data = '' self.ucode_data = ''
self.collate = False self.collate = False
self.ucode_offset = None self.ucode_offset = None
@ -29,10 +29,12 @@ class Entry_u_boot_dtb_with_ucode(Entry_blob):
def ObtainContents(self): def ObtainContents(self):
Entry_blob.ObtainContents(self) Entry_blob.ObtainContents(self)
# If the image does not need microcode, there is nothing to do # If the section does not need microcode, there is nothing to do
ucode_dest_entry = self.image.FindEntryType('u-boot-spl-with-ucode-ptr') ucode_dest_entry = self.section.FindEntryType(
'u-boot-spl-with-ucode-ptr')
if not ucode_dest_entry or not ucode_dest_entry.target_pos: if not ucode_dest_entry or not ucode_dest_entry.target_pos:
ucode_dest_entry = self.image.FindEntryType('u-boot-with-ucode-ptr') ucode_dest_entry = self.section.FindEntryType(
'u-boot-with-ucode-ptr')
if not ucode_dest_entry or not ucode_dest_entry.target_pos: if not ucode_dest_entry or not ucode_dest_entry.target_pos:
return True return True

View File

@ -9,8 +9,8 @@ from entry import Entry
from blob import Entry_blob from blob import Entry_blob
class Entry_u_boot_img(Entry_blob): class Entry_u_boot_img(Entry_blob):
def __init__(self, image, etype, node): def __init__(self, section, etype, node):
Entry_blob.__init__(self, image, etype, node) Entry_blob.__init__(self, section, etype, node)
def GetDefaultFilename(self): def GetDefaultFilename(self):
return 'u-boot.img' return 'u-boot.img'

View File

@ -9,8 +9,8 @@ from entry import Entry
from blob import Entry_blob from blob import Entry_blob
class Entry_u_boot_nodtb(Entry_blob): class Entry_u_boot_nodtb(Entry_blob):
def __init__(self, image, etype, node): def __init__(self, section, etype, node):
Entry_blob.__init__(self, image, etype, node) Entry_blob.__init__(self, section, etype, node)
def GetDefaultFilename(self): def GetDefaultFilename(self):
return 'u-boot-nodtb.bin' return 'u-boot-nodtb.bin'

View File

@ -11,12 +11,12 @@ from entry import Entry
from blob import Entry_blob from blob import Entry_blob
class Entry_u_boot_spl(Entry_blob): class Entry_u_boot_spl(Entry_blob):
def __init__(self, image, etype, node): def __init__(self, section, etype, node):
Entry_blob.__init__(self, image, etype, node) Entry_blob.__init__(self, section, etype, node)
self.elf_fname = 'spl/u-boot-spl' self.elf_fname = 'spl/u-boot-spl'
def GetDefaultFilename(self): def GetDefaultFilename(self):
return 'spl/u-boot-spl.bin' return 'spl/u-boot-spl.bin'
def WriteSymbols(self, image): def WriteSymbols(self, section):
elf.LookupAndWriteSymbols(self.elf_fname, self, image) elf.LookupAndWriteSymbols(self.elf_fname, self, section)

View File

@ -14,8 +14,8 @@ from blob import Entry_blob
import tools import tools
class Entry_u_boot_spl_bss_pad(Entry_blob): class Entry_u_boot_spl_bss_pad(Entry_blob):
def __init__(self, image, etype, node): def __init__(self, section, etype, node):
Entry_blob.__init__(self, image, etype, node) Entry_blob.__init__(self, section, etype, node)
def ObtainContents(self): def ObtainContents(self):
fname = tools.GetInputFilename('spl/u-boot-spl') fname = tools.GetInputFilename('spl/u-boot-spl')

View File

@ -9,8 +9,8 @@ from entry import Entry
from blob import Entry_blob from blob import Entry_blob
class Entry_u_boot_spl_dtb(Entry_blob): class Entry_u_boot_spl_dtb(Entry_blob):
def __init__(self, image, etype, node): def __init__(self, section, etype, node):
Entry_blob.__init__(self, image, etype, node) Entry_blob.__init__(self, section, etype, node)
def GetDefaultFilename(self): def GetDefaultFilename(self):
return 'spl/u-boot-spl.dtb' return 'spl/u-boot-spl.dtb'

View File

@ -9,8 +9,8 @@ from entry import Entry
from blob import Entry_blob from blob import Entry_blob
class Entry_u_boot_spl_nodtb(Entry_blob): class Entry_u_boot_spl_nodtb(Entry_blob):
def __init__(self, image, etype, node): def __init__(self, section, etype, node):
Entry_blob.__init__(self, image, etype, node) Entry_blob.__init__(self, section, etype, node)
def GetDefaultFilename(self): def GetDefaultFilename(self):
return 'spl/u-boot-spl-nodtb.bin' return 'spl/u-boot-spl-nodtb.bin'

View File

@ -19,8 +19,8 @@ class Entry_u_boot_spl_with_ucode_ptr(Entry_u_boot_with_ucode_ptr):
See Entry_u_boot_ucode for full details of the entries involved in this See Entry_u_boot_ucode for full details of the entries involved in this
process. process.
""" """
def __init__(self, image, etype, node): def __init__(self, section, etype, node):
Entry_u_boot_with_ucode_ptr.__init__(self, image, etype, node) Entry_u_boot_with_ucode_ptr.__init__(self, section, etype, node)
self.elf_fname = 'spl/u-boot-spl' self.elf_fname = 'spl/u-boot-spl'
def GetDefaultFilename(self): def GetDefaultFilename(self):

View File

@ -51,13 +51,13 @@ class Entry_u_boot_ucode(Entry_blob):
the Entry_u_boot_dtb_with_ucode entry, and uses it as the the Entry_u_boot_dtb_with_ucode entry, and uses it as the
contents of this entry. contents of this entry.
""" """
def __init__(self, image, etype, node): def __init__(self, section, etype, node):
Entry_blob.__init__(self, image, etype, node) Entry_blob.__init__(self, section, etype, node)
def ObtainContents(self): def ObtainContents(self):
# If the image does not need microcode, there is nothing to do # If the section does not need microcode, there is nothing to do
ucode_dest_entry = self.image.FindEntryType('u-boot-with-ucode-ptr') ucode_dest_entry = self.section.FindEntryType('u-boot-with-ucode-ptr')
ucode_dest_entry_spl = self.image.FindEntryType( ucode_dest_entry_spl = self.section.FindEntryType(
'u-boot-spl-with-ucode-ptr') 'u-boot-spl-with-ucode-ptr')
if ((not ucode_dest_entry or not ucode_dest_entry.target_pos) and 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)): (not ucode_dest_entry_spl or not ucode_dest_entry_spl.target_pos)):
@ -65,12 +65,12 @@ class Entry_u_boot_ucode(Entry_blob):
return True return True
# Get the microcode from the device tree entry # Get the microcode from the device tree entry
fdt_entry = self.image.FindEntryType('u-boot-dtb-with-ucode') fdt_entry = self.section.FindEntryType('u-boot-dtb-with-ucode')
if not fdt_entry or not fdt_entry.ucode_data: if not fdt_entry or not fdt_entry.ucode_data:
return False return False
if not fdt_entry.collate: if not fdt_entry.collate:
# This section can be empty # This binary can be empty
self.data = '' self.data = ''
return True return True

View File

@ -20,8 +20,8 @@ class Entry_u_boot_with_ucode_ptr(Entry_blob):
See Entry_u_boot_ucode for full details of the 3 entries involved in this See Entry_u_boot_ucode for full details of the 3 entries involved in this
process. process.
""" """
def __init__(self, image, etype, node): def __init__(self, section, etype, node):
Entry_blob.__init__(self, image, etype, node) Entry_blob.__init__(self, section, etype, node)
self.elf_fname = 'u-boot' self.elf_fname = 'u-boot'
self.target_pos = None self.target_pos = None
@ -45,24 +45,24 @@ class Entry_u_boot_with_ucode_ptr(Entry_blob):
return return
# Get the position of the microcode # Get the position of the microcode
ucode_entry = self.image.FindEntryType('u-boot-ucode') ucode_entry = self.section.FindEntryType('u-boot-ucode')
if not ucode_entry: if not ucode_entry:
self.Raise('Cannot find microcode region u-boot-ucode') self.Raise('Cannot find microcode region u-boot-ucode')
# Check the target pos is in the image. If it is not, then U-Boot is # Check the target pos is in the section. If it is not, then U-Boot is
# being linked incorrectly, or is being placed at the wrong position # being linked incorrectly, or is being placed at the wrong position
# in the image. # in the section.
# #
# The image must be set up so that U-Boot is placed at the # The section must be set up so that U-Boot is placed at the
# flash address to which it is linked. For example, if # flash address to which it is linked. For example, if
# CONFIG_SYS_TEXT_BASE is 0xfff00000, and the ROM is 8MB, then # CONFIG_SYS_TEXT_BASE is 0xfff00000, and the ROM is 8MB, then
# the U-Boot region must start at position 7MB in the image. In this # the U-Boot region must start at position 7MB in the section. In this
# case the ROM starts at 0xff800000, so the position of the first # case the ROM starts at 0xff800000, so the position of the first
# entry in the image corresponds to that. # entry in the section corresponds to that.
if (self.target_pos < self.pos or if (self.target_pos < self.pos or
self.target_pos >= self.pos + self.size): self.target_pos >= self.pos + self.size):
self.Raise('Microcode pointer _dt_ucode_base_size at %08x is ' self.Raise('Microcode pointer _dt_ucode_base_size at %08x is '
'outside the image ranging from %08x to %08x' % 'outside the section ranging from %08x to %08x' %
(self.target_pos, self.pos, self.pos + self.size)) (self.target_pos, self.pos, self.pos + self.size))
# Get the microcode, either from u-boot-ucode or u-boot-dtb-with-ucode. # Get the microcode, either from u-boot-ucode or u-boot-dtb-with-ucode.
@ -72,7 +72,7 @@ class Entry_u_boot_with_ucode_ptr(Entry_blob):
if ucode_entry.size: if ucode_entry.size:
pos, size = ucode_entry.pos, ucode_entry.size pos, size = ucode_entry.pos, ucode_entry.size
else: else:
dtb_entry = self.image.FindEntryType('u-boot-dtb-with-ucode') dtb_entry = self.section.FindEntryType('u-boot-dtb-with-ucode')
if not dtb_entry: if not dtb_entry:
self.Raise('Cannot find microcode region u-boot-dtb-with-ucode') self.Raise('Cannot find microcode region u-boot-dtb-with-ucode')
pos = dtb_entry.pos + dtb_entry.ucode_offset pos = dtb_entry.pos + dtb_entry.ucode_offset

View File

@ -9,8 +9,8 @@ from entry import Entry
from blob import Entry_blob from blob import Entry_blob
class Entry_x86_start16(Entry_blob): class Entry_x86_start16(Entry_blob):
def __init__(self, image, etype, node): def __init__(self, section, etype, node):
Entry_blob.__init__(self, image, etype, node) Entry_blob.__init__(self, section, etype, node)
def GetDefaultFilename(self): def GetDefaultFilename(self):
return 'u-boot-x86-16bit.bin' return 'u-boot-x86-16bit.bin'

View File

@ -9,8 +9,8 @@ from entry import Entry
from blob import Entry_blob from blob import Entry_blob
class Entry_x86_start16_spl(Entry_blob): class Entry_x86_start16_spl(Entry_blob):
def __init__(self, image, etype, node): def __init__(self, section, etype, node):
Entry_blob.__init__(self, image, etype, node) Entry_blob.__init__(self, section, etype, node)
def GetDefaultFilename(self): def GetDefaultFilename(self):
return 'spl/u-boot-x86-16bit-spl.bin' return 'spl/u-boot-x86-16bit-spl.bin'

View File

@ -146,15 +146,19 @@ class TestFunctional(unittest.TestCase):
# options.verbosity = tout.DEBUG # options.verbosity = tout.DEBUG
return control.Binman(options, args) return control.Binman(options, args)
def _DoTestFile(self, fname, debug=False): def _DoTestFile(self, fname, debug=False, map=False):
"""Run binman with a given test file """Run binman with a given test file
Args: Args:
fname: Device tree source filename to use (e.g. 05_simple.dts) fname: Device-tree source filename to use (e.g. 05_simple.dts)
debug: True to enable debugging output
map: True to output map files for the images
""" """
args = ['-p', '-I', self._indir, '-d', self.TestFile(fname)] args = ['-p', '-I', self._indir, '-d', self.TestFile(fname)]
if debug: if debug:
args.append('-D') args.append('-D')
if map:
args.append('-m')
return self._DoBinman(*args) return self._DoBinman(*args)
def _SetupDtb(self, fname, outfile='u-boot.dtb'): def _SetupDtb(self, fname, outfile='u-boot.dtb'):
@ -165,10 +169,10 @@ class TestFunctional(unittest.TestCase):
Args: Args:
fname: Filename of .dts file to read fname: Filename of .dts file to read
outfile: Output filename for compiled device tree binary outfile: Output filename for compiled device-tree binary
Returns: Returns:
Contents of device tree binary Contents of device-tree binary
""" """
if not self._output_setup: if not self._output_setup:
tools.PrepareOutputDir(self._indir, True) tools.PrepareOutputDir(self._indir, True)
@ -179,7 +183,7 @@ class TestFunctional(unittest.TestCase):
TestFunctional._MakeInputFile(outfile, data) TestFunctional._MakeInputFile(outfile, data)
return data return data
def _DoReadFileDtb(self, fname, use_real_dtb=False): def _DoReadFileDtb(self, fname, use_real_dtb=False, map=False):
"""Run binman and return the resulting image """Run binman and return the resulting image
This runs binman with a given test file and then reads the resulting This runs binman with a given test file and then reads the resulting
@ -189,16 +193,18 @@ class TestFunctional(unittest.TestCase):
Raises an assertion failure if binman returns a non-zero exit code. Raises an assertion failure if binman returns a non-zero exit code.
Args: Args:
fname: Device tree source filename to use (e.g. 05_simple.dts) fname: Device-tree source filename to use (e.g. 05_simple.dts)
use_real_dtb: True to use the test file as the contents of use_real_dtb: True to use the test file as the contents of
the u-boot-dtb entry. Normally this is not needed and the the u-boot-dtb entry. Normally this is not needed and the
test contents (the U_BOOT_DTB_DATA string) can be used. test contents (the U_BOOT_DTB_DATA string) can be used.
But in some test we need the real contents. But in some test we need the real contents.
map: True to output map files for the images
Returns: Returns:
Tuple: Tuple:
Resulting image contents Resulting image contents
Device tree contents Device tree contents
Map data showing contents of image (or None if none)
""" """
dtb_data = None dtb_data = None
# Use the compiled test file as the u-boot-dtb input # Use the compiled test file as the u-boot-dtb input
@ -206,22 +212,36 @@ class TestFunctional(unittest.TestCase):
dtb_data = self._SetupDtb(fname) dtb_data = self._SetupDtb(fname)
try: try:
retcode = self._DoTestFile(fname) retcode = self._DoTestFile(fname, map=map)
self.assertEqual(0, retcode) self.assertEqual(0, retcode)
# Find the (only) image, read it and return its contents # Find the (only) image, read it and return its contents
image = control.images['image'] image = control.images['image']
fname = tools.GetOutputFilename('image.bin') fname = tools.GetOutputFilename('image.bin')
self.assertTrue(os.path.exists(fname)) self.assertTrue(os.path.exists(fname))
if map:
map_fname = tools.GetOutputFilename('image.map')
with open(map_fname) as fd:
map_data = fd.read()
else:
map_data = None
with open(fname) as fd: with open(fname) as fd:
return fd.read(), dtb_data return fd.read(), dtb_data, map_data
finally: finally:
# Put the test file back # Put the test file back
if use_real_dtb: if use_real_dtb:
TestFunctional._MakeInputFile('u-boot.dtb', U_BOOT_DTB_DATA) TestFunctional._MakeInputFile('u-boot.dtb', U_BOOT_DTB_DATA)
def _DoReadFile(self, fname, use_real_dtb=False): def _DoReadFile(self, fname, use_real_dtb=False):
"""Helper function which discards the device-tree binary""" """Helper function which discards the device-tree binary
Args:
fname: Device-tree source filename to use (e.g. 05_simple.dts)
use_real_dtb: True to use the test file as the contents of
the u-boot-dtb entry. Normally this is not needed and the
test contents (the U_BOOT_DTB_DATA string) can be used.
But in some test we need the real contents.
"""
return self._DoReadFileDtb(fname, use_real_dtb)[0] return self._DoReadFileDtb(fname, use_real_dtb)[0]
@classmethod @classmethod
@ -270,13 +290,13 @@ class TestFunctional(unittest.TestCase):
pos += entry.size pos += entry.size
def GetFdtLen(self, dtb): def GetFdtLen(self, dtb):
"""Get the totalsize field from a device tree binary """Get the totalsize field from a device-tree binary
Args: Args:
dtb: Device tree binary contents dtb: Device-tree binary contents
Returns: Returns:
Total size of device tree binary, from the header Total size of device-tree binary, from the header
""" """
return struct.unpack('>L', dtb[4:8])[0] return struct.unpack('>L', dtb[4:8])[0]
@ -326,7 +346,7 @@ class TestFunctional(unittest.TestCase):
str(e.exception)) str(e.exception))
def testMissingDt(self): def testMissingDt(self):
"""Test that an invalid device tree file generates an error""" """Test that an invalid device-tree file generates an error"""
with self.assertRaises(Exception) as e: with self.assertRaises(Exception) as e:
self._RunBinman('-d', 'missing_file') self._RunBinman('-d', 'missing_file')
# We get one error from libfdt, and a different one from fdtget. # We get one error from libfdt, and a different one from fdtget.
@ -334,7 +354,7 @@ class TestFunctional(unittest.TestCase):
'No such file or directory'], str(e.exception)) 'No such file or directory'], str(e.exception))
def testBrokenDt(self): def testBrokenDt(self):
"""Test that an invalid device tree source file generates an error """Test that an invalid device-tree source file generates an error
Since this is a source file it should be compiled and the error Since this is a source file it should be compiled and the error
will come from the device-tree compiler (dtc). will come from the device-tree compiler (dtc).
@ -413,7 +433,7 @@ class TestFunctional(unittest.TestCase):
self.assertEqual(0, retcode) self.assertEqual(0, retcode)
self.assertIn('image', control.images) self.assertIn('image', control.images)
image = control.images['image'] image = control.images['image']
entries = image._entries entries = image.GetEntries()
self.assertEqual(5, len(entries)) self.assertEqual(5, len(entries))
# First u-boot # First u-boot
@ -456,7 +476,7 @@ class TestFunctional(unittest.TestCase):
self.assertEqual(0, retcode) self.assertEqual(0, retcode)
self.assertIn('image', control.images) self.assertIn('image', control.images)
image = control.images['image'] image = control.images['image']
entries = image._entries entries = image.GetEntries()
self.assertEqual(5, len(entries)) self.assertEqual(5, len(entries))
# First u-boot with padding before and after # First u-boot with padding before and after
@ -540,7 +560,7 @@ class TestFunctional(unittest.TestCase):
"""Test that entries which overflow the image size are detected""" """Test that entries which overflow the image size are detected"""
with self.assertRaises(ValueError) as e: with self.assertRaises(ValueError) as e:
self._DoTestFile('16_pack_image_overflow.dts') self._DoTestFile('16_pack_image_overflow.dts')
self.assertIn("Image '/binman': contents size 0x4 (4) exceeds image " self.assertIn("Section '/binman': contents size 0x4 (4) exceeds section "
"size 0x3 (3)", str(e.exception)) "size 0x3 (3)", str(e.exception))
def testPackImageSize(self): def testPackImageSize(self):
@ -563,14 +583,14 @@ class TestFunctional(unittest.TestCase):
"""Test that invalid image alignment is detected""" """Test that invalid image alignment is detected"""
with self.assertRaises(ValueError) as e: with self.assertRaises(ValueError) as e:
self._DoTestFile('19_pack_inv_image_align.dts') self._DoTestFile('19_pack_inv_image_align.dts')
self.assertIn("Image '/binman': Size 0x7 (7) does not match " self.assertIn("Section '/binman': Size 0x7 (7) does not match "
"align-size 0x8 (8)", str(e.exception)) "align-size 0x8 (8)", str(e.exception))
def testPackAlignPowerOf2(self): def testPackAlignPowerOf2(self):
"""Test that invalid image alignment is detected""" """Test that invalid image alignment is detected"""
with self.assertRaises(ValueError) as e: with self.assertRaises(ValueError) as e:
self._DoTestFile('20_pack_inv_image_align_power2.dts') self._DoTestFile('20_pack_inv_image_align_power2.dts')
self.assertIn("Image '/binman': Alignment size 131 must be a power of " self.assertIn("Section '/binman': Alignment size 131 must be a power of "
"two", str(e.exception)) "two", str(e.exception))
def testImagePadByte(self): def testImagePadByte(self):
@ -620,7 +640,7 @@ class TestFunctional(unittest.TestCase):
"""Test that the end-at-4gb property requires a size property""" """Test that the end-at-4gb property requires a size property"""
with self.assertRaises(ValueError) as e: with self.assertRaises(ValueError) as e:
self._DoTestFile('27_pack_4gb_no_size.dts') self._DoTestFile('27_pack_4gb_no_size.dts')
self.assertIn("Image '/binman': Image size must be provided when " self.assertIn("Section '/binman': Section size must be provided when "
"using end-at-4gb", str(e.exception)) "using end-at-4gb", str(e.exception))
def testPackX86RomOutside(self): def testPackX86RomOutside(self):
@ -628,7 +648,7 @@ class TestFunctional(unittest.TestCase):
with self.assertRaises(ValueError) as e: with self.assertRaises(ValueError) as e:
self._DoTestFile('28_pack_4gb_outside.dts') self._DoTestFile('28_pack_4gb_outside.dts')
self.assertIn("Node '/binman/u-boot': Position 0x0 (0) is outside " self.assertIn("Node '/binman/u-boot': Position 0x0 (0) is outside "
"the image starting at 0xffffffe0 (4294967264)", "the section starting at 0xffffffe0 (4294967264)",
str(e.exception)) str(e.exception))
def testPackX86Rom(self): def testPackX86Rom(self):
@ -800,13 +820,13 @@ class TestFunctional(unittest.TestCase):
self._DoReadFile('40_x86_ucode_not_in_image.dts', True) self._DoReadFile('40_x86_ucode_not_in_image.dts', True)
self.assertIn("Node '/binman/u-boot-with-ucode-ptr': Microcode " self.assertIn("Node '/binman/u-boot-with-ucode-ptr': Microcode "
"pointer _dt_ucode_base_size at fffffe14 is outside the " "pointer _dt_ucode_base_size at fffffe14 is outside the "
"image ranging from 00000000 to 0000002e", str(e.exception)) "section ranging from 00000000 to 0000002e", str(e.exception))
def testWithoutMicrocode(self): def testWithoutMicrocode(self):
"""Test that we can cope with an image without microcode (e.g. qemu)""" """Test that we can cope with an image without microcode (e.g. qemu)"""
with open(self.TestFile('u_boot_no_ucode_ptr')) as fd: with open(self.TestFile('u_boot_no_ucode_ptr')) as fd:
TestFunctional._MakeInputFile('u-boot', fd.read()) TestFunctional._MakeInputFile('u-boot', fd.read())
data, dtb = self._DoReadFileDtb('44_x86_optional_ucode.dts', True) data, dtb, _ = self._DoReadFileDtb('44_x86_optional_ucode.dts', True)
# Now check the device tree has no microcode # Now check the device tree has no microcode
self.assertEqual(U_BOOT_NODTB_DATA, data[:len(U_BOOT_NODTB_DATA)]) self.assertEqual(U_BOOT_NODTB_DATA, data[:len(U_BOOT_NODTB_DATA)])
@ -823,7 +843,7 @@ class TestFunctional(unittest.TestCase):
"""Test that microcode must be placed within the image""" """Test that microcode must be placed within the image"""
with self.assertRaises(ValueError) as e: with self.assertRaises(ValueError) as e:
self._DoReadFile('41_unknown_pos_size.dts', True) self._DoReadFile('41_unknown_pos_size.dts', True)
self.assertIn("Image '/binman': Unable to set pos/size for unknown " self.assertIn("Section '/binman': Unable to set pos/size for unknown "
"entry 'invalid-entry'", str(e.exception)) "entry 'invalid-entry'", str(e.exception))
def testPackFsp(self): def testPackFsp(self):
@ -909,6 +929,36 @@ class TestFunctional(unittest.TestCase):
sym_values + U_BOOT_SPL_DATA[16:]) sym_values + U_BOOT_SPL_DATA[16:])
self.assertEqual(expected, data) self.assertEqual(expected, data)
def testPackUnitAddress(self):
"""Test that we support multiple binaries with the same name"""
data = self._DoReadFile('54_unit_address.dts')
self.assertEqual(U_BOOT_DATA + U_BOOT_DATA, data)
def testSections(self):
"""Basic test of sections"""
data = self._DoReadFile('55_sections.dts')
expected = U_BOOT_DATA + '!' * 12 + U_BOOT_DATA + 'a' * 12 + '&' * 8
self.assertEqual(expected, data)
def testMap(self):
"""Tests outputting a map of the images"""
_, _, map_data = self._DoReadFileDtb('55_sections.dts', map=True)
self.assertEqual('''Position Size Name
00000000 00000010 section@0
00000000 00000004 u-boot
00000010 00000010 section@1
00000000 00000004 u-boot
''', map_data)
def testNamePrefix(self):
"""Tests that name prefixes are used"""
_, _, map_data = self._DoReadFileDtb('56_name_prefix.dts', map=True)
self.assertEqual('''Position Size Name
00000000 00000010 section@0
00000000 00000004 ro-u-boot
00000010 00000010 section@1
00000000 00000004 rw-u-boot
''', map_data)
if __name__ == "__main__": if __name__ == "__main__":
unittest.main() unittest.main()

View File

@ -13,6 +13,7 @@ import re
import sys import sys
import fdt_util import fdt_util
import bsection
import tools import tools
class Image: class Image:
@ -27,158 +28,36 @@ class Image:
_node: Node object that contains the image definition in device tree _node: Node object that contains the image definition in device tree
_name: Image name _name: Image name
_size: Image size in bytes, or None if not known yet _size: Image size in bytes, or None if not known yet
_align_size: Image size alignment, or None
_pad_before: Number of bytes before the first entry starts. This
effectively changes the place where entry position 0 starts
_pad_after: Number of bytes after the last entry ends. The last
entry will finish on or before this boundary
_pad_byte: Byte to use to pad the image where there is no entry
_filename: Output filename for image _filename: Output filename for image
_sort: True if entries should be sorted by position, False if they _sections: Sections present in this image (may be one or more)
must be in-order in the device tree description
_skip_at_start: Number of bytes before the first entry starts. These Args:
effecively adjust the starting position of entries. For example, test: True if this is being called from a test of Images. This this case
if _pad_before is 16, then the first entry would start at 16. there is no device tree defining the structure of the section, so
An entry with pos = 20 would in fact be written at position 4 we create a section manually.
in the image file.
_end_4gb: Indicates that the image ends at the 4GB boundary. This is
used for x86 images, which want to use positions such that a
memory address (like 0xff800000) is the first entry position.
This causes _skip_at_start to be set to the starting memory
address.
_entries: OrderedDict() of entries
""" """
def __init__(self, name, node, test=False): def __init__(self, name, node, test=False):
global entry
global Entry
import entry
from entry import Entry
self._node = node self._node = node
self._name = name self._name = name
self._size = None self._size = None
self._align_size = None
self._pad_before = 0
self._pad_after = 0
self._pad_byte = 0
self._filename = '%s.bin' % self._name self._filename = '%s.bin' % self._name
self._sort = False if test:
self._skip_at_start = 0 self._section = bsection.Section('main-section', self._node, True)
self._end_4gb = False else:
self._entries = OrderedDict()
if not test:
self._ReadNode() self._ReadNode()
self._ReadEntries()
def _ReadNode(self): def _ReadNode(self):
"""Read properties from the image node""" """Read properties from the image node"""
self._size = fdt_util.GetInt(self._node, 'size') self._size = fdt_util.GetInt(self._node, 'size')
self._align_size = fdt_util.GetInt(self._node, 'align-size')
if tools.NotPowerOfTwo(self._align_size):
self._Raise("Alignment size %s must be a power of two" %
self._align_size)
self._pad_before = fdt_util.GetInt(self._node, 'pad-before', 0)
self._pad_after = fdt_util.GetInt(self._node, 'pad-after', 0)
self._pad_byte = fdt_util.GetInt(self._node, 'pad-byte', 0)
filename = fdt_util.GetString(self._node, 'filename') filename = fdt_util.GetString(self._node, 'filename')
if filename: if filename:
self._filename = filename self._filename = filename
self._sort = fdt_util.GetBool(self._node, 'sort-by-pos') self._section = bsection.Section('main-section', self._node)
self._end_4gb = fdt_util.GetBool(self._node, 'end-at-4gb')
if self._end_4gb and not self._size:
self._Raise("Image size must be provided when using end-at-4gb")
if self._end_4gb:
self._skip_at_start = 0x100000000 - self._size
def CheckSize(self):
"""Check that the image contents does not exceed its size, etc."""
contents_size = 0
for entry in self._entries.values():
contents_size = max(contents_size, entry.pos + entry.size)
contents_size -= self._skip_at_start
size = self._size
if not size:
size = self._pad_before + contents_size + self._pad_after
size = tools.Align(size, self._align_size)
if self._size and contents_size > self._size:
self._Raise("contents size %#x (%d) exceeds image size %#x (%d)" %
(contents_size, contents_size, self._size, self._size))
if not self._size:
self._size = size
if self._size != tools.Align(self._size, self._align_size):
self._Raise("Size %#x (%d) does not match align-size %#x (%d)" %
(self._size, self._size, self._align_size, self._align_size))
def _Raise(self, msg):
"""Raises an error for this image
Args:
msg: Error message to use in the raise string
Raises:
ValueError()
"""
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)
def FindEntryType(self, etype):
"""Find an entry type in the image
Args:
etype: Entry type to find
Returns:
entry matching that type, or None if not found
"""
for entry in self._entries.values():
if entry.etype == etype:
return entry
return None
def GetEntryContents(self): def GetEntryContents(self):
"""Call ObtainContents() for each entry """Call ObtainContents() for the section
This calls each entry's ObtainContents() a few times until they all
return True. We stop calling an entry's function once it returns
True. This allows the contents of one entry to depend on another.
After 3 rounds we give up since it's likely an error.
""" """
todo = self._entries.values() self._section.GetEntryContents()
for passnum in range(3):
next_todo = []
for entry in todo:
if not entry.ObtainContents():
next_todo.append(entry)
todo = next_todo
if not todo:
break
def _SetEntryPosSize(self, name, pos, size):
"""Set the position and size of an entry
Args:
name: Entry name to update
pos: New position
size: New size
"""
entry = self._entries.get(name)
if not entry:
self._Raise("Unable to set pos/size for unknown entry '%s'" % name)
entry.SetPositionSize(self._skip_at_start + pos, size)
def GetEntryPositions(self): def GetEntryPositions(self):
"""Handle entries that want to set the position/size of other entries """Handle entries that want to set the position/size of other entries
@ -186,119 +65,44 @@ class Image:
This calls each entry's GetPositions() method. If it returns a list This calls each entry's GetPositions() method. If it returns a list
of entries to update, it updates them. of entries to update, it updates them.
""" """
for entry in self._entries.values(): self._section.GetEntryPositions()
pos_dict = entry.GetPositions()
for name, info in pos_dict.iteritems():
self._SetEntryPosSize(name, *info)
def PackEntries(self): def PackEntries(self):
"""Pack all entries into the image""" """Pack all entries into the image"""
pos = self._skip_at_start self._section.PackEntries()
for entry in self._entries.values():
pos = entry.Pack(pos)
def _SortEntries(self): def CheckSize(self):
"""Sort entries by position""" """Check that the image contents does not exceed its size, etc."""
entries = sorted(self._entries.values(), key=lambda entry: entry.pos) self._size = self._section.CheckSize()
self._entries.clear()
for entry in entries:
self._entries[entry._node.name] = entry
def CheckEntries(self): def CheckEntries(self):
"""Check that entries do not overlap or extend outside the image""" """Check that entries do not overlap or extend outside the image"""
if self._sort: self._section.CheckEntries()
self._SortEntries()
pos = 0
prev_name = 'None'
for entry in self._entries.values():
if (entry.pos < self._skip_at_start or
entry.pos >= self._skip_at_start + self._size):
entry.Raise("Position %#x (%d) is outside the image starting "
"at %#x (%d)" %
(entry.pos, entry.pos, self._skip_at_start,
self._skip_at_start))
if entry.pos < pos:
entry.Raise("Position %#x (%d) overlaps with previous entry '%s' "
"ending at %#x (%d)" %
(entry.pos, entry.pos, prev_name, pos, pos))
pos = entry.pos + entry.size
prev_name = entry.GetPath()
def ProcessEntryContents(self): def ProcessEntryContents(self):
"""Call the ProcessContents() method for each entry """Call the ProcessContents() method for each entry
This is intended to adjust the contents as needed by the entry type. This is intended to adjust the contents as needed by the entry type.
""" """
for entry in self._entries.values(): self._section.ProcessEntryContents()
entry.ProcessContents()
def WriteSymbols(self): def WriteSymbols(self):
"""Write symbol values into binary files for access at run time""" """Write symbol values into binary files for access at run time"""
for entry in self._entries.values(): self._section.WriteSymbols()
entry.WriteSymbols(self)
def BuildImage(self): def BuildImage(self):
"""Write the image to a file""" """Write the image to a file"""
fname = tools.GetOutputFilename(self._filename) fname = tools.GetOutputFilename(self._filename)
with open(fname, 'wb') as fd: with open(fname, 'wb') as fd:
fd.write(chr(self._pad_byte) * self._size) self._section.BuildSection(fd, 0)
for entry in self._entries.values(): def GetEntries(self):
data = entry.GetData() return self._section.GetEntries()
fd.seek(self._pad_before + entry.pos - self._skip_at_start)
fd.write(data)
def LookupSymbol(self, sym_name, optional, msg): def WriteMap(self):
"""Look up a symbol in an ELF file """Write a map of the image to a .map file"""
filename = '%s.map' % self._name
Looks up a symbol in an ELF file. Only entry types which come from an fname = tools.GetOutputFilename(filename)
ELF image can be used by this function. with open(fname, 'w') as fd:
print('%8s %8s %s' % ('Position', 'Size', 'Name'), file=fd)
At present the only entry property supported is pos. self._section.WriteMap(fd, 0)
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))

View File

@ -12,25 +12,28 @@ from elf_test import capture_sys_output
class TestImage(unittest.TestCase): class TestImage(unittest.TestCase):
def testInvalidFormat(self): def testInvalidFormat(self):
image = Image('name', 'node', test=True) image = Image('name', 'node', test=True)
section = image._section
with self.assertRaises(ValueError) as e: with self.assertRaises(ValueError) as e:
image.LookupSymbol('_binman_something_prop_', False, 'msg') section.LookupSymbol('_binman_something_prop_', False, 'msg')
self.assertIn( self.assertIn(
"msg: Symbol '_binman_something_prop_' has invalid format", "msg: Symbol '_binman_something_prop_' has invalid format",
str(e.exception)) str(e.exception))
def testMissingSymbol(self): def testMissingSymbol(self):
image = Image('name', 'node', test=True) image = Image('name', 'node', test=True)
image._entries = {} section = image._section
section._entries = {}
with self.assertRaises(ValueError) as e: with self.assertRaises(ValueError) as e:
image.LookupSymbol('_binman_type_prop_pname', False, 'msg') section.LookupSymbol('_binman_type_prop_pname', False, 'msg')
self.assertIn("msg: Entry 'type' not found in list ()", self.assertIn("msg: Entry 'type' not found in list ()",
str(e.exception)) str(e.exception))
def testMissingSymbolOptional(self): def testMissingSymbolOptional(self):
image = Image('name', 'node', test=True) image = Image('name', 'node', test=True)
image._entries = {} section = image._section
section._entries = {}
with capture_sys_output() as (stdout, stderr): with capture_sys_output() as (stdout, stderr):
val = image.LookupSymbol('_binman_type_prop_pname', True, 'msg') val = section.LookupSymbol('_binman_type_prop_pname', True, 'msg')
self.assertEqual(val, None) self.assertEqual(val, None)
self.assertEqual("Warning: msg: Entry 'type' not found in list ()\n", self.assertEqual("Warning: msg: Entry 'type' not found in list ()\n",
stderr.getvalue()) stderr.getvalue())
@ -38,7 +41,8 @@ class TestImage(unittest.TestCase):
def testBadProperty(self): def testBadProperty(self):
image = Image('name', 'node', test=True) image = Image('name', 'node', test=True)
image._entries = {'u-boot': 1} section = image._section
section._entries = {'u-boot': 1}
with self.assertRaises(ValueError) as e: with self.assertRaises(ValueError) as e:
image.LookupSymbol('_binman_u_boot_prop_bad', False, 'msg') section.LookupSymbol('_binman_u_boot_prop_bad', False, 'msg')
self.assertIn("msg: No such property 'bad", str(e.exception)) self.assertIn("msg: No such property 'bad", str(e.exception))

View File

@ -0,0 +1,15 @@
// SPDX-License-Identifier: GPL-2.0+
/dts-v1/;
/ {
#address-cells = <1>;
#size-cells = <1>;
binman {
u-boot@0 {
};
u-boot@1 {
};
};
};

View File

@ -0,0 +1,28 @@
// SPDX-License-Identifier: GPL-2.0+
/dts-v1/;
/ {
#address-cells = <1>;
#size-cells = <1>;
binman {
pad-byte = <0x26>;
size = <0x28>;
section@0 {
read-only;
size = <0x10>;
pad-byte = <0x21>;
u-boot {
};
};
section@1 {
size = <0x10>;
pad-byte = <0x61>;
u-boot {
};
};
};
};

View File

@ -0,0 +1,30 @@
// SPDX-License-Identifier: GPL-2.0+
/dts-v1/;
/ {
#address-cells = <1>;
#size-cells = <1>;
binman {
pad-byte = <0x26>;
size = <0x28>;
section@0 {
read-only;
name-prefix = "ro-";
size = <0x10>;
pad-byte = <0x21>;
u-boot {
};
};
section@1 {
name-prefix = "rw-";
size = <0x10>;
pad-byte = <0x61>;
u-boot {
};
};
};
};

View File

@ -1008,6 +1008,34 @@ variables. This avoids lots of useless output when converting a CONFIG
option to Kconfig. To disable this behaviour, use --squash-config-y. option to Kconfig. To disable this behaviour, use --squash-config-y.
Checking the environment
========================
When converting CONFIG options which manipulate the default environment,
a common requirement is to check that the default environment has not
changed due to the conversion. Buildman supports this with the -U option,
used after a build. This shows differences in the default environment
between one commit and the next.
For example:
$ buildman -b squash brppt1 -sU
boards.cfg is up to date. Nothing to do.
Summary of 2 commits for 3 boards (3 threads, 3 jobs per thread)
01: Migrate bootlimit to Kconfig
02: Squashed commit of the following:
c brppt1_mmc: altbootcmd=mmc dev 1; run mmcboot0; -> mmc dev 1; run mmcboot0
c brppt1_spi: altbootcmd=mmc dev 1; run mmcboot0; -> mmc dev 1; run mmcboot0
+ brppt1_nand: altbootcmd=run usbscript
- brppt1_nand: altbootcmd=run usbscript
(no errors to report)
This shows that commit 2 modified the value of 'altbootcmd' for 'brppt1_mmc'
and 'brppt1_spi', removing a trailing semicolon. 'brppt1_nand' gained an a
value for 'altbootcmd', but lost one for ' altbootcmd'.
The -U option uses the u-boot.env files which are produced by a build.
Other options Other options
============= =============

View File

@ -127,6 +127,15 @@ class Config:
val = val ^ hash(key) & hash(value) val = val ^ hash(key) & hash(value)
return val return val
class Environment:
"""Holds information about environment variables for a board."""
def __init__(self, target):
self.target = target
self.environment = {}
def Add(self, key, value):
self.environment[key] = value
class Builder: class Builder:
"""Class for building U-Boot for a particular commit. """Class for building U-Boot for a particular commit.
@ -199,13 +208,17 @@ class Builder:
value is itself a dictionary: value is itself a dictionary:
key: config name key: config name
value: config value value: config value
environment: Dictionary keyed by environment variable, Each
value is the value of environment variable.
""" """
def __init__(self, rc, err_lines, sizes, func_sizes, config): def __init__(self, rc, err_lines, sizes, func_sizes, config,
environment):
self.rc = rc self.rc = rc
self.err_lines = err_lines self.err_lines = err_lines
self.sizes = sizes self.sizes = sizes
self.func_sizes = func_sizes self.func_sizes = func_sizes
self.config = config self.config = config
self.environment = environment
def __init__(self, toolchains, base_dir, git_dir, num_threads, num_jobs, def __init__(self, toolchains, base_dir, git_dir, num_threads, num_jobs,
gnu_make='make', checkout=True, show_unknown=True, step=1, gnu_make='make', checkout=True, show_unknown=True, step=1,
@ -310,7 +323,8 @@ class Builder:
def SetDisplayOptions(self, show_errors=False, show_sizes=False, def SetDisplayOptions(self, show_errors=False, show_sizes=False,
show_detail=False, show_bloat=False, show_detail=False, show_bloat=False,
list_error_boards=False, show_config=False): list_error_boards=False, show_config=False,
show_environment=False):
"""Setup display options for the builder. """Setup display options for the builder.
show_errors: True to show summarised error/warning info show_errors: True to show summarised error/warning info
@ -319,6 +333,7 @@ class Builder:
show_bloat: Show detail for each function show_bloat: Show detail for each function
list_error_boards: Show the boards which caused each error/warning list_error_boards: Show the boards which caused each error/warning
show_config: Show config deltas show_config: Show config deltas
show_environment: Show environment deltas
""" """
self._show_errors = show_errors self._show_errors = show_errors
self._show_sizes = show_sizes self._show_sizes = show_sizes
@ -326,6 +341,7 @@ class Builder:
self._show_bloat = show_bloat self._show_bloat = show_bloat
self._list_error_boards = list_error_boards self._list_error_boards = list_error_boards
self._show_config = show_config self._show_config = show_config
self._show_environment = show_environment
def _AddTimestamp(self): def _AddTimestamp(self):
"""Add a new timestamp to the list and record the build period. """Add a new timestamp to the list and record the build period.
@ -609,8 +625,33 @@ class Builder:
config[key] = value config[key] = value
return config return config
def _ProcessEnvironment(self, fname):
"""Read in a uboot.env file
This function reads in environment variables from a file.
Args:
fname: Filename to read
Returns:
Dictionary:
key: environment variable (e.g. bootlimit)
value: value of environment variable (e.g. 1)
"""
environment = {}
if os.path.exists(fname):
with open(fname) as fd:
for line in fd.read().split('\0'):
try:
key, value = line.split('=', 1)
environment[key] = value
except ValueError:
# ignore lines we can't parse
pass
return environment
def GetBuildOutcome(self, commit_upto, target, read_func_sizes, def GetBuildOutcome(self, commit_upto, target, read_func_sizes,
read_config): read_config, read_environment):
"""Work out the outcome of a build. """Work out the outcome of a build.
Args: Args:
@ -618,6 +659,7 @@ class Builder:
target: Target board to check target: Target board to check
read_func_sizes: True to read function size information read_func_sizes: True to read function size information
read_config: True to read .config and autoconf.h files read_config: True to read .config and autoconf.h files
read_environment: True to read uboot.env files
Returns: Returns:
Outcome object Outcome object
@ -627,6 +669,7 @@ class Builder:
sizes = {} sizes = {}
func_sizes = {} func_sizes = {}
config = {} config = {}
environment = {}
if os.path.exists(done_file): if os.path.exists(done_file):
with open(done_file, 'r') as fd: with open(done_file, 'r') as fd:
return_code = int(fd.readline()) return_code = int(fd.readline())
@ -676,12 +719,18 @@ class Builder:
fname = os.path.join(output_dir, name) fname = os.path.join(output_dir, name)
config[name] = self._ProcessConfig(fname) config[name] = self._ProcessConfig(fname)
return Builder.Outcome(rc, err_lines, sizes, func_sizes, config) if read_environment:
output_dir = self.GetBuildDir(commit_upto, target)
fname = os.path.join(output_dir, 'uboot.env')
environment = self._ProcessEnvironment(fname)
return Builder.Outcome(OUTCOME_UNKNOWN, [], {}, {}, {}) return Builder.Outcome(rc, err_lines, sizes, func_sizes, config,
environment)
return Builder.Outcome(OUTCOME_UNKNOWN, [], {}, {}, {}, {})
def GetResultSummary(self, boards_selected, commit_upto, read_func_sizes, def GetResultSummary(self, boards_selected, commit_upto, read_func_sizes,
read_config): read_config, read_environment):
"""Calculate a summary of the results of building a commit. """Calculate a summary of the results of building a commit.
Args: Args:
@ -689,6 +738,7 @@ class Builder:
commit_upto: Commit number to summarize (0..self.count-1) commit_upto: Commit number to summarize (0..self.count-1)
read_func_sizes: True to read function size information read_func_sizes: True to read function size information
read_config: True to read .config and autoconf.h files read_config: True to read .config and autoconf.h files
read_environment: True to read uboot.env files
Returns: Returns:
Tuple: Tuple:
@ -705,6 +755,9 @@ class Builder:
value is itself a dictionary: value is itself a dictionary:
key: config name key: config name
value: config value value: config value
Dictionary keyed by board.target. Each value is a dictionary:
key: environment variable
value: value of environment variable
""" """
def AddLine(lines_summary, lines_boards, line, board): def AddLine(lines_summary, lines_boards, line, board):
line = line.rstrip() line = line.rstrip()
@ -720,10 +773,12 @@ class Builder:
warn_lines_summary = [] warn_lines_summary = []
warn_lines_boards = {} warn_lines_boards = {}
config = {} config = {}
environment = {}
for board in boards_selected.itervalues(): for board in boards_selected.itervalues():
outcome = self.GetBuildOutcome(commit_upto, board.target, outcome = self.GetBuildOutcome(commit_upto, board.target,
read_func_sizes, read_config) read_func_sizes, read_config,
read_environment)
board_dict[board.target] = outcome board_dict[board.target] = outcome
last_func = None last_func = None
last_was_warning = False last_was_warning = False
@ -756,8 +811,14 @@ class Builder:
tconfig.Add(fname, key, value) tconfig.Add(fname, key, value)
config[board.target] = tconfig config[board.target] = tconfig
tenvironment = Environment(board.target)
if outcome.environment:
for key, value in outcome.environment.iteritems():
tenvironment.Add(key, value)
environment[board.target] = tenvironment
return (board_dict, err_lines_summary, err_lines_boards, return (board_dict, err_lines_summary, err_lines_boards,
warn_lines_summary, warn_lines_boards, config) warn_lines_summary, warn_lines_boards, config, environment)
def AddOutcome(self, board_dict, arch_list, changes, char, color): def AddOutcome(self, board_dict, arch_list, changes, char, color):
"""Add an output to our list of outcomes for each architecture """Add an output to our list of outcomes for each architecture
@ -810,12 +871,14 @@ class Builder:
""" """
self._base_board_dict = {} self._base_board_dict = {}
for board in board_selected: for board in board_selected:
self._base_board_dict[board] = Builder.Outcome(0, [], [], {}, {}) self._base_board_dict[board] = Builder.Outcome(0, [], [], {}, {},
{})
self._base_err_lines = [] self._base_err_lines = []
self._base_warn_lines = [] self._base_warn_lines = []
self._base_err_line_boards = {} self._base_err_line_boards = {}
self._base_warn_line_boards = {} self._base_warn_line_boards = {}
self._base_config = None self._base_config = None
self._base_environment = None
def PrintFuncSizeDetail(self, fname, old, new): def PrintFuncSizeDetail(self, fname, old, new):
grow, shrink, add, remove, up, down = 0, 0, 0, 0, 0, 0 grow, shrink, add, remove, up, down = 0, 0, 0, 0, 0, 0
@ -1010,8 +1073,8 @@ class Builder:
def PrintResultSummary(self, board_selected, board_dict, err_lines, def PrintResultSummary(self, board_selected, board_dict, err_lines,
err_line_boards, warn_lines, warn_line_boards, err_line_boards, warn_lines, warn_line_boards,
config, show_sizes, show_detail, show_bloat, config, environment, show_sizes, show_detail,
show_config): show_bloat, show_config, show_environment):
"""Compare results with the base results and display delta. """Compare results with the base results and display delta.
Only boards mentioned in board_selected will be considered. This Only boards mentioned in board_selected will be considered. This
@ -1036,10 +1099,13 @@ class Builder:
value is itself a dictionary: value is itself a dictionary:
key: config name key: config name
value: config value value: config value
environment: Dictionary keyed by environment variable, Each
value is the value of environment variable.
show_sizes: Show image size deltas show_sizes: Show image size deltas
show_detail: Show detail for each board show_detail: Show detail for each board
show_bloat: Show detail for each function show_bloat: Show detail for each function
show_config: Show config changes show_config: Show config changes
show_environment: Show environment changes
""" """
def _BoardList(line, line_boards): def _BoardList(line, line_boards):
"""Helper function to get a line of boards containing a line """Helper function to get a line of boards containing a line
@ -1188,6 +1254,36 @@ class Builder:
self.PrintSizeSummary(board_selected, board_dict, show_detail, self.PrintSizeSummary(board_selected, board_dict, show_detail,
show_bloat) show_bloat)
if show_environment and self._base_environment:
lines = []
for target in board_dict:
if target not in board_selected:
continue
tbase = self._base_environment[target]
tenvironment = environment[target]
environment_plus = {}
environment_minus = {}
environment_change = {}
base = tbase.environment
for key, value in tenvironment.environment.iteritems():
if key not in base:
environment_plus[key] = value
for key, value in base.iteritems():
if key not in tenvironment.environment:
environment_minus[key] = value
for key, value in base.iteritems():
new_value = tenvironment.environment.get(key)
if new_value and value != new_value:
desc = '%s -> %s' % (value, new_value)
environment_change[key] = desc
_AddConfig(lines, target, environment_plus, environment_minus,
environment_change)
_OutputConfigInfo(lines)
if show_config and self._base_config: if show_config and self._base_config:
summary = {} summary = {}
arch_config_plus = {} arch_config_plus = {}
@ -1294,6 +1390,7 @@ class Builder:
self._base_err_line_boards = err_line_boards self._base_err_line_boards = err_line_boards
self._base_warn_line_boards = warn_line_boards self._base_warn_line_boards = warn_line_boards
self._base_config = config self._base_config = config
self._base_environment = environment
# Get a list of boards that did not get built, if needed # Get a list of boards that did not get built, if needed
not_built = [] not_built = []
@ -1306,10 +1403,11 @@ class Builder:
def ProduceResultSummary(self, commit_upto, commits, board_selected): def ProduceResultSummary(self, commit_upto, commits, board_selected):
(board_dict, err_lines, err_line_boards, warn_lines, (board_dict, err_lines, err_line_boards, warn_lines,
warn_line_boards, config) = self.GetResultSummary( warn_line_boards, config, environment) = self.GetResultSummary(
board_selected, commit_upto, board_selected, commit_upto,
read_func_sizes=self._show_bloat, read_func_sizes=self._show_bloat,
read_config=self._show_config) read_config=self._show_config,
read_environment=self._show_environment)
if commits: if commits:
msg = '%02d: %s' % (commit_upto + 1, msg = '%02d: %s' % (commit_upto + 1,
commits[commit_upto].subject) commits[commit_upto].subject)
@ -1317,8 +1415,8 @@ class Builder:
self.PrintResultSummary(board_selected, board_dict, self.PrintResultSummary(board_selected, board_dict,
err_lines if self._show_errors else [], err_line_boards, err_lines if self._show_errors else [], err_line_boards,
warn_lines if self._show_errors else [], warn_line_boards, warn_lines if self._show_errors else [], warn_line_boards,
config, self._show_sizes, self._show_detail, config, environment, self._show_sizes, self._show_detail,
self._show_bloat, self._show_config) self._show_bloat, self._show_config, self._show_environment)
def ShowSummary(self, commits, board_selected): def ShowSummary(self, commits, board_selected):
"""Show a build summary for U-Boot for a given board list. """Show a build summary for U-Boot for a given board list.

View File

@ -351,6 +351,16 @@ class BuilderThread(threading.Thread):
lines.append(size_result.stdout.splitlines()[1] + ' ' + lines.append(size_result.stdout.splitlines()[1] + ' ' +
rodata_size) rodata_size)
# Extract the environment from U-Boot and dump it out
cmd = ['%sobjcopy' % self.toolchain.cross, '-O', 'binary',
'-j', '.rodata.default_environment',
'env/built-in.o', 'uboot.env']
command.RunPipe([cmd], capture=True,
capture_stderr=True, cwd=result.out_dir,
raise_on_error=False, env=env)
ubootenv = os.path.join(result.out_dir, 'uboot.env')
self.CopyFiles(result.out_dir, build_dir, '', ['uboot.env'])
# Write out the image sizes file. This is similar to the output # Write out the image sizes file. This is similar to the output
# of binutil's 'size' utility, but it omits the header line and # of binutil's 'size' utility, but it omits the header line and
# adds an additional hex value at the end of each line for the # adds an additional hex value at the end of each line for the

View File

@ -92,6 +92,8 @@ def ParseArgs():
default=None, help='Number of builder threads to use') default=None, help='Number of builder threads to use')
parser.add_option('-u', '--show_unknown', action='store_true', parser.add_option('-u', '--show_unknown', action='store_true',
default=False, help='Show boards with unknown build result') default=False, help='Show boards with unknown build result')
parser.add_option('-U', '--show-environment', action='store_true',
default=False, help='Show environment changes in summary')
parser.add_option('-v', '--verbose', action='store_true', parser.add_option('-v', '--verbose', action='store_true',
default=False, help='Show build results while the build progresses') default=False, help='Show build results while the build progresses')
parser.add_option('-V', '--verbose-build', action='store_true', parser.add_option('-V', '--verbose-build', action='store_true',

View File

@ -319,7 +319,8 @@ def DoBuildman(options, args, toolchains=None, make_func=None, boards=None,
builder.SetDisplayOptions(options.show_errors, options.show_sizes, builder.SetDisplayOptions(options.show_errors, options.show_sizes,
options.show_detail, options.show_bloat, options.show_detail, options.show_bloat,
options.list_error_boards, options.list_error_boards,
options.show_config) options.show_config,
options.show_environment)
if options.summary: if options.summary:
builder.ShowSummary(commits, board_selected) builder.ShowSummary(commits, board_selected)
else: else:

View File

@ -327,6 +327,9 @@ class TestFunctional(unittest.TestCase):
def _HandleCommandObjdump(self, args): def _HandleCommandObjdump(self, args):
return command.CommandResult(return_code=0) return command.CommandResult(return_code=0)
def _HandleCommandObjcopy(self, args):
return command.CommandResult(return_code=0)
def _HandleCommandSize(self, args): def _HandleCommandSize(self, args):
return command.CommandResult(return_code=0) return command.CommandResult(return_code=0)
@ -359,6 +362,8 @@ class TestFunctional(unittest.TestCase):
return self._HandleCommandNm(args) return self._HandleCommandNm(args)
elif cmd.endswith('objdump'): elif cmd.endswith('objdump'):
return self._HandleCommandObjdump(args) return self._HandleCommandObjdump(args)
elif cmd.endswith('objcopy'):
return self._HandleCommandObjcopy(args)
elif cmd.endswith( 'size'): elif cmd.endswith( 'size'):
return self._HandleCommandSize(args) return self._HandleCommandSize(args)

View File

@ -148,7 +148,7 @@ index 0000000..2234c87
--- /dev/null --- /dev/null
+++ b/common/bootstage.c +++ b/common/bootstage.c
@@ -0,0 +1,37 @@ @@ -0,0 +1,37 @@
+/* SPDX-License-Identifier: GPL-2.0+ */ +// SPDX-License-Identifier: GPL-2.0+
+/* +/*
+ * Copyright (c) 2011, Google Inc. All rights reserved. + * Copyright (c) 2011, Google Inc. All rights reserved.
+ * + *