binman: Support updating the device tree with calc'd info

It is useful to write the position and size of each entry back to the
device tree so that U-Boot can access this at runtime. Add a feature to
support this, along with associated tests.

Signed-off-by: Simon Glass <sjg@chromium.org>
lime2-spi
Simon Glass 6 years ago
parent 078ab1a2f5
commit 16b8d6b769
  1. 6
      tools/binman/README
  2. 2
      tools/binman/control.py
  3. 10
      tools/binman/etype/_testing.py
  4. 77
      tools/binman/ftest.py
  5. 31
      tools/binman/test/60_fdt_update.dts
  6. 32
      tools/binman/test/61_fdt_update_bad.dts

@ -669,13 +669,11 @@ To do
-----
Some ideas:
- Fill out the device tree to include the final position and size of each
entry (since the input file may not always specify these). See also
'Access to binman entry positions at run time' above
- Use of-platdata to make the information available to code that is unable
to use device tree (such as a very small SPL image)
- 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). This is nearing
completion but some work remains
- Add an option to decode an image into the constituent binaries
- Support building an image for a board (-b) more completely, with a
configurable build directory

@ -168,6 +168,8 @@ def Binman(options, args):
image.BuildImage()
if options.map:
image.WriteMap()
with open(fname, 'wb') as outfd:
outfd.write(dtb.GetContents())
finally:
tools.FinaliseOutputDir()
finally:

@ -24,6 +24,9 @@ class Entry__testing(Entry):
'return-unknown-contents')
self.bad_update_contents = fdt_util.GetBool(self._node,
'bad-update-contents')
self.process_fdt_ready = False
self.never_complete_process_fdt = fdt_util.GetBool(self._node,
'never-complete-process-fdt')
def ObtainContents(self):
if self.return_unknown_contents:
@ -42,3 +45,10 @@ class Entry__testing(Entry):
# Request to update the conents with something larger, to cause a
# failure.
self.ProcessContentsUpdate('aa')
def ProcessFdt(self, fdt):
"""Force reprocessing the first time"""
ready = self.process_fdt_ready
if not self.never_complete_process_fdt:
self.process_fdt_ready = True
return ready

