Merge git://git.denx.de/u-boot-dm
This commit is contained in:
commit
8f48cf9f17
3
test/run
3
test/run
@ -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
|
||||||
|
@ -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>
|
||||||
|
@ -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
318
tools/binman/bsection.py
Normal 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)
|
@ -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')
|
||||||
|
@ -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:
|
||||||
|
@ -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:
|
||||||
|
@ -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)
|
||||||
|
|
||||||
|
@ -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)
|
@ -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'
|
||||||
|
@ -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):
|
||||||
|
@ -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)
|
||||||
|
@ -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):
|
||||||
|
@ -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)
|
||||||
|
@ -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)
|
||||||
|
@ -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'
|
||||||
|
@ -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)
|
||||||
|
@ -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)
|
||||||
|
59
tools/binman/etype/section.py
Normal file
59
tools/binman/etype/section.py
Normal 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)
|
@ -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'
|
||||||
|
@ -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'
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
@ -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'
|
||||||
|
@ -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'
|
||||||
|
@ -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)
|
||||||
|
@ -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')
|
||||||
|
@ -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'
|
||||||
|
@ -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'
|
||||||
|
@ -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):
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
@ -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'
|
||||||
|
@ -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'
|
||||||
|
@ -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()
|
||||||
|
@ -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))
|
|
||||||
|
@ -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))
|
||||||
|
15
tools/binman/test/54_unit_address.dts
Normal file
15
tools/binman/test/54_unit_address.dts
Normal 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 {
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
28
tools/binman/test/55_sections.dts
Normal file
28
tools/binman/test/55_sections.dts
Normal 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 {
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
30
tools/binman/test/56_name_prefix.dts
Normal file
30
tools/binman/test/56_name_prefix.dts
Normal 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 {
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
@ -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
|
||||||
=============
|
=============
|
||||||
|
|
||||||
|
@ -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.
|
||||||
|
@ -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
|
||||||
|
@ -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',
|
||||||
|
@ -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:
|
||||||
|
@ -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)
|
||||||
|
|
||||||
|
@ -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.
|
||||||
+ *
|
+ *
|
||||||
|
Loading…
Reference in New Issue
Block a user