@ -146,19 +146,23 @@ class TestFunctional(unittest.TestCase):
# options.verbosity = tout.DEBUG
return control.Binman(options, args)
def _DoTestFile(self, fname, debug=False, map=False):
def _DoTestFile(self, fname, debug=False, map=False, update_dtb=False):
"""Run binman with a given test file
Args:
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
update_dtb: Update the position and size of each entry in the device
tree before packing it into the image
"""
args = ['-p', '-I', self._indir, '-d', self.TestFile(fname)]
if debug:
args.append('-D')
if map:
args.append('-m')
if update_dtb:
args.append('-up')
return self._DoBinman(*args)
def _SetupDtb(self, fname, outfile='u-boot.dtb'):
@ -183,7 +187,8 @@ class TestFunctional(unittest.TestCase):
TestFunctional._MakeInputFile(outfile, data)
return data
def _DoReadFileDtb(self, fname, use_real_dtb=False, map=False):
def _DoReadFileDtb(self, fname, use_real_dtb=False, map=False,
update_dtb=False):
"""Run binman and return the resulting image
This runs binman with a given test file and then reads the resulting
@ -199,6 +204,8 @@ class TestFunctional(unittest.TestCase):
test contents (the U_BOOT_DTB_DATA string) can be used.
But in some test we need the real contents.
map: True to output map files for the images
update_dtb: Update the position and size of each entry in the device
tree before packing it into the image
Returns:
Tuple:
@ -212,21 +219,22 @@ class TestFunctional(unittest.TestCase):
dtb_data = self._SetupDtb(fname)
try:
retcode = self._DoTestFile(fname, map=map)
retcode = self._DoTestFile(fname, map=map, update_dtb=update_dtb)
self.assertEqual(0, retcode)
out_dtb_fname = control.GetFdtPath('u-boot.dtb')
# Find the (only) image, read it and return its contents
image = control.images['image']
fname = tools.GetOutputFilename('image.bin')
self.assertTrue(os.path.exists(fname))
image_fname = tools.GetOutputFilename('image.bin')
self.assertTrue(os.path.exists(image_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:
return fd.read(), dtb_data, map_data
with open(image_fname) as fd:
return fd.read(), dtb_data, map_data, out_dtb_fname
finally:
# Put the test file back
if use_real_dtb:
@ -300,6 +308,26 @@ class TestFunctional(unittest.TestCase):
"""
return struct.unpack('>L', dtb[4:8])[0]
def _GetPropTree(self, dtb_data, node_names):
def AddNode(node, path):
if node.name != '/':
path += '/' + node.name
#print 'path', path
for subnode in node.subnodes:
for prop in subnode.props.values():
if prop.name in node_names:
prop_path = path + '/' + subnode.name + ':' + prop.name
tree[prop_path[len('/binman/'):]] = fdt_util.fdt32_to_cpu(
prop.value)
#print ' ', prop.name
AddNode(subnode, path)
tree = {}
dtb = fdt.Fdt(dtb_data)
dtb.Scan()
AddNode(dtb.GetRoot(), '')
return tree
def testRun(self):
"""Test a basic run with valid args"""
result = self._RunBinman('-h')
@ -845,7 +873,7 @@ class TestFunctional(unittest.TestCase):
"""Test that we can cope with an image without microcode (e.g. qemu)"""
with open(self.TestFile('u_boot_no_ucode_ptr')) as fd:
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
self.assertEqual(U_BOOT_NODTB_DATA, data[:len(U_BOOT_NODTB_DATA)])
@ -980,7 +1008,7 @@ class TestFunctional(unittest.TestCase):
def testMap(self):
"""Tests outputting a map of the images"""
_, _, map_data = self._DoReadFileDtb('55_sections.dts', map=True)
_, _, map_data, _ = self._DoReadFileDtb('55_sections.dts', map=True)
self.assertEqual('''Position Size Name
00000000 00000010 section@0
00000000 00000004 u-boot
@ -990,7 +1018,7 @@ class TestFunctional(unittest.TestCase):
def testNamePrefix(self):
"""Tests that name prefixes are used"""
_, _, map_data = self._DoReadFileDtb('56_name_prefix.dts', map=True)
_, _, map_data, _ = self._DoReadFileDtb('56_name_prefix.dts', map=True)
self.assertEqual('''Position Size Name
00000000 00000010 section@0
00000000 00000004 ro-u-boot
@ -1013,6 +1041,35 @@ class TestFunctional(unittest.TestCase):
self.assertIn("Node '/binman/_testing': Cannot update entry size from "
'2 to 1', str(e.exception))
def testUpdateFdt(self):
"""Test that we can update the device tree with pos/size info"""
_, _, _, out_dtb_fname = self._DoReadFileDtb('60_fdt_update.dts',
update_dtb=True)
props = self._GetPropTree(out_dtb_fname, ['pos', 'size'])
with open('/tmp/x.dtb', 'wb') as outf:
with open(out_dtb_fname) as inf:
outf.write(inf.read())
self.assertEqual({
'_testing:pos': 32,
'_testing:size': 1,
'section@0/u-boot:pos': 0,
'section@0/u-boot:size': len(U_BOOT_DATA),
'section@0:pos': 0,
'section@0:size': 16,
'section@1/u-boot:pos': 0,
'section@1/u-boot:size': len(U_BOOT_DATA),
'section@1:pos': 16,
'section@1:size': 16,
'size': 40
}, props)
def testUpdateFdtBad(self):
"""Test that we detect when ProcessFdt never completes"""
with self.assertRaises(ValueError) as e:
self._DoReadFileDtb('61_fdt_update_bad.dts', update_dtb=True)
self.assertIn('Could not complete processing of Fdt: remaining '
'[<_testing.Entry__testing', str(e.exception))
if __name__ == "__main__":
unittest.main()

@ -0,0 +1,31 @@
// 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 {
};
};
_testing {
};
};
};

@ -0,0 +1,32 @@
// 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 {
};
};
_testing {
never-complete-process-fdt;
};
};
};
Loading…
Cancel
Save