binman: Remove templates after use
[platform/kernel/u-boot.git] / tools / binman / ftest.py
1 # SPDX-License-Identifier: GPL-2.0+
2 # Copyright (c) 2016 Google, Inc
3 # Written by Simon Glass <sjg@chromium.org>
4 #
5 # To run a single test, change to this directory, and:
6 #
7 #    python -m unittest func_test.TestFunctional.testHelp
8
9 import collections
10 import gzip
11 import hashlib
12 from optparse import OptionParser
13 import os
14 import re
15 import shutil
16 import struct
17 import sys
18 import tempfile
19 import unittest
20 import unittest.mock
21 import urllib.error
22
23 from binman import bintool
24 from binman import cbfs_util
25 from binman import cmdline
26 from binman import control
27 from binman import elf
28 from binman import elf_test
29 from binman import fip_util
30 from binman import fmap_util
31 from binman import state
32 from dtoc import fdt
33 from dtoc import fdt_util
34 from binman.etype import fdtmap
35 from binman.etype import image_header
36 from binman.image import Image
37 from u_boot_pylib import command
38 from u_boot_pylib import test_util
39 from u_boot_pylib import tools
40 from u_boot_pylib import tout
41
42 # Contents of test files, corresponding to different entry types
43 U_BOOT_DATA           = b'1234'
44 U_BOOT_IMG_DATA       = b'img'
45 U_BOOT_SPL_DATA       = b'56780123456789abcdefghijklm'
46 U_BOOT_TPL_DATA       = b'tpl9876543210fedcbazywvuts'
47 U_BOOT_VPL_DATA       = b'vpl76543210fedcbazywxyz_'
48 BLOB_DATA             = b'89'
49 ME_DATA               = b'0abcd'
50 VGA_DATA              = b'vga'
51 U_BOOT_DTB_DATA       = b'udtb'
52 U_BOOT_SPL_DTB_DATA   = b'spldtb'
53 U_BOOT_TPL_DTB_DATA   = b'tpldtb'
54 U_BOOT_VPL_DTB_DATA   = b'vpldtb'
55 X86_START16_DATA      = b'start16'
56 X86_START16_SPL_DATA  = b'start16spl'
57 X86_START16_TPL_DATA  = b'start16tpl'
58 X86_RESET16_DATA      = b'reset16'
59 X86_RESET16_SPL_DATA  = b'reset16spl'
60 X86_RESET16_TPL_DATA  = b'reset16tpl'
61 PPC_MPC85XX_BR_DATA   = b'ppcmpc85xxbr'
62 U_BOOT_NODTB_DATA     = b'nodtb with microcode pointer somewhere in here'
63 U_BOOT_SPL_NODTB_DATA = b'splnodtb with microcode pointer somewhere in here'
64 U_BOOT_TPL_NODTB_DATA = b'tplnodtb with microcode pointer somewhere in here'
65 U_BOOT_VPL_NODTB_DATA = b'vplnodtb'
66 U_BOOT_EXP_DATA       = U_BOOT_NODTB_DATA + U_BOOT_DTB_DATA
67 U_BOOT_SPL_EXP_DATA   = U_BOOT_SPL_NODTB_DATA + U_BOOT_SPL_DTB_DATA
68 U_BOOT_TPL_EXP_DATA   = U_BOOT_TPL_NODTB_DATA + U_BOOT_TPL_DTB_DATA
69 FSP_DATA              = b'fsp'
70 CMC_DATA              = b'cmc'
71 VBT_DATA              = b'vbt'
72 MRC_DATA              = b'mrc'
73 TEXT_DATA             = 'text'
74 TEXT_DATA2            = 'text2'
75 TEXT_DATA3            = 'text3'
76 CROS_EC_RW_DATA       = b'ecrw'
77 GBB_DATA              = b'gbbd'
78 BMPBLK_DATA           = b'bmp'
79 VBLOCK_DATA           = b'vblk'
80 FILES_DATA            = (b"sorry I'm late\nOh, don't bother apologising, I'm " +
81                          b"sorry you're alive\n")
82 COMPRESS_DATA         = b'compress xxxxxxxxxxxxxxxxxxxxxx data'
83 COMPRESS_DATA_BIG     = COMPRESS_DATA * 2
84 REFCODE_DATA          = b'refcode'
85 FSP_M_DATA            = b'fsp_m'
86 FSP_S_DATA            = b'fsp_s'
87 FSP_T_DATA            = b'fsp_t'
88 ATF_BL31_DATA         = b'bl31'
89 TEE_OS_DATA           = b'this is some tee OS data'
90 ATF_BL2U_DATA         = b'bl2u'
91 OPENSBI_DATA          = b'opensbi'
92 SCP_DATA              = b'scp'
93 ROCKCHIP_TPL_DATA     = b'rockchip-tpl'
94 TEST_FDT1_DATA        = b'fdt1'
95 TEST_FDT2_DATA        = b'test-fdt2'
96 ENV_DATA              = b'var1=1\nvar2="2"'
97 ENCRYPTED_IV_DATA     = b'123456'
98 ENCRYPTED_KEY_DATA    = b'abcde'
99 PRE_LOAD_MAGIC        = b'UBSH'
100 PRE_LOAD_VERSION      = 0x11223344.to_bytes(4, 'big')
101 PRE_LOAD_HDR_SIZE     = 0x00001000.to_bytes(4, 'big')
102 TI_BOARD_CONFIG_DATA  = b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
103 TI_UNSECURE_DATA      = b'unsecuredata'
104
105 # Subdirectory of the input dir to use to put test FDTs
106 TEST_FDT_SUBDIR       = 'fdts'
107
108 # The expected size for the device tree in some tests
109 EXTRACT_DTB_SIZE = 0x3c9
110
111 # Properties expected to be in the device tree when update_dtb is used
112 BASE_DTB_PROPS = ['offset', 'size', 'image-pos']
113
114 # Extra properties expected to be in the device tree when allow-repack is used
115 REPACK_DTB_PROPS = ['orig-offset', 'orig-size']
116
117 # Supported compression bintools
118 COMP_BINTOOLS = ['bzip2', 'gzip', 'lz4', 'lzma_alone', 'lzop', 'xz', 'zstd']
119
120 TEE_ADDR = 0x5678
121
122 class TestFunctional(unittest.TestCase):
123     """Functional tests for binman
124
125     Most of these use a sample .dts file to build an image and then check
126     that it looks correct. The sample files are in the test/ subdirectory
127     and are numbered.
128
129     For each entry type a very small test file is created using fixed
130     string contents. This makes it easy to test that things look right, and
131     debug problems.
132
133     In some cases a 'real' file must be used - these are also supplied in
134     the test/ diurectory.
135     """
136     @classmethod
137     def setUpClass(cls):
138         global entry
139         from binman import entry
140
141         # Handle the case where argv[0] is 'python'
142         cls._binman_dir = os.path.dirname(os.path.realpath(sys.argv[0]))
143         cls._binman_pathname = os.path.join(cls._binman_dir, 'binman')
144
145         # Create a temporary directory for input files
146         cls._indir = tempfile.mkdtemp(prefix='binmant.')
147
148         # Create some test files
149         TestFunctional._MakeInputFile('u-boot.bin', U_BOOT_DATA)
150         TestFunctional._MakeInputFile('u-boot.img', U_BOOT_IMG_DATA)
151         TestFunctional._MakeInputFile('spl/u-boot-spl.bin', U_BOOT_SPL_DATA)
152         TestFunctional._MakeInputFile('tpl/u-boot-tpl.bin', U_BOOT_TPL_DATA)
153         TestFunctional._MakeInputFile('vpl/u-boot-vpl.bin', U_BOOT_VPL_DATA)
154         TestFunctional._MakeInputFile('blobfile', BLOB_DATA)
155         TestFunctional._MakeInputFile('me.bin', ME_DATA)
156         TestFunctional._MakeInputFile('vga.bin', VGA_DATA)
157         cls._ResetDtbs()
158
159         TestFunctional._MakeInputFile('u-boot-br.bin', PPC_MPC85XX_BR_DATA)
160
161         TestFunctional._MakeInputFile('u-boot-x86-start16.bin', X86_START16_DATA)
162         TestFunctional._MakeInputFile('spl/u-boot-x86-start16-spl.bin',
163                                       X86_START16_SPL_DATA)
164         TestFunctional._MakeInputFile('tpl/u-boot-x86-start16-tpl.bin',
165                                       X86_START16_TPL_DATA)
166
167         TestFunctional._MakeInputFile('u-boot-x86-reset16.bin',
168                                       X86_RESET16_DATA)
169         TestFunctional._MakeInputFile('spl/u-boot-x86-reset16-spl.bin',
170                                       X86_RESET16_SPL_DATA)
171         TestFunctional._MakeInputFile('tpl/u-boot-x86-reset16-tpl.bin',
172                                       X86_RESET16_TPL_DATA)
173
174         TestFunctional._MakeInputFile('u-boot-nodtb.bin', U_BOOT_NODTB_DATA)
175         TestFunctional._MakeInputFile('spl/u-boot-spl-nodtb.bin',
176                                       U_BOOT_SPL_NODTB_DATA)
177         TestFunctional._MakeInputFile('tpl/u-boot-tpl-nodtb.bin',
178                                       U_BOOT_TPL_NODTB_DATA)
179         TestFunctional._MakeInputFile('vpl/u-boot-vpl-nodtb.bin',
180                                       U_BOOT_VPL_NODTB_DATA)
181         TestFunctional._MakeInputFile('fsp.bin', FSP_DATA)
182         TestFunctional._MakeInputFile('cmc.bin', CMC_DATA)
183         TestFunctional._MakeInputFile('vbt.bin', VBT_DATA)
184         TestFunctional._MakeInputFile('mrc.bin', MRC_DATA)
185         TestFunctional._MakeInputFile('ecrw.bin', CROS_EC_RW_DATA)
186         TestFunctional._MakeInputDir('devkeys')
187         TestFunctional._MakeInputFile('bmpblk.bin', BMPBLK_DATA)
188         TestFunctional._MakeInputFile('refcode.bin', REFCODE_DATA)
189         TestFunctional._MakeInputFile('fsp_m.bin', FSP_M_DATA)
190         TestFunctional._MakeInputFile('fsp_s.bin', FSP_S_DATA)
191         TestFunctional._MakeInputFile('fsp_t.bin', FSP_T_DATA)
192
193         cls._elf_testdir = os.path.join(cls._indir, 'elftest')
194         elf_test.BuildElfTestFiles(cls._elf_testdir)
195
196         # ELF file with a '_dt_ucode_base_size' symbol
197         TestFunctional._MakeInputFile('u-boot',
198             tools.read_file(cls.ElfTestFile('u_boot_ucode_ptr')))
199
200         # Intel flash descriptor file
201         cls._SetupDescriptor()
202
203         shutil.copytree(cls.TestFile('files'),
204                         os.path.join(cls._indir, 'files'))
205
206         shutil.copytree(cls.TestFile('yaml'),
207                         os.path.join(cls._indir, 'yaml'))
208
209         TestFunctional._MakeInputFile('compress', COMPRESS_DATA)
210         TestFunctional._MakeInputFile('compress_big', COMPRESS_DATA_BIG)
211         TestFunctional._MakeInputFile('bl31.bin', ATF_BL31_DATA)
212         TestFunctional._MakeInputFile('tee-pager.bin', TEE_OS_DATA)
213         TestFunctional._MakeInputFile('bl2u.bin', ATF_BL2U_DATA)
214         TestFunctional._MakeInputFile('fw_dynamic.bin', OPENSBI_DATA)
215         TestFunctional._MakeInputFile('scp.bin', SCP_DATA)
216         TestFunctional._MakeInputFile('rockchip-tpl.bin', ROCKCHIP_TPL_DATA)
217         TestFunctional._MakeInputFile('ti_unsecure.bin', TI_UNSECURE_DATA)
218
219         # Add a few .dtb files for testing
220         TestFunctional._MakeInputFile('%s/test-fdt1.dtb' % TEST_FDT_SUBDIR,
221                                       TEST_FDT1_DATA)
222         TestFunctional._MakeInputFile('%s/test-fdt2.dtb' % TEST_FDT_SUBDIR,
223                                       TEST_FDT2_DATA)
224
225         TestFunctional._MakeInputFile('env.txt', ENV_DATA)
226
227         # ELF file with two sections in different parts of memory, used for both
228         # ATF and OP_TEE
229         TestFunctional._MakeInputFile('bl31.elf',
230             tools.read_file(cls.ElfTestFile('elf_sections')))
231         TestFunctional._MakeInputFile('tee.elf',
232             tools.read_file(cls.ElfTestFile('elf_sections')))
233
234         # Newer OP_TEE file in v1 binary format
235         cls.make_tee_bin('tee.bin')
236
237         # test files for encrypted tests
238         TestFunctional._MakeInputFile('encrypted-file.iv', ENCRYPTED_IV_DATA)
239         TestFunctional._MakeInputFile('encrypted-file.key', ENCRYPTED_KEY_DATA)
240
241         cls.comp_bintools = {}
242         for name in COMP_BINTOOLS:
243             cls.comp_bintools[name] = bintool.Bintool.create(name)
244
245     @classmethod
246     def tearDownClass(cls):
247         """Remove the temporary input directory and its contents"""
248         if cls.preserve_indir:
249             print('Preserving input dir: %s' % cls._indir)
250         else:
251             if cls._indir:
252                 shutil.rmtree(cls._indir)
253         cls._indir = None
254
255     @classmethod
256     def setup_test_args(cls, preserve_indir=False, preserve_outdirs=False,
257                         toolpath=None, verbosity=None):
258         """Accept arguments controlling test execution
259
260         Args:
261             preserve_indir: Preserve the shared input directory used by all
262                 tests in this class.
263             preserve_outdir: Preserve the output directories used by tests. Each
264                 test has its own, so this is normally only useful when running a
265                 single test.
266             toolpath: ist of paths to use for tools
267         """
268         cls.preserve_indir = preserve_indir
269         cls.preserve_outdirs = preserve_outdirs
270         cls.toolpath = toolpath
271         cls.verbosity = verbosity
272
273     def _CheckBintool(self, bintool):
274         if not bintool.is_present():
275             self.skipTest('%s not available' % bintool.name)
276
277     def _CheckLz4(self):
278         bintool = self.comp_bintools['lz4']
279         self._CheckBintool(bintool)
280
281     def _CleanupOutputDir(self):
282         """Remove the temporary output directory"""
283         if self.preserve_outdirs:
284             print('Preserving output dir: %s' % tools.outdir)
285         else:
286             tools._finalise_for_test()
287
288     def setUp(self):
289         # Enable this to turn on debugging output
290         # tout.init(tout.DEBUG)
291         command.test_result = None
292
293     def tearDown(self):
294         """Remove the temporary output directory"""
295         self._CleanupOutputDir()
296
297     def _SetupImageInTmpdir(self):
298         """Set up the output image in a new temporary directory
299
300         This is used when an image has been generated in the output directory,
301         but we want to run binman again. This will create a new output
302         directory and fail to delete the original one.
303
304         This creates a new temporary directory, copies the image to it (with a
305         new name) and removes the old output directory.
306
307         Returns:
308             Tuple:
309                 Temporary directory to use
310                 New image filename
311         """
312         image_fname = tools.get_output_filename('image.bin')
313         tmpdir = tempfile.mkdtemp(prefix='binman.')
314         updated_fname = os.path.join(tmpdir, 'image-updated.bin')
315         tools.write_file(updated_fname, tools.read_file(image_fname))
316         self._CleanupOutputDir()
317         return tmpdir, updated_fname
318
319     @classmethod
320     def _ResetDtbs(cls):
321         TestFunctional._MakeInputFile('u-boot.dtb', U_BOOT_DTB_DATA)
322         TestFunctional._MakeInputFile('spl/u-boot-spl.dtb', U_BOOT_SPL_DTB_DATA)
323         TestFunctional._MakeInputFile('tpl/u-boot-tpl.dtb', U_BOOT_TPL_DTB_DATA)
324         TestFunctional._MakeInputFile('vpl/u-boot-vpl.dtb', U_BOOT_VPL_DTB_DATA)
325
326     def _RunBinman(self, *args, **kwargs):
327         """Run binman using the command line
328
329         Args:
330             Arguments to pass, as a list of strings
331             kwargs: Arguments to pass to Command.RunPipe()
332         """
333         result = command.run_pipe([[self._binman_pathname] + list(args)],
334                 capture=True, capture_stderr=True, raise_on_error=False)
335         if result.return_code and kwargs.get('raise_on_error', True):
336             raise Exception("Error running '%s': %s" % (' '.join(args),
337                             result.stdout + result.stderr))
338         return result
339
340     def _DoBinman(self, *argv):
341         """Run binman using directly (in the same process)
342
343         Args:
344             Arguments to pass, as a list of strings
345         Returns:
346             Return value (0 for success)
347         """
348         argv = list(argv)
349         args = cmdline.ParseArgs(argv)
350         args.pager = 'binman-invalid-pager'
351         args.build_dir = self._indir
352
353         # For testing, you can force an increase in verbosity here
354         # args.verbosity = tout.DEBUG
355         return control.Binman(args)
356
357     def _DoTestFile(self, fname, debug=False, map=False, update_dtb=False,
358                     entry_args=None, images=None, use_real_dtb=False,
359                     use_expanded=False, verbosity=None, allow_missing=False,
360                     allow_fake_blobs=False, extra_indirs=None, threads=None,
361                     test_section_timeout=False, update_fdt_in_elf=None,
362                     force_missing_bintools='', ignore_missing=False, output_dir=None):
363         """Run binman with a given test file
364
365         Args:
366             fname: Device-tree source filename to use (e.g. 005_simple.dts)
367             debug: True to enable debugging output
368             map: True to output map files for the images
369             update_dtb: Update the offset and size of each entry in the device
370                 tree before packing it into the image
371             entry_args: Dict of entry args to supply to binman
372                 key: arg name
373                 value: value of that arg
374             images: List of image names to build
375             use_real_dtb: True to use the test file as the contents of
376                 the u-boot-dtb entry. Normally this is not needed and the
377                 test contents (the U_BOOT_DTB_DATA string) can be used.
378                 But in some test we need the real contents.
379             use_expanded: True to use expanded entries where available, e.g.
380                 'u-boot-expanded' instead of 'u-boot'
381             verbosity: Verbosity level to use (0-3, None=don't set it)
382             allow_missing: Set the '--allow-missing' flag so that missing
383                 external binaries just produce a warning instead of an error
384             allow_fake_blobs: Set the '--fake-ext-blobs' flag
385             extra_indirs: Extra input directories to add using -I
386             threads: Number of threads to use (None for default, 0 for
387                 single-threaded)
388             test_section_timeout: True to force the first time to timeout, as
389                 used in testThreadTimeout()
390             update_fdt_in_elf: Value to pass with --update-fdt-in-elf=xxx
391             force_missing_tools (str): comma-separated list of bintools to
392                 regard as missing
393             output_dir: Specific output directory to use for image using -O
394
395         Returns:
396             int return code, 0 on success
397         """
398         args = []
399         if debug:
400             args.append('-D')
401         if verbosity is not None:
402             args.append('-v%d' % verbosity)
403         elif self.verbosity:
404             args.append('-v%d' % self.verbosity)
405         if self.toolpath:
406             for path in self.toolpath:
407                 args += ['--toolpath', path]
408         if threads is not None:
409             args.append('-T%d' % threads)
410         if test_section_timeout:
411             args.append('--test-section-timeout')
412         args += ['build', '-p', '-I', self._indir, '-d', self.TestFile(fname)]
413         if map:
414             args.append('-m')
415         if update_dtb:
416             args.append('-u')
417         if not use_real_dtb:
418             args.append('--fake-dtb')
419         if not use_expanded:
420             args.append('--no-expanded')
421         if entry_args:
422             for arg, value in entry_args.items():
423                 args.append('-a%s=%s' % (arg, value))
424         if allow_missing:
425             args.append('-M')
426             if ignore_missing:
427                 args.append('-W')
428         if allow_fake_blobs:
429             args.append('--fake-ext-blobs')
430         if force_missing_bintools:
431             args += ['--force-missing-bintools', force_missing_bintools]
432         if update_fdt_in_elf:
433             args += ['--update-fdt-in-elf', update_fdt_in_elf]
434         if images:
435             for image in images:
436                 args += ['-i', image]
437         if extra_indirs:
438             for indir in extra_indirs:
439                 args += ['-I', indir]
440         if output_dir:
441             args += ['-O', output_dir]
442         return self._DoBinman(*args)
443
444     def _SetupDtb(self, fname, outfile='u-boot.dtb'):
445         """Set up a new test device-tree file
446
447         The given file is compiled and set up as the device tree to be used
448         for ths test.
449
450         Args:
451             fname: Filename of .dts file to read
452             outfile: Output filename for compiled device-tree binary
453
454         Returns:
455             Contents of device-tree binary
456         """
457         tmpdir = tempfile.mkdtemp(prefix='binmant.')
458         dtb = fdt_util.EnsureCompiled(self.TestFile(fname), tmpdir)
459         with open(dtb, 'rb') as fd:
460             data = fd.read()
461             TestFunctional._MakeInputFile(outfile, data)
462         shutil.rmtree(tmpdir)
463         return data
464
465     def _GetDtbContentsForSpls(self, dtb_data, name):
466         """Create a version of the main DTB for SPL / TPL / VPL
467
468         For testing we don't actually have different versions of the DTB. With
469         U-Boot we normally run fdtgrep to remove unwanted nodes, but for tests
470         we don't normally have any unwanted nodes.
471
472         We still want the DTBs for SPL and TPL to be different though, since
473         otherwise it is confusing to know which one we are looking at. So add
474         an 'spl' or 'tpl' property to the top-level node.
475
476         Args:
477             dtb_data: dtb data to modify (this should be a value devicetree)
478             name: Name of a new property to add
479
480         Returns:
481             New dtb data with the property added
482         """
483         dtb = fdt.Fdt.FromData(dtb_data)
484         dtb.Scan()
485         dtb.GetNode('/binman').AddZeroProp(name)
486         dtb.Sync(auto_resize=True)
487         dtb.Pack()
488         return dtb.GetContents()
489
490     def _DoReadFileDtb(self, fname, use_real_dtb=False, use_expanded=False,
491                        map=False, update_dtb=False, entry_args=None,
492                        reset_dtbs=True, extra_indirs=None, threads=None):
493         """Run binman and return the resulting image
494
495         This runs binman with a given test file and then reads the resulting
496         output file. It is a shortcut function since most tests need to do
497         these steps.
498
499         Raises an assertion failure if binman returns a non-zero exit code.
500
501         Args:
502             fname: Device-tree source filename to use (e.g. 005_simple.dts)
503             use_real_dtb: True to use the test file as the contents of
504                 the u-boot-dtb entry. Normally this is not needed and the
505                 test contents (the U_BOOT_DTB_DATA string) can be used.
506                 But in some test we need the real contents.
507             use_expanded: True to use expanded entries where available, e.g.
508                 'u-boot-expanded' instead of 'u-boot'
509             map: True to output map files for the images
510             update_dtb: Update the offset and size of each entry in the device
511                 tree before packing it into the image
512             entry_args: Dict of entry args to supply to binman
513                 key: arg name
514                 value: value of that arg
515             reset_dtbs: With use_real_dtb the test dtb is overwritten by this
516                 function. If reset_dtbs is True, then the original test dtb
517                 is written back before this function finishes
518             extra_indirs: Extra input directories to add using -I
519             threads: Number of threads to use (None for default, 0 for
520                 single-threaded)
521
522         Returns:
523             Tuple:
524                 Resulting image contents
525                 Device tree contents
526                 Map data showing contents of image (or None if none)
527                 Output device tree binary filename ('u-boot.dtb' path)
528         """
529         dtb_data = None
530         # Use the compiled test file as the u-boot-dtb input
531         if use_real_dtb:
532             dtb_data = self._SetupDtb(fname)
533
534             # For testing purposes, make a copy of the DT for SPL and TPL. Add
535             # a node indicating which it is, so aid verification.
536             for name in ['spl', 'tpl', 'vpl']:
537                 dtb_fname = '%s/u-boot-%s.dtb' % (name, name)
538                 outfile = os.path.join(self._indir, dtb_fname)
539                 TestFunctional._MakeInputFile(dtb_fname,
540                         self._GetDtbContentsForSpls(dtb_data, name))
541
542         try:
543             retcode = self._DoTestFile(fname, map=map, update_dtb=update_dtb,
544                     entry_args=entry_args, use_real_dtb=use_real_dtb,
545                     use_expanded=use_expanded, extra_indirs=extra_indirs,
546                     threads=threads)
547             self.assertEqual(0, retcode)
548             out_dtb_fname = tools.get_output_filename('u-boot.dtb.out')
549
550             # Find the (only) image, read it and return its contents
551             image = control.images['image']
552             image_fname = tools.get_output_filename('image.bin')
553             self.assertTrue(os.path.exists(image_fname))
554             if map:
555                 map_fname = tools.get_output_filename('image.map')
556                 with open(map_fname) as fd:
557                     map_data = fd.read()
558             else:
559                 map_data = None
560             with open(image_fname, 'rb') as fd:
561                 return fd.read(), dtb_data, map_data, out_dtb_fname
562         finally:
563             # Put the test file back
564             if reset_dtbs and use_real_dtb:
565                 self._ResetDtbs()
566
567     def _DoReadFileRealDtb(self, fname):
568         """Run binman with a real .dtb file and return the resulting data
569
570         Args:
571             fname: DT source filename to use (e.g. 082_fdt_update_all.dts)
572
573         Returns:
574             Resulting image contents
575         """
576         return self._DoReadFileDtb(fname, use_real_dtb=True, update_dtb=True)[0]
577
578     def _DoReadFile(self, fname, use_real_dtb=False):
579         """Helper function which discards the device-tree binary
580
581         Args:
582             fname: Device-tree source filename to use (e.g. 005_simple.dts)
583             use_real_dtb: True to use the test file as the contents of
584                 the u-boot-dtb entry. Normally this is not needed and the
585                 test contents (the U_BOOT_DTB_DATA string) can be used.
586                 But in some test we need the real contents.
587
588         Returns:
589             Resulting image contents
590         """
591         return self._DoReadFileDtb(fname, use_real_dtb)[0]
592
593     @classmethod
594     def _MakeInputFile(cls, fname, contents):
595         """Create a new test input file, creating directories as needed
596
597         Args:
598             fname: Filename to create
599             contents: File contents to write in to the file
600         Returns:
601             Full pathname of file created
602         """
603         pathname = os.path.join(cls._indir, fname)
604         dirname = os.path.dirname(pathname)
605         if dirname and not os.path.exists(dirname):
606             os.makedirs(dirname)
607         with open(pathname, 'wb') as fd:
608             fd.write(contents)
609         return pathname
610
611     @classmethod
612     def _MakeInputDir(cls, dirname):
613         """Create a new test input directory, creating directories as needed
614
615         Args:
616             dirname: Directory name to create
617
618         Returns:
619             Full pathname of directory created
620         """
621         pathname = os.path.join(cls._indir, dirname)
622         if not os.path.exists(pathname):
623             os.makedirs(pathname)
624         return pathname
625
626     @classmethod
627     def _SetupSplElf(cls, src_fname='bss_data'):
628         """Set up an ELF file with a '_dt_ucode_base_size' symbol
629
630         Args:
631             Filename of ELF file to use as SPL
632         """
633         TestFunctional._MakeInputFile('spl/u-boot-spl',
634             tools.read_file(cls.ElfTestFile(src_fname)))
635
636     @classmethod
637     def _SetupTplElf(cls, src_fname='bss_data'):
638         """Set up an ELF file with a '_dt_ucode_base_size' symbol
639
640         Args:
641             Filename of ELF file to use as TPL
642         """
643         TestFunctional._MakeInputFile('tpl/u-boot-tpl',
644             tools.read_file(cls.ElfTestFile(src_fname)))
645
646     @classmethod
647     def _SetupVplElf(cls, src_fname='bss_data'):
648         """Set up an ELF file with a '_dt_ucode_base_size' symbol
649
650         Args:
651             Filename of ELF file to use as VPL
652         """
653         TestFunctional._MakeInputFile('vpl/u-boot-vpl',
654             tools.read_file(cls.ElfTestFile(src_fname)))
655
656     @classmethod
657     def _SetupPmuFwlElf(cls, src_fname='bss_data'):
658         """Set up an ELF file with a '_dt_ucode_base_size' symbol
659
660         Args:
661             Filename of ELF file to use as VPL
662         """
663         TestFunctional._MakeInputFile('pmu-firmware.elf',
664             tools.read_file(cls.ElfTestFile(src_fname)))
665
666     @classmethod
667     def _SetupDescriptor(cls):
668         with open(cls.TestFile('descriptor.bin'), 'rb') as fd:
669             TestFunctional._MakeInputFile('descriptor.bin', fd.read())
670
671     @classmethod
672     def TestFile(cls, fname):
673         return os.path.join(cls._binman_dir, 'test', fname)
674
675     @classmethod
676     def ElfTestFile(cls, fname):
677         return os.path.join(cls._elf_testdir, fname)
678
679     @classmethod
680     def make_tee_bin(cls, fname, paged_sz=0, extra_data=b''):
681         init_sz, start_hi, start_lo, dummy = (len(U_BOOT_DATA), 0, TEE_ADDR, 0)
682         data = b'OPTE\x01xxx' + struct.pack('<5I', init_sz, start_hi, start_lo,
683                                             dummy, paged_sz) + U_BOOT_DATA
684         data += extra_data
685         TestFunctional._MakeInputFile(fname, data)
686
687     def AssertInList(self, grep_list, target):
688         """Assert that at least one of a list of things is in a target
689
690         Args:
691             grep_list: List of strings to check
692             target: Target string
693         """
694         for grep in grep_list:
695             if grep in target:
696                 return
697         self.fail("Error: '%s' not found in '%s'" % (grep_list, target))
698
699     def CheckNoGaps(self, entries):
700         """Check that all entries fit together without gaps
701
702         Args:
703             entries: List of entries to check
704         """
705         offset = 0
706         for entry in entries.values():
707             self.assertEqual(offset, entry.offset)
708             offset += entry.size
709
710     def GetFdtLen(self, dtb):
711         """Get the totalsize field from a device-tree binary
712
713         Args:
714             dtb: Device-tree binary contents
715
716         Returns:
717             Total size of device-tree binary, from the header
718         """
719         return struct.unpack('>L', dtb[4:8])[0]
720
721     def _GetPropTree(self, dtb, prop_names, prefix='/binman/'):
722         def AddNode(node, path):
723             if node.name != '/':
724                 path += '/' + node.name
725             for prop in node.props.values():
726                 if prop.name in prop_names:
727                     prop_path = path + ':' + prop.name
728                     tree[prop_path[len(prefix):]] = fdt_util.fdt32_to_cpu(
729                         prop.value)
730             for subnode in node.subnodes:
731                 AddNode(subnode, path)
732
733         tree = {}
734         AddNode(dtb.GetRoot(), '')
735         return tree
736
737     def _CheckSign(self, fit, key):
738         try:
739             tools.run('fit_check_sign', '-k', key, '-f', fit)
740         except:
741             self.fail('Expected signed FIT container')
742             return False
743         return True
744
745     def testRun(self):
746         """Test a basic run with valid args"""
747         result = self._RunBinman('-h')
748
749     def testFullHelp(self):
750         """Test that the full help is displayed with -H"""
751         result = self._RunBinman('-H')
752         help_file = os.path.join(self._binman_dir, 'README.rst')
753         # Remove possible extraneous strings
754         extra = '::::::::::::::\n' + help_file + '\n::::::::::::::\n'
755         gothelp = result.stdout.replace(extra, '')
756         self.assertEqual(len(gothelp), os.path.getsize(help_file))
757         self.assertEqual(0, len(result.stderr))
758         self.assertEqual(0, result.return_code)
759
760     def testFullHelpInternal(self):
761         """Test that the full help is displayed with -H"""
762         try:
763             command.test_result = command.CommandResult()
764             result = self._DoBinman('-H')
765             help_file = os.path.join(self._binman_dir, 'README.rst')
766         finally:
767             command.test_result = None
768
769     def testHelp(self):
770         """Test that the basic help is displayed with -h"""
771         result = self._RunBinman('-h')
772         self.assertTrue(len(result.stdout) > 200)
773         self.assertEqual(0, len(result.stderr))
774         self.assertEqual(0, result.return_code)
775
776     def testBoard(self):
777         """Test that we can run it with a specific board"""
778         self._SetupDtb('005_simple.dts', 'sandbox/u-boot.dtb')
779         TestFunctional._MakeInputFile('sandbox/u-boot.bin', U_BOOT_DATA)
780         result = self._DoBinman('build', '-n', '-b', 'sandbox')
781         self.assertEqual(0, result)
782
783     def testNeedBoard(self):
784         """Test that we get an error when no board ius supplied"""
785         with self.assertRaises(ValueError) as e:
786             result = self._DoBinman('build')
787         self.assertIn("Must provide a board to process (use -b <board>)",
788                 str(e.exception))
789
790     def testMissingDt(self):
791         """Test that an invalid device-tree file generates an error"""
792         with self.assertRaises(Exception) as e:
793             self._RunBinman('build', '-d', 'missing_file')
794         # We get one error from libfdt, and a different one from fdtget.
795         self.AssertInList(["Couldn't open blob from 'missing_file'",
796                            'No such file or directory'], str(e.exception))
797
798     def testBrokenDt(self):
799         """Test that an invalid device-tree source file generates an error
800
801         Since this is a source file it should be compiled and the error
802         will come from the device-tree compiler (dtc).
803         """
804         with self.assertRaises(Exception) as e:
805             self._RunBinman('build', '-d', self.TestFile('001_invalid.dts'))
806         self.assertIn("FATAL ERROR: Unable to parse input tree",
807                 str(e.exception))
808
809     def testMissingNode(self):
810         """Test that a device tree without a 'binman' node generates an error"""
811         with self.assertRaises(Exception) as e:
812             self._DoBinman('build', '-d', self.TestFile('002_missing_node.dts'))
813         self.assertIn("does not have a 'binman' node", str(e.exception))
814
815     def testEmpty(self):
816         """Test that an empty binman node works OK (i.e. does nothing)"""
817         result = self._RunBinman('build', '-d', self.TestFile('003_empty.dts'))
818         self.assertEqual(0, len(result.stderr))
819         self.assertEqual(0, result.return_code)
820
821     def testInvalidEntry(self):
822         """Test that an invalid entry is flagged"""
823         with self.assertRaises(Exception) as e:
824             result = self._RunBinman('build', '-d',
825                                      self.TestFile('004_invalid_entry.dts'))
826         self.assertIn("Unknown entry type 'not-a-valid-type' in node "
827                 "'/binman/not-a-valid-type'", str(e.exception))
828
829     def testSimple(self):
830         """Test a simple binman with a single file"""
831         data = self._DoReadFile('005_simple.dts')
832         self.assertEqual(U_BOOT_DATA, data)
833
834     def testSimpleDebug(self):
835         """Test a simple binman run with debugging enabled"""
836         self._DoTestFile('005_simple.dts', debug=True)
837
838     def testDual(self):
839         """Test that we can handle creating two images
840
841         This also tests image padding.
842         """
843         retcode = self._DoTestFile('006_dual_image.dts')
844         self.assertEqual(0, retcode)
845
846         image = control.images['image1']
847         self.assertEqual(len(U_BOOT_DATA), image.size)
848         fname = tools.get_output_filename('image1.bin')
849         self.assertTrue(os.path.exists(fname))
850         with open(fname, 'rb') as fd:
851             data = fd.read()
852             self.assertEqual(U_BOOT_DATA, data)
853
854         image = control.images['image2']
855         self.assertEqual(3 + len(U_BOOT_DATA) + 5, image.size)
856         fname = tools.get_output_filename('image2.bin')
857         self.assertTrue(os.path.exists(fname))
858         with open(fname, 'rb') as fd:
859             data = fd.read()
860             self.assertEqual(U_BOOT_DATA, data[3:7])
861             self.assertEqual(tools.get_bytes(0, 3), data[:3])
862             self.assertEqual(tools.get_bytes(0, 5), data[7:])
863
864     def testBadAlign(self):
865         """Test that an invalid alignment value is detected"""
866         with self.assertRaises(ValueError) as e:
867             self._DoTestFile('007_bad_align.dts')
868         self.assertIn("Node '/binman/u-boot': Alignment 23 must be a power "
869                       "of two", str(e.exception))
870
871     def testPackSimple(self):
872         """Test that packing works as expected"""
873         retcode = self._DoTestFile('008_pack.dts')
874         self.assertEqual(0, retcode)
875         self.assertIn('image', control.images)
876         image = control.images['image']
877         entries = image.GetEntries()
878         self.assertEqual(5, len(entries))
879
880         # First u-boot
881         self.assertIn('u-boot', entries)
882         entry = entries['u-boot']
883         self.assertEqual(0, entry.offset)
884         self.assertEqual(len(U_BOOT_DATA), entry.size)
885
886         # Second u-boot, aligned to 16-byte boundary
887         self.assertIn('u-boot-align', entries)
888         entry = entries['u-boot-align']
889         self.assertEqual(16, entry.offset)
890         self.assertEqual(len(U_BOOT_DATA), entry.size)
891
892         # Third u-boot, size 23 bytes
893         self.assertIn('u-boot-size', entries)
894         entry = entries['u-boot-size']
895         self.assertEqual(20, entry.offset)
896         self.assertEqual(len(U_BOOT_DATA), entry.contents_size)
897         self.assertEqual(23, entry.size)
898
899         # Fourth u-boot, placed immediate after the above
900         self.assertIn('u-boot-next', entries)
901         entry = entries['u-boot-next']
902         self.assertEqual(43, entry.offset)
903         self.assertEqual(len(U_BOOT_DATA), entry.size)
904
905         # Fifth u-boot, placed at a fixed offset
906         self.assertIn('u-boot-fixed', entries)
907         entry = entries['u-boot-fixed']
908         self.assertEqual(61, entry.offset)
909         self.assertEqual(len(U_BOOT_DATA), entry.size)
910
911         self.assertEqual(65, image.size)
912
913     def testPackExtra(self):
914         """Test that extra packing feature works as expected"""
915         data, _, _, out_dtb_fname = self._DoReadFileDtb('009_pack_extra.dts',
916                                                         update_dtb=True)
917
918         self.assertIn('image', control.images)
919         image = control.images['image']
920         entries = image.GetEntries()
921         self.assertEqual(6, len(entries))
922
923         # First u-boot with padding before and after (included in minimum size)
924         self.assertIn('u-boot', entries)
925         entry = entries['u-boot']
926         self.assertEqual(0, entry.offset)
927         self.assertEqual(3, entry.pad_before)
928         self.assertEqual(3 + 5 + len(U_BOOT_DATA), entry.size)
929         self.assertEqual(U_BOOT_DATA, entry.data)
930         self.assertEqual(tools.get_bytes(0, 3) + U_BOOT_DATA +
931                          tools.get_bytes(0, 5), data[:entry.size])
932         pos = entry.size
933
934         # Second u-boot has an aligned size, but it has no effect
935         self.assertIn('u-boot-align-size-nop', entries)
936         entry = entries['u-boot-align-size-nop']
937         self.assertEqual(pos, entry.offset)
938         self.assertEqual(len(U_BOOT_DATA), entry.size)
939         self.assertEqual(U_BOOT_DATA, entry.data)
940         self.assertEqual(U_BOOT_DATA, data[pos:pos + entry.size])
941         pos += entry.size
942
943         # Third u-boot has an aligned size too
944         self.assertIn('u-boot-align-size', entries)
945         entry = entries['u-boot-align-size']
946         self.assertEqual(pos, entry.offset)
947         self.assertEqual(32, entry.size)
948         self.assertEqual(U_BOOT_DATA, entry.data)
949         self.assertEqual(U_BOOT_DATA + tools.get_bytes(0, 32 - len(U_BOOT_DATA)),
950                          data[pos:pos + entry.size])
951         pos += entry.size
952
953         # Fourth u-boot has an aligned end
954         self.assertIn('u-boot-align-end', entries)
955         entry = entries['u-boot-align-end']
956         self.assertEqual(48, entry.offset)
957         self.assertEqual(16, entry.size)
958         self.assertEqual(U_BOOT_DATA, entry.data[:len(U_BOOT_DATA)])
959         self.assertEqual(U_BOOT_DATA + tools.get_bytes(0, 16 - len(U_BOOT_DATA)),
960                          data[pos:pos + entry.size])
961         pos += entry.size
962
963         # Fifth u-boot immediately afterwards
964         self.assertIn('u-boot-align-both', entries)
965         entry = entries['u-boot-align-both']
966         self.assertEqual(64, entry.offset)
967         self.assertEqual(64, entry.size)
968         self.assertEqual(U_BOOT_DATA, entry.data[:len(U_BOOT_DATA)])
969         self.assertEqual(U_BOOT_DATA + tools.get_bytes(0, 64 - len(U_BOOT_DATA)),
970                          data[pos:pos + entry.size])
971
972         # Sixth u-boot with both minimum size and aligned size
973         self.assertIn('u-boot-min-size', entries)
974         entry = entries['u-boot-min-size']
975         self.assertEqual(128, entry.offset)
976         self.assertEqual(32, entry.size)
977         self.assertEqual(U_BOOT_DATA, entry.data[:len(U_BOOT_DATA)])
978         self.assertEqual(U_BOOT_DATA + tools.get_bytes(0, 32 - len(U_BOOT_DATA)),
979                          data[pos:pos + entry.size])
980
981         self.CheckNoGaps(entries)
982         self.assertEqual(160, image.size)
983
984         dtb = fdt.Fdt(out_dtb_fname)
985         dtb.Scan()
986         props = self._GetPropTree(dtb, ['size', 'offset', 'image-pos'])
987         expected = {
988             'image-pos': 0,
989             'offset': 0,
990             'size': 160,
991
992             'u-boot:image-pos': 0,
993             'u-boot:offset': 0,
994             'u-boot:size': 3 + 5 + len(U_BOOT_DATA),
995
996             'u-boot-align-size-nop:image-pos': 12,
997             'u-boot-align-size-nop:offset': 12,
998             'u-boot-align-size-nop:size': 4,
999
1000             'u-boot-align-size:image-pos': 16,
1001             'u-boot-align-size:offset': 16,
1002             'u-boot-align-size:size': 32,
1003
1004             'u-boot-align-end:image-pos': 48,
1005             'u-boot-align-end:offset': 48,
1006             'u-boot-align-end:size': 16,
1007
1008             'u-boot-align-both:image-pos': 64,
1009             'u-boot-align-both:offset': 64,
1010             'u-boot-align-both:size': 64,
1011
1012             'u-boot-min-size:image-pos': 128,
1013             'u-boot-min-size:offset': 128,
1014             'u-boot-min-size:size': 32,
1015             }
1016         self.assertEqual(expected, props)
1017
1018     def testPackAlignPowerOf2(self):
1019         """Test that invalid entry alignment is detected"""
1020         with self.assertRaises(ValueError) as e:
1021             self._DoTestFile('010_pack_align_power2.dts')
1022         self.assertIn("Node '/binman/u-boot': Alignment 5 must be a power "
1023                       "of two", str(e.exception))
1024
1025     def testPackAlignSizePowerOf2(self):
1026         """Test that invalid entry size alignment is detected"""
1027         with self.assertRaises(ValueError) as e:
1028             self._DoTestFile('011_pack_align_size_power2.dts')
1029         self.assertIn("Node '/binman/u-boot': Alignment size 55 must be a "
1030                       "power of two", str(e.exception))
1031
1032     def testPackInvalidAlign(self):
1033         """Test detection of an offset that does not match its alignment"""
1034         with self.assertRaises(ValueError) as e:
1035             self._DoTestFile('012_pack_inv_align.dts')
1036         self.assertIn("Node '/binman/u-boot': Offset 0x5 (5) does not match "
1037                       "align 0x4 (4)", str(e.exception))
1038
1039     def testPackInvalidSizeAlign(self):
1040         """Test that invalid entry size alignment is detected"""
1041         with self.assertRaises(ValueError) as e:
1042             self._DoTestFile('013_pack_inv_size_align.dts')
1043         self.assertIn("Node '/binman/u-boot': Size 0x5 (5) does not match "
1044                       "align-size 0x4 (4)", str(e.exception))
1045
1046     def testPackOverlap(self):
1047         """Test that overlapping regions are detected"""
1048         with self.assertRaises(ValueError) as e:
1049             self._DoTestFile('014_pack_overlap.dts')
1050         self.assertIn("Node '/binman/u-boot-align': Offset 0x3 (3) overlaps "
1051                       "with previous entry '/binman/u-boot' ending at 0x4 (4)",
1052                       str(e.exception))
1053
1054     def testPackEntryOverflow(self):
1055         """Test that entries that overflow their size are detected"""
1056         with self.assertRaises(ValueError) as e:
1057             self._DoTestFile('015_pack_overflow.dts')
1058         self.assertIn("Node '/binman/u-boot': Entry contents size is 0x4 (4) "
1059                       "but entry size is 0x3 (3)", str(e.exception))
1060
1061     def testPackImageOverflow(self):
1062         """Test that entries which overflow the image size are detected"""
1063         with self.assertRaises(ValueError) as e:
1064             self._DoTestFile('016_pack_image_overflow.dts')
1065         self.assertIn("Section '/binman': contents size 0x4 (4) exceeds section "
1066                       "size 0x3 (3)", str(e.exception))
1067
1068     def testPackImageSize(self):
1069         """Test that the image size can be set"""
1070         retcode = self._DoTestFile('017_pack_image_size.dts')
1071         self.assertEqual(0, retcode)
1072         self.assertIn('image', control.images)
1073         image = control.images['image']
1074         self.assertEqual(7, image.size)
1075
1076     def testPackImageSizeAlign(self):
1077         """Test that image size alignemnt works as expected"""
1078         retcode = self._DoTestFile('018_pack_image_align.dts')
1079         self.assertEqual(0, retcode)
1080         self.assertIn('image', control.images)
1081         image = control.images['image']
1082         self.assertEqual(16, image.size)
1083
1084     def testPackInvalidImageAlign(self):
1085         """Test that invalid image alignment is detected"""
1086         with self.assertRaises(ValueError) as e:
1087             self._DoTestFile('019_pack_inv_image_align.dts')
1088         self.assertIn("Section '/binman': Size 0x7 (7) does not match "
1089                       "align-size 0x8 (8)", str(e.exception))
1090
1091     def testPackAlignPowerOf2Inv(self):
1092         """Test that invalid image alignment is detected"""
1093         with self.assertRaises(ValueError) as e:
1094             self._DoTestFile('020_pack_inv_image_align_power2.dts')
1095         self.assertIn("Image '/binman': Alignment size 131 must be a power of "
1096                       "two", str(e.exception))
1097
1098     def testImagePadByte(self):
1099         """Test that the image pad byte can be specified"""
1100         self._SetupSplElf()
1101         data = self._DoReadFile('021_image_pad.dts')
1102         self.assertEqual(U_BOOT_SPL_DATA + tools.get_bytes(0xff, 1) +
1103                          U_BOOT_DATA, data)
1104
1105     def testImageName(self):
1106         """Test that image files can be named"""
1107         retcode = self._DoTestFile('022_image_name.dts')
1108         self.assertEqual(0, retcode)
1109         image = control.images['image1']
1110         fname = tools.get_output_filename('test-name')
1111         self.assertTrue(os.path.exists(fname))
1112
1113         image = control.images['image2']
1114         fname = tools.get_output_filename('test-name.xx')
1115         self.assertTrue(os.path.exists(fname))
1116
1117     def testBlobFilename(self):
1118         """Test that generic blobs can be provided by filename"""
1119         data = self._DoReadFile('023_blob.dts')
1120         self.assertEqual(BLOB_DATA, data)
1121
1122     def testPackSorted(self):
1123         """Test that entries can be sorted"""
1124         self._SetupSplElf()
1125         data = self._DoReadFile('024_sorted.dts')
1126         self.assertEqual(tools.get_bytes(0, 1) + U_BOOT_SPL_DATA +
1127                          tools.get_bytes(0, 2) + U_BOOT_DATA, data)
1128
1129     def testPackZeroOffset(self):
1130         """Test that an entry at offset 0 is not given a new offset"""
1131         self._SetupSplElf()
1132         with self.assertRaises(ValueError) as e:
1133             self._DoTestFile('025_pack_zero_size.dts')
1134         self.assertIn("Node '/binman/u-boot-spl': Offset 0x0 (0) overlaps "
1135                       "with previous entry '/binman/u-boot' ending at 0x4 (4)",
1136                       str(e.exception))
1137
1138     def testPackUbootDtb(self):
1139         """Test that a device tree can be added to U-Boot"""
1140         data = self._DoReadFile('026_pack_u_boot_dtb.dts')
1141         self.assertEqual(U_BOOT_NODTB_DATA + U_BOOT_DTB_DATA, data)
1142
1143     def testPackX86RomNoSize(self):
1144         """Test that the end-at-4gb property requires a size property"""
1145         self._SetupSplElf()
1146         with self.assertRaises(ValueError) as e:
1147             self._DoTestFile('027_pack_4gb_no_size.dts')
1148         self.assertIn("Image '/binman': Section size must be provided when "
1149                       "using end-at-4gb", str(e.exception))
1150
1151     def test4gbAndSkipAtStartTogether(self):
1152         """Test that the end-at-4gb and skip-at-size property can't be used
1153         together"""
1154         self._SetupSplElf()
1155         with self.assertRaises(ValueError) as e:
1156             self._DoTestFile('098_4gb_and_skip_at_start_together.dts')
1157         self.assertIn("Image '/binman': Provide either 'end-at-4gb' or "
1158                       "'skip-at-start'", str(e.exception))
1159
1160     def testPackX86RomOutside(self):
1161         """Test that the end-at-4gb property checks for offset boundaries"""
1162         self._SetupSplElf()
1163         with self.assertRaises(ValueError) as e:
1164             self._DoTestFile('028_pack_4gb_outside.dts')
1165         self.assertIn("Node '/binman/u-boot': Offset 0x0 (0) size 0x4 (4) "
1166                       "is outside the section '/binman' starting at "
1167                       '0xffffffe0 (4294967264) of size 0x20 (32)',
1168                       str(e.exception))
1169
1170     def testPackX86Rom(self):
1171         """Test that a basic x86 ROM can be created"""
1172         self._SetupSplElf()
1173         data = self._DoReadFile('029_x86_rom.dts')
1174         self.assertEqual(U_BOOT_DATA + tools.get_bytes(0, 3) + U_BOOT_SPL_DATA +
1175                          tools.get_bytes(0, 2), data)
1176
1177     def testPackX86RomMeNoDesc(self):
1178         """Test that an invalid Intel descriptor entry is detected"""
1179         try:
1180             TestFunctional._MakeInputFile('descriptor-empty.bin', b'')
1181             with self.assertRaises(ValueError) as e:
1182                 self._DoTestFile('163_x86_rom_me_empty.dts')
1183             self.assertIn("Node '/binman/intel-descriptor': Cannot find Intel Flash Descriptor (FD) signature",
1184                           str(e.exception))
1185         finally:
1186             self._SetupDescriptor()
1187
1188     def testPackX86RomBadDesc(self):
1189         """Test that the Intel requires a descriptor entry"""
1190         with self.assertRaises(ValueError) as e:
1191             self._DoTestFile('030_x86_rom_me_no_desc.dts')
1192         self.assertIn("Node '/binman/intel-me': No offset set with "
1193                       "offset-unset: should another entry provide this correct "
1194                       "offset?", str(e.exception))
1195
1196     def testPackX86RomMe(self):
1197         """Test that an x86 ROM with an ME region can be created"""
1198         data = self._DoReadFile('031_x86_rom_me.dts')
1199         expected_desc = tools.read_file(self.TestFile('descriptor.bin'))
1200         if data[:0x1000] != expected_desc:
1201             self.fail('Expected descriptor binary at start of image')
1202         self.assertEqual(ME_DATA, data[0x1000:0x1000 + len(ME_DATA)])
1203
1204     def testPackVga(self):
1205         """Test that an image with a VGA binary can be created"""
1206         data = self._DoReadFile('032_intel_vga.dts')
1207         self.assertEqual(VGA_DATA, data[:len(VGA_DATA)])
1208
1209     def testPackStart16(self):
1210         """Test that an image with an x86 start16 region can be created"""
1211         data = self._DoReadFile('033_x86_start16.dts')
1212         self.assertEqual(X86_START16_DATA, data[:len(X86_START16_DATA)])
1213
1214     def testPackPowerpcMpc85xxBootpgResetvec(self):
1215         """Test that an image with powerpc-mpc85xx-bootpg-resetvec can be
1216         created"""
1217         data = self._DoReadFile('150_powerpc_mpc85xx_bootpg_resetvec.dts')
1218         self.assertEqual(PPC_MPC85XX_BR_DATA, data[:len(PPC_MPC85XX_BR_DATA)])
1219
1220     def _RunMicrocodeTest(self, dts_fname, nodtb_data, ucode_second=False):
1221         """Handle running a test for insertion of microcode
1222
1223         Args:
1224             dts_fname: Name of test .dts file
1225             nodtb_data: Data that we expect in the first section
1226             ucode_second: True if the microsecond entry is second instead of
1227                 third
1228
1229         Returns:
1230             Tuple:
1231                 Contents of first region (U-Boot or SPL)
1232                 Offset and size components of microcode pointer, as inserted
1233                     in the above (two 4-byte words)
1234         """
1235         data = self._DoReadFile(dts_fname, True)
1236
1237         # Now check the device tree has no microcode
1238         if ucode_second:
1239             ucode_content = data[len(nodtb_data):]
1240             ucode_pos = len(nodtb_data)
1241             dtb_with_ucode = ucode_content[16:]
1242             fdt_len = self.GetFdtLen(dtb_with_ucode)
1243         else:
1244             dtb_with_ucode = data[len(nodtb_data):]
1245             fdt_len = self.GetFdtLen(dtb_with_ucode)
1246             ucode_content = dtb_with_ucode[fdt_len:]
1247             ucode_pos = len(nodtb_data) + fdt_len
1248         fname = tools.get_output_filename('test.dtb')
1249         with open(fname, 'wb') as fd:
1250             fd.write(dtb_with_ucode)
1251         dtb = fdt.FdtScan(fname)
1252         ucode = dtb.GetNode('/microcode')
1253         self.assertTrue(ucode)
1254         for node in ucode.subnodes:
1255             self.assertFalse(node.props.get('data'))
1256
1257         # Check that the microcode appears immediately after the Fdt
1258         # This matches the concatenation of the data properties in
1259         # the /microcode/update@xxx nodes in 34_x86_ucode.dts.
1260         ucode_data = struct.pack('>4L', 0x12345678, 0x12345679, 0xabcd0000,
1261                                  0x78235609)
1262         self.assertEqual(ucode_data, ucode_content[:len(ucode_data)])
1263
1264         # Check that the microcode pointer was inserted. It should match the
1265         # expected offset and size
1266         pos_and_size = struct.pack('<2L', 0xfffffe00 + ucode_pos,
1267                                    len(ucode_data))
1268         u_boot = data[:len(nodtb_data)]
1269         return u_boot, pos_and_size
1270
1271     def testPackUbootMicrocode(self):
1272         """Test that x86 microcode can be handled correctly
1273
1274         We expect to see the following in the image, in order:
1275             u-boot-nodtb.bin with a microcode pointer inserted at the correct
1276                 place
1277             u-boot.dtb with the microcode removed
1278             the microcode
1279         """
1280         first, pos_and_size = self._RunMicrocodeTest('034_x86_ucode.dts',
1281                                                      U_BOOT_NODTB_DATA)
1282         self.assertEqual(b'nodtb with microcode' + pos_and_size +
1283                          b' somewhere in here', first)
1284
1285     def _RunPackUbootSingleMicrocode(self):
1286         """Test that x86 microcode can be handled correctly
1287
1288         We expect to see the following in the image, in order:
1289             u-boot-nodtb.bin with a microcode pointer inserted at the correct
1290                 place
1291             u-boot.dtb with the microcode
1292             an empty microcode region
1293         """
1294         # We need the libfdt library to run this test since only that allows
1295         # finding the offset of a property. This is required by
1296         # Entry_u_boot_dtb_with_ucode.ObtainContents().
1297         data = self._DoReadFile('035_x86_single_ucode.dts', True)
1298
1299         second = data[len(U_BOOT_NODTB_DATA):]
1300
1301         fdt_len = self.GetFdtLen(second)
1302         third = second[fdt_len:]
1303         second = second[:fdt_len]
1304
1305         ucode_data = struct.pack('>2L', 0x12345678, 0x12345679)
1306         self.assertIn(ucode_data, second)
1307         ucode_pos = second.find(ucode_data) + len(U_BOOT_NODTB_DATA)
1308
1309         # Check that the microcode pointer was inserted. It should match the
1310         # expected offset and size
1311         pos_and_size = struct.pack('<2L', 0xfffffe00 + ucode_pos,
1312                                    len(ucode_data))
1313         first = data[:len(U_BOOT_NODTB_DATA)]
1314         self.assertEqual(b'nodtb with microcode' + pos_and_size +
1315                          b' somewhere in here', first)
1316
1317     def testPackUbootSingleMicrocode(self):
1318         """Test that x86 microcode can be handled correctly with fdt_normal.
1319         """
1320         self._RunPackUbootSingleMicrocode()
1321
1322     def testUBootImg(self):
1323         """Test that u-boot.img can be put in a file"""
1324         data = self._DoReadFile('036_u_boot_img.dts')
1325         self.assertEqual(U_BOOT_IMG_DATA, data)
1326
1327     def testNoMicrocode(self):
1328         """Test that a missing microcode region is detected"""
1329         with self.assertRaises(ValueError) as e:
1330             self._DoReadFile('037_x86_no_ucode.dts', True)
1331         self.assertIn("Node '/binman/u-boot-dtb-with-ucode': No /microcode "
1332                       "node found in ", str(e.exception))
1333
1334     def testMicrocodeWithoutNode(self):
1335         """Test that a missing u-boot-dtb-with-ucode node is detected"""
1336         with self.assertRaises(ValueError) as e:
1337             self._DoReadFile('038_x86_ucode_missing_node.dts', True)
1338         self.assertIn("Node '/binman/u-boot-with-ucode-ptr': Cannot find "
1339                 "microcode region u-boot-dtb-with-ucode", str(e.exception))
1340
1341     def testMicrocodeWithoutNode2(self):
1342         """Test that a missing u-boot-ucode node is detected"""
1343         with self.assertRaises(ValueError) as e:
1344             self._DoReadFile('039_x86_ucode_missing_node2.dts', True)
1345         self.assertIn("Node '/binman/u-boot-with-ucode-ptr': Cannot find "
1346             "microcode region u-boot-ucode", str(e.exception))
1347
1348     def testMicrocodeWithoutPtrInElf(self):
1349         """Test that a U-Boot binary without the microcode symbol is detected"""
1350         # ELF file without a '_dt_ucode_base_size' symbol
1351         try:
1352             TestFunctional._MakeInputFile('u-boot',
1353                 tools.read_file(self.ElfTestFile('u_boot_no_ucode_ptr')))
1354
1355             with self.assertRaises(ValueError) as e:
1356                 self._RunPackUbootSingleMicrocode()
1357             self.assertIn("Node '/binman/u-boot-with-ucode-ptr': Cannot locate "
1358                     "_dt_ucode_base_size symbol in u-boot", str(e.exception))
1359
1360         finally:
1361             # Put the original file back
1362             TestFunctional._MakeInputFile('u-boot',
1363                 tools.read_file(self.ElfTestFile('u_boot_ucode_ptr')))
1364
1365     def testMicrocodeNotInImage(self):
1366         """Test that microcode must be placed within the image"""
1367         with self.assertRaises(ValueError) as e:
1368             self._DoReadFile('040_x86_ucode_not_in_image.dts', True)
1369         self.assertIn("Node '/binman/u-boot-with-ucode-ptr': Microcode "
1370                 "pointer _dt_ucode_base_size at fffffe14 is outside the "
1371                 "section ranging from 00000000 to 0000002e", str(e.exception))
1372
1373     def testWithoutMicrocode(self):
1374         """Test that we can cope with an image without microcode (e.g. qemu)"""
1375         TestFunctional._MakeInputFile('u-boot',
1376             tools.read_file(self.ElfTestFile('u_boot_no_ucode_ptr')))
1377         data, dtb, _, _ = self._DoReadFileDtb('044_x86_optional_ucode.dts', True)
1378
1379         # Now check the device tree has no microcode
1380         self.assertEqual(U_BOOT_NODTB_DATA, data[:len(U_BOOT_NODTB_DATA)])
1381         second = data[len(U_BOOT_NODTB_DATA):]
1382
1383         fdt_len = self.GetFdtLen(second)
1384         self.assertEqual(dtb, second[:fdt_len])
1385
1386         used_len = len(U_BOOT_NODTB_DATA) + fdt_len
1387         third = data[used_len:]
1388         self.assertEqual(tools.get_bytes(0, 0x200 - used_len), third)
1389
1390     def testUnknownPosSize(self):
1391         """Test that microcode must be placed within the image"""
1392         with self.assertRaises(ValueError) as e:
1393             self._DoReadFile('041_unknown_pos_size.dts', True)
1394         self.assertIn("Section '/binman': Unable to set offset/size for unknown "
1395                 "entry 'invalid-entry'", str(e.exception))
1396
1397     def testPackFsp(self):
1398         """Test that an image with a FSP binary can be created"""
1399         data = self._DoReadFile('042_intel_fsp.dts')
1400         self.assertEqual(FSP_DATA, data[:len(FSP_DATA)])
1401
1402     def testPackCmc(self):
1403         """Test that an image with a CMC binary can be created"""
1404         data = self._DoReadFile('043_intel_cmc.dts')
1405         self.assertEqual(CMC_DATA, data[:len(CMC_DATA)])
1406
1407     def testPackVbt(self):
1408         """Test that an image with a VBT binary can be created"""
1409         data = self._DoReadFile('046_intel_vbt.dts')
1410         self.assertEqual(VBT_DATA, data[:len(VBT_DATA)])
1411
1412     def testSplBssPad(self):
1413         """Test that we can pad SPL's BSS with zeros"""
1414         # ELF file with a '__bss_size' symbol
1415         self._SetupSplElf()
1416         data = self._DoReadFile('047_spl_bss_pad.dts')
1417         self.assertEqual(U_BOOT_SPL_DATA + tools.get_bytes(0, 10) + U_BOOT_DATA,
1418                          data)
1419
1420     def testSplBssPadMissing(self):
1421         """Test that a missing symbol is detected"""
1422         self._SetupSplElf('u_boot_ucode_ptr')
1423         with self.assertRaises(ValueError) as e:
1424             self._DoReadFile('047_spl_bss_pad.dts')
1425         self.assertIn('Expected __bss_size symbol in spl/u-boot-spl',
1426                       str(e.exception))
1427
1428     def testPackStart16Spl(self):
1429         """Test that an image with an x86 start16 SPL region can be created"""
1430         data = self._DoReadFile('048_x86_start16_spl.dts')
1431         self.assertEqual(X86_START16_SPL_DATA, data[:len(X86_START16_SPL_DATA)])
1432
1433     def _PackUbootSplMicrocode(self, dts, ucode_second=False):
1434         """Helper function for microcode tests
1435
1436         We expect to see the following in the image, in order:
1437             u-boot-spl-nodtb.bin with a microcode pointer inserted at the
1438                 correct place
1439             u-boot.dtb with the microcode removed
1440             the microcode
1441
1442         Args:
1443             dts: Device tree file to use for test
1444             ucode_second: True if the microsecond entry is second instead of
1445                 third
1446         """
1447         self._SetupSplElf('u_boot_ucode_ptr')
1448         first, pos_and_size = self._RunMicrocodeTest(dts, U_BOOT_SPL_NODTB_DATA,
1449                                                      ucode_second=ucode_second)
1450         self.assertEqual(b'splnodtb with microc' + pos_and_size +
1451                          b'ter somewhere in here', first)
1452
1453     def testPackUbootSplMicrocode(self):
1454         """Test that x86 microcode can be handled correctly in SPL"""
1455         self._SetupSplElf()
1456         self._PackUbootSplMicrocode('049_x86_ucode_spl.dts')
1457
1458     def testPackUbootSplMicrocodeReorder(self):
1459         """Test that order doesn't matter for microcode entries
1460
1461         This is the same as testPackUbootSplMicrocode but when we process the
1462         u-boot-ucode entry we have not yet seen the u-boot-dtb-with-ucode
1463         entry, so we reply on binman to try later.
1464         """
1465         self._PackUbootSplMicrocode('058_x86_ucode_spl_needs_retry.dts',
1466                                     ucode_second=True)
1467
1468     def testPackMrc(self):
1469         """Test that an image with an MRC binary can be created"""
1470         data = self._DoReadFile('050_intel_mrc.dts')
1471         self.assertEqual(MRC_DATA, data[:len(MRC_DATA)])
1472
1473     def testSplDtb(self):
1474         """Test that an image with spl/u-boot-spl.dtb can be created"""
1475         self._SetupSplElf()
1476         data = self._DoReadFile('051_u_boot_spl_dtb.dts')
1477         self.assertEqual(U_BOOT_SPL_DTB_DATA, data[:len(U_BOOT_SPL_DTB_DATA)])
1478
1479     def testSplNoDtb(self):
1480         """Test that an image with spl/u-boot-spl-nodtb.bin can be created"""
1481         self._SetupSplElf()
1482         data = self._DoReadFile('052_u_boot_spl_nodtb.dts')
1483         self.assertEqual(U_BOOT_SPL_NODTB_DATA, data[:len(U_BOOT_SPL_NODTB_DATA)])
1484
1485     def checkSymbols(self, dts, base_data, u_boot_offset, entry_args=None,
1486                      use_expanded=False, no_write_symbols=False):
1487         """Check the image contains the expected symbol values
1488
1489         Args:
1490             dts: Device tree file to use for test
1491             base_data: Data before and after 'u-boot' section
1492             u_boot_offset: Offset of 'u-boot' section in image
1493             entry_args: Dict of entry args to supply to binman
1494                 key: arg name
1495                 value: value of that arg
1496             use_expanded: True to use expanded entries where available, e.g.
1497                 'u-boot-expanded' instead of 'u-boot'
1498         """
1499         elf_fname = self.ElfTestFile('u_boot_binman_syms')
1500         syms = elf.GetSymbols(elf_fname, ['binman', 'image'])
1501         addr = elf.GetSymbolAddress(elf_fname, '__image_copy_start')
1502         self.assertEqual(syms['_binman_sym_magic'].address, addr)
1503         self.assertEqual(syms['_binman_u_boot_spl_any_prop_offset'].address,
1504                          addr + 4)
1505
1506         self._SetupSplElf('u_boot_binman_syms')
1507         data = self._DoReadFileDtb(dts, entry_args=entry_args,
1508                                    use_expanded=use_expanded)[0]
1509         # The image should contain the symbols from u_boot_binman_syms.c
1510         # Note that image_pos is adjusted by the base address of the image,
1511         # which is 0x10 in our test image
1512         sym_values = struct.pack('<LLQLL', elf.BINMAN_SYM_MAGIC_VALUE,
1513                                  0x00, u_boot_offset + len(U_BOOT_DATA),
1514                                  0x10 + u_boot_offset, 0x04)
1515         if no_write_symbols:
1516             expected = (base_data +
1517                         tools.get_bytes(0xff, 0x38 - len(base_data)) +
1518                         U_BOOT_DATA + base_data)
1519         else:
1520             expected = (sym_values + base_data[24:] +
1521                         tools.get_bytes(0xff, 1) + U_BOOT_DATA + sym_values +
1522                         base_data[24:])
1523         self.assertEqual(expected, data)
1524
1525     def testSymbols(self):
1526         """Test binman can assign symbols embedded in U-Boot"""
1527         self.checkSymbols('053_symbols.dts', U_BOOT_SPL_DATA, 0x1c)
1528
1529     def testSymbolsNoDtb(self):
1530         """Test binman can assign symbols embedded in U-Boot SPL"""
1531         self.checkSymbols('196_symbols_nodtb.dts',
1532                           U_BOOT_SPL_NODTB_DATA + U_BOOT_SPL_DTB_DATA,
1533                           0x38)
1534
1535     def testPackUnitAddress(self):
1536         """Test that we support multiple binaries with the same name"""
1537         data = self._DoReadFile('054_unit_address.dts')
1538         self.assertEqual(U_BOOT_DATA + U_BOOT_DATA, data)
1539
1540     def testSections(self):
1541         """Basic test of sections"""
1542         data = self._DoReadFile('055_sections.dts')
1543         expected = (U_BOOT_DATA + tools.get_bytes(ord('!'), 12) +
1544                     U_BOOT_DATA + tools.get_bytes(ord('a'), 12) +
1545                     U_BOOT_DATA + tools.get_bytes(ord('&'), 4))
1546         self.assertEqual(expected, data)
1547
1548     def testMap(self):
1549         """Tests outputting a map of the images"""
1550         _, _, map_data, _ = self._DoReadFileDtb('055_sections.dts', map=True)
1551         self.assertEqual('''ImagePos    Offset      Size  Name
1552 00000000  00000000  00000028  image
1553 00000000   00000000  00000010  section@0
1554 00000000    00000000  00000004  u-boot
1555 00000010   00000010  00000010  section@1
1556 00000010    00000000  00000004  u-boot
1557 00000020   00000020  00000004  section@2
1558 00000020    00000000  00000004  u-boot
1559 ''', map_data)
1560
1561     def testNamePrefix(self):
1562         """Tests that name prefixes are used"""
1563         _, _, map_data, _ = self._DoReadFileDtb('056_name_prefix.dts', map=True)
1564         self.assertEqual('''ImagePos    Offset      Size  Name
1565 00000000  00000000  00000028  image
1566 00000000   00000000  00000010  section@0
1567 00000000    00000000  00000004  ro-u-boot
1568 00000010   00000010  00000010  section@1
1569 00000010    00000000  00000004  rw-u-boot
1570 ''', map_data)
1571
1572     def testUnknownContents(self):
1573         """Test that obtaining the contents works as expected"""
1574         with self.assertRaises(ValueError) as e:
1575             self._DoReadFile('057_unknown_contents.dts', True)
1576         self.assertIn("Image '/binman': Internal error: Could not complete "
1577                 "processing of contents: remaining ["
1578                 "<binman.etype._testing.Entry__testing ", str(e.exception))
1579
1580     def testBadChangeSize(self):
1581         """Test that trying to change the size of an entry fails"""
1582         try:
1583             state.SetAllowEntryExpansion(False)
1584             with self.assertRaises(ValueError) as e:
1585                 self._DoReadFile('059_change_size.dts', True)
1586             self.assertIn("Node '/binman/_testing': Cannot update entry size from 2 to 3",
1587                           str(e.exception))
1588         finally:
1589             state.SetAllowEntryExpansion(True)
1590
1591     def testUpdateFdt(self):
1592         """Test that we can update the device tree with offset/size info"""
1593         _, _, _, out_dtb_fname = self._DoReadFileDtb('060_fdt_update.dts',
1594                                                      update_dtb=True)
1595         dtb = fdt.Fdt(out_dtb_fname)
1596         dtb.Scan()
1597         props = self._GetPropTree(dtb, BASE_DTB_PROPS + REPACK_DTB_PROPS)
1598         self.assertEqual({
1599             'image-pos': 0,
1600             'offset': 0,
1601             '_testing:offset': 32,
1602             '_testing:size': 2,
1603             '_testing:image-pos': 32,
1604             'section@0/u-boot:offset': 0,
1605             'section@0/u-boot:size': len(U_BOOT_DATA),
1606             'section@0/u-boot:image-pos': 0,
1607             'section@0:offset': 0,
1608             'section@0:size': 16,
1609             'section@0:image-pos': 0,
1610
1611             'section@1/u-boot:offset': 0,
1612             'section@1/u-boot:size': len(U_BOOT_DATA),
1613             'section@1/u-boot:image-pos': 16,
1614             'section@1:offset': 16,
1615             'section@1:size': 16,
1616             'section@1:image-pos': 16,
1617             'size': 40
1618         }, props)
1619
1620     def testUpdateFdtBad(self):
1621         """Test that we detect when ProcessFdt never completes"""
1622         with self.assertRaises(ValueError) as e:
1623             self._DoReadFileDtb('061_fdt_update_bad.dts', update_dtb=True)
1624         self.assertIn('Could not complete processing of Fdt: remaining '
1625                       '[<binman.etype._testing.Entry__testing',
1626                         str(e.exception))
1627
1628     def testEntryArgs(self):
1629         """Test passing arguments to entries from the command line"""
1630         entry_args = {
1631             'test-str-arg': 'test1',
1632             'test-int-arg': '456',
1633         }
1634         self._DoReadFileDtb('062_entry_args.dts', entry_args=entry_args)
1635         self.assertIn('image', control.images)
1636         entry = control.images['image'].GetEntries()['_testing']
1637         self.assertEqual('test0', entry.test_str_fdt)
1638         self.assertEqual('test1', entry.test_str_arg)
1639         self.assertEqual(123, entry.test_int_fdt)
1640         self.assertEqual(456, entry.test_int_arg)
1641
1642     def testEntryArgsMissing(self):
1643         """Test missing arguments and properties"""
1644         entry_args = {
1645             'test-int-arg': '456',
1646         }
1647         self._DoReadFileDtb('063_entry_args_missing.dts', entry_args=entry_args)
1648         entry = control.images['image'].GetEntries()['_testing']
1649         self.assertEqual('test0', entry.test_str_fdt)
1650         self.assertEqual(None, entry.test_str_arg)
1651         self.assertEqual(None, entry.test_int_fdt)
1652         self.assertEqual(456, entry.test_int_arg)
1653
1654     def testEntryArgsRequired(self):
1655         """Test missing arguments and properties"""
1656         entry_args = {
1657             'test-int-arg': '456',
1658         }
1659         with self.assertRaises(ValueError) as e:
1660             self._DoReadFileDtb('064_entry_args_required.dts')
1661         self.assertIn("Node '/binman/_testing': "
1662             'Missing required properties/entry args: test-str-arg, '
1663             'test-int-fdt, test-int-arg',
1664             str(e.exception))
1665
1666     def testEntryArgsInvalidFormat(self):
1667         """Test that an invalid entry-argument format is detected"""
1668         args = ['build', '-d', self.TestFile('064_entry_args_required.dts'),
1669                 '-ano-value']
1670         with self.assertRaises(ValueError) as e:
1671             self._DoBinman(*args)
1672         self.assertIn("Invalid entry arguemnt 'no-value'", str(e.exception))
1673
1674     def testEntryArgsInvalidInteger(self):
1675         """Test that an invalid entry-argument integer is detected"""
1676         entry_args = {
1677             'test-int-arg': 'abc',
1678         }
1679         with self.assertRaises(ValueError) as e:
1680             self._DoReadFileDtb('062_entry_args.dts', entry_args=entry_args)
1681         self.assertIn("Node '/binman/_testing': Cannot convert entry arg "
1682                       "'test-int-arg' (value 'abc') to integer",
1683             str(e.exception))
1684
1685     def testEntryArgsInvalidDatatype(self):
1686         """Test that an invalid entry-argument datatype is detected
1687
1688         This test could be written in entry_test.py except that it needs
1689         access to control.entry_args, which seems more than that module should
1690         be able to see.
1691         """
1692         entry_args = {
1693             'test-bad-datatype-arg': '12',
1694         }
1695         with self.assertRaises(ValueError) as e:
1696             self._DoReadFileDtb('065_entry_args_unknown_datatype.dts',
1697                                 entry_args=entry_args)
1698         self.assertIn('GetArg() internal error: Unknown data type ',
1699                       str(e.exception))
1700
1701     def testText(self):
1702         """Test for a text entry type"""
1703         entry_args = {
1704             'test-id': TEXT_DATA,
1705             'test-id2': TEXT_DATA2,
1706             'test-id3': TEXT_DATA3,
1707         }
1708         data, _, _, _ = self._DoReadFileDtb('066_text.dts',
1709                                             entry_args=entry_args)
1710         expected = (tools.to_bytes(TEXT_DATA) +
1711                     tools.get_bytes(0, 8 - len(TEXT_DATA)) +
1712                     tools.to_bytes(TEXT_DATA2) + tools.to_bytes(TEXT_DATA3) +
1713                     b'some text' + b'more text')
1714         self.assertEqual(expected, data)
1715
1716     def testEntryDocs(self):
1717         """Test for creation of entry documentation"""
1718         with test_util.capture_sys_output() as (stdout, stderr):
1719             control.WriteEntryDocs(control.GetEntryModules())
1720         self.assertTrue(len(stdout.getvalue()) > 0)
1721
1722     def testEntryDocsMissing(self):
1723         """Test handling of missing entry documentation"""
1724         with self.assertRaises(ValueError) as e:
1725             with test_util.capture_sys_output() as (stdout, stderr):
1726                 control.WriteEntryDocs(control.GetEntryModules(), 'u_boot')
1727         self.assertIn('Documentation is missing for modules: u_boot',
1728                       str(e.exception))
1729
1730     def testFmap(self):
1731         """Basic test of generation of a flashrom fmap"""
1732         data = self._DoReadFile('067_fmap.dts')
1733         fhdr, fentries = fmap_util.DecodeFmap(data[32:])
1734         expected = (U_BOOT_DATA + tools.get_bytes(ord('!'), 12) +
1735                     U_BOOT_DATA + tools.get_bytes(ord('a'), 12))
1736         self.assertEqual(expected, data[:32])
1737         self.assertEqual(b'__FMAP__', fhdr.signature)
1738         self.assertEqual(1, fhdr.ver_major)
1739         self.assertEqual(0, fhdr.ver_minor)
1740         self.assertEqual(0, fhdr.base)
1741         expect_size = fmap_util.FMAP_HEADER_LEN + fmap_util.FMAP_AREA_LEN * 5
1742         self.assertEqual(16 + 16 + expect_size, fhdr.image_size)
1743         self.assertEqual(b'FMAP', fhdr.name)
1744         self.assertEqual(5, fhdr.nareas)
1745         fiter = iter(fentries)
1746
1747         fentry = next(fiter)
1748         self.assertEqual(b'SECTION0', fentry.name)
1749         self.assertEqual(0, fentry.offset)
1750         self.assertEqual(16, fentry.size)
1751         self.assertEqual(fmap_util.FMAP_AREA_PRESERVE, fentry.flags)
1752
1753         fentry = next(fiter)
1754         self.assertEqual(b'RO_U_BOOT', fentry.name)
1755         self.assertEqual(0, fentry.offset)
1756         self.assertEqual(4, fentry.size)
1757         self.assertEqual(0, fentry.flags)
1758
1759         fentry = next(fiter)
1760         self.assertEqual(b'SECTION1', fentry.name)
1761         self.assertEqual(16, fentry.offset)
1762         self.assertEqual(16, fentry.size)
1763         self.assertEqual(0, fentry.flags)
1764
1765         fentry = next(fiter)
1766         self.assertEqual(b'RW_U_BOOT', fentry.name)
1767         self.assertEqual(16, fentry.offset)
1768         self.assertEqual(4, fentry.size)
1769         self.assertEqual(0, fentry.flags)
1770
1771         fentry = next(fiter)
1772         self.assertEqual(b'FMAP', fentry.name)
1773         self.assertEqual(32, fentry.offset)
1774         self.assertEqual(expect_size, fentry.size)
1775         self.assertEqual(0, fentry.flags)
1776
1777     def testBlobNamedByArg(self):
1778         """Test we can add a blob with the filename coming from an entry arg"""
1779         entry_args = {
1780             'cros-ec-rw-path': 'ecrw.bin',
1781         }
1782         self._DoReadFileDtb('068_blob_named_by_arg.dts', entry_args=entry_args)
1783
1784     def testFill(self):
1785         """Test for an fill entry type"""
1786         data = self._DoReadFile('069_fill.dts')
1787         expected = tools.get_bytes(0xff, 8) + tools.get_bytes(0, 8)
1788         self.assertEqual(expected, data)
1789
1790     def testFillNoSize(self):
1791         """Test for an fill entry type with no size"""
1792         with self.assertRaises(ValueError) as e:
1793             self._DoReadFile('070_fill_no_size.dts')
1794         self.assertIn("'fill' entry is missing properties: size",
1795                       str(e.exception))
1796
1797     def _HandleGbbCommand(self, pipe_list):
1798         """Fake calls to the futility utility"""
1799         if 'futility' in pipe_list[0][0]:
1800             fname = pipe_list[0][-1]
1801             # Append our GBB data to the file, which will happen every time the
1802             # futility command is called.
1803             with open(fname, 'ab') as fd:
1804                 fd.write(GBB_DATA)
1805             return command.CommandResult()
1806
1807     def testGbb(self):
1808         """Test for the Chromium OS Google Binary Block"""
1809         command.test_result = self._HandleGbbCommand
1810         entry_args = {
1811             'keydir': 'devkeys',
1812             'bmpblk': 'bmpblk.bin',
1813         }
1814         data, _, _, _ = self._DoReadFileDtb('071_gbb.dts', entry_args=entry_args)
1815
1816         # Since futility
1817         expected = (GBB_DATA + GBB_DATA + tools.get_bytes(0, 8) +
1818                     tools.get_bytes(0, 0x2180 - 16))
1819         self.assertEqual(expected, data)
1820
1821     def testGbbTooSmall(self):
1822         """Test for the Chromium OS Google Binary Block being large enough"""
1823         with self.assertRaises(ValueError) as e:
1824             self._DoReadFileDtb('072_gbb_too_small.dts')
1825         self.assertIn("Node '/binman/gbb': GBB is too small",
1826                       str(e.exception))
1827
1828     def testGbbNoSize(self):
1829         """Test for the Chromium OS Google Binary Block having a size"""
1830         with self.assertRaises(ValueError) as e:
1831             self._DoReadFileDtb('073_gbb_no_size.dts')
1832         self.assertIn("Node '/binman/gbb': GBB must have a fixed size",
1833                       str(e.exception))
1834
1835     def testGbbMissing(self):
1836         """Test that binman still produces an image if futility is missing"""
1837         entry_args = {
1838             'keydir': 'devkeys',
1839         }
1840         with test_util.capture_sys_output() as (_, stderr):
1841             self._DoTestFile('071_gbb.dts', force_missing_bintools='futility',
1842                              entry_args=entry_args)
1843         err = stderr.getvalue()
1844         self.assertRegex(err, "Image 'image'.*missing bintools.*: futility")
1845
1846     def _HandleVblockCommand(self, pipe_list):
1847         """Fake calls to the futility utility
1848
1849         The expected pipe is:
1850
1851            [('futility', 'vbutil_firmware', '--vblock',
1852              'vblock.vblock', '--keyblock', 'devkeys/firmware.keyblock',
1853              '--signprivate', 'devkeys/firmware_data_key.vbprivk',
1854              '--version', '1', '--fv', 'input.vblock', '--kernelkey',
1855              'devkeys/kernel_subkey.vbpubk', '--flags', '1')]
1856
1857         This writes to the output file (here, 'vblock.vblock'). If
1858         self._hash_data is False, it writes VBLOCK_DATA, else it writes a hash
1859         of the input data (here, 'input.vblock').
1860         """
1861         if 'futility' in pipe_list[0][0]:
1862             fname = pipe_list[0][3]
1863             with open(fname, 'wb') as fd:
1864                 if self._hash_data:
1865                     infile = pipe_list[0][11]
1866                     m = hashlib.sha256()
1867                     data = tools.read_file(infile)
1868                     m.update(data)
1869                     fd.write(m.digest())
1870                 else:
1871                     fd.write(VBLOCK_DATA)
1872
1873             return command.CommandResult()
1874
1875     def testVblock(self):
1876         """Test for the Chromium OS Verified Boot Block"""
1877         self._hash_data = False
1878         command.test_result = self._HandleVblockCommand
1879         entry_args = {
1880             'keydir': 'devkeys',
1881         }
1882         data, _, _, _ = self._DoReadFileDtb('074_vblock.dts',
1883                                             entry_args=entry_args)
1884         expected = U_BOOT_DATA + VBLOCK_DATA + U_BOOT_DTB_DATA
1885         self.assertEqual(expected, data)
1886
1887     def testVblockNoContent(self):
1888         """Test we detect a vblock which has no content to sign"""
1889         with self.assertRaises(ValueError) as e:
1890             self._DoReadFile('075_vblock_no_content.dts')
1891         self.assertIn("Node '/binman/vblock': Collection must have a 'content' "
1892                       'property', str(e.exception))
1893
1894     def testVblockBadPhandle(self):
1895         """Test that we detect a vblock with an invalid phandle in contents"""
1896         with self.assertRaises(ValueError) as e:
1897             self._DoReadFile('076_vblock_bad_phandle.dts')
1898         self.assertIn("Node '/binman/vblock': Cannot find node for phandle "
1899                       '1000', str(e.exception))
1900
1901     def testVblockBadEntry(self):
1902         """Test that we detect an entry that points to a non-entry"""
1903         with self.assertRaises(ValueError) as e:
1904             self._DoReadFile('077_vblock_bad_entry.dts')
1905         self.assertIn("Node '/binman/vblock': Cannot find entry for node "
1906                       "'other'", str(e.exception))
1907
1908     def testVblockContent(self):
1909         """Test that the vblock signs the right data"""
1910         self._hash_data = True
1911         command.test_result = self._HandleVblockCommand
1912         entry_args = {
1913             'keydir': 'devkeys',
1914         }
1915         data = self._DoReadFileDtb(
1916             '189_vblock_content.dts', use_real_dtb=True, update_dtb=True,
1917             entry_args=entry_args)[0]
1918         hashlen = 32  # SHA256 hash is 32 bytes
1919         self.assertEqual(U_BOOT_DATA, data[:len(U_BOOT_DATA)])
1920         hashval = data[-hashlen:]
1921         dtb = data[len(U_BOOT_DATA):-hashlen]
1922
1923         expected_data = U_BOOT_DATA + dtb
1924
1925         # The hashval should be a hash of the dtb
1926         m = hashlib.sha256()
1927         m.update(expected_data)
1928         expected_hashval = m.digest()
1929         self.assertEqual(expected_hashval, hashval)
1930
1931     def testVblockMissing(self):
1932         """Test that binman still produces an image if futility is missing"""
1933         entry_args = {
1934             'keydir': 'devkeys',
1935         }
1936         with test_util.capture_sys_output() as (_, stderr):
1937             self._DoTestFile('074_vblock.dts',
1938                              force_missing_bintools='futility',
1939                              entry_args=entry_args)
1940         err = stderr.getvalue()
1941         self.assertRegex(err, "Image 'image'.*missing bintools.*: futility")
1942
1943     def testTpl(self):
1944         """Test that an image with TPL and its device tree can be created"""
1945         # ELF file with a '__bss_size' symbol
1946         self._SetupTplElf()
1947         data = self._DoReadFile('078_u_boot_tpl.dts')
1948         self.assertEqual(U_BOOT_TPL_DATA + U_BOOT_TPL_DTB_DATA, data)
1949
1950     def testUsesPos(self):
1951         """Test that the 'pos' property cannot be used anymore"""
1952         with self.assertRaises(ValueError) as e:
1953            data = self._DoReadFile('079_uses_pos.dts')
1954         self.assertIn("Node '/binman/u-boot': Please use 'offset' instead of "
1955                       "'pos'", str(e.exception))
1956
1957     def testFillZero(self):
1958         """Test for an fill entry type with a size of 0"""
1959         data = self._DoReadFile('080_fill_empty.dts')
1960         self.assertEqual(tools.get_bytes(0, 16), data)
1961
1962     def testTextMissing(self):
1963         """Test for a text entry type where there is no text"""
1964         with self.assertRaises(ValueError) as e:
1965             self._DoReadFileDtb('066_text.dts',)
1966         self.assertIn("Node '/binman/text': No value provided for text label "
1967                       "'test-id'", str(e.exception))
1968
1969     def testPackStart16Tpl(self):
1970         """Test that an image with an x86 start16 TPL region can be created"""
1971         data = self._DoReadFile('081_x86_start16_tpl.dts')
1972         self.assertEqual(X86_START16_TPL_DATA, data[:len(X86_START16_TPL_DATA)])
1973
1974     def testSelectImage(self):
1975         """Test that we can select which images to build"""
1976         expected = 'Skipping images: image1'
1977
1978         # We should only get the expected message in verbose mode
1979         for verbosity in (0, 2):
1980             with test_util.capture_sys_output() as (stdout, stderr):
1981                 retcode = self._DoTestFile('006_dual_image.dts',
1982                                            verbosity=verbosity,
1983                                            images=['image2'])
1984             self.assertEqual(0, retcode)
1985             if verbosity:
1986                 self.assertIn(expected, stdout.getvalue())
1987             else:
1988                 self.assertNotIn(expected, stdout.getvalue())
1989
1990             self.assertFalse(os.path.exists(tools.get_output_filename('image1.bin')))
1991             self.assertTrue(os.path.exists(tools.get_output_filename('image2.bin')))
1992             self._CleanupOutputDir()
1993
1994     def testUpdateFdtAll(self):
1995         """Test that all device trees are updated with offset/size info"""
1996         self._SetupSplElf()
1997         self._SetupTplElf()
1998         data = self._DoReadFileRealDtb('082_fdt_update_all.dts')
1999
2000         base_expected = {
2001             'offset': 0,
2002             'image-pos': 0,
2003             'size': 2320,
2004             'section:offset': 0,
2005             'section:image-pos': 0,
2006             'section:size': 565,
2007             'section/u-boot-dtb:offset': 0,
2008             'section/u-boot-dtb:image-pos': 0,
2009             'section/u-boot-dtb:size': 565,
2010             'u-boot-spl-dtb:offset': 565,
2011             'u-boot-spl-dtb:image-pos': 565,
2012             'u-boot-spl-dtb:size': 585,
2013             'u-boot-tpl-dtb:offset': 1150,
2014             'u-boot-tpl-dtb:image-pos': 1150,
2015             'u-boot-tpl-dtb:size': 585,
2016             'u-boot-vpl-dtb:image-pos': 1735,
2017             'u-boot-vpl-dtb:offset': 1735,
2018             'u-boot-vpl-dtb:size': 585,
2019         }
2020
2021         # We expect three device-tree files in the output, one after the other.
2022         # Read them in sequence. We look for an 'spl' property in the SPL tree,
2023         # and 'tpl' in the TPL tree, to make sure they are distinct from the
2024         # main U-Boot tree. All three should have the same postions and offset.
2025         start = 0
2026         self.maxDiff = None
2027         for item in ['', 'spl', 'tpl', 'vpl']:
2028             dtb = fdt.Fdt.FromData(data[start:])
2029             dtb.Scan()
2030             props = self._GetPropTree(dtb, BASE_DTB_PROPS + REPACK_DTB_PROPS +
2031                                       ['spl', 'tpl', 'vpl'])
2032             expected = dict(base_expected)
2033             if item:
2034                 expected[item] = 0
2035             self.assertEqual(expected, props)
2036             start += dtb._fdt_obj.totalsize()
2037
2038     def testUpdateFdtOutput(self):
2039         """Test that output DTB files are updated"""
2040         try:
2041             data, dtb_data, _, _ = self._DoReadFileDtb('082_fdt_update_all.dts',
2042                     use_real_dtb=True, update_dtb=True, reset_dtbs=False)
2043
2044             # Unfortunately, compiling a source file always results in a file
2045             # called source.dtb (see fdt_util.EnsureCompiled()). The test
2046             # source file (e.g. test/075_fdt_update_all.dts) thus does not enter
2047             # binman as a file called u-boot.dtb. To fix this, copy the file
2048             # over to the expected place.
2049             start = 0
2050             for fname in ['u-boot.dtb.out', 'spl/u-boot-spl.dtb.out',
2051                           'tpl/u-boot-tpl.dtb.out', 'vpl/u-boot-vpl.dtb.out']:
2052                 dtb = fdt.Fdt.FromData(data[start:])
2053                 size = dtb._fdt_obj.totalsize()
2054                 pathname = tools.get_output_filename(os.path.split(fname)[1])
2055                 outdata = tools.read_file(pathname)
2056                 name = os.path.split(fname)[0]
2057
2058                 if name:
2059                     orig_indata = self._GetDtbContentsForSpls(dtb_data, name)
2060                 else:
2061                     orig_indata = dtb_data
2062                 self.assertNotEqual(outdata, orig_indata,
2063                         "Expected output file '%s' be updated" % pathname)
2064                 self.assertEqual(outdata, data[start:start + size],
2065                         "Expected output file '%s' to match output image" %
2066                         pathname)
2067                 start += size
2068         finally:
2069             self._ResetDtbs()
2070
2071     def _decompress(self, data):
2072         bintool = self.comp_bintools['lz4']
2073         return bintool.decompress(data)
2074
2075     def testCompress(self):
2076         """Test compression of blobs"""
2077         self._CheckLz4()
2078         data, _, _, out_dtb_fname = self._DoReadFileDtb('083_compress.dts',
2079                                             use_real_dtb=True, update_dtb=True)
2080         dtb = fdt.Fdt(out_dtb_fname)
2081         dtb.Scan()
2082         props = self._GetPropTree(dtb, ['size', 'uncomp-size'])
2083         orig = self._decompress(data)
2084         self.assertEquals(COMPRESS_DATA, orig)
2085
2086         # Do a sanity check on various fields
2087         image = control.images['image']
2088         entries = image.GetEntries()
2089         self.assertEqual(1, len(entries))
2090
2091         entry = entries['blob']
2092         self.assertEqual(COMPRESS_DATA, entry.uncomp_data)
2093         self.assertEqual(len(COMPRESS_DATA), entry.uncomp_size)
2094         orig = self._decompress(entry.data)
2095         self.assertEqual(orig, entry.uncomp_data)
2096
2097         self.assertEqual(image.data, entry.data)
2098
2099         expected = {
2100             'blob:uncomp-size': len(COMPRESS_DATA),
2101             'blob:size': len(data),
2102             'size': len(data),
2103             }
2104         self.assertEqual(expected, props)
2105
2106     def testFiles(self):
2107         """Test bringing in multiple files"""
2108         data = self._DoReadFile('084_files.dts')
2109         self.assertEqual(FILES_DATA, data)
2110
2111     def testFilesCompress(self):
2112         """Test bringing in multiple files and compressing them"""
2113         self._CheckLz4()
2114         data = self._DoReadFile('085_files_compress.dts')
2115
2116         image = control.images['image']
2117         entries = image.GetEntries()
2118         files = entries['files']
2119         entries = files._entries
2120
2121         orig = b''
2122         for i in range(1, 3):
2123             key = '%d.dat' % i
2124             start = entries[key].image_pos
2125             len = entries[key].size
2126             chunk = data[start:start + len]
2127             orig += self._decompress(chunk)
2128
2129         self.assertEqual(FILES_DATA, orig)
2130
2131     def testFilesMissing(self):
2132         """Test missing files"""
2133         with self.assertRaises(ValueError) as e:
2134             data = self._DoReadFile('086_files_none.dts')
2135         self.assertIn("Node '/binman/files': Pattern \'files/*.none\' matched "
2136                       'no files', str(e.exception))
2137
2138     def testFilesNoPattern(self):
2139         """Test missing files"""
2140         with self.assertRaises(ValueError) as e:
2141             data = self._DoReadFile('087_files_no_pattern.dts')
2142         self.assertIn("Node '/binman/files': Missing 'pattern' property",
2143                       str(e.exception))
2144
2145     def testExtendSize(self):
2146         """Test an extending entry"""
2147         data, _, map_data, _ = self._DoReadFileDtb('088_extend_size.dts',
2148                                                    map=True)
2149         expect = (tools.get_bytes(ord('a'), 8) + U_BOOT_DATA +
2150                   MRC_DATA + tools.get_bytes(ord('b'), 1) + U_BOOT_DATA +
2151                   tools.get_bytes(ord('c'), 8) + U_BOOT_DATA +
2152                   tools.get_bytes(ord('d'), 8))
2153         self.assertEqual(expect, data)
2154         self.assertEqual('''ImagePos    Offset      Size  Name
2155 00000000  00000000  00000028  image
2156 00000000   00000000  00000008  fill
2157 00000008   00000008  00000004  u-boot
2158 0000000c   0000000c  00000004  section
2159 0000000c    00000000  00000003  intel-mrc
2160 00000010   00000010  00000004  u-boot2
2161 00000014   00000014  0000000c  section2
2162 00000014    00000000  00000008  fill
2163 0000001c    00000008  00000004  u-boot
2164 00000020   00000020  00000008  fill2
2165 ''', map_data)
2166
2167     def testExtendSizeBad(self):
2168         """Test an extending entry which fails to provide contents"""
2169         with test_util.capture_sys_output() as (stdout, stderr):
2170             with self.assertRaises(ValueError) as e:
2171                 self._DoReadFileDtb('089_extend_size_bad.dts', map=True)
2172         self.assertIn("Node '/binman/_testing': Cannot obtain contents when "
2173                       'expanding entry', str(e.exception))
2174
2175     def testHash(self):
2176         """Test hashing of the contents of an entry"""
2177         _, _, _, out_dtb_fname = self._DoReadFileDtb('090_hash.dts',
2178                 use_real_dtb=True, update_dtb=True)
2179         dtb = fdt.Fdt(out_dtb_fname)
2180         dtb.Scan()
2181         hash_node = dtb.GetNode('/binman/u-boot/hash').props['value']
2182         m = hashlib.sha256()
2183         m.update(U_BOOT_DATA)
2184         self.assertEqual(m.digest(), b''.join(hash_node.value))
2185
2186     def testHashNoAlgo(self):
2187         with self.assertRaises(ValueError) as e:
2188             self._DoReadFileDtb('091_hash_no_algo.dts', update_dtb=True)
2189         self.assertIn("Node \'/binman/u-boot\': Missing \'algo\' property for "
2190                       'hash node', str(e.exception))
2191
2192     def testHashBadAlgo(self):
2193         with self.assertRaises(ValueError) as e:
2194             self._DoReadFileDtb('092_hash_bad_algo.dts', update_dtb=True)
2195         self.assertIn("Node '/binman/u-boot': Unknown hash algorithm 'invalid'",
2196                       str(e.exception))
2197
2198     def testHashSection(self):
2199         """Test hashing of the contents of an entry"""
2200         _, _, _, out_dtb_fname = self._DoReadFileDtb('099_hash_section.dts',
2201                 use_real_dtb=True, update_dtb=True)
2202         dtb = fdt.Fdt(out_dtb_fname)
2203         dtb.Scan()
2204         hash_node = dtb.GetNode('/binman/section/hash').props['value']
2205         m = hashlib.sha256()
2206         m.update(U_BOOT_DATA)
2207         m.update(tools.get_bytes(ord('a'), 16))
2208         self.assertEqual(m.digest(), b''.join(hash_node.value))
2209
2210     def testPackUBootTplMicrocode(self):
2211         """Test that x86 microcode can be handled correctly in TPL
2212
2213         We expect to see the following in the image, in order:
2214             u-boot-tpl-nodtb.bin with a microcode pointer inserted at the correct
2215                 place
2216             u-boot-tpl.dtb with the microcode removed
2217             the microcode
2218         """
2219         self._SetupTplElf('u_boot_ucode_ptr')
2220         first, pos_and_size = self._RunMicrocodeTest('093_x86_tpl_ucode.dts',
2221                                                      U_BOOT_TPL_NODTB_DATA)
2222         self.assertEqual(b'tplnodtb with microc' + pos_and_size +
2223                          b'ter somewhere in here', first)
2224
2225     def testFmapX86(self):
2226         """Basic test of generation of a flashrom fmap"""
2227         data = self._DoReadFile('094_fmap_x86.dts')
2228         fhdr, fentries = fmap_util.DecodeFmap(data[32:])
2229         expected = U_BOOT_DATA + MRC_DATA + tools.get_bytes(ord('a'), 32 - 7)
2230         self.assertEqual(expected, data[:32])
2231         fhdr, fentries = fmap_util.DecodeFmap(data[32:])
2232
2233         self.assertEqual(0x100, fhdr.image_size)
2234
2235         self.assertEqual(0, fentries[0].offset)
2236         self.assertEqual(4, fentries[0].size)
2237         self.assertEqual(b'U_BOOT', fentries[0].name)
2238
2239         self.assertEqual(4, fentries[1].offset)
2240         self.assertEqual(3, fentries[1].size)
2241         self.assertEqual(b'INTEL_MRC', fentries[1].name)
2242
2243         self.assertEqual(32, fentries[2].offset)
2244         self.assertEqual(fmap_util.FMAP_HEADER_LEN +
2245                          fmap_util.FMAP_AREA_LEN * 3, fentries[2].size)
2246         self.assertEqual(b'FMAP', fentries[2].name)
2247
2248     def testFmapX86Section(self):
2249         """Basic test of generation of a flashrom fmap"""
2250         data = self._DoReadFile('095_fmap_x86_section.dts')
2251         expected = U_BOOT_DATA + MRC_DATA + tools.get_bytes(ord('b'), 32 - 7)
2252         self.assertEqual(expected, data[:32])
2253         fhdr, fentries = fmap_util.DecodeFmap(data[36:])
2254
2255         self.assertEqual(0x180, fhdr.image_size)
2256         expect_size = fmap_util.FMAP_HEADER_LEN + fmap_util.FMAP_AREA_LEN * 4
2257         fiter = iter(fentries)
2258
2259         fentry = next(fiter)
2260         self.assertEqual(b'U_BOOT', fentry.name)
2261         self.assertEqual(0, fentry.offset)
2262         self.assertEqual(4, fentry.size)
2263
2264         fentry = next(fiter)
2265         self.assertEqual(b'SECTION', fentry.name)
2266         self.assertEqual(4, fentry.offset)
2267         self.assertEqual(0x20 + expect_size, fentry.size)
2268
2269         fentry = next(fiter)
2270         self.assertEqual(b'INTEL_MRC', fentry.name)
2271         self.assertEqual(4, fentry.offset)
2272         self.assertEqual(3, fentry.size)
2273
2274         fentry = next(fiter)
2275         self.assertEqual(b'FMAP', fentry.name)
2276         self.assertEqual(36, fentry.offset)
2277         self.assertEqual(expect_size, fentry.size)
2278
2279     def testElf(self):
2280         """Basic test of ELF entries"""
2281         self._SetupSplElf()
2282         self._SetupTplElf()
2283         with open(self.ElfTestFile('bss_data'), 'rb') as fd:
2284             TestFunctional._MakeInputFile('-boot', fd.read())
2285         data = self._DoReadFile('096_elf.dts')
2286
2287     def testElfStrip(self):
2288         """Basic test of ELF entries"""
2289         self._SetupSplElf()
2290         with open(self.ElfTestFile('bss_data'), 'rb') as fd:
2291             TestFunctional._MakeInputFile('-boot', fd.read())
2292         data = self._DoReadFile('097_elf_strip.dts')
2293
2294     def testPackOverlapMap(self):
2295         """Test that overlapping regions are detected"""
2296         with test_util.capture_sys_output() as (stdout, stderr):
2297             with self.assertRaises(ValueError) as e:
2298                 self._DoTestFile('014_pack_overlap.dts', map=True)
2299         map_fname = tools.get_output_filename('image.map')
2300         self.assertEqual("Wrote map file '%s' to show errors\n" % map_fname,
2301                          stdout.getvalue())
2302
2303         # We should not get an inmage, but there should be a map file
2304         self.assertFalse(os.path.exists(tools.get_output_filename('image.bin')))
2305         self.assertTrue(os.path.exists(map_fname))
2306         map_data = tools.read_file(map_fname, binary=False)
2307         self.assertEqual('''ImagePos    Offset      Size  Name
2308 <none>    00000000  00000008  image
2309 <none>     00000000  00000004  u-boot
2310 <none>     00000003  00000004  u-boot-align
2311 ''', map_data)
2312
2313     def testPackRefCode(self):
2314         """Test that an image with an Intel Reference code binary works"""
2315         data = self._DoReadFile('100_intel_refcode.dts')
2316         self.assertEqual(REFCODE_DATA, data[:len(REFCODE_DATA)])
2317
2318     def testSectionOffset(self):
2319         """Tests use of a section with an offset"""
2320         data, _, map_data, _ = self._DoReadFileDtb('101_sections_offset.dts',
2321                                                    map=True)
2322         self.assertEqual('''ImagePos    Offset      Size  Name
2323 00000000  00000000  00000038  image
2324 00000004   00000004  00000010  section@0
2325 00000004    00000000  00000004  u-boot
2326 00000018   00000018  00000010  section@1
2327 00000018    00000000  00000004  u-boot
2328 0000002c   0000002c  00000004  section@2
2329 0000002c    00000000  00000004  u-boot
2330 ''', map_data)
2331         self.assertEqual(data,
2332                          tools.get_bytes(0x26, 4) + U_BOOT_DATA +
2333                              tools.get_bytes(0x21, 12) +
2334                          tools.get_bytes(0x26, 4) + U_BOOT_DATA +
2335                              tools.get_bytes(0x61, 12) +
2336                          tools.get_bytes(0x26, 4) + U_BOOT_DATA +
2337                              tools.get_bytes(0x26, 8))
2338
2339     def testCbfsRaw(self):
2340         """Test base handling of a Coreboot Filesystem (CBFS)
2341
2342         The exact contents of the CBFS is verified by similar tests in
2343         cbfs_util_test.py. The tests here merely check that the files added to
2344         the CBFS can be found in the final image.
2345         """
2346         data = self._DoReadFile('102_cbfs_raw.dts')
2347         size = 0xb0
2348
2349         cbfs = cbfs_util.CbfsReader(data)
2350         self.assertEqual(size, cbfs.rom_size)
2351
2352         self.assertIn('u-boot-dtb', cbfs.files)
2353         cfile = cbfs.files['u-boot-dtb']
2354         self.assertEqual(U_BOOT_DTB_DATA, cfile.data)
2355
2356     def testCbfsArch(self):
2357         """Test on non-x86 architecture"""
2358         data = self._DoReadFile('103_cbfs_raw_ppc.dts')
2359         size = 0x100
2360
2361         cbfs = cbfs_util.CbfsReader(data)
2362         self.assertEqual(size, cbfs.rom_size)
2363
2364         self.assertIn('u-boot-dtb', cbfs.files)
2365         cfile = cbfs.files['u-boot-dtb']
2366         self.assertEqual(U_BOOT_DTB_DATA, cfile.data)
2367
2368     def testCbfsStage(self):
2369         """Tests handling of a Coreboot Filesystem (CBFS)"""
2370         if not elf.ELF_TOOLS:
2371             self.skipTest('Python elftools not available')
2372         elf_fname = os.path.join(self._indir, 'cbfs-stage.elf')
2373         elf.MakeElf(elf_fname, U_BOOT_DATA, U_BOOT_DTB_DATA)
2374         size = 0xb0
2375
2376         data = self._DoReadFile('104_cbfs_stage.dts')
2377         cbfs = cbfs_util.CbfsReader(data)
2378         self.assertEqual(size, cbfs.rom_size)
2379
2380         self.assertIn('u-boot', cbfs.files)
2381         cfile = cbfs.files['u-boot']
2382         self.assertEqual(U_BOOT_DATA + U_BOOT_DTB_DATA, cfile.data)
2383
2384     def testCbfsRawCompress(self):
2385         """Test handling of compressing raw files"""
2386         self._CheckLz4()
2387         data = self._DoReadFile('105_cbfs_raw_compress.dts')
2388         size = 0x140
2389
2390         cbfs = cbfs_util.CbfsReader(data)
2391         self.assertIn('u-boot', cbfs.files)
2392         cfile = cbfs.files['u-boot']
2393         self.assertEqual(COMPRESS_DATA, cfile.data)
2394
2395     def testCbfsBadArch(self):
2396         """Test handling of a bad architecture"""
2397         with self.assertRaises(ValueError) as e:
2398             self._DoReadFile('106_cbfs_bad_arch.dts')
2399         self.assertIn("Invalid architecture 'bad-arch'", str(e.exception))
2400
2401     def testCbfsNoSize(self):
2402         """Test handling of a missing size property"""
2403         with self.assertRaises(ValueError) as e:
2404             self._DoReadFile('107_cbfs_no_size.dts')
2405         self.assertIn('entry must have a size property', str(e.exception))
2406
2407     def testCbfsNoContents(self):
2408         """Test handling of a CBFS entry which does not provide contentsy"""
2409         with self.assertRaises(ValueError) as e:
2410             self._DoReadFile('108_cbfs_no_contents.dts')
2411         self.assertIn('Could not complete processing of contents',
2412                       str(e.exception))
2413
2414     def testCbfsBadCompress(self):
2415         """Test handling of a bad architecture"""
2416         with self.assertRaises(ValueError) as e:
2417             self._DoReadFile('109_cbfs_bad_compress.dts')
2418         self.assertIn("Invalid compression in 'u-boot': 'invalid-algo'",
2419                       str(e.exception))
2420
2421     def testCbfsNamedEntries(self):
2422         """Test handling of named entries"""
2423         data = self._DoReadFile('110_cbfs_name.dts')
2424
2425         cbfs = cbfs_util.CbfsReader(data)
2426         self.assertIn('FRED', cbfs.files)
2427         cfile1 = cbfs.files['FRED']
2428         self.assertEqual(U_BOOT_DATA, cfile1.data)
2429
2430         self.assertIn('hello', cbfs.files)
2431         cfile2 = cbfs.files['hello']
2432         self.assertEqual(U_BOOT_DTB_DATA, cfile2.data)
2433
2434     def _SetupIfwi(self, fname):
2435         """Set up to run an IFWI test
2436
2437         Args:
2438             fname: Filename of input file to provide (fitimage.bin or ifwi.bin)
2439         """
2440         self._SetupSplElf()
2441         self._SetupTplElf()
2442
2443         # Intel Integrated Firmware Image (IFWI) file
2444         with gzip.open(self.TestFile('%s.gz' % fname), 'rb') as fd:
2445             data = fd.read()
2446         TestFunctional._MakeInputFile(fname,data)
2447
2448     def _CheckIfwi(self, data):
2449         """Check that an image with an IFWI contains the correct output
2450
2451         Args:
2452             data: Conents of output file
2453         """
2454         expected_desc = tools.read_file(self.TestFile('descriptor.bin'))
2455         if data[:0x1000] != expected_desc:
2456             self.fail('Expected descriptor binary at start of image')
2457
2458         # We expect to find the TPL wil in subpart IBBP entry IBBL
2459         image_fname = tools.get_output_filename('image.bin')
2460         tpl_fname = tools.get_output_filename('tpl.out')
2461         ifwitool = bintool.Bintool.create('ifwitool')
2462         ifwitool.extract(image_fname, 'IBBP', 'IBBL', tpl_fname)
2463
2464         tpl_data = tools.read_file(tpl_fname)
2465         self.assertEqual(U_BOOT_TPL_DATA, tpl_data[:len(U_BOOT_TPL_DATA)])
2466
2467     def testPackX86RomIfwi(self):
2468         """Test that an x86 ROM with Integrated Firmware Image can be created"""
2469         self._SetupIfwi('fitimage.bin')
2470         data = self._DoReadFile('111_x86_rom_ifwi.dts')
2471         self._CheckIfwi(data)
2472
2473     def testPackX86RomIfwiNoDesc(self):
2474         """Test that an x86 ROM with IFWI can be created from an ifwi.bin file"""
2475         self._SetupIfwi('ifwi.bin')
2476         data = self._DoReadFile('112_x86_rom_ifwi_nodesc.dts')
2477         self._CheckIfwi(data)
2478
2479     def testPackX86RomIfwiNoData(self):
2480         """Test that an x86 ROM with IFWI handles missing data"""
2481         self._SetupIfwi('ifwi.bin')
2482         with self.assertRaises(ValueError) as e:
2483             data = self._DoReadFile('113_x86_rom_ifwi_nodata.dts')
2484         self.assertIn('Could not complete processing of contents',
2485                       str(e.exception))
2486
2487     def testIfwiMissing(self):
2488         """Test that binman still produces an image if ifwitool is missing"""
2489         self._SetupIfwi('fitimage.bin')
2490         with test_util.capture_sys_output() as (_, stderr):
2491             self._DoTestFile('111_x86_rom_ifwi.dts',
2492                              force_missing_bintools='ifwitool')
2493         err = stderr.getvalue()
2494         self.assertRegex(err,
2495                          "Image 'image'.*missing bintools.*: ifwitool")
2496
2497     def testCbfsOffset(self):
2498         """Test a CBFS with files at particular offsets
2499
2500         Like all CFBS tests, this is just checking the logic that calls
2501         cbfs_util. See cbfs_util_test for fully tests (e.g. test_cbfs_offset()).
2502         """
2503         data = self._DoReadFile('114_cbfs_offset.dts')
2504         size = 0x200
2505
2506         cbfs = cbfs_util.CbfsReader(data)
2507         self.assertEqual(size, cbfs.rom_size)
2508
2509         self.assertIn('u-boot', cbfs.files)
2510         cfile = cbfs.files['u-boot']
2511         self.assertEqual(U_BOOT_DATA, cfile.data)
2512         self.assertEqual(0x40, cfile.cbfs_offset)
2513
2514         self.assertIn('u-boot-dtb', cbfs.files)
2515         cfile2 = cbfs.files['u-boot-dtb']
2516         self.assertEqual(U_BOOT_DTB_DATA, cfile2.data)
2517         self.assertEqual(0x140, cfile2.cbfs_offset)
2518
2519     def testFdtmap(self):
2520         """Test an FDT map can be inserted in the image"""
2521         data = self.data = self._DoReadFileRealDtb('115_fdtmap.dts')
2522         fdtmap_data = data[len(U_BOOT_DATA):]
2523         magic = fdtmap_data[:8]
2524         self.assertEqual(b'_FDTMAP_', magic)
2525         self.assertEqual(tools.get_bytes(0, 8), fdtmap_data[8:16])
2526
2527         fdt_data = fdtmap_data[16:]
2528         dtb = fdt.Fdt.FromData(fdt_data)
2529         dtb.Scan()
2530         props = self._GetPropTree(dtb, BASE_DTB_PROPS, prefix='/')
2531         self.assertEqual({
2532             'image-pos': 0,
2533             'offset': 0,
2534             'u-boot:offset': 0,
2535             'u-boot:size': len(U_BOOT_DATA),
2536             'u-boot:image-pos': 0,
2537             'fdtmap:image-pos': 4,
2538             'fdtmap:offset': 4,
2539             'fdtmap:size': len(fdtmap_data),
2540             'size': len(data),
2541         }, props)
2542
2543     def testFdtmapNoMatch(self):
2544         """Check handling of an FDT map when the section cannot be found"""
2545         self.data = self._DoReadFileRealDtb('115_fdtmap.dts')
2546
2547         # Mangle the section name, which should cause a mismatch between the
2548         # correct FDT path and the one expected by the section
2549         image = control.images['image']
2550         image._node.path += '-suffix'
2551         entries = image.GetEntries()
2552         fdtmap = entries['fdtmap']
2553         with self.assertRaises(ValueError) as e:
2554             fdtmap._GetFdtmap()
2555         self.assertIn("Cannot locate node for path '/binman-suffix'",
2556                       str(e.exception))
2557
2558     def testFdtmapHeader(self):
2559         """Test an FDT map and image header can be inserted in the image"""
2560         data = self.data = self._DoReadFileRealDtb('116_fdtmap_hdr.dts')
2561         fdtmap_pos = len(U_BOOT_DATA)
2562         fdtmap_data = data[fdtmap_pos:]
2563         fdt_data = fdtmap_data[16:]
2564         dtb = fdt.Fdt.FromData(fdt_data)
2565         fdt_size = dtb.GetFdtObj().totalsize()
2566         hdr_data = data[-8:]
2567         self.assertEqual(b'BinM', hdr_data[:4])
2568         offset = struct.unpack('<I', hdr_data[4:])[0] & 0xffffffff
2569         self.assertEqual(fdtmap_pos - 0x400, offset - (1 << 32))
2570
2571     def testFdtmapHeaderStart(self):
2572         """Test an image header can be inserted at the image start"""
2573         data = self.data = self._DoReadFileRealDtb('117_fdtmap_hdr_start.dts')
2574         fdtmap_pos = 0x100 + len(U_BOOT_DATA)
2575         hdr_data = data[:8]
2576         self.assertEqual(b'BinM', hdr_data[:4])
2577         offset = struct.unpack('<I', hdr_data[4:])[0]
2578         self.assertEqual(fdtmap_pos, offset)
2579
2580     def testFdtmapHeaderPos(self):
2581         """Test an image header can be inserted at a chosen position"""
2582         data = self.data = self._DoReadFileRealDtb('118_fdtmap_hdr_pos.dts')
2583         fdtmap_pos = 0x100 + len(U_BOOT_DATA)
2584         hdr_data = data[0x80:0x88]
2585         self.assertEqual(b'BinM', hdr_data[:4])
2586         offset = struct.unpack('<I', hdr_data[4:])[0]
2587         self.assertEqual(fdtmap_pos, offset)
2588
2589     def testHeaderMissingFdtmap(self):
2590         """Test an image header requires an fdtmap"""
2591         with self.assertRaises(ValueError) as e:
2592             self.data = self._DoReadFileRealDtb('119_fdtmap_hdr_missing.dts')
2593         self.assertIn("'image_header' section must have an 'fdtmap' sibling",
2594                       str(e.exception))
2595
2596     def testHeaderNoLocation(self):
2597         """Test an image header with a no specified location is detected"""
2598         with self.assertRaises(ValueError) as e:
2599             self.data = self._DoReadFileRealDtb('120_hdr_no_location.dts')
2600         self.assertIn("Invalid location 'None', expected 'start' or 'end'",
2601                       str(e.exception))
2602
2603     def testEntryExpand(self):
2604         """Test extending an entry after it is packed"""
2605         data = self._DoReadFile('121_entry_extend.dts')
2606         self.assertEqual(b'aaa', data[:3])
2607         self.assertEqual(U_BOOT_DATA, data[3:3 + len(U_BOOT_DATA)])
2608         self.assertEqual(b'aaa', data[-3:])
2609
2610     def testEntryExtendBad(self):
2611         """Test extending an entry after it is packed, twice"""
2612         with self.assertRaises(ValueError) as e:
2613             self._DoReadFile('122_entry_extend_twice.dts')
2614         self.assertIn("Image '/binman': Entries changed size after packing",
2615                       str(e.exception))
2616
2617     def testEntryExtendSection(self):
2618         """Test extending an entry within a section after it is packed"""
2619         data = self._DoReadFile('123_entry_extend_section.dts')
2620         self.assertEqual(b'aaa', data[:3])
2621         self.assertEqual(U_BOOT_DATA, data[3:3 + len(U_BOOT_DATA)])
2622         self.assertEqual(b'aaa', data[-3:])
2623
2624     def testCompressDtb(self):
2625         """Test that compress of device-tree files is supported"""
2626         self._CheckLz4()
2627         data = self.data = self._DoReadFileRealDtb('124_compress_dtb.dts')
2628         self.assertEqual(U_BOOT_DATA, data[:len(U_BOOT_DATA)])
2629         comp_data = data[len(U_BOOT_DATA):]
2630         orig = self._decompress(comp_data)
2631         dtb = fdt.Fdt.FromData(orig)
2632         dtb.Scan()
2633         props = self._GetPropTree(dtb, ['size', 'uncomp-size'])
2634         expected = {
2635             'u-boot:size': len(U_BOOT_DATA),
2636             'u-boot-dtb:uncomp-size': len(orig),
2637             'u-boot-dtb:size': len(comp_data),
2638             'size': len(data),
2639             }
2640         self.assertEqual(expected, props)
2641
2642     def testCbfsUpdateFdt(self):
2643         """Test that we can update the device tree with CBFS offset/size info"""
2644         self._CheckLz4()
2645         data, _, _, out_dtb_fname = self._DoReadFileDtb('125_cbfs_update.dts',
2646                                                         update_dtb=True)
2647         dtb = fdt.Fdt(out_dtb_fname)
2648         dtb.Scan()
2649         props = self._GetPropTree(dtb, BASE_DTB_PROPS + ['uncomp-size'])
2650         del props['cbfs/u-boot:size']
2651         self.assertEqual({
2652             'offset': 0,
2653             'size': len(data),
2654             'image-pos': 0,
2655             'cbfs:offset': 0,
2656             'cbfs:size': len(data),
2657             'cbfs:image-pos': 0,
2658             'cbfs/u-boot:offset': 0x38,
2659             'cbfs/u-boot:uncomp-size': len(U_BOOT_DATA),
2660             'cbfs/u-boot:image-pos': 0x38,
2661             'cbfs/u-boot-dtb:offset': 0xb8,
2662             'cbfs/u-boot-dtb:size': len(U_BOOT_DATA),
2663             'cbfs/u-boot-dtb:image-pos': 0xb8,
2664             }, props)
2665
2666     def testCbfsBadType(self):
2667         """Test an image header with a no specified location is detected"""
2668         with self.assertRaises(ValueError) as e:
2669             self._DoReadFile('126_cbfs_bad_type.dts')
2670         self.assertIn("Unknown cbfs-type 'badtype'", str(e.exception))
2671
2672     def testList(self):
2673         """Test listing the files in an image"""
2674         self._CheckLz4()
2675         data = self._DoReadFile('127_list.dts')
2676         image = control.images['image']
2677         entries = image.BuildEntryList()
2678         self.assertEqual(7, len(entries))
2679
2680         ent = entries[0]
2681         self.assertEqual(0, ent.indent)
2682         self.assertEqual('image', ent.name)
2683         self.assertEqual('section', ent.etype)
2684         self.assertEqual(len(data), ent.size)
2685         self.assertEqual(0, ent.image_pos)
2686         self.assertEqual(None, ent.uncomp_size)
2687         self.assertEqual(0, ent.offset)
2688
2689         ent = entries[1]
2690         self.assertEqual(1, ent.indent)
2691         self.assertEqual('u-boot', ent.name)
2692         self.assertEqual('u-boot', ent.etype)
2693         self.assertEqual(len(U_BOOT_DATA), ent.size)
2694         self.assertEqual(0, ent.image_pos)
2695         self.assertEqual(None, ent.uncomp_size)
2696         self.assertEqual(0, ent.offset)
2697
2698         ent = entries[2]
2699         self.assertEqual(1, ent.indent)
2700         self.assertEqual('section', ent.name)
2701         self.assertEqual('section', ent.etype)
2702         section_size = ent.size
2703         self.assertEqual(0x100, ent.image_pos)
2704         self.assertEqual(None, ent.uncomp_size)
2705         self.assertEqual(0x100, ent.offset)
2706
2707         ent = entries[3]
2708         self.assertEqual(2, ent.indent)
2709         self.assertEqual('cbfs', ent.name)
2710         self.assertEqual('cbfs', ent.etype)
2711         self.assertEqual(0x400, ent.size)
2712         self.assertEqual(0x100, ent.image_pos)
2713         self.assertEqual(None, ent.uncomp_size)
2714         self.assertEqual(0, ent.offset)
2715
2716         ent = entries[4]
2717         self.assertEqual(3, ent.indent)
2718         self.assertEqual('u-boot', ent.name)
2719         self.assertEqual('u-boot', ent.etype)
2720         self.assertEqual(len(U_BOOT_DATA), ent.size)
2721         self.assertEqual(0x138, ent.image_pos)
2722         self.assertEqual(None, ent.uncomp_size)
2723         self.assertEqual(0x38, ent.offset)
2724
2725         ent = entries[5]
2726         self.assertEqual(3, ent.indent)
2727         self.assertEqual('u-boot-dtb', ent.name)
2728         self.assertEqual('text', ent.etype)
2729         self.assertGreater(len(COMPRESS_DATA), ent.size)
2730         self.assertEqual(0x178, ent.image_pos)
2731         self.assertEqual(len(COMPRESS_DATA), ent.uncomp_size)
2732         self.assertEqual(0x78, ent.offset)
2733
2734         ent = entries[6]
2735         self.assertEqual(2, ent.indent)
2736         self.assertEqual('u-boot-dtb', ent.name)
2737         self.assertEqual('u-boot-dtb', ent.etype)
2738         self.assertEqual(0x500, ent.image_pos)
2739         self.assertEqual(len(U_BOOT_DTB_DATA), ent.uncomp_size)
2740         dtb_size = ent.size
2741         # Compressing this data expands it since headers are added
2742         self.assertGreater(dtb_size, len(U_BOOT_DTB_DATA))
2743         self.assertEqual(0x400, ent.offset)
2744
2745         self.assertEqual(len(data), 0x100 + section_size)
2746         self.assertEqual(section_size, 0x400 + dtb_size)
2747
2748     def testFindFdtmap(self):
2749         """Test locating an FDT map in an image"""
2750         self._CheckLz4()
2751         data = self.data = self._DoReadFileRealDtb('128_decode_image.dts')
2752         image = control.images['image']
2753         entries = image.GetEntries()
2754         entry = entries['fdtmap']
2755         self.assertEqual(entry.image_pos, fdtmap.LocateFdtmap(data))
2756
2757     def testFindFdtmapMissing(self):
2758         """Test failing to locate an FDP map"""
2759         data = self._DoReadFile('005_simple.dts')
2760         self.assertEqual(None, fdtmap.LocateFdtmap(data))
2761
2762     def testFindImageHeader(self):
2763         """Test locating a image header"""
2764         self._CheckLz4()
2765         data = self.data = self._DoReadFileRealDtb('128_decode_image.dts')
2766         image = control.images['image']
2767         entries = image.GetEntries()
2768         entry = entries['fdtmap']
2769         # The header should point to the FDT map
2770         self.assertEqual(entry.image_pos, image_header.LocateHeaderOffset(data))
2771
2772     def testFindImageHeaderStart(self):
2773         """Test locating a image header located at the start of an image"""
2774         data = self.data = self._DoReadFileRealDtb('117_fdtmap_hdr_start.dts')
2775         image = control.images['image']
2776         entries = image.GetEntries()
2777         entry = entries['fdtmap']
2778         # The header should point to the FDT map
2779         self.assertEqual(entry.image_pos, image_header.LocateHeaderOffset(data))
2780
2781     def testFindImageHeaderMissing(self):
2782         """Test failing to locate an image header"""
2783         data = self._DoReadFile('005_simple.dts')
2784         self.assertEqual(None, image_header.LocateHeaderOffset(data))
2785
2786     def testReadImage(self):
2787         """Test reading an image and accessing its FDT map"""
2788         self._CheckLz4()
2789         data = self.data = self._DoReadFileRealDtb('128_decode_image.dts')
2790         image_fname = tools.get_output_filename('image.bin')
2791         orig_image = control.images['image']
2792         image = Image.FromFile(image_fname)
2793         self.assertEqual(orig_image.GetEntries().keys(),
2794                          image.GetEntries().keys())
2795
2796         orig_entry = orig_image.GetEntries()['fdtmap']
2797         entry = image.GetEntries()['fdtmap']
2798         self.assertEquals(orig_entry.offset, entry.offset)
2799         self.assertEquals(orig_entry.size, entry.size)
2800         self.assertEquals(orig_entry.image_pos, entry.image_pos)
2801
2802     def testReadImageNoHeader(self):
2803         """Test accessing an image's FDT map without an image header"""
2804         self._CheckLz4()
2805         data = self._DoReadFileRealDtb('129_decode_image_nohdr.dts')
2806         image_fname = tools.get_output_filename('image.bin')
2807         image = Image.FromFile(image_fname)
2808         self.assertTrue(isinstance(image, Image))
2809         self.assertEqual('image', image.image_name[-5:])
2810
2811     def testReadImageFail(self):
2812         """Test failing to read an image image's FDT map"""
2813         self._DoReadFile('005_simple.dts')
2814         image_fname = tools.get_output_filename('image.bin')
2815         with self.assertRaises(ValueError) as e:
2816             image = Image.FromFile(image_fname)
2817         self.assertIn("Cannot find FDT map in image", str(e.exception))
2818
2819     def testListCmd(self):
2820         """Test listing the files in an image using an Fdtmap"""
2821         self._CheckLz4()
2822         data = self._DoReadFileRealDtb('130_list_fdtmap.dts')
2823
2824         # lz4 compression size differs depending on the version
2825         image = control.images['image']
2826         entries = image.GetEntries()
2827         section_size = entries['section'].size
2828         fdt_size = entries['section'].GetEntries()['u-boot-dtb'].size
2829         fdtmap_offset = entries['fdtmap'].offset
2830
2831         try:
2832             tmpdir, updated_fname = self._SetupImageInTmpdir()
2833             with test_util.capture_sys_output() as (stdout, stderr):
2834                 self._DoBinman('ls', '-i', updated_fname)
2835         finally:
2836             shutil.rmtree(tmpdir)
2837         lines = stdout.getvalue().splitlines()
2838         expected = [
2839 'Name              Image-pos  Size  Entry-type    Offset  Uncomp-size',
2840 '----------------------------------------------------------------------',
2841 'image                     0   c00  section            0',
2842 '  u-boot                  0     4  u-boot             0',
2843 '  section               100   %x  section          100' % section_size,
2844 '    cbfs                100   400  cbfs               0',
2845 '      u-boot            138     4  u-boot            38',
2846 '      u-boot-dtb        180   105  u-boot-dtb        80          3c9',
2847 '    u-boot-dtb          500   %x  u-boot-dtb       400          3c9' % fdt_size,
2848 '  fdtmap                %x   3bd  fdtmap           %x' %
2849         (fdtmap_offset, fdtmap_offset),
2850 '  image-header          bf8     8  image-header     bf8',
2851             ]
2852         self.assertEqual(expected, lines)
2853
2854     def testListCmdFail(self):
2855         """Test failing to list an image"""
2856         self._DoReadFile('005_simple.dts')
2857         try:
2858             tmpdir, updated_fname = self._SetupImageInTmpdir()
2859             with self.assertRaises(ValueError) as e:
2860                 self._DoBinman('ls', '-i', updated_fname)
2861         finally:
2862             shutil.rmtree(tmpdir)
2863         self.assertIn("Cannot find FDT map in image", str(e.exception))
2864
2865     def _RunListCmd(self, paths, expected):
2866         """List out entries and check the result
2867
2868         Args:
2869             paths: List of paths to pass to the list command
2870             expected: Expected list of filenames to be returned, in order
2871         """
2872         self._CheckLz4()
2873         self._DoReadFileRealDtb('130_list_fdtmap.dts')
2874         image_fname = tools.get_output_filename('image.bin')
2875         image = Image.FromFile(image_fname)
2876         lines = image.GetListEntries(paths)[1]
2877         files = [line[0].strip() for line in lines[1:]]
2878         self.assertEqual(expected, files)
2879
2880     def testListCmdSection(self):
2881         """Test listing the files in a section"""
2882         self._RunListCmd(['section'],
2883             ['section', 'cbfs', 'u-boot', 'u-boot-dtb', 'u-boot-dtb'])
2884
2885     def testListCmdFile(self):
2886         """Test listing a particular file"""
2887         self._RunListCmd(['*u-boot-dtb'], ['u-boot-dtb', 'u-boot-dtb'])
2888
2889     def testListCmdWildcard(self):
2890         """Test listing a wildcarded file"""
2891         self._RunListCmd(['*boot*'],
2892             ['u-boot', 'u-boot', 'u-boot-dtb', 'u-boot-dtb'])
2893
2894     def testListCmdWildcardMulti(self):
2895         """Test listing a wildcarded file"""
2896         self._RunListCmd(['*cb*', '*head*'],
2897             ['cbfs', 'u-boot', 'u-boot-dtb', 'image-header'])
2898
2899     def testListCmdEmpty(self):
2900         """Test listing a wildcarded file"""
2901         self._RunListCmd(['nothing'], [])
2902
2903     def testListCmdPath(self):
2904         """Test listing the files in a sub-entry of a section"""
2905         self._RunListCmd(['section/cbfs'], ['cbfs', 'u-boot', 'u-boot-dtb'])
2906
2907     def _RunExtractCmd(self, entry_name, decomp=True):
2908         """Extract an entry from an image
2909
2910         Args:
2911             entry_name: Entry name to extract
2912             decomp: True to decompress the data if compressed, False to leave
2913                 it in its raw uncompressed format
2914
2915         Returns:
2916             data from entry
2917         """
2918         self._CheckLz4()
2919         self._DoReadFileRealDtb('130_list_fdtmap.dts')
2920         image_fname = tools.get_output_filename('image.bin')
2921         return control.ReadEntry(image_fname, entry_name, decomp)
2922
2923     def testExtractSimple(self):
2924         """Test extracting a single file"""
2925         data = self._RunExtractCmd('u-boot')
2926         self.assertEqual(U_BOOT_DATA, data)
2927
2928     def testExtractSection(self):
2929         """Test extracting the files in a section"""
2930         data = self._RunExtractCmd('section')
2931         cbfs_data = data[:0x400]
2932         cbfs = cbfs_util.CbfsReader(cbfs_data)
2933         self.assertEqual(['u-boot', 'u-boot-dtb', ''], list(cbfs.files.keys()))
2934         dtb_data = data[0x400:]
2935         dtb = self._decompress(dtb_data)
2936         self.assertEqual(EXTRACT_DTB_SIZE, len(dtb))
2937
2938     def testExtractCompressed(self):
2939         """Test extracting compressed data"""
2940         data = self._RunExtractCmd('section/u-boot-dtb')
2941         self.assertEqual(EXTRACT_DTB_SIZE, len(data))
2942
2943     def testExtractRaw(self):
2944         """Test extracting compressed data without decompressing it"""
2945         data = self._RunExtractCmd('section/u-boot-dtb', decomp=False)
2946         dtb = self._decompress(data)
2947         self.assertEqual(EXTRACT_DTB_SIZE, len(dtb))
2948
2949     def testExtractCbfs(self):
2950         """Test extracting CBFS data"""
2951         data = self._RunExtractCmd('section/cbfs/u-boot')
2952         self.assertEqual(U_BOOT_DATA, data)
2953
2954     def testExtractCbfsCompressed(self):
2955         """Test extracting CBFS compressed data"""
2956         data = self._RunExtractCmd('section/cbfs/u-boot-dtb')
2957         self.assertEqual(EXTRACT_DTB_SIZE, len(data))
2958
2959     def testExtractCbfsRaw(self):
2960         """Test extracting CBFS compressed data without decompressing it"""
2961         bintool = self.comp_bintools['lzma_alone']
2962         self._CheckBintool(bintool)
2963         data = self._RunExtractCmd('section/cbfs/u-boot-dtb', decomp=False)
2964         dtb = bintool.decompress(data)
2965         self.assertEqual(EXTRACT_DTB_SIZE, len(dtb))
2966
2967     def testExtractBadEntry(self):
2968         """Test extracting a bad section path"""
2969         with self.assertRaises(ValueError) as e:
2970             self._RunExtractCmd('section/does-not-exist')
2971         self.assertIn("Entry 'does-not-exist' not found in '/section'",
2972                       str(e.exception))
2973
2974     def testExtractMissingFile(self):
2975         """Test extracting file that does not exist"""
2976         with self.assertRaises(IOError) as e:
2977             control.ReadEntry('missing-file', 'name')
2978
2979     def testExtractBadFile(self):
2980         """Test extracting an invalid file"""
2981         fname = os.path.join(self._indir, 'badfile')
2982         tools.write_file(fname, b'')
2983         with self.assertRaises(ValueError) as e:
2984             control.ReadEntry(fname, 'name')
2985
2986     def testExtractCmd(self):
2987         """Test extracting a file fron an image on the command line"""
2988         self._CheckLz4()
2989         self._DoReadFileRealDtb('130_list_fdtmap.dts')
2990         fname = os.path.join(self._indir, 'output.extact')
2991         try:
2992             tmpdir, updated_fname = self._SetupImageInTmpdir()
2993             with test_util.capture_sys_output() as (stdout, stderr):
2994                 self._DoBinman('extract', '-i', updated_fname, 'u-boot',
2995                                '-f', fname)
2996         finally:
2997             shutil.rmtree(tmpdir)
2998         data = tools.read_file(fname)
2999         self.assertEqual(U_BOOT_DATA, data)
3000
3001     def testExtractOneEntry(self):
3002         """Test extracting a single entry fron an image """
3003         self._CheckLz4()
3004         self._DoReadFileRealDtb('130_list_fdtmap.dts')
3005         image_fname = tools.get_output_filename('image.bin')
3006         fname = os.path.join(self._indir, 'output.extact')
3007         control.ExtractEntries(image_fname, fname, None, ['u-boot'])
3008         data = tools.read_file(fname)
3009         self.assertEqual(U_BOOT_DATA, data)
3010
3011     def _CheckExtractOutput(self, decomp):
3012         """Helper to test file output with and without decompression
3013
3014         Args:
3015             decomp: True to decompress entry data, False to output it raw
3016         """
3017         def _CheckPresent(entry_path, expect_data, expect_size=None):
3018             """Check and remove expected file
3019
3020             This checks the data/size of a file and removes the file both from
3021             the outfiles set and from the output directory. Once all files are
3022             processed, both the set and directory should be empty.
3023
3024             Args:
3025                 entry_path: Entry path
3026                 expect_data: Data to expect in file, or None to skip check
3027                 expect_size: Size of data to expect in file, or None to skip
3028             """
3029             path = os.path.join(outdir, entry_path)
3030             data = tools.read_file(path)
3031             os.remove(path)
3032             if expect_data:
3033                 self.assertEqual(expect_data, data)
3034             elif expect_size:
3035                 self.assertEqual(expect_size, len(data))
3036             outfiles.remove(path)
3037
3038         def _CheckDirPresent(name):
3039             """Remove expected directory
3040
3041             This gives an error if the directory does not exist as expected
3042
3043             Args:
3044                 name: Name of directory to remove
3045             """
3046             path = os.path.join(outdir, name)
3047             os.rmdir(path)
3048
3049         self._DoReadFileRealDtb('130_list_fdtmap.dts')
3050         image_fname = tools.get_output_filename('image.bin')
3051         outdir = os.path.join(self._indir, 'extract')
3052         einfos = control.ExtractEntries(image_fname, None, outdir, [], decomp)
3053
3054         # Create a set of all file that were output (should be 9)
3055         outfiles = set()
3056         for root, dirs, files in os.walk(outdir):
3057             outfiles |= set([os.path.join(root, fname) for fname in files])
3058         self.assertEqual(9, len(outfiles))
3059         self.assertEqual(9, len(einfos))
3060
3061         image = control.images['image']
3062         entries = image.GetEntries()
3063
3064         # Check the 9 files in various ways
3065         section = entries['section']
3066         section_entries = section.GetEntries()
3067         cbfs_entries = section_entries['cbfs'].GetEntries()
3068         _CheckPresent('u-boot', U_BOOT_DATA)
3069         _CheckPresent('section/cbfs/u-boot', U_BOOT_DATA)
3070         dtb_len = EXTRACT_DTB_SIZE
3071         if not decomp:
3072             dtb_len = cbfs_entries['u-boot-dtb'].size
3073         _CheckPresent('section/cbfs/u-boot-dtb', None, dtb_len)
3074         if not decomp:
3075             dtb_len = section_entries['u-boot-dtb'].size
3076         _CheckPresent('section/u-boot-dtb', None, dtb_len)
3077
3078         fdtmap = entries['fdtmap']
3079         _CheckPresent('fdtmap', fdtmap.data)
3080         hdr = entries['image-header']
3081         _CheckPresent('image-header', hdr.data)
3082
3083         _CheckPresent('section/root', section.data)
3084         cbfs = section_entries['cbfs']
3085         _CheckPresent('section/cbfs/root', cbfs.data)
3086         data = tools.read_file(image_fname)
3087         _CheckPresent('root', data)
3088
3089         # There should be no files left. Remove all the directories to check.
3090         # If there are any files/dirs remaining, one of these checks will fail.
3091         self.assertEqual(0, len(outfiles))
3092         _CheckDirPresent('section/cbfs')
3093         _CheckDirPresent('section')
3094         _CheckDirPresent('')
3095         self.assertFalse(os.path.exists(outdir))
3096
3097     def testExtractAllEntries(self):
3098         """Test extracting all entries"""
3099         self._CheckLz4()
3100         self._CheckExtractOutput(decomp=True)
3101
3102     def testExtractAllEntriesRaw(self):
3103         """Test extracting all entries without decompressing them"""
3104         self._CheckLz4()
3105         self._CheckExtractOutput(decomp=False)
3106
3107     def testExtractSelectedEntries(self):
3108         """Test extracting some entries"""
3109         self._CheckLz4()
3110         self._DoReadFileRealDtb('130_list_fdtmap.dts')
3111         image_fname = tools.get_output_filename('image.bin')
3112         outdir = os.path.join(self._indir, 'extract')
3113         einfos = control.ExtractEntries(image_fname, None, outdir,
3114                                         ['*cb*', '*head*'])
3115
3116         # File output is tested by testExtractAllEntries(), so just check that
3117         # the expected entries are selected
3118         names = [einfo.name for einfo in einfos]
3119         self.assertEqual(names,
3120                          ['cbfs', 'u-boot', 'u-boot-dtb', 'image-header'])
3121
3122     def testExtractNoEntryPaths(self):
3123         """Test extracting some entries"""
3124         self._CheckLz4()
3125         self._DoReadFileRealDtb('130_list_fdtmap.dts')
3126         image_fname = tools.get_output_filename('image.bin')
3127         with self.assertRaises(ValueError) as e:
3128             control.ExtractEntries(image_fname, 'fname', None, [])
3129         self.assertIn('Must specify an entry path to write with -f',
3130                       str(e.exception))
3131
3132     def testExtractTooManyEntryPaths(self):
3133         """Test extracting some entries"""
3134         self._CheckLz4()
3135         self._DoReadFileRealDtb('130_list_fdtmap.dts')
3136         image_fname = tools.get_output_filename('image.bin')
3137         with self.assertRaises(ValueError) as e:
3138             control.ExtractEntries(image_fname, 'fname', None, ['a', 'b'])
3139         self.assertIn('Must specify exactly one entry path to write with -f',
3140                       str(e.exception))
3141
3142     def testPackAlignSection(self):
3143         """Test that sections can have alignment"""
3144         self._DoReadFile('131_pack_align_section.dts')
3145
3146         self.assertIn('image', control.images)
3147         image = control.images['image']
3148         entries = image.GetEntries()
3149         self.assertEqual(3, len(entries))
3150
3151         # First u-boot
3152         self.assertIn('u-boot', entries)
3153         entry = entries['u-boot']
3154         self.assertEqual(0, entry.offset)
3155         self.assertEqual(0, entry.image_pos)
3156         self.assertEqual(len(U_BOOT_DATA), entry.contents_size)
3157         self.assertEqual(len(U_BOOT_DATA), entry.size)
3158
3159         # Section0
3160         self.assertIn('section0', entries)
3161         section0 = entries['section0']
3162         self.assertEqual(0x10, section0.offset)
3163         self.assertEqual(0x10, section0.image_pos)
3164         self.assertEqual(len(U_BOOT_DATA), section0.size)
3165
3166         # Second u-boot
3167         section_entries = section0.GetEntries()
3168         self.assertIn('u-boot', section_entries)
3169         entry = section_entries['u-boot']
3170         self.assertEqual(0, entry.offset)
3171         self.assertEqual(0x10, entry.image_pos)
3172         self.assertEqual(len(U_BOOT_DATA), entry.contents_size)
3173         self.assertEqual(len(U_BOOT_DATA), entry.size)
3174
3175         # Section1
3176         self.assertIn('section1', entries)
3177         section1 = entries['section1']
3178         self.assertEqual(0x14, section1.offset)
3179         self.assertEqual(0x14, section1.image_pos)
3180         self.assertEqual(0x20, section1.size)
3181
3182         # Second u-boot
3183         section_entries = section1.GetEntries()
3184         self.assertIn('u-boot', section_entries)
3185         entry = section_entries['u-boot']
3186         self.assertEqual(0, entry.offset)
3187         self.assertEqual(0x14, entry.image_pos)
3188         self.assertEqual(len(U_BOOT_DATA), entry.contents_size)
3189         self.assertEqual(len(U_BOOT_DATA), entry.size)
3190
3191         # Section2
3192         self.assertIn('section2', section_entries)
3193         section2 = section_entries['section2']
3194         self.assertEqual(0x4, section2.offset)
3195         self.assertEqual(0x18, section2.image_pos)
3196         self.assertEqual(4, section2.size)
3197
3198         # Third u-boot
3199         section_entries = section2.GetEntries()
3200         self.assertIn('u-boot', section_entries)
3201         entry = section_entries['u-boot']
3202         self.assertEqual(0, entry.offset)
3203         self.assertEqual(0x18, entry.image_pos)
3204         self.assertEqual(len(U_BOOT_DATA), entry.contents_size)
3205         self.assertEqual(len(U_BOOT_DATA), entry.size)
3206
3207     def _RunReplaceCmd(self, entry_name, data, decomp=True, allow_resize=True,
3208                        dts='132_replace.dts'):
3209         """Replace an entry in an image
3210
3211         This writes the entry data to update it, then opens the updated file and
3212         returns the value that it now finds there.
3213
3214         Args:
3215             entry_name: Entry name to replace
3216             data: Data to replace it with
3217             decomp: True to compress the data if needed, False if data is
3218                 already compressed so should be used as is
3219             allow_resize: True to allow entries to change size, False to raise
3220                 an exception
3221
3222         Returns:
3223             Tuple:
3224                 data from entry
3225                 data from fdtmap (excluding header)
3226                 Image object that was modified
3227         """
3228         dtb_data = self._DoReadFileDtb(dts, use_real_dtb=True,
3229                                        update_dtb=True)[1]
3230
3231         self.assertIn('image', control.images)
3232         image = control.images['image']
3233         entries = image.GetEntries()
3234         orig_dtb_data = entries['u-boot-dtb'].data
3235         orig_fdtmap_data = entries['fdtmap'].data
3236
3237         image_fname = tools.get_output_filename('image.bin')
3238         updated_fname = tools.get_output_filename('image-updated.bin')
3239         tools.write_file(updated_fname, tools.read_file(image_fname))
3240         image = control.WriteEntry(updated_fname, entry_name, data, decomp,
3241                                    allow_resize)
3242         data = control.ReadEntry(updated_fname, entry_name, decomp)
3243
3244         # The DT data should not change unless resized:
3245         if not allow_resize:
3246             new_dtb_data = entries['u-boot-dtb'].data
3247             self.assertEqual(new_dtb_data, orig_dtb_data)
3248             new_fdtmap_data = entries['fdtmap'].data
3249             self.assertEqual(new_fdtmap_data, orig_fdtmap_data)
3250
3251         return data, orig_fdtmap_data[fdtmap.FDTMAP_HDR_LEN:], image
3252
3253     def testReplaceSimple(self):
3254         """Test replacing a single file"""
3255         expected = b'x' * len(U_BOOT_DATA)
3256         data, expected_fdtmap, _ = self._RunReplaceCmd('u-boot', expected,
3257                                                     allow_resize=False)
3258         self.assertEqual(expected, data)
3259
3260         # Test that the state looks right. There should be an FDT for the fdtmap
3261         # that we jsut read back in, and it should match what we find in the
3262         # 'control' tables. Checking for an FDT that does not exist should
3263         # return None.
3264         path, fdtmap = state.GetFdtContents('fdtmap')
3265         self.assertIsNotNone(path)
3266         self.assertEqual(expected_fdtmap, fdtmap)
3267
3268         dtb = state.GetFdtForEtype('fdtmap')
3269         self.assertEqual(dtb.GetContents(), fdtmap)
3270
3271         missing_path, missing_fdtmap = state.GetFdtContents('missing')
3272         self.assertIsNone(missing_path)
3273         self.assertIsNone(missing_fdtmap)
3274
3275         missing_dtb = state.GetFdtForEtype('missing')
3276         self.assertIsNone(missing_dtb)
3277
3278         self.assertEqual('/binman', state.fdt_path_prefix)
3279
3280     def testReplaceResizeFail(self):
3281         """Test replacing a file by something larger"""
3282         expected = U_BOOT_DATA + b'x'
3283         with self.assertRaises(ValueError) as e:
3284             self._RunReplaceCmd('u-boot', expected, allow_resize=False,
3285                                 dts='139_replace_repack.dts')
3286         self.assertIn("Node '/u-boot': Entry data size does not match, but resize is disabled",
3287                       str(e.exception))
3288
3289     def testReplaceMulti(self):
3290         """Test replacing entry data where multiple images are generated"""
3291         data = self._DoReadFileDtb('133_replace_multi.dts', use_real_dtb=True,
3292                                    update_dtb=True)[0]
3293         expected = b'x' * len(U_BOOT_DATA)
3294         updated_fname = tools.get_output_filename('image-updated.bin')
3295         tools.write_file(updated_fname, data)
3296         entry_name = 'u-boot'
3297         control.WriteEntry(updated_fname, entry_name, expected,
3298                            allow_resize=False)
3299         data = control.ReadEntry(updated_fname, entry_name)
3300         self.assertEqual(expected, data)
3301
3302         # Check the state looks right.
3303         self.assertEqual('/binman/image', state.fdt_path_prefix)
3304
3305         # Now check we can write the first image
3306         image_fname = tools.get_output_filename('first-image.bin')
3307         updated_fname = tools.get_output_filename('first-updated.bin')
3308         tools.write_file(updated_fname, tools.read_file(image_fname))
3309         entry_name = 'u-boot'
3310         control.WriteEntry(updated_fname, entry_name, expected,
3311                            allow_resize=False)
3312         data = control.ReadEntry(updated_fname, entry_name)
3313         self.assertEqual(expected, data)
3314
3315         # Check the state looks right.
3316         self.assertEqual('/binman/first-image', state.fdt_path_prefix)
3317
3318     def testUpdateFdtAllRepack(self):
3319         """Test that all device trees are updated with offset/size info"""
3320         self._SetupSplElf()
3321         self._SetupTplElf()
3322         data = self._DoReadFileRealDtb('134_fdt_update_all_repack.dts')
3323         SECTION_SIZE = 0x300
3324         DTB_SIZE = 602
3325         FDTMAP_SIZE = 608
3326         base_expected = {
3327             'offset': 0,
3328             'size': SECTION_SIZE + DTB_SIZE * 2 + FDTMAP_SIZE,
3329             'image-pos': 0,
3330             'section:offset': 0,
3331             'section:size': SECTION_SIZE,
3332             'section:image-pos': 0,
3333             'section/u-boot-dtb:offset': 4,
3334             'section/u-boot-dtb:size': 636,
3335             'section/u-boot-dtb:image-pos': 4,
3336             'u-boot-spl-dtb:offset': SECTION_SIZE,
3337             'u-boot-spl-dtb:size': DTB_SIZE,
3338             'u-boot-spl-dtb:image-pos': SECTION_SIZE,
3339             'u-boot-tpl-dtb:offset': SECTION_SIZE + DTB_SIZE,
3340             'u-boot-tpl-dtb:image-pos': SECTION_SIZE + DTB_SIZE,
3341             'u-boot-tpl-dtb:size': DTB_SIZE,
3342             'fdtmap:offset': SECTION_SIZE + DTB_SIZE * 2,
3343             'fdtmap:size': FDTMAP_SIZE,
3344             'fdtmap:image-pos': SECTION_SIZE + DTB_SIZE * 2,
3345         }
3346         main_expected = {
3347             'section:orig-size': SECTION_SIZE,
3348             'section/u-boot-dtb:orig-offset': 4,
3349         }
3350
3351         # We expect three device-tree files in the output, with the first one
3352         # within a fixed-size section.
3353         # Read them in sequence. We look for an 'spl' property in the SPL tree,
3354         # and 'tpl' in the TPL tree, to make sure they are distinct from the
3355         # main U-Boot tree. All three should have the same positions and offset
3356         # except that the main tree should include the main_expected properties
3357         start = 4
3358         for item in ['', 'spl', 'tpl', None]:
3359             if item is None:
3360                 start += 16  # Move past fdtmap header
3361             dtb = fdt.Fdt.FromData(data[start:])
3362             dtb.Scan()
3363             props = self._GetPropTree(dtb,
3364                 BASE_DTB_PROPS + REPACK_DTB_PROPS + ['spl', 'tpl'],
3365                 prefix='/' if item is None else '/binman/')
3366             expected = dict(base_expected)
3367             if item:
3368                 expected[item] = 0
3369             else:
3370                 # Main DTB and fdtdec should include the 'orig-' properties
3371                 expected.update(main_expected)
3372             # Helpful for debugging:
3373             #for prop in sorted(props):
3374                 #print('prop %s %s %s' % (prop, props[prop], expected[prop]))
3375             self.assertEqual(expected, props)
3376             if item == '':
3377                 start = SECTION_SIZE
3378             else:
3379                 start += dtb._fdt_obj.totalsize()
3380
3381     def testFdtmapHeaderMiddle(self):
3382         """Test an FDT map in the middle of an image when it should be at end"""
3383         with self.assertRaises(ValueError) as e:
3384             self._DoReadFileRealDtb('135_fdtmap_hdr_middle.dts')
3385         self.assertIn("Invalid sibling order 'middle' for image-header: Must be at 'end' to match location",
3386                       str(e.exception))
3387
3388     def testFdtmapHeaderStartBad(self):
3389         """Test an FDT map in middle of an image when it should be at start"""
3390         with self.assertRaises(ValueError) as e:
3391             self._DoReadFileRealDtb('136_fdtmap_hdr_startbad.dts')
3392         self.assertIn("Invalid sibling order 'end' for image-header: Must be at 'start' to match location",
3393                       str(e.exception))
3394
3395     def testFdtmapHeaderEndBad(self):
3396         """Test an FDT map at the start of an image when it should be at end"""
3397         with self.assertRaises(ValueError) as e:
3398             self._DoReadFileRealDtb('137_fdtmap_hdr_endbad.dts')
3399         self.assertIn("Invalid sibling order 'start' for image-header: Must be at 'end' to match location",
3400                       str(e.exception))
3401
3402     def testFdtmapHeaderNoSize(self):
3403         """Test an image header at the end of an image with undefined size"""
3404         self._DoReadFileRealDtb('138_fdtmap_hdr_nosize.dts')
3405
3406     def testReplaceResize(self):
3407         """Test replacing a single file in an entry with a larger file"""
3408         expected = U_BOOT_DATA + b'x'
3409         data, _, image = self._RunReplaceCmd('u-boot', expected,
3410                                              dts='139_replace_repack.dts')
3411         self.assertEqual(expected, data)
3412
3413         entries = image.GetEntries()
3414         dtb_data = entries['u-boot-dtb'].data
3415         dtb = fdt.Fdt.FromData(dtb_data)
3416         dtb.Scan()
3417
3418         # The u-boot section should now be larger in the dtb
3419         node = dtb.GetNode('/binman/u-boot')
3420         self.assertEqual(len(expected), fdt_util.GetInt(node, 'size'))
3421
3422         # Same for the fdtmap
3423         fdata = entries['fdtmap'].data
3424         fdtb = fdt.Fdt.FromData(fdata[fdtmap.FDTMAP_HDR_LEN:])
3425         fdtb.Scan()
3426         fnode = fdtb.GetNode('/u-boot')
3427         self.assertEqual(len(expected), fdt_util.GetInt(fnode, 'size'))
3428
3429     def testReplaceResizeNoRepack(self):
3430         """Test replacing an entry with a larger file when not allowed"""
3431         expected = U_BOOT_DATA + b'x'
3432         with self.assertRaises(ValueError) as e:
3433             self._RunReplaceCmd('u-boot', expected)
3434         self.assertIn('Entry data size does not match, but allow-repack is not present for this image',
3435                       str(e.exception))
3436
3437     def testEntryShrink(self):
3438         """Test contracting an entry after it is packed"""
3439         try:
3440             state.SetAllowEntryContraction(True)
3441             data = self._DoReadFileDtb('140_entry_shrink.dts',
3442                                        update_dtb=True)[0]
3443         finally:
3444             state.SetAllowEntryContraction(False)
3445         self.assertEqual(b'a', data[:1])
3446         self.assertEqual(U_BOOT_DATA, data[1:1 + len(U_BOOT_DATA)])
3447         self.assertEqual(b'a', data[-1:])
3448
3449     def testEntryShrinkFail(self):
3450         """Test not being allowed to contract an entry after it is packed"""
3451         data = self._DoReadFileDtb('140_entry_shrink.dts', update_dtb=True)[0]
3452
3453         # In this case there is a spare byte at the end of the data. The size of
3454         # the contents is only 1 byte but we still have the size before it
3455         # shrunk.
3456         self.assertEqual(b'a\0', data[:2])
3457         self.assertEqual(U_BOOT_DATA, data[2:2 + len(U_BOOT_DATA)])
3458         self.assertEqual(b'a\0', data[-2:])
3459
3460     def testDescriptorOffset(self):
3461         """Test that the Intel descriptor is always placed at at the start"""
3462         data = self._DoReadFileDtb('141_descriptor_offset.dts')
3463         image = control.images['image']
3464         entries = image.GetEntries()
3465         desc = entries['intel-descriptor']
3466         self.assertEqual(0xff800000, desc.offset);
3467         self.assertEqual(0xff800000, desc.image_pos);
3468
3469     def testReplaceCbfs(self):
3470         """Test replacing a single file in CBFS without changing the size"""
3471         self._CheckLz4()
3472         expected = b'x' * len(U_BOOT_DATA)
3473         data = self._DoReadFileRealDtb('142_replace_cbfs.dts')
3474         updated_fname = tools.get_output_filename('image-updated.bin')
3475         tools.write_file(updated_fname, data)
3476         entry_name = 'section/cbfs/u-boot'
3477         control.WriteEntry(updated_fname, entry_name, expected,
3478                            allow_resize=True)
3479         data = control.ReadEntry(updated_fname, entry_name)
3480         self.assertEqual(expected, data)
3481
3482     def testReplaceResizeCbfs(self):
3483         """Test replacing a single file in CBFS with one of a different size"""
3484         self._CheckLz4()
3485         expected = U_BOOT_DATA + b'x'
3486         data = self._DoReadFileRealDtb('142_replace_cbfs.dts')
3487         updated_fname = tools.get_output_filename('image-updated.bin')
3488         tools.write_file(updated_fname, data)
3489         entry_name = 'section/cbfs/u-boot'
3490         control.WriteEntry(updated_fname, entry_name, expected,
3491                            allow_resize=True)
3492         data = control.ReadEntry(updated_fname, entry_name)
3493         self.assertEqual(expected, data)
3494
3495     def _SetupForReplace(self):
3496         """Set up some files to use to replace entries
3497
3498         This generates an image, copies it to a new file, extracts all the files
3499         in it and updates some of them
3500
3501         Returns:
3502             List
3503                 Image filename
3504                 Output directory
3505                 Expected values for updated entries, each a string
3506         """
3507         data = self._DoReadFileRealDtb('143_replace_all.dts')
3508
3509         updated_fname = tools.get_output_filename('image-updated.bin')
3510         tools.write_file(updated_fname, data)
3511
3512         outdir = os.path.join(self._indir, 'extract')
3513         einfos = control.ExtractEntries(updated_fname, None, outdir, [])
3514
3515         expected1 = b'x' + U_BOOT_DATA + b'y'
3516         u_boot_fname1 = os.path.join(outdir, 'u-boot')
3517         tools.write_file(u_boot_fname1, expected1)
3518
3519         expected2 = b'a' + U_BOOT_DATA + b'b'
3520         u_boot_fname2 = os.path.join(outdir, 'u-boot2')
3521         tools.write_file(u_boot_fname2, expected2)
3522
3523         expected_text = b'not the same text'
3524         text_fname = os.path.join(outdir, 'text')
3525         tools.write_file(text_fname, expected_text)
3526
3527         dtb_fname = os.path.join(outdir, 'u-boot-dtb')
3528         dtb = fdt.FdtScan(dtb_fname)
3529         node = dtb.GetNode('/binman/text')
3530         node.AddString('my-property', 'the value')
3531         dtb.Sync(auto_resize=True)
3532         dtb.Flush()
3533
3534         return updated_fname, outdir, expected1, expected2, expected_text
3535
3536     def _CheckReplaceMultiple(self, entry_paths):
3537         """Handle replacing the contents of multiple entries
3538
3539         Args:
3540             entry_paths: List of entry paths to replace
3541
3542         Returns:
3543             List
3544                 Dict of entries in the image:
3545                     key: Entry name
3546                     Value: Entry object
3547             Expected values for updated entries, each a string
3548         """
3549         updated_fname, outdir, expected1, expected2, expected_text = (
3550             self._SetupForReplace())
3551         control.ReplaceEntries(updated_fname, None, outdir, entry_paths)
3552
3553         image = Image.FromFile(updated_fname)
3554         image.LoadData()
3555         return image.GetEntries(), expected1, expected2, expected_text
3556
3557     def testReplaceAll(self):
3558         """Test replacing the contents of all entries"""
3559         entries, expected1, expected2, expected_text = (
3560             self._CheckReplaceMultiple([]))
3561         data = entries['u-boot'].data
3562         self.assertEqual(expected1, data)
3563
3564         data = entries['u-boot2'].data
3565         self.assertEqual(expected2, data)
3566
3567         data = entries['text'].data
3568         self.assertEqual(expected_text, data)
3569
3570         # Check that the device tree is updated
3571         data = entries['u-boot-dtb'].data
3572         dtb = fdt.Fdt.FromData(data)
3573         dtb.Scan()
3574         node = dtb.GetNode('/binman/text')
3575         self.assertEqual('the value', node.props['my-property'].value)
3576
3577     def testReplaceSome(self):
3578         """Test replacing the contents of a few entries"""
3579         entries, expected1, expected2, expected_text = (
3580             self._CheckReplaceMultiple(['u-boot2', 'text']))
3581
3582         # This one should not change
3583         data = entries['u-boot'].data
3584         self.assertEqual(U_BOOT_DATA, data)
3585
3586         data = entries['u-boot2'].data
3587         self.assertEqual(expected2, data)
3588
3589         data = entries['text'].data
3590         self.assertEqual(expected_text, data)
3591
3592     def testReplaceCmd(self):
3593         """Test replacing a file fron an image on the command line"""
3594         self._DoReadFileRealDtb('143_replace_all.dts')
3595
3596         try:
3597             tmpdir, updated_fname = self._SetupImageInTmpdir()
3598
3599             fname = os.path.join(tmpdir, 'update-u-boot.bin')
3600             expected = b'x' * len(U_BOOT_DATA)
3601             tools.write_file(fname, expected)
3602
3603             self._DoBinman('replace', '-i', updated_fname, 'u-boot', '-f', fname)
3604             data = tools.read_file(updated_fname)
3605             self.assertEqual(expected, data[:len(expected)])
3606             map_fname = os.path.join(tmpdir, 'image-updated.map')
3607             self.assertFalse(os.path.exists(map_fname))
3608         finally:
3609             shutil.rmtree(tmpdir)
3610
3611     def testReplaceCmdSome(self):
3612         """Test replacing some files fron an image on the command line"""
3613         updated_fname, outdir, expected1, expected2, expected_text = (
3614             self._SetupForReplace())
3615
3616         self._DoBinman('replace', '-i', updated_fname, '-I', outdir,
3617                        'u-boot2', 'text')
3618
3619         tools.prepare_output_dir(None)
3620         image = Image.FromFile(updated_fname)
3621         image.LoadData()
3622         entries = image.GetEntries()
3623
3624         # This one should not change
3625         data = entries['u-boot'].data
3626         self.assertEqual(U_BOOT_DATA, data)
3627
3628         data = entries['u-boot2'].data
3629         self.assertEqual(expected2, data)
3630
3631         data = entries['text'].data
3632         self.assertEqual(expected_text, data)
3633
3634     def testReplaceMissing(self):
3635         """Test replacing entries where the file is missing"""
3636         updated_fname, outdir, expected1, expected2, expected_text = (
3637             self._SetupForReplace())
3638
3639         # Remove one of the files, to generate a warning
3640         u_boot_fname1 = os.path.join(outdir, 'u-boot')
3641         os.remove(u_boot_fname1)
3642
3643         with test_util.capture_sys_output() as (stdout, stderr):
3644             control.ReplaceEntries(updated_fname, None, outdir, [])
3645         self.assertIn("Skipping entry '/u-boot' from missing file",
3646                       stderr.getvalue())
3647
3648     def testReplaceCmdMap(self):
3649         """Test replacing a file fron an image on the command line"""
3650         self._DoReadFileRealDtb('143_replace_all.dts')
3651
3652         try:
3653             tmpdir, updated_fname = self._SetupImageInTmpdir()
3654
3655             fname = os.path.join(self._indir, 'update-u-boot.bin')
3656             expected = b'x' * len(U_BOOT_DATA)
3657             tools.write_file(fname, expected)
3658
3659             self._DoBinman('replace', '-i', updated_fname, 'u-boot',
3660                            '-f', fname, '-m')
3661             map_fname = os.path.join(tmpdir, 'image-updated.map')
3662             self.assertTrue(os.path.exists(map_fname))
3663         finally:
3664             shutil.rmtree(tmpdir)
3665
3666     def testReplaceNoEntryPaths(self):
3667         """Test replacing an entry without an entry path"""
3668         self._DoReadFileRealDtb('143_replace_all.dts')
3669         image_fname = tools.get_output_filename('image.bin')
3670         with self.assertRaises(ValueError) as e:
3671             control.ReplaceEntries(image_fname, 'fname', None, [])
3672         self.assertIn('Must specify an entry path to read with -f',
3673                       str(e.exception))
3674
3675     def testReplaceTooManyEntryPaths(self):
3676         """Test extracting some entries"""
3677         self._DoReadFileRealDtb('143_replace_all.dts')
3678         image_fname = tools.get_output_filename('image.bin')
3679         with self.assertRaises(ValueError) as e:
3680             control.ReplaceEntries(image_fname, 'fname', None, ['a', 'b'])
3681         self.assertIn('Must specify exactly one entry path to write with -f',
3682                       str(e.exception))
3683
3684     def testPackReset16(self):
3685         """Test that an image with an x86 reset16 region can be created"""
3686         data = self._DoReadFile('144_x86_reset16.dts')
3687         self.assertEqual(X86_RESET16_DATA, data[:len(X86_RESET16_DATA)])
3688
3689     def testPackReset16Spl(self):
3690         """Test that an image with an x86 reset16-spl region can be created"""
3691         data = self._DoReadFile('145_x86_reset16_spl.dts')
3692         self.assertEqual(X86_RESET16_SPL_DATA, data[:len(X86_RESET16_SPL_DATA)])
3693
3694     def testPackReset16Tpl(self):
3695         """Test that an image with an x86 reset16-tpl region can be created"""
3696         data = self._DoReadFile('146_x86_reset16_tpl.dts')
3697         self.assertEqual(X86_RESET16_TPL_DATA, data[:len(X86_RESET16_TPL_DATA)])
3698
3699     def testPackIntelFit(self):
3700         """Test that an image with an Intel FIT and pointer can be created"""
3701         data = self._DoReadFile('147_intel_fit.dts')
3702         self.assertEqual(U_BOOT_DATA, data[:len(U_BOOT_DATA)])
3703         fit = data[16:32];
3704         self.assertEqual(b'_FIT_   \x01\x00\x00\x00\x00\x01\x80}' , fit)
3705         ptr = struct.unpack('<i', data[0x40:0x44])[0]
3706
3707         image = control.images['image']
3708         entries = image.GetEntries()
3709         expected_ptr = entries['intel-fit'].image_pos - (1 << 32)
3710         self.assertEqual(expected_ptr, ptr)
3711
3712     def testPackIntelFitMissing(self):
3713         """Test detection of a FIT pointer with not FIT region"""
3714         with self.assertRaises(ValueError) as e:
3715             self._DoReadFile('148_intel_fit_missing.dts')
3716         self.assertIn("'intel-fit-ptr' section must have an 'intel-fit' sibling",
3717                       str(e.exception))
3718
3719     def _CheckSymbolsTplSection(self, dts, expected_vals):
3720         data = self._DoReadFile(dts)
3721         sym_values = struct.pack('<LLQLL', elf.BINMAN_SYM_MAGIC_VALUE, *expected_vals)
3722         upto1 = 4 + len(U_BOOT_SPL_DATA)
3723         expected1 = tools.get_bytes(0xff, 4) + sym_values + U_BOOT_SPL_DATA[24:]
3724         self.assertEqual(expected1, data[:upto1])
3725
3726         upto2 = upto1 + 1 + len(U_BOOT_SPL_DATA)
3727         expected2 = tools.get_bytes(0xff, 1) + sym_values + U_BOOT_SPL_DATA[24:]
3728         self.assertEqual(expected2, data[upto1:upto2])
3729
3730         upto3 = 0x3c + len(U_BOOT_DATA)
3731         expected3 = tools.get_bytes(0xff, 1) + U_BOOT_DATA
3732         self.assertEqual(expected3, data[upto2:upto3])
3733
3734         expected4 = sym_values + U_BOOT_TPL_DATA[24:]
3735         self.assertEqual(expected4, data[upto3:upto3 + len(U_BOOT_TPL_DATA)])
3736
3737     def testSymbolsTplSection(self):
3738         """Test binman can assign symbols embedded in U-Boot TPL in a section"""
3739         self._SetupSplElf('u_boot_binman_syms')
3740         self._SetupTplElf('u_boot_binman_syms')
3741         self._CheckSymbolsTplSection('149_symbols_tpl.dts',
3742                                      [0x04, 0x20, 0x10 + 0x3c, 0x04])
3743
3744     def testSymbolsTplSectionX86(self):
3745         """Test binman can assign symbols in a section with end-at-4gb"""
3746         self._SetupSplElf('u_boot_binman_syms_x86')
3747         self._SetupTplElf('u_boot_binman_syms_x86')
3748         self._CheckSymbolsTplSection('155_symbols_tpl_x86.dts',
3749                                      [0xffffff04, 0xffffff20, 0xffffff3c,
3750                                       0x04])
3751
3752     def testPackX86RomIfwiSectiom(self):
3753         """Test that a section can be placed in an IFWI region"""
3754         self._SetupIfwi('fitimage.bin')
3755         data = self._DoReadFile('151_x86_rom_ifwi_section.dts')
3756         self._CheckIfwi(data)
3757
3758     def testPackFspM(self):
3759         """Test that an image with a FSP memory-init binary can be created"""
3760         data = self._DoReadFile('152_intel_fsp_m.dts')
3761         self.assertEqual(FSP_M_DATA, data[:len(FSP_M_DATA)])
3762
3763     def testPackFspS(self):
3764         """Test that an image with a FSP silicon-init binary can be created"""
3765         data = self._DoReadFile('153_intel_fsp_s.dts')
3766         self.assertEqual(FSP_S_DATA, data[:len(FSP_S_DATA)])
3767
3768     def testPackFspT(self):
3769         """Test that an image with a FSP temp-ram-init binary can be created"""
3770         data = self._DoReadFile('154_intel_fsp_t.dts')
3771         self.assertEqual(FSP_T_DATA, data[:len(FSP_T_DATA)])
3772
3773     def testMkimage(self):
3774         """Test using mkimage to build an image"""
3775         self._SetupSplElf()
3776         data = self._DoReadFile('156_mkimage.dts')
3777
3778         # Just check that the data appears in the file somewhere
3779         self.assertIn(U_BOOT_SPL_DATA, data)
3780
3781     def testMkimageMissing(self):
3782         """Test that binman still produces an image if mkimage is missing"""
3783         self._SetupSplElf()
3784         with test_util.capture_sys_output() as (_, stderr):
3785             self._DoTestFile('156_mkimage.dts',
3786                              force_missing_bintools='mkimage')
3787         err = stderr.getvalue()
3788         self.assertRegex(err, "Image 'image'.*missing bintools.*: mkimage")
3789
3790     def testExtblob(self):
3791         """Test an image with an external blob"""
3792         data = self._DoReadFile('157_blob_ext.dts')
3793         self.assertEqual(REFCODE_DATA, data)
3794
3795     def testExtblobMissing(self):
3796         """Test an image with a missing external blob"""
3797         with self.assertRaises(ValueError) as e:
3798             self._DoReadFile('158_blob_ext_missing.dts')
3799         self.assertIn("Filename 'missing-file' not found in input path",
3800                       str(e.exception))
3801
3802     def testExtblobMissingOk(self):
3803         """Test an image with an missing external blob that is allowed"""
3804         with test_util.capture_sys_output() as (stdout, stderr):
3805             ret = self._DoTestFile('158_blob_ext_missing.dts',
3806                                    allow_missing=True)
3807         self.assertEqual(103, ret)
3808         err = stderr.getvalue()
3809         self.assertIn('(missing-file)', err)
3810         self.assertRegex(err, "Image 'image'.*missing.*: blob-ext")
3811         self.assertIn('Some images are invalid', err)
3812
3813     def testExtblobMissingOkFlag(self):
3814         """Test an image with an missing external blob allowed with -W"""
3815         with test_util.capture_sys_output() as (stdout, stderr):
3816             ret = self._DoTestFile('158_blob_ext_missing.dts',
3817                                    allow_missing=True, ignore_missing=True)
3818         self.assertEqual(0, ret)
3819         err = stderr.getvalue()
3820         self.assertIn('(missing-file)', err)
3821         self.assertRegex(err, "Image 'image'.*missing.*: blob-ext")
3822         self.assertIn('Some images are invalid', err)
3823
3824     def testExtblobMissingOkSect(self):
3825         """Test an image with an missing external blob that is allowed"""
3826         with test_util.capture_sys_output() as (stdout, stderr):
3827             self._DoTestFile('159_blob_ext_missing_sect.dts',
3828                              allow_missing=True)
3829         err = stderr.getvalue()
3830         self.assertRegex(err, "Image 'image'.*missing.*: blob-ext blob-ext2")
3831
3832     def testPackX86RomMeMissingDesc(self):
3833         """Test that an missing Intel descriptor entry is allowed"""
3834         with test_util.capture_sys_output() as (stdout, stderr):
3835             self._DoTestFile('164_x86_rom_me_missing.dts', allow_missing=True)
3836         err = stderr.getvalue()
3837         self.assertRegex(err, "Image 'image'.*missing.*: intel-descriptor")
3838
3839     def testPackX86RomMissingIfwi(self):
3840         """Test that an x86 ROM with Integrated Firmware Image can be created"""
3841         self._SetupIfwi('fitimage.bin')
3842         pathname = os.path.join(self._indir, 'fitimage.bin')
3843         os.remove(pathname)
3844         with test_util.capture_sys_output() as (stdout, stderr):
3845             self._DoTestFile('111_x86_rom_ifwi.dts', allow_missing=True)
3846         err = stderr.getvalue()
3847         self.assertRegex(err, "Image 'image'.*missing.*: intel-ifwi")
3848
3849     def testPackOverlapZero(self):
3850         """Test that zero-size overlapping regions are ignored"""
3851         self._DoTestFile('160_pack_overlap_zero.dts')
3852
3853     def _CheckSimpleFitData(self, fit_data, kernel_data, fdt1_data):
3854         # The data should be inside the FIT
3855         dtb = fdt.Fdt.FromData(fit_data)
3856         dtb.Scan()
3857         fnode = dtb.GetNode('/images/kernel')
3858         self.assertIn('data', fnode.props)
3859
3860         fname = os.path.join(self._indir, 'fit_data.fit')
3861         tools.write_file(fname, fit_data)
3862         out = tools.run('dumpimage', '-l', fname)
3863
3864         # Check a few features to make sure the plumbing works. We don't need
3865         # to test the operation of mkimage or dumpimage here. First convert the
3866         # output into a dict where the keys are the fields printed by dumpimage
3867         # and the values are a list of values for each field
3868         lines = out.splitlines()
3869
3870         # Converts "Compression:  gzip compressed" into two groups:
3871         # 'Compression' and 'gzip compressed'
3872         re_line = re.compile(r'^ *([^:]*)(?:: *(.*))?$')
3873         vals = collections.defaultdict(list)
3874         for line in lines:
3875             mat = re_line.match(line)
3876             vals[mat.group(1)].append(mat.group(2))
3877
3878         self.assertEquals('FIT description: test-desc', lines[0])
3879         self.assertIn('Created:', lines[1])
3880         self.assertIn('Image 0 (kernel)', vals)
3881         self.assertIn('Hash value', vals)
3882         data_sizes = vals.get('Data Size')
3883         self.assertIsNotNone(data_sizes)
3884         self.assertEqual(2, len(data_sizes))
3885         # Format is "4 Bytes = 0.00 KiB = 0.00 MiB" so take the first word
3886         self.assertEqual(len(kernel_data), int(data_sizes[0].split()[0]))
3887         self.assertEqual(len(fdt1_data), int(data_sizes[1].split()[0]))
3888
3889         # Check if entry listing correctly omits /images/
3890         image = control.images['image']
3891         fit_entry = image.GetEntries()['fit']
3892         subentries = list(fit_entry.GetEntries().keys())
3893         expected = ['kernel', 'fdt-1']
3894         self.assertEqual(expected, subentries)
3895
3896     def testSimpleFit(self):
3897         """Test an image with a FIT inside"""
3898         self._SetupSplElf()
3899         data = self._DoReadFile('161_fit.dts')
3900         self.assertEqual(U_BOOT_DATA, data[:len(U_BOOT_DATA)])
3901         self.assertEqual(U_BOOT_NODTB_DATA, data[-len(U_BOOT_NODTB_DATA):])
3902         fit_data = data[len(U_BOOT_DATA):-len(U_BOOT_NODTB_DATA)]
3903
3904         self._CheckSimpleFitData(fit_data, U_BOOT_DATA, U_BOOT_SPL_DTB_DATA)
3905
3906     def testSimpleFitExpandsSubentries(self):
3907         """Test that FIT images expand their subentries"""
3908         data = self._DoReadFileDtb('161_fit.dts', use_expanded=True)[0]
3909         self.assertEqual(U_BOOT_EXP_DATA, data[:len(U_BOOT_EXP_DATA)])
3910         self.assertEqual(U_BOOT_NODTB_DATA, data[-len(U_BOOT_NODTB_DATA):])
3911         fit_data = data[len(U_BOOT_EXP_DATA):-len(U_BOOT_NODTB_DATA)]
3912
3913         self._CheckSimpleFitData(fit_data, U_BOOT_EXP_DATA, U_BOOT_SPL_DTB_DATA)
3914
3915     def testSimpleFitImagePos(self):
3916         """Test that we have correct image-pos for FIT subentries"""
3917         data, _, _, out_dtb_fname = self._DoReadFileDtb('161_fit.dts',
3918                                                         update_dtb=True)
3919         dtb = fdt.Fdt(out_dtb_fname)
3920         dtb.Scan()
3921         props = self._GetPropTree(dtb, BASE_DTB_PROPS + REPACK_DTB_PROPS)
3922
3923         self.maxDiff = None
3924         self.assertEqual({
3925             'image-pos': 0,
3926             'offset': 0,
3927             'size': 1890,
3928
3929             'u-boot:image-pos': 0,
3930             'u-boot:offset': 0,
3931             'u-boot:size': 4,
3932
3933             'fit:image-pos': 4,
3934             'fit:offset': 4,
3935             'fit:size': 1840,
3936
3937             'fit/images/kernel:image-pos': 304,
3938             'fit/images/kernel:offset': 300,
3939             'fit/images/kernel:size': 4,
3940
3941             'fit/images/kernel/u-boot:image-pos': 304,
3942             'fit/images/kernel/u-boot:offset': 0,
3943             'fit/images/kernel/u-boot:size': 4,
3944
3945             'fit/images/fdt-1:image-pos': 552,
3946             'fit/images/fdt-1:offset': 548,
3947             'fit/images/fdt-1:size': 6,
3948
3949             'fit/images/fdt-1/u-boot-spl-dtb:image-pos': 552,
3950             'fit/images/fdt-1/u-boot-spl-dtb:offset': 0,
3951             'fit/images/fdt-1/u-boot-spl-dtb:size': 6,
3952
3953             'u-boot-nodtb:image-pos': 1844,
3954             'u-boot-nodtb:offset': 1844,
3955             'u-boot-nodtb:size': 46,
3956         }, props)
3957
3958         # Actually check the data is where we think it is
3959         for node, expected in [
3960             ("u-boot", U_BOOT_DATA),
3961             ("fit/images/kernel", U_BOOT_DATA),
3962             ("fit/images/kernel/u-boot", U_BOOT_DATA),
3963             ("fit/images/fdt-1", U_BOOT_SPL_DTB_DATA),
3964             ("fit/images/fdt-1/u-boot-spl-dtb", U_BOOT_SPL_DTB_DATA),
3965             ("u-boot-nodtb", U_BOOT_NODTB_DATA),
3966         ]:
3967             image_pos = props[f"{node}:image-pos"]
3968             size = props[f"{node}:size"]
3969             self.assertEqual(len(expected), size)
3970             self.assertEqual(expected, data[image_pos:image_pos+size])
3971
3972     def testFitExternal(self):
3973         """Test an image with an FIT with external images"""
3974         data = self._DoReadFile('162_fit_external.dts')
3975         fit_data = data[len(U_BOOT_DATA):-2]  # _testing is 2 bytes
3976
3977         # Size of the external-data region as set up by mkimage
3978         external_data_size = len(U_BOOT_DATA) + 2
3979         expected_size = (len(U_BOOT_DATA) + 0x400 +
3980                          tools.align(external_data_size, 4) +
3981                          len(U_BOOT_NODTB_DATA))
3982
3983         # The data should be outside the FIT
3984         dtb = fdt.Fdt.FromData(fit_data)
3985         dtb.Scan()
3986         fnode = dtb.GetNode('/images/kernel')
3987         self.assertNotIn('data', fnode.props)
3988         self.assertEqual(len(U_BOOT_DATA),
3989                          fdt_util.fdt32_to_cpu(fnode.props['data-size'].value))
3990         fit_pos = 0x400;
3991         self.assertEqual(
3992             fit_pos,
3993             fdt_util.fdt32_to_cpu(fnode.props['data-position'].value))
3994
3995         self.assertEquals(expected_size, len(data))
3996         actual_pos = len(U_BOOT_DATA) + fit_pos
3997         self.assertEqual(U_BOOT_DATA + b'aa',
3998                          data[actual_pos:actual_pos + external_data_size])
3999
4000     def testFitExternalImagePos(self):
4001         """Test that we have correct image-pos for external FIT subentries"""
4002         data, _, _, out_dtb_fname = self._DoReadFileDtb('162_fit_external.dts',
4003                                                         update_dtb=True)
4004         dtb = fdt.Fdt(out_dtb_fname)
4005         dtb.Scan()
4006         props = self._GetPropTree(dtb, BASE_DTB_PROPS + REPACK_DTB_PROPS)
4007
4008         self.assertEqual({
4009             'image-pos': 0,
4010             'offset': 0,
4011             'size': 1082,
4012
4013             'u-boot:image-pos': 0,
4014             'u-boot:offset': 0,
4015             'u-boot:size': 4,
4016
4017             'fit:size': 1032,
4018             'fit:offset': 4,
4019             'fit:image-pos': 4,
4020
4021             'fit/images/kernel:size': 4,
4022             'fit/images/kernel:offset': 1024,
4023             'fit/images/kernel:image-pos': 1028,
4024
4025             'fit/images/kernel/u-boot:size': 4,
4026             'fit/images/kernel/u-boot:offset': 0,
4027             'fit/images/kernel/u-boot:image-pos': 1028,
4028
4029             'fit/images/fdt-1:size': 2,
4030             'fit/images/fdt-1:offset': 1028,
4031             'fit/images/fdt-1:image-pos': 1032,
4032
4033             'fit/images/fdt-1/_testing:size': 2,
4034             'fit/images/fdt-1/_testing:offset': 0,
4035             'fit/images/fdt-1/_testing:image-pos': 1032,
4036
4037             'u-boot-nodtb:image-pos': 1036,
4038             'u-boot-nodtb:offset': 1036,
4039             'u-boot-nodtb:size': 46,
4040          }, props)
4041
4042         # Actually check the data is where we think it is
4043         for node, expected in [
4044             ("u-boot", U_BOOT_DATA),
4045             ("fit/images/kernel", U_BOOT_DATA),
4046             ("fit/images/kernel/u-boot", U_BOOT_DATA),
4047             ("fit/images/fdt-1", b'aa'),
4048             ("fit/images/fdt-1/_testing", b'aa'),
4049             ("u-boot-nodtb", U_BOOT_NODTB_DATA),
4050         ]:
4051             image_pos = props[f"{node}:image-pos"]
4052             size = props[f"{node}:size"]
4053             self.assertEqual(len(expected), size)
4054             self.assertEqual(expected, data[image_pos:image_pos+size])
4055
4056     def testFitMissing(self):
4057         """Test that binman complains if mkimage is missing"""
4058         with self.assertRaises(ValueError) as e:
4059             self._DoTestFile('162_fit_external.dts',
4060                              force_missing_bintools='mkimage')
4061         self.assertIn("Node '/binman/fit': Missing tool: 'mkimage'",
4062                       str(e.exception))
4063
4064     def testFitMissingOK(self):
4065         """Test that binman still produces a FIT image if mkimage is missing"""
4066         with test_util.capture_sys_output() as (_, stderr):
4067             self._DoTestFile('162_fit_external.dts', allow_missing=True,
4068                              force_missing_bintools='mkimage')
4069         err = stderr.getvalue()
4070         self.assertRegex(err, "Image 'image'.*missing bintools.*: mkimage")
4071
4072     def testSectionIgnoreHashSignature(self):
4073         """Test that sections ignore hash, signature nodes for its data"""
4074         data = self._DoReadFile('165_section_ignore_hash_signature.dts')
4075         expected = (U_BOOT_DATA + U_BOOT_DATA)
4076         self.assertEqual(expected, data)
4077
4078     def testPadInSections(self):
4079         """Test pad-before, pad-after for entries in sections"""
4080         data, _, _, out_dtb_fname = self._DoReadFileDtb(
4081             '166_pad_in_sections.dts', update_dtb=True)
4082         expected = (U_BOOT_DATA + tools.get_bytes(ord('!'), 12) +
4083                     U_BOOT_DATA + tools.get_bytes(ord('!'), 6) +
4084                     U_BOOT_DATA)
4085         self.assertEqual(expected, data)
4086
4087         dtb = fdt.Fdt(out_dtb_fname)
4088         dtb.Scan()
4089         props = self._GetPropTree(dtb, ['size', 'image-pos', 'offset'])
4090         expected = {
4091             'image-pos': 0,
4092             'offset': 0,
4093             'size': 12 + 6 + 3 * len(U_BOOT_DATA),
4094
4095             'section:image-pos': 0,
4096             'section:offset': 0,
4097             'section:size': 12 + 6 + 3 * len(U_BOOT_DATA),
4098
4099             'section/before:image-pos': 0,
4100             'section/before:offset': 0,
4101             'section/before:size': len(U_BOOT_DATA),
4102
4103             'section/u-boot:image-pos': 4,
4104             'section/u-boot:offset': 4,
4105             'section/u-boot:size': 12 + len(U_BOOT_DATA) + 6,
4106
4107             'section/after:image-pos': 26,
4108             'section/after:offset': 26,
4109             'section/after:size': len(U_BOOT_DATA),
4110             }
4111         self.assertEqual(expected, props)
4112
4113     def testFitImageSubentryAlignment(self):
4114         """Test relative alignability of FIT image subentries"""
4115         self._SetupSplElf()
4116         entry_args = {
4117             'test-id': TEXT_DATA,
4118         }
4119         data, _, _, _ = self._DoReadFileDtb('167_fit_image_subentry_alignment.dts',
4120                                             entry_args=entry_args)
4121         dtb = fdt.Fdt.FromData(data)
4122         dtb.Scan()
4123
4124         node = dtb.GetNode('/images/kernel')
4125         data = dtb.GetProps(node)["data"].bytes
4126         align_pad = 0x10 - (len(U_BOOT_SPL_DATA) % 0x10)
4127         expected = (tools.get_bytes(0, 0x20) + U_BOOT_SPL_DATA +
4128                     tools.get_bytes(0, align_pad) + U_BOOT_DATA)
4129         self.assertEqual(expected, data)
4130
4131         node = dtb.GetNode('/images/fdt-1')
4132         data = dtb.GetProps(node)["data"].bytes
4133         expected = (U_BOOT_SPL_DTB_DATA + tools.get_bytes(0, 20) +
4134                     tools.to_bytes(TEXT_DATA) + tools.get_bytes(0, 30) +
4135                     U_BOOT_DTB_DATA)
4136         self.assertEqual(expected, data)
4137
4138     def testFitExtblobMissingOk(self):
4139         """Test a FIT with a missing external blob that is allowed"""
4140         with test_util.capture_sys_output() as (stdout, stderr):
4141             self._DoTestFile('168_fit_missing_blob.dts',
4142                              allow_missing=True)
4143         err = stderr.getvalue()
4144         self.assertRegex(err, "Image 'image'.*missing.*: atf-bl31")
4145
4146     def testBlobNamedByArgMissing(self):
4147         """Test handling of a missing entry arg"""
4148         with self.assertRaises(ValueError) as e:
4149             self._DoReadFile('068_blob_named_by_arg.dts')
4150         self.assertIn("Missing required properties/entry args: cros-ec-rw-path",
4151                       str(e.exception))
4152
4153     def testPackBl31(self):
4154         """Test that an image with an ATF BL31 binary can be created"""
4155         data = self._DoReadFile('169_atf_bl31.dts')
4156         self.assertEqual(ATF_BL31_DATA, data[:len(ATF_BL31_DATA)])
4157
4158     def testPackScp(self):
4159         """Test that an image with an SCP binary can be created"""
4160         data = self._DoReadFile('172_scp.dts')
4161         self.assertEqual(SCP_DATA, data[:len(SCP_DATA)])
4162
4163     def testFitFdt(self):
4164         """Test an image with an FIT with multiple FDT images"""
4165         def _CheckFdt(seq, expected_data):
4166             """Check the FDT nodes
4167
4168             Args:
4169                 seq: Sequence number to check (0 or 1)
4170                 expected_data: Expected contents of 'data' property
4171             """
4172             name = 'fdt-%d' % seq
4173             fnode = dtb.GetNode('/images/%s' % name)
4174             self.assertIsNotNone(fnode)
4175             self.assertEqual({'description','type', 'compression', 'data'},
4176                              set(fnode.props.keys()))
4177             self.assertEqual(expected_data, fnode.props['data'].bytes)
4178             self.assertEqual('fdt-test-fdt%d.dtb' % seq,
4179                              fnode.props['description'].value)
4180             self.assertEqual(fnode.subnodes[0].name, 'hash')
4181
4182         def _CheckConfig(seq, expected_data):
4183             """Check the configuration nodes
4184
4185             Args:
4186                 seq: Sequence number to check (0 or 1)
4187                 expected_data: Expected contents of 'data' property
4188             """
4189             cnode = dtb.GetNode('/configurations')
4190             self.assertIn('default', cnode.props)
4191             self.assertEqual('config-2', cnode.props['default'].value)
4192
4193             name = 'config-%d' % seq
4194             fnode = dtb.GetNode('/configurations/%s' % name)
4195             self.assertIsNotNone(fnode)
4196             self.assertEqual({'description','firmware', 'loadables', 'fdt'},
4197                              set(fnode.props.keys()))
4198             self.assertEqual('conf-test-fdt%d.dtb' % seq,
4199                              fnode.props['description'].value)
4200             self.assertEqual('fdt-%d' % seq, fnode.props['fdt'].value)
4201
4202         entry_args = {
4203             'of-list': 'test-fdt1 test-fdt2',
4204             'default-dt': 'test-fdt2',
4205         }
4206         data = self._DoReadFileDtb(
4207             '170_fit_fdt.dts',
4208             entry_args=entry_args,
4209             extra_indirs=[os.path.join(self._indir, TEST_FDT_SUBDIR)])[0]
4210         self.assertEqual(U_BOOT_NODTB_DATA, data[-len(U_BOOT_NODTB_DATA):])
4211         fit_data = data[len(U_BOOT_DATA):-len(U_BOOT_NODTB_DATA)]
4212
4213         dtb = fdt.Fdt.FromData(fit_data)
4214         dtb.Scan()
4215         fnode = dtb.GetNode('/images/kernel')
4216         self.assertIn('data', fnode.props)
4217
4218         # Check all the properties in fdt-1 and fdt-2
4219         _CheckFdt(1, TEST_FDT1_DATA)
4220         _CheckFdt(2, TEST_FDT2_DATA)
4221
4222         # Check configurations
4223         _CheckConfig(1, TEST_FDT1_DATA)
4224         _CheckConfig(2, TEST_FDT2_DATA)
4225
4226     def testFitFdtMissingList(self):
4227         """Test handling of a missing 'of-list' entry arg"""
4228         with self.assertRaises(ValueError) as e:
4229             self._DoReadFile('170_fit_fdt.dts')
4230         self.assertIn("Generator node requires 'of-list' entry argument",
4231                       str(e.exception))
4232
4233     def testFitFdtEmptyList(self):
4234         """Test handling of an empty 'of-list' entry arg"""
4235         entry_args = {
4236             'of-list': '',
4237         }
4238         data = self._DoReadFileDtb('170_fit_fdt.dts', entry_args=entry_args)[0]
4239
4240     def testFitFdtMissingProp(self):
4241         """Test handling of a missing 'fit,fdt-list' property"""
4242         with self.assertRaises(ValueError) as e:
4243             self._DoReadFile('171_fit_fdt_missing_prop.dts')
4244         self.assertIn("Generator node requires 'fit,fdt-list' property",
4245                       str(e.exception))
4246
4247     def testFitFdtMissing(self):
4248         """Test handling of a missing 'default-dt' entry arg"""
4249         entry_args = {
4250             'of-list': 'test-fdt1 test-fdt2',
4251         }
4252         with self.assertRaises(ValueError) as e:
4253             self._DoReadFileDtb(
4254                 '170_fit_fdt.dts',
4255                 entry_args=entry_args,
4256                 extra_indirs=[os.path.join(self._indir, TEST_FDT_SUBDIR)])[0]
4257         self.assertIn("Generated 'default' node requires default-dt entry argument",
4258                       str(e.exception))
4259
4260     def testFitFdtNotInList(self):
4261         """Test handling of a default-dt that is not in the of-list"""
4262         entry_args = {
4263             'of-list': 'test-fdt1 test-fdt2',
4264             'default-dt': 'test-fdt3',
4265         }
4266         with self.assertRaises(ValueError) as e:
4267             self._DoReadFileDtb(
4268                 '170_fit_fdt.dts',
4269                 entry_args=entry_args,
4270                 extra_indirs=[os.path.join(self._indir, TEST_FDT_SUBDIR)])[0]
4271         self.assertIn("default-dt entry argument 'test-fdt3' not found in fdt list: test-fdt1, test-fdt2",
4272                       str(e.exception))
4273
4274     def testFitExtblobMissingHelp(self):
4275         """Test display of help messages when an external blob is missing"""
4276         control.missing_blob_help = control._ReadMissingBlobHelp()
4277         control.missing_blob_help['wibble'] = 'Wibble test'
4278         control.missing_blob_help['another'] = 'Another test'
4279         with test_util.capture_sys_output() as (stdout, stderr):
4280             self._DoTestFile('168_fit_missing_blob.dts',
4281                              allow_missing=True)
4282         err = stderr.getvalue()
4283
4284         # We can get the tag from the name, the type or the missing-msg
4285         # property. Check all three.
4286         self.assertIn('You may need to build ARM Trusted', err)
4287         self.assertIn('Wibble test', err)
4288         self.assertIn('Another test', err)
4289
4290     def testMissingBlob(self):
4291         """Test handling of a blob containing a missing file"""
4292         with self.assertRaises(ValueError) as e:
4293             self._DoTestFile('173_missing_blob.dts', allow_missing=True)
4294         self.assertIn("Filename 'missing' not found in input path",
4295                       str(e.exception))
4296
4297     def testEnvironment(self):
4298         """Test adding a U-Boot environment"""
4299         data = self._DoReadFile('174_env.dts')
4300         self.assertEqual(U_BOOT_DATA, data[:len(U_BOOT_DATA)])
4301         self.assertEqual(U_BOOT_NODTB_DATA, data[-len(U_BOOT_NODTB_DATA):])
4302         env = data[len(U_BOOT_DATA):-len(U_BOOT_NODTB_DATA)]
4303         self.assertEqual(b'\x1b\x97\x22\x7c\x01var1=1\0var2="2"\0\0\xff\xff',
4304                          env)
4305
4306     def testEnvironmentNoSize(self):
4307         """Test that a missing 'size' property is detected"""
4308         with self.assertRaises(ValueError) as e:
4309             self._DoTestFile('175_env_no_size.dts')
4310         self.assertIn("'u-boot-env' entry must have a size property",
4311                       str(e.exception))
4312
4313     def testEnvironmentTooSmall(self):
4314         """Test handling of an environment that does not fit"""
4315         with self.assertRaises(ValueError) as e:
4316             self._DoTestFile('176_env_too_small.dts')
4317
4318         # checksum, start byte, environment with \0 terminator, final \0
4319         need = 4 + 1 + len(ENV_DATA) + 1 + 1
4320         short = need - 0x8
4321         self.assertIn("too small to hold data (need %#x more bytes)" % short,
4322                       str(e.exception))
4323
4324     def testSkipAtStart(self):
4325         """Test handling of skip-at-start section"""
4326         data = self._DoReadFile('177_skip_at_start.dts')
4327         self.assertEqual(U_BOOT_DATA, data)
4328
4329         image = control.images['image']
4330         entries = image.GetEntries()
4331         section = entries['section']
4332         self.assertEqual(0, section.offset)
4333         self.assertEqual(len(U_BOOT_DATA), section.size)
4334         self.assertEqual(U_BOOT_DATA, section.GetData())
4335
4336         entry = section.GetEntries()['u-boot']
4337         self.assertEqual(16, entry.offset)
4338         self.assertEqual(len(U_BOOT_DATA), entry.size)
4339         self.assertEqual(U_BOOT_DATA, entry.data)
4340
4341     def testSkipAtStartPad(self):
4342         """Test handling of skip-at-start section with padded entry"""
4343         data = self._DoReadFile('178_skip_at_start_pad.dts')
4344         before = tools.get_bytes(0, 8)
4345         after = tools.get_bytes(0, 4)
4346         all = before + U_BOOT_DATA + after
4347         self.assertEqual(all, data)
4348
4349         image = control.images['image']
4350         entries = image.GetEntries()
4351         section = entries['section']
4352         self.assertEqual(0, section.offset)
4353         self.assertEqual(len(all), section.size)
4354         self.assertEqual(all, section.GetData())
4355
4356         entry = section.GetEntries()['u-boot']
4357         self.assertEqual(16, entry.offset)
4358         self.assertEqual(len(all), entry.size)
4359         self.assertEqual(U_BOOT_DATA, entry.data)
4360
4361     def testSkipAtStartSectionPad(self):
4362         """Test handling of skip-at-start section with padding"""
4363         data = self._DoReadFile('179_skip_at_start_section_pad.dts')
4364         before = tools.get_bytes(0, 8)
4365         after = tools.get_bytes(0, 4)
4366         all = before + U_BOOT_DATA + after
4367         self.assertEqual(all, data)
4368
4369         image = control.images['image']
4370         entries = image.GetEntries()
4371         section = entries['section']
4372         self.assertEqual(0, section.offset)
4373         self.assertEqual(len(all), section.size)
4374         self.assertEqual(U_BOOT_DATA, section.data)
4375         self.assertEqual(all, section.GetPaddedData())
4376
4377         entry = section.GetEntries()['u-boot']
4378         self.assertEqual(16, entry.offset)
4379         self.assertEqual(len(U_BOOT_DATA), entry.size)
4380         self.assertEqual(U_BOOT_DATA, entry.data)
4381
4382     def testSectionPad(self):
4383         """Testing padding with sections"""
4384         data = self._DoReadFile('180_section_pad.dts')
4385         expected = (tools.get_bytes(ord('&'), 3) +
4386                     tools.get_bytes(ord('!'), 5) +
4387                     U_BOOT_DATA +
4388                     tools.get_bytes(ord('!'), 1) +
4389                     tools.get_bytes(ord('&'), 2))
4390         self.assertEqual(expected, data)
4391
4392     def testSectionAlign(self):
4393         """Testing alignment with sections"""
4394         data = self._DoReadFileDtb('181_section_align.dts', map=True)[0]
4395         expected = (b'\0' +                         # fill section
4396                     tools.get_bytes(ord('&'), 1) +   # padding to section align
4397                     b'\0' +                         # fill section
4398                     tools.get_bytes(ord('!'), 3) +   # padding to u-boot align
4399                     U_BOOT_DATA +
4400                     tools.get_bytes(ord('!'), 4) +   # padding to u-boot size
4401                     tools.get_bytes(ord('!'), 4))    # padding to section size
4402         self.assertEqual(expected, data)
4403
4404     def testCompressImage(self):
4405         """Test compression of the entire image"""
4406         self._CheckLz4()
4407         data, _, _, out_dtb_fname = self._DoReadFileDtb(
4408             '182_compress_image.dts', use_real_dtb=True, update_dtb=True)
4409         dtb = fdt.Fdt(out_dtb_fname)
4410         dtb.Scan()
4411         props = self._GetPropTree(dtb, ['offset', 'image-pos', 'size',
4412                                         'uncomp-size'])
4413         orig = self._decompress(data)
4414         self.assertEquals(COMPRESS_DATA + U_BOOT_DATA, orig)
4415
4416         # Do a sanity check on various fields
4417         image = control.images['image']
4418         entries = image.GetEntries()
4419         self.assertEqual(2, len(entries))
4420
4421         entry = entries['blob']
4422         self.assertEqual(COMPRESS_DATA, entry.data)
4423         self.assertEqual(len(COMPRESS_DATA), entry.size)
4424
4425         entry = entries['u-boot']
4426         self.assertEqual(U_BOOT_DATA, entry.data)
4427         self.assertEqual(len(U_BOOT_DATA), entry.size)
4428
4429         self.assertEqual(len(data), image.size)
4430         self.assertEqual(COMPRESS_DATA + U_BOOT_DATA, image.uncomp_data)
4431         self.assertEqual(len(COMPRESS_DATA + U_BOOT_DATA), image.uncomp_size)
4432         orig = self._decompress(image.data)
4433         self.assertEqual(orig, image.uncomp_data)
4434
4435         expected = {
4436             'blob:offset': 0,
4437             'blob:size': len(COMPRESS_DATA),
4438             'u-boot:offset': len(COMPRESS_DATA),
4439             'u-boot:size': len(U_BOOT_DATA),
4440             'uncomp-size': len(COMPRESS_DATA + U_BOOT_DATA),
4441             'offset': 0,
4442             'image-pos': 0,
4443             'size': len(data),
4444             }
4445         self.assertEqual(expected, props)
4446
4447     def testCompressImageLess(self):
4448         """Test compression where compression reduces the image size"""
4449         self._CheckLz4()
4450         data, _, _, out_dtb_fname = self._DoReadFileDtb(
4451             '183_compress_image_less.dts', use_real_dtb=True, update_dtb=True)
4452         dtb = fdt.Fdt(out_dtb_fname)
4453         dtb.Scan()
4454         props = self._GetPropTree(dtb, ['offset', 'image-pos', 'size',
4455                                         'uncomp-size'])
4456         orig = self._decompress(data)
4457
4458         self.assertEquals(COMPRESS_DATA + COMPRESS_DATA + U_BOOT_DATA, orig)
4459
4460         # Do a sanity check on various fields
4461         image = control.images['image']
4462         entries = image.GetEntries()
4463         self.assertEqual(2, len(entries))
4464
4465         entry = entries['blob']
4466         self.assertEqual(COMPRESS_DATA_BIG, entry.data)
4467         self.assertEqual(len(COMPRESS_DATA_BIG), entry.size)
4468
4469         entry = entries['u-boot']
4470         self.assertEqual(U_BOOT_DATA, entry.data)
4471         self.assertEqual(len(U_BOOT_DATA), entry.size)
4472
4473         self.assertEqual(len(data), image.size)
4474         self.assertEqual(COMPRESS_DATA_BIG + U_BOOT_DATA, image.uncomp_data)
4475         self.assertEqual(len(COMPRESS_DATA_BIG + U_BOOT_DATA),
4476                          image.uncomp_size)
4477         orig = self._decompress(image.data)
4478         self.assertEqual(orig, image.uncomp_data)
4479
4480         expected = {
4481             'blob:offset': 0,
4482             'blob:size': len(COMPRESS_DATA_BIG),
4483             'u-boot:offset': len(COMPRESS_DATA_BIG),
4484             'u-boot:size': len(U_BOOT_DATA),
4485             'uncomp-size': len(COMPRESS_DATA_BIG + U_BOOT_DATA),
4486             'offset': 0,
4487             'image-pos': 0,
4488             'size': len(data),
4489             }
4490         self.assertEqual(expected, props)
4491
4492     def testCompressSectionSize(self):
4493         """Test compression of a section with a fixed size"""
4494         self._CheckLz4()
4495         data, _, _, out_dtb_fname = self._DoReadFileDtb(
4496             '184_compress_section_size.dts', use_real_dtb=True, update_dtb=True)
4497         dtb = fdt.Fdt(out_dtb_fname)
4498         dtb.Scan()
4499         props = self._GetPropTree(dtb, ['offset', 'image-pos', 'size',
4500                                         'uncomp-size'])
4501         orig = self._decompress(data)
4502         self.assertEquals(COMPRESS_DATA + U_BOOT_DATA, orig)
4503         expected = {
4504             'section/blob:offset': 0,
4505             'section/blob:size': len(COMPRESS_DATA),
4506             'section/u-boot:offset': len(COMPRESS_DATA),
4507             'section/u-boot:size': len(U_BOOT_DATA),
4508             'section:offset': 0,
4509             'section:image-pos': 0,
4510             'section:uncomp-size': len(COMPRESS_DATA + U_BOOT_DATA),
4511             'section:size': 0x30,
4512             'offset': 0,
4513             'image-pos': 0,
4514             'size': 0x30,
4515             }
4516         self.assertEqual(expected, props)
4517
4518     def testCompressSection(self):
4519         """Test compression of a section with no fixed size"""
4520         self._CheckLz4()
4521         data, _, _, out_dtb_fname = self._DoReadFileDtb(
4522             '185_compress_section.dts', use_real_dtb=True, update_dtb=True)
4523         dtb = fdt.Fdt(out_dtb_fname)
4524         dtb.Scan()
4525         props = self._GetPropTree(dtb, ['offset', 'image-pos', 'size',
4526                                         'uncomp-size'])
4527         orig = self._decompress(data)
4528         self.assertEquals(COMPRESS_DATA + U_BOOT_DATA, orig)
4529         expected = {
4530             'section/blob:offset': 0,
4531             'section/blob:size': len(COMPRESS_DATA),
4532             'section/u-boot:offset': len(COMPRESS_DATA),
4533             'section/u-boot:size': len(U_BOOT_DATA),
4534             'section:offset': 0,
4535             'section:image-pos': 0,
4536             'section:uncomp-size': len(COMPRESS_DATA + U_BOOT_DATA),
4537             'section:size': len(data),
4538             'offset': 0,
4539             'image-pos': 0,
4540             'size': len(data),
4541             }
4542         self.assertEqual(expected, props)
4543
4544     def testLz4Missing(self):
4545         """Test that binman still produces an image if lz4 is missing"""
4546         with test_util.capture_sys_output() as (_, stderr):
4547             self._DoTestFile('185_compress_section.dts',
4548                              force_missing_bintools='lz4')
4549         err = stderr.getvalue()
4550         self.assertRegex(err, "Image 'image'.*missing bintools.*: lz4")
4551
4552     def testCompressExtra(self):
4553         """Test compression of a section with no fixed size"""
4554         self._CheckLz4()
4555         data, _, _, out_dtb_fname = self._DoReadFileDtb(
4556             '186_compress_extra.dts', use_real_dtb=True, update_dtb=True)
4557         dtb = fdt.Fdt(out_dtb_fname)
4558         dtb.Scan()
4559         props = self._GetPropTree(dtb, ['offset', 'image-pos', 'size',
4560                                         'uncomp-size'])
4561
4562         base = data[len(U_BOOT_DATA):]
4563         self.assertEquals(U_BOOT_DATA, base[:len(U_BOOT_DATA)])
4564         rest = base[len(U_BOOT_DATA):]
4565
4566         # Check compressed data
4567         bintool = self.comp_bintools['lz4']
4568         expect1 = bintool.compress(COMPRESS_DATA + U_BOOT_DATA)
4569         data1 = rest[:len(expect1)]
4570         section1 = self._decompress(data1)
4571         self.assertEquals(expect1, data1)
4572         self.assertEquals(COMPRESS_DATA + U_BOOT_DATA, section1)
4573         rest1 = rest[len(expect1):]
4574
4575         expect2 = bintool.compress(COMPRESS_DATA + COMPRESS_DATA)
4576         data2 = rest1[:len(expect2)]
4577         section2 = self._decompress(data2)
4578         self.assertEquals(expect2, data2)
4579         self.assertEquals(COMPRESS_DATA + COMPRESS_DATA, section2)
4580         rest2 = rest1[len(expect2):]
4581
4582         expect_size = (len(U_BOOT_DATA) + len(U_BOOT_DATA) + len(expect1) +
4583                        len(expect2) + len(U_BOOT_DATA))
4584         #self.assertEquals(expect_size, len(data))
4585
4586         #self.assertEquals(U_BOOT_DATA, rest2)
4587
4588         self.maxDiff = None
4589         expected = {
4590             'u-boot:offset': 0,
4591             'u-boot:image-pos': 0,
4592             'u-boot:size': len(U_BOOT_DATA),
4593
4594             'base:offset': len(U_BOOT_DATA),
4595             'base:image-pos': len(U_BOOT_DATA),
4596             'base:size': len(data) - len(U_BOOT_DATA),
4597             'base/u-boot:offset': 0,
4598             'base/u-boot:image-pos': len(U_BOOT_DATA),
4599             'base/u-boot:size': len(U_BOOT_DATA),
4600             'base/u-boot2:offset': len(U_BOOT_DATA) + len(expect1) +
4601                 len(expect2),
4602             'base/u-boot2:image-pos': len(U_BOOT_DATA) * 2 + len(expect1) +
4603                 len(expect2),
4604             'base/u-boot2:size': len(U_BOOT_DATA),
4605
4606             'base/section:offset': len(U_BOOT_DATA),
4607             'base/section:image-pos': len(U_BOOT_DATA) * 2,
4608             'base/section:size': len(expect1),
4609             'base/section:uncomp-size': len(COMPRESS_DATA + U_BOOT_DATA),
4610             'base/section/blob:offset': 0,
4611             'base/section/blob:size': len(COMPRESS_DATA),
4612             'base/section/u-boot:offset': len(COMPRESS_DATA),
4613             'base/section/u-boot:size': len(U_BOOT_DATA),
4614
4615             'base/section2:offset': len(U_BOOT_DATA) + len(expect1),
4616             'base/section2:image-pos': len(U_BOOT_DATA) * 2 + len(expect1),
4617             'base/section2:size': len(expect2),
4618             'base/section2:uncomp-size': len(COMPRESS_DATA + COMPRESS_DATA),
4619             'base/section2/blob:offset': 0,
4620             'base/section2/blob:size': len(COMPRESS_DATA),
4621             'base/section2/blob2:offset': len(COMPRESS_DATA),
4622             'base/section2/blob2:size': len(COMPRESS_DATA),
4623
4624             'offset': 0,
4625             'image-pos': 0,
4626             'size': len(data),
4627             }
4628         self.assertEqual(expected, props)
4629
4630     def testSymbolsSubsection(self):
4631         """Test binman can assign symbols from a subsection"""
4632         self.checkSymbols('187_symbols_sub.dts', U_BOOT_SPL_DATA, 0x1c)
4633
4634     def testReadImageEntryArg(self):
4635         """Test reading an image that would need an entry arg to generate"""
4636         entry_args = {
4637             'cros-ec-rw-path': 'ecrw.bin',
4638         }
4639         data = self.data = self._DoReadFileDtb(
4640             '188_image_entryarg.dts',use_real_dtb=True, update_dtb=True,
4641             entry_args=entry_args)
4642
4643         image_fname = tools.get_output_filename('image.bin')
4644         orig_image = control.images['image']
4645
4646         # This should not generate an error about the missing 'cros-ec-rw-path'
4647         # since we are reading the image from a file. Compare with
4648         # testEntryArgsRequired()
4649         image = Image.FromFile(image_fname)
4650         self.assertEqual(orig_image.GetEntries().keys(),
4651                          image.GetEntries().keys())
4652
4653     def testFilesAlign(self):
4654         """Test alignment with files"""
4655         data = self._DoReadFile('190_files_align.dts')
4656
4657         # The first string is 15 bytes so will align to 16
4658         expect = FILES_DATA[:15] + b'\0' + FILES_DATA[15:]
4659         self.assertEqual(expect, data)
4660
4661     def testReadImageSkip(self):
4662         """Test reading an image and accessing its FDT map"""
4663         data = self.data = self._DoReadFileRealDtb('191_read_image_skip.dts')
4664         image_fname = tools.get_output_filename('image.bin')
4665         orig_image = control.images['image']
4666         image = Image.FromFile(image_fname)
4667         self.assertEqual(orig_image.GetEntries().keys(),
4668                          image.GetEntries().keys())
4669
4670         orig_entry = orig_image.GetEntries()['fdtmap']
4671         entry = image.GetEntries()['fdtmap']
4672         self.assertEqual(orig_entry.offset, entry.offset)
4673         self.assertEqual(orig_entry.size, entry.size)
4674         self.assertEqual(16, entry.image_pos)
4675
4676         u_boot = image.GetEntries()['section'].GetEntries()['u-boot']
4677
4678         self.assertEquals(U_BOOT_DATA, u_boot.ReadData())
4679
4680     def testTplNoDtb(self):
4681         """Test that an image with tpl/u-boot-tpl-nodtb.bin can be created"""
4682         self._SetupTplElf()
4683         data = self._DoReadFile('192_u_boot_tpl_nodtb.dts')
4684         self.assertEqual(U_BOOT_TPL_NODTB_DATA,
4685                          data[:len(U_BOOT_TPL_NODTB_DATA)])
4686
4687     def testTplBssPad(self):
4688         """Test that we can pad TPL's BSS with zeros"""
4689         # ELF file with a '__bss_size' symbol
4690         self._SetupTplElf()
4691         data = self._DoReadFile('193_tpl_bss_pad.dts')
4692         self.assertEqual(U_BOOT_TPL_DATA + tools.get_bytes(0, 10) + U_BOOT_DATA,
4693                          data)
4694
4695     def testTplBssPadMissing(self):
4696         """Test that a missing symbol is detected"""
4697         self._SetupTplElf('u_boot_ucode_ptr')
4698         with self.assertRaises(ValueError) as e:
4699             self._DoReadFile('193_tpl_bss_pad.dts')
4700         self.assertIn('Expected __bss_size symbol in tpl/u-boot-tpl',
4701                       str(e.exception))
4702
4703     def checkDtbSizes(self, data, pad_len, start):
4704         """Check the size arguments in a dtb embedded in an image
4705
4706         Args:
4707             data: The image data
4708             pad_len: Length of the pad section in the image, in bytes
4709             start: Start offset of the devicetree to examine, within the image
4710
4711         Returns:
4712             Size of the devicetree in bytes
4713         """
4714         dtb_data = data[start:]
4715         dtb = fdt.Fdt.FromData(dtb_data)
4716         fdt_size = dtb.GetFdtObj().totalsize()
4717         dtb.Scan()
4718         props = self._GetPropTree(dtb, 'size')
4719         self.assertEqual({
4720             'size': len(data),
4721             'u-boot-spl/u-boot-spl-bss-pad:size': pad_len,
4722             'u-boot-spl/u-boot-spl-dtb:size': 801,
4723             'u-boot-spl/u-boot-spl-nodtb:size': len(U_BOOT_SPL_NODTB_DATA),
4724             'u-boot-spl:size': 860,
4725             'u-boot-tpl:size': len(U_BOOT_TPL_DATA),
4726             'u-boot/u-boot-dtb:size': 781,
4727             'u-boot/u-boot-nodtb:size': len(U_BOOT_NODTB_DATA),
4728             'u-boot:size': 827,
4729             }, props)
4730         return fdt_size
4731
4732     def testExpanded(self):
4733         """Test that an expanded entry type is selected when needed"""
4734         self._SetupSplElf()
4735         self._SetupTplElf()
4736
4737         # SPL has a devicetree, TPL does not
4738         entry_args = {
4739             'spl-dtb': '1',
4740             'spl-bss-pad': 'y',
4741             'tpl-dtb': '',
4742         }
4743         self._DoReadFileDtb('194_fdt_incl.dts', use_expanded=True,
4744                             entry_args=entry_args)
4745         image = control.images['image']
4746         entries = image.GetEntries()
4747         self.assertEqual(3, len(entries))
4748
4749         # First, u-boot, which should be expanded into u-boot-nodtb and dtb
4750         self.assertIn('u-boot', entries)
4751         entry = entries['u-boot']
4752         self.assertEqual('u-boot-expanded', entry.etype)
4753         subent = entry.GetEntries()
4754         self.assertEqual(2, len(subent))
4755         self.assertIn('u-boot-nodtb', subent)
4756         self.assertIn('u-boot-dtb', subent)
4757
4758         # Second, u-boot-spl, which should be expanded into three parts
4759         self.assertIn('u-boot-spl', entries)
4760         entry = entries['u-boot-spl']
4761         self.assertEqual('u-boot-spl-expanded', entry.etype)
4762         subent = entry.GetEntries()
4763         self.assertEqual(3, len(subent))
4764         self.assertIn('u-boot-spl-nodtb', subent)
4765         self.assertIn('u-boot-spl-bss-pad', subent)
4766         self.assertIn('u-boot-spl-dtb', subent)
4767
4768         # Third, u-boot-tpl, which should be not be expanded, since TPL has no
4769         # devicetree
4770         self.assertIn('u-boot-tpl', entries)
4771         entry = entries['u-boot-tpl']
4772         self.assertEqual('u-boot-tpl', entry.etype)
4773         self.assertEqual(None, entry.GetEntries())
4774
4775     def testExpandedTpl(self):
4776         """Test that an expanded entry type is selected for TPL when needed"""
4777         self._SetupTplElf()
4778
4779         entry_args = {
4780             'tpl-bss-pad': 'y',
4781             'tpl-dtb': 'y',
4782         }
4783         self._DoReadFileDtb('195_fdt_incl_tpl.dts', use_expanded=True,
4784                             entry_args=entry_args)
4785         image = control.images['image']
4786         entries = image.GetEntries()
4787         self.assertEqual(1, len(entries))
4788
4789         # We only have u-boot-tpl, which be expanded
4790         self.assertIn('u-boot-tpl', entries)
4791         entry = entries['u-boot-tpl']
4792         self.assertEqual('u-boot-tpl-expanded', entry.etype)
4793         subent = entry.GetEntries()
4794         self.assertEqual(3, len(subent))
4795         self.assertIn('u-boot-tpl-nodtb', subent)
4796         self.assertIn('u-boot-tpl-bss-pad', subent)
4797         self.assertIn('u-boot-tpl-dtb', subent)
4798
4799     def testExpandedNoPad(self):
4800         """Test an expanded entry without BSS pad enabled"""
4801         self._SetupSplElf()
4802         self._SetupTplElf()
4803
4804         # SPL has a devicetree, TPL does not
4805         entry_args = {
4806             'spl-dtb': 'something',
4807             'spl-bss-pad': 'n',
4808             'tpl-dtb': '',
4809         }
4810         self._DoReadFileDtb('194_fdt_incl.dts', use_expanded=True,
4811                             entry_args=entry_args)
4812         image = control.images['image']
4813         entries = image.GetEntries()
4814
4815         # Just check u-boot-spl, which should be expanded into two parts
4816         self.assertIn('u-boot-spl', entries)
4817         entry = entries['u-boot-spl']
4818         self.assertEqual('u-boot-spl-expanded', entry.etype)
4819         subent = entry.GetEntries()
4820         self.assertEqual(2, len(subent))
4821         self.assertIn('u-boot-spl-nodtb', subent)
4822         self.assertIn('u-boot-spl-dtb', subent)
4823
4824     def testExpandedTplNoPad(self):
4825         """Test that an expanded entry type with padding disabled in TPL"""
4826         self._SetupTplElf()
4827
4828         entry_args = {
4829             'tpl-bss-pad': '',
4830             'tpl-dtb': 'y',
4831         }
4832         self._DoReadFileDtb('195_fdt_incl_tpl.dts', use_expanded=True,
4833                             entry_args=entry_args)
4834         image = control.images['image']
4835         entries = image.GetEntries()
4836         self.assertEqual(1, len(entries))
4837
4838         # We only have u-boot-tpl, which be expanded
4839         self.assertIn('u-boot-tpl', entries)
4840         entry = entries['u-boot-tpl']
4841         self.assertEqual('u-boot-tpl-expanded', entry.etype)
4842         subent = entry.GetEntries()
4843         self.assertEqual(2, len(subent))
4844         self.assertIn('u-boot-tpl-nodtb', subent)
4845         self.assertIn('u-boot-tpl-dtb', subent)
4846
4847     def testFdtInclude(self):
4848         """Test that an Fdt is update within all binaries"""
4849         self._SetupSplElf()
4850         self._SetupTplElf()
4851
4852         # SPL has a devicetree, TPL does not
4853         self.maxDiff = None
4854         entry_args = {
4855             'spl-dtb': '1',
4856             'spl-bss-pad': 'y',
4857             'tpl-dtb': '',
4858         }
4859         # Build the image. It includes two separate devicetree binaries, each
4860         # with their own contents, but all contain the binman definition.
4861         data = self._DoReadFileDtb(
4862             '194_fdt_incl.dts', use_real_dtb=True, use_expanded=True,
4863             update_dtb=True, entry_args=entry_args)[0]
4864         pad_len = 10
4865
4866         # Check the U-Boot dtb
4867         start = len(U_BOOT_NODTB_DATA)
4868         fdt_size = self.checkDtbSizes(data, pad_len, start)
4869
4870         # Now check SPL
4871         start += fdt_size + len(U_BOOT_SPL_NODTB_DATA) + pad_len
4872         fdt_size = self.checkDtbSizes(data, pad_len, start)
4873
4874         # TPL has no devicetree
4875         start += fdt_size + len(U_BOOT_TPL_DATA)
4876         self.assertEqual(len(data), start)
4877
4878     def testSymbolsExpanded(self):
4879         """Test binman can assign symbols in expanded entries"""
4880         entry_args = {
4881             'spl-dtb': '1',
4882         }
4883         self.checkSymbols('197_symbols_expand.dts', U_BOOT_SPL_NODTB_DATA +
4884                           U_BOOT_SPL_DTB_DATA, 0x38,
4885                           entry_args=entry_args, use_expanded=True)
4886
4887     def testCollection(self):
4888         """Test a collection"""
4889         data = self._DoReadFile('198_collection.dts')
4890         self.assertEqual(U_BOOT_NODTB_DATA + U_BOOT_DTB_DATA +
4891                          tools.get_bytes(0xff, 2) + U_BOOT_NODTB_DATA +
4892                          tools.get_bytes(0xfe, 3) + U_BOOT_DTB_DATA,
4893                          data)
4894
4895     def testCollectionSection(self):
4896         """Test a collection where a section must be built first"""
4897         # Sections never have their contents when GetData() is called, but when
4898         # BuildSectionData() is called with required=True, a section will force
4899         # building the contents, producing an error is anything is still
4900         # missing.
4901         data = self._DoReadFile('199_collection_section.dts')
4902         section = U_BOOT_NODTB_DATA + U_BOOT_DTB_DATA
4903         self.assertEqual(section + U_BOOT_DATA + tools.get_bytes(0xff, 2) +
4904                          section + tools.get_bytes(0xfe, 3) + U_BOOT_DATA,
4905                          data)
4906
4907     def testAlignDefault(self):
4908         """Test that default alignment works on sections"""
4909         data = self._DoReadFile('200_align_default.dts')
4910         expected = (U_BOOT_DATA + tools.get_bytes(0, 8 - len(U_BOOT_DATA)) +
4911                     U_BOOT_DATA)
4912         # Special alignment for section
4913         expected += tools.get_bytes(0, 32 - len(expected))
4914         # No alignment within the nested section
4915         expected += U_BOOT_DATA + U_BOOT_NODTB_DATA;
4916         # Now the final piece, which should be default-aligned
4917         expected += tools.get_bytes(0, 88 - len(expected)) + U_BOOT_NODTB_DATA
4918         self.assertEqual(expected, data)
4919
4920     def testPackOpenSBI(self):
4921         """Test that an image with an OpenSBI binary can be created"""
4922         data = self._DoReadFile('201_opensbi.dts')
4923         self.assertEqual(OPENSBI_DATA, data[:len(OPENSBI_DATA)])
4924
4925     def testSectionsSingleThread(self):
4926         """Test sections without multithreading"""
4927         data = self._DoReadFileDtb('055_sections.dts', threads=0)[0]
4928         expected = (U_BOOT_DATA + tools.get_bytes(ord('!'), 12) +
4929                     U_BOOT_DATA + tools.get_bytes(ord('a'), 12) +
4930                     U_BOOT_DATA + tools.get_bytes(ord('&'), 4))
4931         self.assertEqual(expected, data)
4932
4933     def testThreadTimeout(self):
4934         """Test handling a thread that takes too long"""
4935         with self.assertRaises(ValueError) as e:
4936             self._DoTestFile('202_section_timeout.dts',
4937                              test_section_timeout=True)
4938         self.assertIn("Timed out obtaining contents", str(e.exception))
4939
4940     def testTiming(self):
4941         """Test output of timing information"""
4942         data = self._DoReadFile('055_sections.dts')
4943         with test_util.capture_sys_output() as (stdout, stderr):
4944             state.TimingShow()
4945         self.assertIn('read:', stdout.getvalue())
4946         self.assertIn('compress:', stdout.getvalue())
4947
4948     def testUpdateFdtInElf(self):
4949         """Test that we can update the devicetree in an ELF file"""
4950         if not elf.ELF_TOOLS:
4951             self.skipTest('Python elftools not available')
4952         infile = elf_fname = self.ElfTestFile('u_boot_binman_embed')
4953         outfile = os.path.join(self._indir, 'u-boot.out')
4954         begin_sym = 'dtb_embed_begin'
4955         end_sym = 'dtb_embed_end'
4956         retcode = self._DoTestFile(
4957             '060_fdt_update.dts', update_dtb=True,
4958             update_fdt_in_elf=','.join([infile,outfile,begin_sym,end_sym]))
4959         self.assertEqual(0, retcode)
4960
4961         # Check that the output file does in fact contact a dtb with the binman
4962         # definition in the correct place
4963         syms = elf.GetSymbolFileOffset(infile,
4964                                        ['dtb_embed_begin', 'dtb_embed_end'])
4965         data = tools.read_file(outfile)
4966         dtb_data = data[syms['dtb_embed_begin'].offset:
4967                         syms['dtb_embed_end'].offset]
4968
4969         dtb = fdt.Fdt.FromData(dtb_data)
4970         dtb.Scan()
4971         props = self._GetPropTree(dtb, BASE_DTB_PROPS + REPACK_DTB_PROPS)
4972         self.assertEqual({
4973             'image-pos': 0,
4974             'offset': 0,
4975             '_testing:offset': 32,
4976             '_testing:size': 2,
4977             '_testing:image-pos': 32,
4978             'section@0/u-boot:offset': 0,
4979             'section@0/u-boot:size': len(U_BOOT_DATA),
4980             'section@0/u-boot:image-pos': 0,
4981             'section@0:offset': 0,
4982             'section@0:size': 16,
4983             'section@0:image-pos': 0,
4984
4985             'section@1/u-boot:offset': 0,
4986             'section@1/u-boot:size': len(U_BOOT_DATA),
4987             'section@1/u-boot:image-pos': 16,
4988             'section@1:offset': 16,
4989             'section@1:size': 16,
4990             'section@1:image-pos': 16,
4991             'size': 40
4992         }, props)
4993
4994     def testUpdateFdtInElfInvalid(self):
4995         """Test that invalid args are detected with --update-fdt-in-elf"""
4996         with self.assertRaises(ValueError) as e:
4997             self._DoTestFile('060_fdt_update.dts', update_fdt_in_elf='fred')
4998         self.assertIn("Invalid args ['fred'] to --update-fdt-in-elf",
4999                       str(e.exception))
5000
5001     def testUpdateFdtInElfNoSyms(self):
5002         """Test that missing symbols are detected with --update-fdt-in-elf"""
5003         if not elf.ELF_TOOLS:
5004             self.skipTest('Python elftools not available')
5005         infile = elf_fname = self.ElfTestFile('u_boot_binman_embed')
5006         outfile = ''
5007         begin_sym = 'wrong_begin'
5008         end_sym = 'wrong_end'
5009         with self.assertRaises(ValueError) as e:
5010             self._DoTestFile(
5011                 '060_fdt_update.dts',
5012                 update_fdt_in_elf=','.join([infile,outfile,begin_sym,end_sym]))
5013         self.assertIn("Expected two symbols 'wrong_begin' and 'wrong_end': got 0:",
5014                       str(e.exception))
5015
5016     def testUpdateFdtInElfTooSmall(self):
5017         """Test that an over-large dtb is detected with --update-fdt-in-elf"""
5018         if not elf.ELF_TOOLS:
5019             self.skipTest('Python elftools not available')
5020         infile = elf_fname = self.ElfTestFile('u_boot_binman_embed_sm')
5021         outfile = os.path.join(self._indir, 'u-boot.out')
5022         begin_sym = 'dtb_embed_begin'
5023         end_sym = 'dtb_embed_end'
5024         with self.assertRaises(ValueError) as e:
5025             self._DoTestFile(
5026                 '060_fdt_update.dts', update_dtb=True,
5027                 update_fdt_in_elf=','.join([infile,outfile,begin_sym,end_sym]))
5028         self.assertRegex(
5029             str(e.exception),
5030             "Not enough space in '.*u_boot_binman_embed_sm' for data length.*")
5031
5032     def testVersion(self):
5033         """Test we can get the binman version"""
5034         version = '(unreleased)'
5035         self.assertEqual(version, state.GetVersion(self._indir))
5036
5037         with self.assertRaises(SystemExit):
5038             with test_util.capture_sys_output() as (_, stderr):
5039                 self._DoBinman('-V')
5040         self.assertEqual('Binman %s\n' % version, stderr.getvalue())
5041
5042         # Try running the tool too, just to be safe
5043         result = self._RunBinman('-V')
5044         self.assertEqual('Binman %s\n' % version, result.stderr)
5045
5046         # Set up a version file to make sure that works
5047         version = 'v2025.01-rc2'
5048         tools.write_file(os.path.join(self._indir, 'version'), version,
5049                         binary=False)
5050         self.assertEqual(version, state.GetVersion(self._indir))
5051
5052     def testAltFormat(self):
5053         """Test that alternative formats can be used to extract"""
5054         self._DoReadFileRealDtb('213_fdtmap_alt_format.dts')
5055
5056         try:
5057             tmpdir, updated_fname = self._SetupImageInTmpdir()
5058             with test_util.capture_sys_output() as (stdout, _):
5059                 self._DoBinman('extract', '-i', updated_fname, '-F', 'list')
5060             self.assertEqual(
5061                 '''Flag (-F)   Entry type            Description
5062 fdt         fdtmap                Extract the devicetree blob from the fdtmap
5063 ''',
5064                 stdout.getvalue())
5065
5066             dtb = os.path.join(tmpdir, 'fdt.dtb')
5067             self._DoBinman('extract', '-i', updated_fname, '-F', 'fdt', '-f',
5068                            dtb, 'fdtmap')
5069
5070             # Check that we can read it and it can be scanning, meaning it does
5071             # not have a 16-byte fdtmap header
5072             data = tools.read_file(dtb)
5073             dtb = fdt.Fdt.FromData(data)
5074             dtb.Scan()
5075
5076             # Now check u-boot which has no alt_format
5077             fname = os.path.join(tmpdir, 'fdt.dtb')
5078             self._DoBinman('extract', '-i', updated_fname, '-F', 'dummy',
5079                            '-f', fname, 'u-boot')
5080             data = tools.read_file(fname)
5081             self.assertEqual(U_BOOT_DATA, data)
5082
5083         finally:
5084             shutil.rmtree(tmpdir)
5085
5086     def testExtblobList(self):
5087         """Test an image with an external blob list"""
5088         data = self._DoReadFile('215_blob_ext_list.dts')
5089         self.assertEqual(REFCODE_DATA + FSP_M_DATA, data)
5090
5091     def testExtblobListMissing(self):
5092         """Test an image with a missing external blob"""
5093         with self.assertRaises(ValueError) as e:
5094             self._DoReadFile('216_blob_ext_list_missing.dts')
5095         self.assertIn("Filename 'missing-file' not found in input path",
5096                       str(e.exception))
5097
5098     def testExtblobListMissingOk(self):
5099         """Test an image with an missing external blob that is allowed"""
5100         with test_util.capture_sys_output() as (stdout, stderr):
5101             self._DoTestFile('216_blob_ext_list_missing.dts',
5102                              allow_missing=True)
5103         err = stderr.getvalue()
5104         self.assertRegex(err, "Image 'image'.*missing.*: blob-ext")
5105
5106     def testFip(self):
5107         """Basic test of generation of an ARM Firmware Image Package (FIP)"""
5108         data = self._DoReadFile('203_fip.dts')
5109         hdr, fents = fip_util.decode_fip(data)
5110         self.assertEqual(fip_util.HEADER_MAGIC, hdr.name)
5111         self.assertEqual(fip_util.HEADER_SERIAL, hdr.serial)
5112         self.assertEqual(0x123, hdr.flags)
5113
5114         self.assertEqual(2, len(fents))
5115
5116         fent = fents[0]
5117         self.assertEqual(
5118             bytes([0x47,  0xd4, 0x08, 0x6d, 0x4c, 0xfe, 0x98, 0x46,
5119                   0x9b, 0x95, 0x29, 0x50, 0xcb, 0xbd, 0x5a, 0x0]), fent.uuid)
5120         self.assertEqual('soc-fw', fent.fip_type)
5121         self.assertEqual(0x88, fent.offset)
5122         self.assertEqual(len(ATF_BL31_DATA), fent.size)
5123         self.assertEqual(0x123456789abcdef, fent.flags)
5124         self.assertEqual(ATF_BL31_DATA, fent.data)
5125         self.assertEqual(True, fent.valid)
5126
5127         fent = fents[1]
5128         self.assertEqual(
5129             bytes([0x65, 0x92, 0x27, 0x03, 0x2f, 0x74, 0xe6, 0x44,
5130              0x8d, 0xff, 0x57, 0x9a, 0xc1, 0xff, 0x06, 0x10]), fent.uuid)
5131         self.assertEqual('scp-fwu-cfg', fent.fip_type)
5132         self.assertEqual(0x8c, fent.offset)
5133         self.assertEqual(len(ATF_BL31_DATA), fent.size)
5134         self.assertEqual(0, fent.flags)
5135         self.assertEqual(ATF_BL2U_DATA, fent.data)
5136         self.assertEqual(True, fent.valid)
5137
5138     def testFipOther(self):
5139         """Basic FIP with something that isn't a external blob"""
5140         data = self._DoReadFile('204_fip_other.dts')
5141         hdr, fents = fip_util.decode_fip(data)
5142
5143         self.assertEqual(2, len(fents))
5144         fent = fents[1]
5145         self.assertEqual('rot-cert', fent.fip_type)
5146         self.assertEqual(b'aa', fent.data)
5147
5148     def testFipNoType(self):
5149         """FIP with an entry of an unknown type"""
5150         with self.assertRaises(ValueError) as e:
5151             self._DoReadFile('205_fip_no_type.dts')
5152         self.assertIn("Must provide a fip-type (node name 'u-boot' is not a known FIP type)",
5153                       str(e.exception))
5154
5155     def testFipUuid(self):
5156         """Basic FIP with a manual uuid"""
5157         data = self._DoReadFile('206_fip_uuid.dts')
5158         hdr, fents = fip_util.decode_fip(data)
5159
5160         self.assertEqual(2, len(fents))
5161         fent = fents[1]
5162         self.assertEqual(None, fent.fip_type)
5163         self.assertEqual(
5164             bytes([0xfc, 0x65, 0x13, 0x92, 0x4a, 0x5b, 0x11, 0xec,
5165                    0x94, 0x35, 0xff, 0x2d, 0x1c, 0xfc, 0x79, 0x9c]),
5166             fent.uuid)
5167         self.assertEqual(U_BOOT_DATA, fent.data)
5168
5169     def testFipLs(self):
5170         """Test listing a FIP"""
5171         data = self._DoReadFileRealDtb('207_fip_ls.dts')
5172         hdr, fents = fip_util.decode_fip(data)
5173
5174         try:
5175             tmpdir, updated_fname = self._SetupImageInTmpdir()
5176             with test_util.capture_sys_output() as (stdout, stderr):
5177                 self._DoBinman('ls', '-i', updated_fname)
5178         finally:
5179             shutil.rmtree(tmpdir)
5180         lines = stdout.getvalue().splitlines()
5181         expected = [
5182 'Name        Image-pos  Size  Entry-type  Offset  Uncomp-size',
5183 '--------------------------------------------------------------',
5184 'image               0   2d3  section          0',
5185 '  atf-fip           0    90  atf-fip          0',
5186 '    soc-fw         88     4  blob-ext        88',
5187 '    u-boot         8c     4  u-boot          8c',
5188 '  fdtmap           90   243  fdtmap          90',
5189 ]
5190         self.assertEqual(expected, lines)
5191
5192         image = control.images['image']
5193         entries = image.GetEntries()
5194         fdtmap = entries['fdtmap']
5195
5196         fdtmap_data = data[fdtmap.image_pos:fdtmap.image_pos + fdtmap.size]
5197         magic = fdtmap_data[:8]
5198         self.assertEqual(b'_FDTMAP_', magic)
5199         self.assertEqual(tools.get_bytes(0, 8), fdtmap_data[8:16])
5200
5201         fdt_data = fdtmap_data[16:]
5202         dtb = fdt.Fdt.FromData(fdt_data)
5203         dtb.Scan()
5204         props = self._GetPropTree(dtb, BASE_DTB_PROPS, prefix='/')
5205         self.assertEqual({
5206             'atf-fip/soc-fw:image-pos': 136,
5207             'atf-fip/soc-fw:offset': 136,
5208             'atf-fip/soc-fw:size': 4,
5209             'atf-fip/u-boot:image-pos': 140,
5210             'atf-fip/u-boot:offset': 140,
5211             'atf-fip/u-boot:size': 4,
5212             'atf-fip:image-pos': 0,
5213             'atf-fip:offset': 0,
5214             'atf-fip:size': 144,
5215             'image-pos': 0,
5216             'offset': 0,
5217             'fdtmap:image-pos': fdtmap.image_pos,
5218             'fdtmap:offset': fdtmap.offset,
5219             'fdtmap:size': len(fdtmap_data),
5220             'size': len(data),
5221         }, props)
5222
5223     def testFipExtractOneEntry(self):
5224         """Test extracting a single entry fron an FIP"""
5225         self._DoReadFileRealDtb('207_fip_ls.dts')
5226         image_fname = tools.get_output_filename('image.bin')
5227         fname = os.path.join(self._indir, 'output.extact')
5228         control.ExtractEntries(image_fname, fname, None, ['atf-fip/u-boot'])
5229         data = tools.read_file(fname)
5230         self.assertEqual(U_BOOT_DATA, data)
5231
5232     def testFipReplace(self):
5233         """Test replacing a single file in a FIP"""
5234         expected = U_BOOT_DATA + tools.get_bytes(0x78, 50)
5235         data = self._DoReadFileRealDtb('208_fip_replace.dts')
5236         updated_fname = tools.get_output_filename('image-updated.bin')
5237         tools.write_file(updated_fname, data)
5238         entry_name = 'atf-fip/u-boot'
5239         control.WriteEntry(updated_fname, entry_name, expected,
5240                            allow_resize=True)
5241         actual = control.ReadEntry(updated_fname, entry_name)
5242         self.assertEqual(expected, actual)
5243
5244         new_data = tools.read_file(updated_fname)
5245         hdr, fents = fip_util.decode_fip(new_data)
5246
5247         self.assertEqual(2, len(fents))
5248
5249         # Check that the FIP entry is updated
5250         fent = fents[1]
5251         self.assertEqual(0x8c, fent.offset)
5252         self.assertEqual(len(expected), fent.size)
5253         self.assertEqual(0, fent.flags)
5254         self.assertEqual(expected, fent.data)
5255         self.assertEqual(True, fent.valid)
5256
5257     def testFipMissing(self):
5258         with test_util.capture_sys_output() as (stdout, stderr):
5259             self._DoTestFile('209_fip_missing.dts', allow_missing=True)
5260         err = stderr.getvalue()
5261         self.assertRegex(err, "Image 'image'.*missing.*: rmm-fw")
5262
5263     def testFipSize(self):
5264         """Test a FIP with a size property"""
5265         data = self._DoReadFile('210_fip_size.dts')
5266         self.assertEqual(0x100 + len(U_BOOT_DATA), len(data))
5267         hdr, fents = fip_util.decode_fip(data)
5268         self.assertEqual(fip_util.HEADER_MAGIC, hdr.name)
5269         self.assertEqual(fip_util.HEADER_SERIAL, hdr.serial)
5270
5271         self.assertEqual(1, len(fents))
5272
5273         fent = fents[0]
5274         self.assertEqual('soc-fw', fent.fip_type)
5275         self.assertEqual(0x60, fent.offset)
5276         self.assertEqual(len(ATF_BL31_DATA), fent.size)
5277         self.assertEqual(ATF_BL31_DATA, fent.data)
5278         self.assertEqual(True, fent.valid)
5279
5280         rest = data[0x60 + len(ATF_BL31_DATA):0x100]
5281         self.assertEqual(tools.get_bytes(0xff, len(rest)), rest)
5282
5283     def testFipBadAlign(self):
5284         """Test that an invalid alignment value in a FIP is detected"""
5285         with self.assertRaises(ValueError) as e:
5286             self._DoTestFile('211_fip_bad_align.dts')
5287         self.assertIn(
5288             "Node \'/binman/atf-fip\': FIP alignment 31 must be a power of two",
5289             str(e.exception))
5290
5291     def testFipCollection(self):
5292         """Test using a FIP in a collection"""
5293         data = self._DoReadFile('212_fip_collection.dts')
5294         entry1 = control.images['image'].GetEntries()['collection']
5295         data1 = data[:entry1.size]
5296         hdr1, fents2 = fip_util.decode_fip(data1)
5297
5298         entry2 = control.images['image'].GetEntries()['atf-fip']
5299         data2 = data[entry2.offset:entry2.offset + entry2.size]
5300         hdr1, fents2 = fip_util.decode_fip(data2)
5301
5302         # The 'collection' entry should have U-Boot included at the end
5303         self.assertEqual(entry1.size - len(U_BOOT_DATA), entry2.size)
5304         self.assertEqual(data1, data2 + U_BOOT_DATA)
5305         self.assertEqual(U_BOOT_DATA, data1[-4:])
5306
5307         # There should be a U-Boot after the final FIP
5308         self.assertEqual(U_BOOT_DATA, data[-4:])
5309
5310     def testFakeBlob(self):
5311         """Test handling of faking an external blob"""
5312         with test_util.capture_sys_output() as (stdout, stderr):
5313             self._DoTestFile('217_fake_blob.dts', allow_missing=True,
5314                              allow_fake_blobs=True)
5315         err = stderr.getvalue()
5316         self.assertRegex(
5317             err,
5318             "Image '.*' has faked external blobs and is non-functional: .*")
5319
5320     def testExtblobListFaked(self):
5321         """Test an extblob with missing external blob that are faked"""
5322         with test_util.capture_sys_output() as (stdout, stderr):
5323             self._DoTestFile('216_blob_ext_list_missing.dts',
5324                              allow_fake_blobs=True)
5325         err = stderr.getvalue()
5326         self.assertRegex(err, "Image 'image'.*faked.*: blob-ext-list")
5327
5328     def testListBintools(self):
5329         args = ['tool', '--list']
5330         with test_util.capture_sys_output() as (stdout, _):
5331             self._DoBinman(*args)
5332         out = stdout.getvalue().splitlines()
5333         self.assertTrue(len(out) >= 2)
5334
5335     def testFetchBintools(self):
5336         def fail_download(url):
5337             """Take the tools.download() function by raising an exception"""
5338             raise urllib.error.URLError('my error')
5339
5340         args = ['tool']
5341         with self.assertRaises(ValueError) as e:
5342             self._DoBinman(*args)
5343         self.assertIn("Invalid arguments to 'tool' subcommand",
5344                       str(e.exception))
5345
5346         args = ['tool', '--fetch']
5347         with self.assertRaises(ValueError) as e:
5348             self._DoBinman(*args)
5349         self.assertIn('Please specify bintools to fetch', str(e.exception))
5350
5351         args = ['tool', '--fetch', '_testing']
5352         with unittest.mock.patch.object(tools, 'download',
5353                                         side_effect=fail_download):
5354             with test_util.capture_sys_output() as (stdout, _):
5355                 self._DoBinman(*args)
5356         self.assertIn('failed to fetch with all methods', stdout.getvalue())
5357
5358     def testBintoolDocs(self):
5359         """Test for creation of bintool documentation"""
5360         with test_util.capture_sys_output() as (stdout, stderr):
5361             control.write_bintool_docs(control.bintool.Bintool.get_tool_list())
5362         self.assertTrue(len(stdout.getvalue()) > 0)
5363
5364     def testBintoolDocsMissing(self):
5365         """Test handling of missing bintool documentation"""
5366         with self.assertRaises(ValueError) as e:
5367             with test_util.capture_sys_output() as (stdout, stderr):
5368                 control.write_bintool_docs(
5369                     control.bintool.Bintool.get_tool_list(), 'mkimage')
5370         self.assertIn('Documentation is missing for modules: mkimage',
5371                       str(e.exception))
5372
5373     def testListWithGenNode(self):
5374         """Check handling of an FDT map when the section cannot be found"""
5375         entry_args = {
5376             'of-list': 'test-fdt1 test-fdt2',
5377         }
5378         data = self._DoReadFileDtb(
5379             '219_fit_gennode.dts',
5380             entry_args=entry_args,
5381             use_real_dtb=True,
5382             extra_indirs=[os.path.join(self._indir, TEST_FDT_SUBDIR)])
5383
5384         try:
5385             tmpdir, updated_fname = self._SetupImageInTmpdir()
5386             with test_util.capture_sys_output() as (stdout, stderr):
5387                 self._RunBinman('ls', '-i', updated_fname)
5388         finally:
5389             shutil.rmtree(tmpdir)
5390
5391     def testFitSubentryUsesBintool(self):
5392         """Test that binman FIT subentries can use bintools"""
5393         command.test_result = self._HandleGbbCommand
5394         entry_args = {
5395             'keydir': 'devkeys',
5396             'bmpblk': 'bmpblk.bin',
5397         }
5398         data, _, _, _ = self._DoReadFileDtb('220_fit_subentry_bintool.dts',
5399                 entry_args=entry_args)
5400
5401         expected = (GBB_DATA + GBB_DATA + tools.get_bytes(0, 8) +
5402                     tools.get_bytes(0, 0x2180 - 16))
5403         self.assertIn(expected, data)
5404
5405     def testFitSubentryMissingBintool(self):
5406         """Test that binman reports missing bintools for FIT subentries"""
5407         entry_args = {
5408             'keydir': 'devkeys',
5409         }
5410         with test_util.capture_sys_output() as (_, stderr):
5411             self._DoTestFile('220_fit_subentry_bintool.dts',
5412                     force_missing_bintools='futility', entry_args=entry_args)
5413         err = stderr.getvalue()
5414         self.assertRegex(err, "Image 'image'.*missing bintools.*: futility")
5415
5416     def testFitSubentryHashSubnode(self):
5417         """Test an image with a FIT inside"""
5418         self._SetupSplElf()
5419         data, _, _, out_dtb_name = self._DoReadFileDtb(
5420             '221_fit_subentry_hash.dts', use_real_dtb=True, update_dtb=True)
5421
5422         mkimage_dtb = fdt.Fdt.FromData(data)
5423         mkimage_dtb.Scan()
5424         binman_dtb = fdt.Fdt(out_dtb_name)
5425         binman_dtb.Scan()
5426
5427         # Check that binman didn't add hash values
5428         fnode = binman_dtb.GetNode('/binman/fit/images/kernel/hash')
5429         self.assertNotIn('value', fnode.props)
5430
5431         fnode = binman_dtb.GetNode('/binman/fit/images/fdt-1/hash')
5432         self.assertNotIn('value', fnode.props)
5433
5434         # Check that mkimage added hash values
5435         fnode = mkimage_dtb.GetNode('/images/kernel/hash')
5436         self.assertIn('value', fnode.props)
5437
5438         fnode = mkimage_dtb.GetNode('/images/fdt-1/hash')
5439         self.assertIn('value', fnode.props)
5440
5441     def testPackTeeOs(self):
5442         """Test that an image with an TEE binary can be created"""
5443         data = self._DoReadFile('222_tee_os.dts')
5444         self.assertEqual(TEE_OS_DATA, data[:len(TEE_OS_DATA)])
5445
5446     def testFitFdtOper(self):
5447         """Check handling of a specified FIT operation"""
5448         entry_args = {
5449             'of-list': 'test-fdt1 test-fdt2',
5450             'default-dt': 'test-fdt2',
5451         }
5452         self._DoReadFileDtb(
5453             '223_fit_fdt_oper.dts',
5454             entry_args=entry_args,
5455             extra_indirs=[os.path.join(self._indir, TEST_FDT_SUBDIR)])[0]
5456
5457     def testFitFdtBadOper(self):
5458         """Check handling of an FDT map when the section cannot be found"""
5459         with self.assertRaises(ValueError) as exc:
5460             self._DoReadFileDtb('224_fit_bad_oper.dts')
5461         self.assertIn("Node '/binman/fit': subnode 'images/@fdt-SEQ': Unknown operation 'unknown'",
5462                       str(exc.exception))
5463
5464     def test_uses_expand_size(self):
5465         """Test that the 'expand-size' property cannot be used anymore"""
5466         with self.assertRaises(ValueError) as e:
5467            data = self._DoReadFile('225_expand_size_bad.dts')
5468         self.assertIn(
5469             "Node '/binman/u-boot': Please use 'extend-size' instead of 'expand-size'",
5470             str(e.exception))
5471
5472     def testFitSplitElf(self):
5473         """Test an image with an FIT with an split-elf operation"""
5474         if not elf.ELF_TOOLS:
5475             self.skipTest('Python elftools not available')
5476         entry_args = {
5477             'of-list': 'test-fdt1 test-fdt2',
5478             'default-dt': 'test-fdt2',
5479             'atf-bl31-path': 'bl31.elf',
5480             'tee-os-path': 'tee.elf',
5481         }
5482         test_subdir = os.path.join(self._indir, TEST_FDT_SUBDIR)
5483         data = self._DoReadFileDtb(
5484             '226_fit_split_elf.dts',
5485             entry_args=entry_args,
5486             extra_indirs=[test_subdir])[0]
5487
5488         self.assertEqual(U_BOOT_NODTB_DATA, data[-len(U_BOOT_NODTB_DATA):])
5489         fit_data = data[len(U_BOOT_DATA):-len(U_BOOT_NODTB_DATA)]
5490
5491         base_keys = {'description', 'type', 'arch', 'os', 'compression',
5492                      'data', 'load'}
5493         dtb = fdt.Fdt.FromData(fit_data)
5494         dtb.Scan()
5495
5496         elf_data = tools.read_file(os.path.join(self._indir, 'bl31.elf'))
5497         segments, entry = elf.read_loadable_segments(elf_data)
5498
5499         # We assume there are two segments
5500         self.assertEquals(2, len(segments))
5501
5502         atf1 = dtb.GetNode('/images/atf-1')
5503         _, start, data = segments[0]
5504         self.assertEqual(base_keys | {'entry'}, atf1.props.keys())
5505         self.assertEqual(entry,
5506                          fdt_util.fdt32_to_cpu(atf1.props['entry'].value))
5507         self.assertEqual(start,
5508                          fdt_util.fdt32_to_cpu(atf1.props['load'].value))
5509         self.assertEqual(data, atf1.props['data'].bytes)
5510
5511         hash_node = atf1.FindNode('hash')
5512         self.assertIsNotNone(hash_node)
5513         self.assertEqual({'algo', 'value'}, hash_node.props.keys())
5514
5515         atf2 = dtb.GetNode('/images/atf-2')
5516         self.assertEqual(base_keys, atf2.props.keys())
5517         _, start, data = segments[1]
5518         self.assertEqual(start,
5519                          fdt_util.fdt32_to_cpu(atf2.props['load'].value))
5520         self.assertEqual(data, atf2.props['data'].bytes)
5521
5522         hash_node = atf2.FindNode('hash')
5523         self.assertIsNotNone(hash_node)
5524         self.assertEqual({'algo', 'value'}, hash_node.props.keys())
5525
5526         hash_node = dtb.GetNode('/images/tee-1/hash-1')
5527         self.assertIsNotNone(hash_node)
5528         self.assertEqual({'algo', 'value'}, hash_node.props.keys())
5529
5530         conf = dtb.GetNode('/configurations')
5531         self.assertEqual({'default'}, conf.props.keys())
5532
5533         for subnode in conf.subnodes:
5534             self.assertEqual({'description', 'fdt', 'loadables'},
5535                              subnode.props.keys())
5536             self.assertEqual(
5537                 ['atf-1', 'atf-2', 'tee-1', 'tee-2'],
5538                 fdt_util.GetStringList(subnode, 'loadables'))
5539
5540     def _check_bad_fit(self, dts):
5541         """Check a bad FIT
5542
5543         This runs with the given dts and returns the assertion raised
5544
5545         Args:
5546             dts (str): dts filename to use
5547
5548         Returns:
5549             str: Assertion string raised
5550         """
5551         entry_args = {
5552             'of-list': 'test-fdt1 test-fdt2',
5553             'default-dt': 'test-fdt2',
5554             'atf-bl31-path': 'bl31.elf',
5555             'tee-os-path': 'tee.elf',
5556         }
5557         test_subdir = os.path.join(self._indir, TEST_FDT_SUBDIR)
5558         with self.assertRaises(ValueError) as exc:
5559             self._DoReadFileDtb(dts, entry_args=entry_args,
5560                                 extra_indirs=[test_subdir])[0]
5561         return str(exc.exception)
5562
5563     def testFitSplitElfBadElf(self):
5564         """Test a FIT split-elf operation with an invalid ELF file"""
5565         if not elf.ELF_TOOLS:
5566             self.skipTest('Python elftools not available')
5567         TestFunctional._MakeInputFile('bad.elf', tools.get_bytes(100, 100))
5568         entry_args = {
5569             'of-list': 'test-fdt1 test-fdt2',
5570             'default-dt': 'test-fdt2',
5571             'atf-bl31-path': 'bad.elf',
5572             'tee-os-path': 'tee.elf',
5573         }
5574         test_subdir = os.path.join(self._indir, TEST_FDT_SUBDIR)
5575         with self.assertRaises(ValueError) as exc:
5576             self._DoReadFileDtb(
5577                 '226_fit_split_elf.dts',
5578                 entry_args=entry_args,
5579                 extra_indirs=[test_subdir])[0]
5580         self.assertIn(
5581             "Node '/binman/fit': subnode 'images/@atf-SEQ': Failed to read ELF file: Magic number does not match",
5582             str(exc.exception))
5583
5584     def checkFitSplitElf(self, **kwargs):
5585         """Test an split-elf FIT with a missing ELF file
5586
5587         Args:
5588             kwargs (dict of str): Arguments to pass to _DoTestFile()
5589
5590         Returns:
5591             tuple:
5592                 str: stdout result
5593                 str: stderr result
5594         """
5595         entry_args = {
5596             'of-list': 'test-fdt1 test-fdt2',
5597             'default-dt': 'test-fdt2',
5598             'atf-bl31-path': 'bl31.elf',
5599             'tee-os-path': 'missing.elf',
5600         }
5601         test_subdir = os.path.join(self._indir, TEST_FDT_SUBDIR)
5602         with test_util.capture_sys_output() as (stdout, stderr):
5603             self._DoTestFile(
5604                 '226_fit_split_elf.dts', entry_args=entry_args,
5605                 extra_indirs=[test_subdir], verbosity=3, **kwargs)
5606             out = stdout.getvalue()
5607             err = stderr.getvalue()
5608         return out, err
5609
5610     def testFitSplitElfBadDirective(self):
5611         """Test a FIT split-elf invalid fit,xxx directive in an image node"""
5612         if not elf.ELF_TOOLS:
5613             self.skipTest('Python elftools not available')
5614         err = self._check_bad_fit('227_fit_bad_dir.dts')
5615         self.assertIn(
5616             "Node '/binman/fit': subnode 'images/@atf-SEQ': Unknown directive 'fit,something'",
5617             err)
5618
5619     def testFitSplitElfBadDirectiveConfig(self):
5620         """Test a FIT split-elf with invalid fit,xxx directive in config"""
5621         if not elf.ELF_TOOLS:
5622             self.skipTest('Python elftools not available')
5623         err = self._check_bad_fit('228_fit_bad_dir_config.dts')
5624         self.assertEqual(
5625             "Node '/binman/fit': subnode 'configurations/@config-SEQ': Unknown directive 'fit,config'",
5626             err)
5627
5628
5629     def testFitSplitElfMissing(self):
5630         """Test an split-elf FIT with a missing ELF file"""
5631         if not elf.ELF_TOOLS:
5632             self.skipTest('Python elftools not available')
5633         out, err = self.checkFitSplitElf(allow_missing=True)
5634         self.assertRegex(
5635             err,
5636             "Image '.*' is missing external blobs and is non-functional: .*")
5637         self.assertNotRegex(out, '.*Faked blob.*')
5638         fname = tools.get_output_filename('binman-fake/missing.elf')
5639         self.assertFalse(os.path.exists(fname))
5640
5641     def testFitSplitElfFaked(self):
5642         """Test an split-elf FIT with faked ELF file"""
5643         if not elf.ELF_TOOLS:
5644             self.skipTest('Python elftools not available')
5645         out, err = self.checkFitSplitElf(allow_missing=True, allow_fake_blobs=True)
5646         self.assertRegex(
5647             err,
5648             "Image '.*' is missing external blobs and is non-functional: .*")
5649         self.assertRegex(
5650             out,
5651             "Entry '/binman/fit/images/@tee-SEQ/tee-os': Faked blob '.*binman-fake/missing.elf")
5652         fname = tools.get_output_filename('binman-fake/missing.elf')
5653         self.assertTrue(os.path.exists(fname))
5654
5655     def testMkimageMissingBlob(self):
5656         """Test using mkimage to build an image"""
5657         with test_util.capture_sys_output() as (stdout, stderr):
5658             self._DoTestFile('229_mkimage_missing.dts', allow_missing=True,
5659                              allow_fake_blobs=True)
5660         err = stderr.getvalue()
5661         self.assertRegex(
5662             err,
5663             "Image '.*' has faked external blobs and is non-functional: .*")
5664
5665     def testPreLoad(self):
5666         """Test an image with a pre-load header"""
5667         entry_args = {
5668             'pre-load-key-path': os.path.join(self._binman_dir, 'test'),
5669         }
5670         data = self._DoReadFileDtb(
5671             '230_pre_load.dts', entry_args=entry_args,
5672             extra_indirs=[os.path.join(self._binman_dir, 'test')])[0]
5673         self.assertEqual(PRE_LOAD_MAGIC, data[:len(PRE_LOAD_MAGIC)])
5674         self.assertEqual(PRE_LOAD_VERSION, data[4:4 + len(PRE_LOAD_VERSION)])
5675         self.assertEqual(PRE_LOAD_HDR_SIZE, data[8:8 + len(PRE_LOAD_HDR_SIZE)])
5676
5677     def testPreLoadNoKey(self):
5678         """Test an image with a pre-load heade0r with missing key"""
5679         with self.assertRaises(FileNotFoundError) as exc:
5680             self._DoReadFile('230_pre_load.dts')
5681         self.assertIn("No such file or directory: 'dev.key'",
5682                       str(exc.exception))
5683
5684     def testPreLoadPkcs(self):
5685         """Test an image with a pre-load header with padding pkcs"""
5686         entry_args = {
5687             'pre-load-key-path': os.path.join(self._binman_dir, 'test'),
5688         }
5689         data = self._DoReadFileDtb('231_pre_load_pkcs.dts',
5690                                    entry_args=entry_args)[0]
5691         self.assertEqual(PRE_LOAD_MAGIC, data[:len(PRE_LOAD_MAGIC)])
5692         self.assertEqual(PRE_LOAD_VERSION, data[4:4 + len(PRE_LOAD_VERSION)])
5693         self.assertEqual(PRE_LOAD_HDR_SIZE, data[8:8 + len(PRE_LOAD_HDR_SIZE)])
5694
5695     def testPreLoadPss(self):
5696         """Test an image with a pre-load header with padding pss"""
5697         entry_args = {
5698             'pre-load-key-path': os.path.join(self._binman_dir, 'test'),
5699         }
5700         data = self._DoReadFileDtb('232_pre_load_pss.dts',
5701                                    entry_args=entry_args)[0]
5702         self.assertEqual(PRE_LOAD_MAGIC, data[:len(PRE_LOAD_MAGIC)])
5703         self.assertEqual(PRE_LOAD_VERSION, data[4:4 + len(PRE_LOAD_VERSION)])
5704         self.assertEqual(PRE_LOAD_HDR_SIZE, data[8:8 + len(PRE_LOAD_HDR_SIZE)])
5705
5706     def testPreLoadInvalidPadding(self):
5707         """Test an image with a pre-load header with an invalid padding"""
5708         entry_args = {
5709             'pre-load-key-path': os.path.join(self._binman_dir, 'test'),
5710         }
5711         with self.assertRaises(ValueError) as e:
5712             self._DoReadFileDtb('233_pre_load_invalid_padding.dts',
5713                                 entry_args=entry_args)
5714
5715     def testPreLoadInvalidSha(self):
5716         """Test an image with a pre-load header with an invalid hash"""
5717         entry_args = {
5718             'pre-load-key-path': os.path.join(self._binman_dir, 'test'),
5719         }
5720         with self.assertRaises(ValueError) as e:
5721             self._DoReadFileDtb('234_pre_load_invalid_sha.dts',
5722                                 entry_args=entry_args)
5723
5724     def testPreLoadInvalidAlgo(self):
5725         """Test an image with a pre-load header with an invalid algo"""
5726         with self.assertRaises(ValueError) as e:
5727             data = self._DoReadFile('235_pre_load_invalid_algo.dts')
5728
5729     def testPreLoadInvalidKey(self):
5730         """Test an image with a pre-load header with an invalid key"""
5731         entry_args = {
5732             'pre-load-key-path': os.path.join(self._binman_dir, 'test'),
5733         }
5734         with self.assertRaises(ValueError) as e:
5735             data = self._DoReadFileDtb('236_pre_load_invalid_key.dts',
5736                                        entry_args=entry_args)
5737
5738     def _CheckSafeUniqueNames(self, *images):
5739         """Check all entries of given images for unsafe unique names"""
5740         for image in images:
5741             entries = {}
5742             image._CollectEntries(entries, {}, image)
5743             for entry in entries.values():
5744                 uniq = entry.GetUniqueName()
5745
5746                 # Used as part of a filename, so must not be absolute paths.
5747                 self.assertFalse(os.path.isabs(uniq))
5748
5749     def testSafeUniqueNames(self):
5750         """Test entry unique names are safe in single image configuration"""
5751         data = self._DoReadFileRealDtb('237_unique_names.dts')
5752
5753         orig_image = control.images['image']
5754         image_fname = tools.get_output_filename('image.bin')
5755         image = Image.FromFile(image_fname)
5756
5757         self._CheckSafeUniqueNames(orig_image, image)
5758
5759     def testSafeUniqueNamesMulti(self):
5760         """Test entry unique names are safe with multiple images"""
5761         data = self._DoReadFileRealDtb('238_unique_names_multi.dts')
5762
5763         orig_image = control.images['image']
5764         image_fname = tools.get_output_filename('image.bin')
5765         image = Image.FromFile(image_fname)
5766
5767         self._CheckSafeUniqueNames(orig_image, image)
5768
5769     def testReplaceCmdWithBintool(self):
5770         """Test replacing an entry that needs a bintool to pack"""
5771         data = self._DoReadFileRealDtb('239_replace_with_bintool.dts')
5772         expected = U_BOOT_DATA + b'aa'
5773         self.assertEqual(expected, data[:len(expected)])
5774
5775         try:
5776             tmpdir, updated_fname = self._SetupImageInTmpdir()
5777             fname = os.path.join(tmpdir, 'update-testing.bin')
5778             tools.write_file(fname, b'zz')
5779             self._DoBinman('replace', '-i', updated_fname,
5780                            '_testing', '-f', fname)
5781
5782             data = tools.read_file(updated_fname)
5783             expected = U_BOOT_DATA + b'zz'
5784             self.assertEqual(expected, data[:len(expected)])
5785         finally:
5786             shutil.rmtree(tmpdir)
5787
5788     def testReplaceCmdOtherWithBintool(self):
5789         """Test replacing an entry when another needs a bintool to pack"""
5790         data = self._DoReadFileRealDtb('239_replace_with_bintool.dts')
5791         expected = U_BOOT_DATA + b'aa'
5792         self.assertEqual(expected, data[:len(expected)])
5793
5794         try:
5795             tmpdir, updated_fname = self._SetupImageInTmpdir()
5796             fname = os.path.join(tmpdir, 'update-u-boot.bin')
5797             tools.write_file(fname, b'x' * len(U_BOOT_DATA))
5798             self._DoBinman('replace', '-i', updated_fname,
5799                            'u-boot', '-f', fname)
5800
5801             data = tools.read_file(updated_fname)
5802             expected = b'x' * len(U_BOOT_DATA) + b'aa'
5803             self.assertEqual(expected, data[:len(expected)])
5804         finally:
5805             shutil.rmtree(tmpdir)
5806
5807     def testReplaceResizeNoRepackSameSize(self):
5808         """Test replacing entries with same-size data without repacking"""
5809         expected = b'x' * len(U_BOOT_DATA)
5810         data, expected_fdtmap, _ = self._RunReplaceCmd('u-boot', expected)
5811         self.assertEqual(expected, data)
5812
5813         path, fdtmap = state.GetFdtContents('fdtmap')
5814         self.assertIsNotNone(path)
5815         self.assertEqual(expected_fdtmap, fdtmap)
5816
5817     def testReplaceResizeNoRepackSmallerSize(self):
5818         """Test replacing entries with smaller-size data without repacking"""
5819         new_data = b'x'
5820         data, expected_fdtmap, _ = self._RunReplaceCmd('u-boot', new_data)
5821         expected = new_data.ljust(len(U_BOOT_DATA), b'\0')
5822         self.assertEqual(expected, data)
5823
5824         path, fdtmap = state.GetFdtContents('fdtmap')
5825         self.assertIsNotNone(path)
5826         self.assertEqual(expected_fdtmap, fdtmap)
5827
5828     def testExtractFit(self):
5829         """Test extracting a FIT section"""
5830         self._DoReadFileRealDtb('240_fit_extract_replace.dts')
5831         image_fname = tools.get_output_filename('image.bin')
5832
5833         fit_data = control.ReadEntry(image_fname, 'fit')
5834         fit = fdt.Fdt.FromData(fit_data)
5835         fit.Scan()
5836
5837         # Check subentry data inside the extracted fit
5838         for node_path, expected in [
5839             ('/images/kernel', U_BOOT_DATA),
5840             ('/images/fdt-1', U_BOOT_NODTB_DATA),
5841             ('/images/scr-1', COMPRESS_DATA),
5842         ]:
5843             node = fit.GetNode(node_path)
5844             data = fit.GetProps(node)['data'].bytes
5845             self.assertEqual(expected, data)
5846
5847     def testExtractFitSubentries(self):
5848         """Test extracting FIT section subentries"""
5849         self._DoReadFileRealDtb('240_fit_extract_replace.dts')
5850         image_fname = tools.get_output_filename('image.bin')
5851
5852         for entry_path, expected in [
5853             ('fit/kernel', U_BOOT_DATA),
5854             ('fit/kernel/u-boot', U_BOOT_DATA),
5855             ('fit/fdt-1', U_BOOT_NODTB_DATA),
5856             ('fit/fdt-1/u-boot-nodtb', U_BOOT_NODTB_DATA),
5857             ('fit/scr-1', COMPRESS_DATA),
5858             ('fit/scr-1/blob', COMPRESS_DATA),
5859         ]:
5860             data = control.ReadEntry(image_fname, entry_path)
5861             self.assertEqual(expected, data)
5862
5863     def testReplaceFitSubentryLeafSameSize(self):
5864         """Test replacing a FIT leaf subentry with same-size data"""
5865         new_data = b'x' * len(U_BOOT_DATA)
5866         data, expected_fdtmap, _ = self._RunReplaceCmd(
5867             'fit/kernel/u-boot', new_data,
5868             dts='240_fit_extract_replace.dts')
5869         self.assertEqual(new_data, data)
5870
5871         path, fdtmap = state.GetFdtContents('fdtmap')
5872         self.assertIsNotNone(path)
5873         self.assertEqual(expected_fdtmap, fdtmap)
5874
5875     def testReplaceFitSubentryLeafBiggerSize(self):
5876         """Test replacing a FIT leaf subentry with bigger-size data"""
5877         new_data = b'ub' * len(U_BOOT_NODTB_DATA)
5878         data, expected_fdtmap, _ = self._RunReplaceCmd(
5879             'fit/fdt-1/u-boot-nodtb', new_data,
5880             dts='240_fit_extract_replace.dts')
5881         self.assertEqual(new_data, data)
5882
5883         # Will be repacked, so fdtmap must change
5884         path, fdtmap = state.GetFdtContents('fdtmap')
5885         self.assertIsNotNone(path)
5886         self.assertNotEqual(expected_fdtmap, fdtmap)
5887
5888     def testReplaceFitSubentryLeafSmallerSize(self):
5889         """Test replacing a FIT leaf subentry with smaller-size data"""
5890         new_data = b'x'
5891         expected = new_data.ljust(len(U_BOOT_NODTB_DATA), b'\0')
5892         data, expected_fdtmap, _ = self._RunReplaceCmd(
5893             'fit/fdt-1/u-boot-nodtb', new_data,
5894             dts='240_fit_extract_replace.dts')
5895         self.assertEqual(expected, data)
5896
5897         path, fdtmap = state.GetFdtContents('fdtmap')
5898         self.assertIsNotNone(path)
5899         self.assertEqual(expected_fdtmap, fdtmap)
5900
5901     def testReplaceSectionSimple(self):
5902         """Test replacing a simple section with same-sized data"""
5903         new_data = b'w' * len(COMPRESS_DATA + U_BOOT_DATA)
5904         data, expected_fdtmap, image = self._RunReplaceCmd('section',
5905             new_data, dts='241_replace_section_simple.dts')
5906         self.assertEqual(new_data, data)
5907
5908         entries = image.GetEntries()
5909         self.assertIn('section', entries)
5910         entry = entries['section']
5911         self.assertEqual(len(new_data), entry.size)
5912
5913     def testReplaceSectionLarger(self):
5914         """Test replacing a simple section with larger data"""
5915         new_data = b'w' * (len(COMPRESS_DATA + U_BOOT_DATA) + 1)
5916         data, expected_fdtmap, image = self._RunReplaceCmd('section',
5917             new_data, dts='241_replace_section_simple.dts')
5918         self.assertEqual(new_data, data)
5919
5920         entries = image.GetEntries()
5921         self.assertIn('section', entries)
5922         entry = entries['section']
5923         self.assertEqual(len(new_data), entry.size)
5924         fentry = entries['fdtmap']
5925         self.assertEqual(entry.offset + entry.size, fentry.offset)
5926
5927     def testReplaceSectionSmaller(self):
5928         """Test replacing a simple section with smaller data"""
5929         new_data = b'w' * (len(COMPRESS_DATA + U_BOOT_DATA) - 1) + b'\0'
5930         data, expected_fdtmap, image = self._RunReplaceCmd('section',
5931             new_data, dts='241_replace_section_simple.dts')
5932         self.assertEqual(new_data, data)
5933
5934         # The new size is the same as the old, just with a pad byte at the end
5935         entries = image.GetEntries()
5936         self.assertIn('section', entries)
5937         entry = entries['section']
5938         self.assertEqual(len(new_data), entry.size)
5939
5940     def testReplaceSectionSmallerAllow(self):
5941         """Test failing to replace a simple section with smaller data"""
5942         new_data = b'w' * (len(COMPRESS_DATA + U_BOOT_DATA) - 1)
5943         try:
5944             state.SetAllowEntryContraction(True)
5945             with self.assertRaises(ValueError) as exc:
5946                 self._RunReplaceCmd('section', new_data,
5947                                     dts='241_replace_section_simple.dts')
5948         finally:
5949             state.SetAllowEntryContraction(False)
5950
5951         # Since we have no information about the position of things within the
5952         # section, we cannot adjust the position of /section-u-boot so it ends
5953         # up outside the section
5954         self.assertIn(
5955             "Node '/section/u-boot': Offset 0x24 (36) size 0x4 (4) is outside "
5956             "the section '/section' starting at 0x0 (0) of size 0x27 (39)",
5957             str(exc.exception))
5958
5959     def testMkimageImagename(self):
5960         """Test using mkimage with -n holding the data too"""
5961         self._SetupSplElf()
5962         data = self._DoReadFile('242_mkimage_name.dts')
5963
5964         # Check that the data appears in the file somewhere
5965         self.assertIn(U_BOOT_SPL_DATA, data)
5966
5967         # Get struct legacy_img_hdr -> ih_name
5968         name = data[0x20:0x40]
5969
5970         # Build the filename that we expect to be placed in there, by virtue of
5971         # the -n paraameter
5972         expect = os.path.join(tools.get_output_dir(), 'mkimage.mkimage')
5973
5974         # Check that the image name is set to the temporary filename used
5975         self.assertEqual(expect.encode('utf-8')[:0x20], name)
5976
5977     def testMkimageImage(self):
5978         """Test using mkimage with -n holding the data too"""
5979         self._SetupSplElf()
5980         data = self._DoReadFile('243_mkimage_image.dts')
5981
5982         # Check that the data appears in the file somewhere
5983         self.assertIn(U_BOOT_SPL_DATA, data)
5984
5985         # Get struct legacy_img_hdr -> ih_name
5986         name = data[0x20:0x40]
5987
5988         # Build the filename that we expect to be placed in there, by virtue of
5989         # the -n paraameter
5990         expect = os.path.join(tools.get_output_dir(), 'mkimage-n.mkimage')
5991
5992         # Check that the image name is set to the temporary filename used
5993         self.assertEqual(expect.encode('utf-8')[:0x20], name)
5994
5995         # Check the corect data is in the imagename file
5996         self.assertEqual(U_BOOT_DATA, tools.read_file(expect))
5997
5998     def testMkimageImageNoContent(self):
5999         """Test using mkimage with -n and no data"""
6000         self._SetupSplElf()
6001         with self.assertRaises(ValueError) as exc:
6002             self._DoReadFile('244_mkimage_image_no_content.dts')
6003         self.assertIn('Could not complete processing of contents',
6004                       str(exc.exception))
6005
6006     def testMkimageImageBad(self):
6007         """Test using mkimage with imagename node and data-to-imagename"""
6008         self._SetupSplElf()
6009         with self.assertRaises(ValueError) as exc:
6010             self._DoReadFile('245_mkimage_image_bad.dts')
6011         self.assertIn('Cannot use both imagename node and data-to-imagename',
6012                       str(exc.exception))
6013
6014     def testCollectionOther(self):
6015         """Test a collection where the data comes from another section"""
6016         data = self._DoReadFile('246_collection_other.dts')
6017         self.assertEqual(U_BOOT_NODTB_DATA + U_BOOT_DTB_DATA +
6018                          tools.get_bytes(0xff, 2) + U_BOOT_NODTB_DATA +
6019                          tools.get_bytes(0xfe, 3) + U_BOOT_DTB_DATA,
6020                          data)
6021
6022     def testMkimageCollection(self):
6023         """Test using a collection referring to an entry in a mkimage entry"""
6024         self._SetupSplElf()
6025         data = self._DoReadFile('247_mkimage_coll.dts')
6026         expect = U_BOOT_SPL_DATA + U_BOOT_DATA
6027         self.assertEqual(expect, data[:len(expect)])
6028
6029     def testCompressDtbPrependInvalid(self):
6030         """Test that invalid header is detected"""
6031         with self.assertRaises(ValueError) as e:
6032             self._DoReadFileDtb('248_compress_dtb_prepend_invalid.dts')
6033         self.assertIn("Node '/binman/u-boot-dtb': Invalid prepend in "
6034                       "'u-boot-dtb': 'invalid'", str(e.exception))
6035
6036     def testCompressDtbPrependLength(self):
6037         """Test that compress with length header works as expected"""
6038         data = self._DoReadFileRealDtb('249_compress_dtb_prepend_length.dts')
6039         image = control.images['image']
6040         entries = image.GetEntries()
6041         self.assertIn('u-boot-dtb', entries)
6042         u_boot_dtb = entries['u-boot-dtb']
6043         self.assertIn('fdtmap', entries)
6044         fdtmap = entries['fdtmap']
6045
6046         image_fname = tools.get_output_filename('image.bin')
6047         orig = control.ReadEntry(image_fname, 'u-boot-dtb')
6048         dtb = fdt.Fdt.FromData(orig)
6049         dtb.Scan()
6050         props = self._GetPropTree(dtb, ['size', 'uncomp-size'])
6051         expected = {
6052             'u-boot:size': len(U_BOOT_DATA),
6053             'u-boot-dtb:uncomp-size': len(orig),
6054             'u-boot-dtb:size': u_boot_dtb.size,
6055             'fdtmap:size': fdtmap.size,
6056             'size': len(data),
6057             }
6058         self.assertEqual(expected, props)
6059
6060         # Check implementation
6061         self.assertEqual(U_BOOT_DATA, data[:len(U_BOOT_DATA)])
6062         rest = data[len(U_BOOT_DATA):]
6063         comp_data_len = struct.unpack('<I', rest[:4])[0]
6064         comp_data = rest[4:4 + comp_data_len]
6065         orig2 = self._decompress(comp_data)
6066         self.assertEqual(orig, orig2)
6067
6068     def testInvalidCompress(self):
6069         """Test that invalid compress algorithm is detected"""
6070         with self.assertRaises(ValueError) as e:
6071             self._DoTestFile('250_compress_dtb_invalid.dts')
6072         self.assertIn("Unknown algorithm 'invalid'", str(e.exception))
6073
6074     def testCompUtilCompressions(self):
6075         """Test compression algorithms"""
6076         for bintool in self.comp_bintools.values():
6077             self._CheckBintool(bintool)
6078             data = bintool.compress(COMPRESS_DATA)
6079             self.assertNotEqual(COMPRESS_DATA, data)
6080             orig = bintool.decompress(data)
6081             self.assertEquals(COMPRESS_DATA, orig)
6082
6083     def testCompUtilVersions(self):
6084         """Test tool version of compression algorithms"""
6085         for bintool in self.comp_bintools.values():
6086             self._CheckBintool(bintool)
6087             version = bintool.version()
6088             self.assertRegex(version, '^v?[0-9]+[0-9.]*')
6089
6090     def testCompUtilPadding(self):
6091         """Test padding of compression algorithms"""
6092         # Skip zstd because it doesn't support padding
6093         for bintool in [v for k,v in self.comp_bintools.items() if k != 'zstd']:
6094             self._CheckBintool(bintool)
6095             data = bintool.compress(COMPRESS_DATA)
6096             self.assertNotEqual(COMPRESS_DATA, data)
6097             data += tools.get_bytes(0, 64)
6098             orig = bintool.decompress(data)
6099             self.assertEquals(COMPRESS_DATA, orig)
6100
6101     def testCompressDtbZstd(self):
6102         """Test that zstd compress of device-tree files failed"""
6103         with self.assertRaises(ValueError) as e:
6104             self._DoTestFile('251_compress_dtb_zstd.dts')
6105         self.assertIn("Node '/binman/u-boot-dtb': The zstd compression "
6106                       "requires a length header", str(e.exception))
6107
6108     def testMkimageMultipleDataFiles(self):
6109         """Test passing multiple files to mkimage in a mkimage entry"""
6110         self._SetupSplElf()
6111         self._SetupTplElf()
6112         data = self._DoReadFile('252_mkimage_mult_data.dts')
6113         # Size of files are packed in their 4B big-endian format
6114         expect = struct.pack('>I', len(U_BOOT_TPL_DATA))
6115         expect += struct.pack('>I', len(U_BOOT_SPL_DATA))
6116         # Size info is always followed by a 4B zero value.
6117         expect += tools.get_bytes(0, 4)
6118         expect += U_BOOT_TPL_DATA
6119         # All but last files are 4B-aligned
6120         align_pad = len(U_BOOT_TPL_DATA) % 4
6121         if align_pad:
6122             expect += tools.get_bytes(0, align_pad)
6123         expect += U_BOOT_SPL_DATA
6124         self.assertEqual(expect, data[-len(expect):])
6125
6126     def testMkimageMultipleExpanded(self):
6127         """Test passing multiple files to mkimage in a mkimage entry"""
6128         self._SetupSplElf()
6129         self._SetupTplElf()
6130         entry_args = {
6131             'spl-bss-pad': 'y',
6132             'spl-dtb': 'y',
6133         }
6134         data = self._DoReadFileDtb('252_mkimage_mult_data.dts',
6135                                    use_expanded=True, entry_args=entry_args)[0]
6136         pad_len = 10
6137         tpl_expect = U_BOOT_TPL_DATA
6138         spl_expect = U_BOOT_SPL_NODTB_DATA + tools.get_bytes(0, pad_len)
6139         spl_expect += U_BOOT_SPL_DTB_DATA
6140
6141         content = data[0x40:]
6142         lens = struct.unpack('>III', content[:12])
6143
6144         # Size of files are packed in their 4B big-endian format
6145         # Size info is always followed by a 4B zero value.
6146         self.assertEqual(len(tpl_expect), lens[0])
6147         self.assertEqual(len(spl_expect), lens[1])
6148         self.assertEqual(0, lens[2])
6149
6150         rest = content[12:]
6151         self.assertEqual(tpl_expect, rest[:len(tpl_expect)])
6152
6153         rest = rest[len(tpl_expect):]
6154         align_pad = len(tpl_expect) % 4
6155         self.assertEqual(tools.get_bytes(0, align_pad), rest[:align_pad])
6156         rest = rest[align_pad:]
6157         self.assertEqual(spl_expect, rest)
6158
6159     def testMkimageMultipleNoContent(self):
6160         """Test passing multiple data files to mkimage with one data file having no content"""
6161         self._SetupSplElf()
6162         with self.assertRaises(ValueError) as exc:
6163             self._DoReadFile('253_mkimage_mult_no_content.dts')
6164         self.assertIn('Could not complete processing of contents',
6165                       str(exc.exception))
6166
6167     def testMkimageFilename(self):
6168         """Test using mkimage to build a binary with a filename"""
6169         self._SetupSplElf()
6170         retcode = self._DoTestFile('254_mkimage_filename.dts')
6171         self.assertEqual(0, retcode)
6172         fname = tools.get_output_filename('mkimage-test.bin')
6173         self.assertTrue(os.path.exists(fname))
6174
6175     def testVpl(self):
6176         """Test that an image with VPL and its device tree can be created"""
6177         # ELF file with a '__bss_size' symbol
6178         self._SetupVplElf()
6179         data = self._DoReadFile('255_u_boot_vpl.dts')
6180         self.assertEqual(U_BOOT_VPL_DATA + U_BOOT_VPL_DTB_DATA, data)
6181
6182     def testVplNoDtb(self):
6183         """Test that an image with vpl/u-boot-vpl-nodtb.bin can be created"""
6184         self._SetupVplElf()
6185         data = self._DoReadFile('256_u_boot_vpl_nodtb.dts')
6186         self.assertEqual(U_BOOT_VPL_NODTB_DATA,
6187                          data[:len(U_BOOT_VPL_NODTB_DATA)])
6188
6189     def testExpandedVpl(self):
6190         """Test that an expanded entry type is selected for TPL when needed"""
6191         self._SetupVplElf()
6192
6193         entry_args = {
6194             'vpl-bss-pad': 'y',
6195             'vpl-dtb': 'y',
6196         }
6197         self._DoReadFileDtb('257_fdt_incl_vpl.dts', use_expanded=True,
6198                             entry_args=entry_args)
6199         image = control.images['image']
6200         entries = image.GetEntries()
6201         self.assertEqual(1, len(entries))
6202
6203         # We only have u-boot-vpl, which be expanded
6204         self.assertIn('u-boot-vpl', entries)
6205         entry = entries['u-boot-vpl']
6206         self.assertEqual('u-boot-vpl-expanded', entry.etype)
6207         subent = entry.GetEntries()
6208         self.assertEqual(3, len(subent))
6209         self.assertIn('u-boot-vpl-nodtb', subent)
6210         self.assertIn('u-boot-vpl-bss-pad', subent)
6211         self.assertIn('u-boot-vpl-dtb', subent)
6212
6213     def testVplBssPadMissing(self):
6214         """Test that a missing symbol is detected"""
6215         self._SetupVplElf('u_boot_ucode_ptr')
6216         with self.assertRaises(ValueError) as e:
6217             self._DoReadFile('258_vpl_bss_pad.dts')
6218         self.assertIn('Expected __bss_size symbol in vpl/u-boot-vpl',
6219                       str(e.exception))
6220
6221     def testSymlink(self):
6222         """Test that image files can be symlinked"""
6223         retcode = self._DoTestFile('259_symlink.dts', debug=True, map=True)
6224         self.assertEqual(0, retcode)
6225         image = control.images['test_image']
6226         fname = tools.get_output_filename('test_image.bin')
6227         sname = tools.get_output_filename('symlink_to_test.bin')
6228         self.assertTrue(os.path.islink(sname))
6229         self.assertEqual(os.readlink(sname), fname)
6230
6231     def testSymlinkOverwrite(self):
6232         """Test that symlinked images can be overwritten"""
6233         testdir = TestFunctional._MakeInputDir('symlinktest')
6234         self._DoTestFile('259_symlink.dts', debug=True, map=True, output_dir=testdir)
6235         # build the same image again in the same directory so that existing symlink is present
6236         self._DoTestFile('259_symlink.dts', debug=True, map=True, output_dir=testdir)
6237         fname = tools.get_output_filename('test_image.bin')
6238         sname = tools.get_output_filename('symlink_to_test.bin')
6239         self.assertTrue(os.path.islink(sname))
6240         self.assertEqual(os.readlink(sname), fname)
6241
6242     def testSymbolsElf(self):
6243         """Test binman can assign symbols embedded in an ELF file"""
6244         if not elf.ELF_TOOLS:
6245             self.skipTest('Python elftools not available')
6246         self._SetupTplElf('u_boot_binman_syms')
6247         self._SetupVplElf('u_boot_binman_syms')
6248         self._SetupSplElf('u_boot_binman_syms')
6249         data = self._DoReadFileDtb('260_symbols_elf.dts')[0]
6250         image_fname = tools.get_output_filename('image.bin')
6251
6252         image = control.images['image']
6253         entries = image.GetEntries()
6254
6255         for entry in entries.values():
6256             # No symbols in u-boot and it has faked contents anyway
6257             if entry.name == 'u-boot':
6258                 continue
6259             edata = data[entry.image_pos:entry.image_pos + entry.size]
6260             efname = tools.get_output_filename(f'edata-{entry.name}')
6261             tools.write_file(efname, edata)
6262
6263             syms = elf.GetSymbolFileOffset(efname, ['_binman_u_boot'])
6264             re_name = re.compile('_binman_(u_boot_(.*))_prop_(.*)')
6265             for name, sym in syms.items():
6266                 msg = 'test'
6267                 val = elf.GetSymbolValue(sym, edata, msg)
6268                 entry_m = re_name.match(name)
6269                 if entry_m:
6270                     ename, prop = entry_m.group(1), entry_m.group(3)
6271                 entry, entry_name, prop_name = image.LookupEntry(entries,
6272                                                                  name, msg)
6273                 if prop_name == 'offset':
6274                     expect_val = entry.offset
6275                 elif prop_name == 'image_pos':
6276                     expect_val = entry.image_pos
6277                 elif prop_name == 'size':
6278                     expect_val = entry.size
6279                 self.assertEqual(expect_val, val)
6280
6281     def testSymbolsElfBad(self):
6282         """Check error when trying to write symbols without the elftools lib"""
6283         if not elf.ELF_TOOLS:
6284             self.skipTest('Python elftools not available')
6285         self._SetupTplElf('u_boot_binman_syms')
6286         self._SetupVplElf('u_boot_binman_syms')
6287         self._SetupSplElf('u_boot_binman_syms')
6288         try:
6289             elf.ELF_TOOLS = False
6290             with self.assertRaises(ValueError) as exc:
6291                 self._DoReadFileDtb('260_symbols_elf.dts')
6292         finally:
6293             elf.ELF_TOOLS = True
6294         self.assertIn(
6295             "Section '/binman': entry '/binman/u-boot-spl-elf': "
6296             'Cannot write symbols to an ELF file without Python elftools',
6297             str(exc.exception))
6298
6299     def testSectionFilename(self):
6300         """Check writing of section contents to a file"""
6301         data = self._DoReadFile('261_section_fname.dts')
6302         expected = (b'&&' + U_BOOT_DATA + b'&&&' +
6303                     tools.get_bytes(ord('!'), 7) +
6304                     U_BOOT_DATA + tools.get_bytes(ord('&'), 12))
6305         self.assertEqual(expected, data)
6306
6307         sect_fname = tools.get_output_filename('outfile.bin')
6308         self.assertTrue(os.path.exists(sect_fname))
6309         sect_data = tools.read_file(sect_fname)
6310         self.assertEqual(U_BOOT_DATA, sect_data)
6311
6312     def testAbsent(self):
6313         """Check handling of absent entries"""
6314         data = self._DoReadFile('262_absent.dts')
6315         self.assertEqual(U_BOOT_DATA + U_BOOT_IMG_DATA, data)
6316
6317     def testPackTeeOsOptional(self):
6318         """Test that an image with an optional TEE binary can be created"""
6319         entry_args = {
6320             'tee-os-path': 'tee.elf',
6321         }
6322         data = self._DoReadFileDtb('263_tee_os_opt.dts',
6323                                    entry_args=entry_args)[0]
6324         self.assertEqual(U_BOOT_DATA + U_BOOT_IMG_DATA, data)
6325
6326     def checkFitTee(self, dts, tee_fname):
6327         """Check that a tee-os entry works and returns data
6328
6329         Args:
6330             dts (str): Device tree filename to use
6331             tee_fname (str): filename containing tee-os
6332
6333         Returns:
6334             bytes: Image contents
6335         """
6336         if not elf.ELF_TOOLS:
6337             self.skipTest('Python elftools not available')
6338         entry_args = {
6339             'of-list': 'test-fdt1 test-fdt2',
6340             'default-dt': 'test-fdt2',
6341             'tee-os-path': tee_fname,
6342         }
6343         test_subdir = os.path.join(self._indir, TEST_FDT_SUBDIR)
6344         data = self._DoReadFileDtb(dts, entry_args=entry_args,
6345                                    extra_indirs=[test_subdir])[0]
6346         return data
6347
6348     def testFitTeeOsOptionalFit(self):
6349         """Test an image with a FIT with an optional OP-TEE binary"""
6350         data = self.checkFitTee('264_tee_os_opt_fit.dts', 'tee.bin')
6351
6352         # There should be only one node, holding the data set up in SetUpClass()
6353         # for tee.bin
6354         dtb = fdt.Fdt.FromData(data)
6355         dtb.Scan()
6356         node = dtb.GetNode('/images/tee-1')
6357         self.assertEqual(TEE_ADDR,
6358                          fdt_util.fdt32_to_cpu(node.props['load'].value))
6359         self.assertEqual(TEE_ADDR,
6360                          fdt_util.fdt32_to_cpu(node.props['entry'].value))
6361         self.assertEqual(U_BOOT_DATA, node.props['data'].bytes)
6362
6363         with test_util.capture_sys_output() as (stdout, stderr):
6364             self.checkFitTee('264_tee_os_opt_fit.dts', '')
6365         err = stderr.getvalue()
6366         self.assertRegex(
6367             err,
6368             "Image '.*' is missing optional external blobs but is still functional: tee-os")
6369
6370     def testFitTeeOsOptionalFitBad(self):
6371         """Test an image with a FIT with an optional OP-TEE binary"""
6372         with self.assertRaises(ValueError) as exc:
6373             self.checkFitTee('265_tee_os_opt_fit_bad.dts', 'tee.bin')
6374         self.assertIn(
6375             "Node '/binman/fit': subnode 'images/@tee-SEQ': Failed to read ELF file: Magic number does not match",
6376             str(exc.exception))
6377
6378     def testFitTeeOsBad(self):
6379         """Test an OP-TEE binary with wrong formats"""
6380         self.make_tee_bin('tee.bad1', 123)
6381         with self.assertRaises(ValueError) as exc:
6382             self.checkFitTee('264_tee_os_opt_fit.dts', 'tee.bad1')
6383         self.assertIn(
6384             "Node '/binman/fit/images/@tee-SEQ/tee-os': OP-TEE paged mode not supported",
6385             str(exc.exception))
6386
6387         self.make_tee_bin('tee.bad2', 0, b'extra data')
6388         with self.assertRaises(ValueError) as exc:
6389             self.checkFitTee('264_tee_os_opt_fit.dts', 'tee.bad2')
6390         self.assertIn(
6391             "Node '/binman/fit/images/@tee-SEQ/tee-os': Invalid OP-TEE file: size mismatch (expected 0x4, have 0xe)",
6392             str(exc.exception))
6393
6394     def testExtblobOptional(self):
6395         """Test an image with an external blob that is optional"""
6396         with test_util.capture_sys_output() as (stdout, stderr):
6397             data = self._DoReadFile('266_blob_ext_opt.dts')
6398         self.assertEqual(REFCODE_DATA, data)
6399         err = stderr.getvalue()
6400         self.assertRegex(
6401             err,
6402             "Image '.*' is missing optional external blobs but is still functional: missing")
6403
6404     def testSectionInner(self):
6405         """Test an inner section with a size"""
6406         data = self._DoReadFile('267_section_inner.dts')
6407         expected = U_BOOT_DATA + tools.get_bytes(0, 12)
6408         self.assertEqual(expected, data)
6409
6410     def testNull(self):
6411         """Test an image with a null entry"""
6412         data = self._DoReadFile('268_null.dts')
6413         self.assertEqual(U_BOOT_DATA + b'\xff\xff\xff\xff' + U_BOOT_IMG_DATA, data)
6414
6415     def testOverlap(self):
6416         """Test an image with a overlapping entry"""
6417         data = self._DoReadFile('269_overlap.dts')
6418         self.assertEqual(U_BOOT_DATA[:1] + b'aa' + U_BOOT_DATA[3:], data)
6419
6420         image = control.images['image']
6421         entries = image.GetEntries()
6422
6423         self.assertIn('inset', entries)
6424         inset = entries['inset']
6425         self.assertEqual(1, inset.offset);
6426         self.assertEqual(1, inset.image_pos);
6427         self.assertEqual(2, inset.size);
6428
6429     def testOverlapNull(self):
6430         """Test an image with a null overlap"""
6431         data = self._DoReadFile('270_overlap_null.dts')
6432         self.assertEqual(U_BOOT_DATA, data[:len(U_BOOT_DATA)])
6433
6434         # Check the FMAP
6435         fhdr, fentries = fmap_util.DecodeFmap(data[len(U_BOOT_DATA):])
6436         self.assertEqual(4, fhdr.nareas)
6437         fiter = iter(fentries)
6438
6439         fentry = next(fiter)
6440         self.assertEqual(b'SECTION', fentry.name)
6441         self.assertEqual(0, fentry.offset)
6442         self.assertEqual(len(U_BOOT_DATA), fentry.size)
6443         self.assertEqual(0, fentry.flags)
6444
6445         fentry = next(fiter)
6446         self.assertEqual(b'U_BOOT', fentry.name)
6447         self.assertEqual(0, fentry.offset)
6448         self.assertEqual(len(U_BOOT_DATA), fentry.size)
6449         self.assertEqual(0, fentry.flags)
6450
6451         # Make sure that the NULL entry appears in the FMAP
6452         fentry = next(fiter)
6453         self.assertEqual(b'NULL', fentry.name)
6454         self.assertEqual(1, fentry.offset)
6455         self.assertEqual(2, fentry.size)
6456         self.assertEqual(0, fentry.flags)
6457
6458         fentry = next(fiter)
6459         self.assertEqual(b'FMAP', fentry.name)
6460         self.assertEqual(len(U_BOOT_DATA), fentry.offset)
6461
6462     def testOverlapBad(self):
6463         """Test an image with a bad overlapping entry"""
6464         with self.assertRaises(ValueError) as exc:
6465             self._DoReadFile('271_overlap_bad.dts')
6466         self.assertIn(
6467             "Node '/binman/inset': Offset 0x10 (16) ending at 0x12 (18) must overlap with existing entries",
6468             str(exc.exception))
6469
6470     def testOverlapNoOffset(self):
6471         """Test an image with a bad overlapping entry"""
6472         with self.assertRaises(ValueError) as exc:
6473             self._DoReadFile('272_overlap_no_size.dts')
6474         self.assertIn(
6475             "Node '/binman/inset': 'fill' entry is missing properties: size",
6476             str(exc.exception))
6477
6478     def testBlobSymbol(self):
6479         """Test a blob with symbols read from an ELF file"""
6480         elf_fname = self.ElfTestFile('blob_syms')
6481         TestFunctional._MakeInputFile('blob_syms', tools.read_file(elf_fname))
6482         TestFunctional._MakeInputFile('blob_syms.bin',
6483             tools.read_file(self.ElfTestFile('blob_syms.bin')))
6484
6485         data = self._DoReadFile('273_blob_symbol.dts')
6486
6487         syms = elf.GetSymbols(elf_fname, ['binman', 'image'])
6488         addr = elf.GetSymbolAddress(elf_fname, '__my_start_sym')
6489         self.assertEqual(syms['_binman_sym_magic'].address, addr)
6490         self.assertEqual(syms['_binman_inset_prop_offset'].address, addr + 4)
6491         self.assertEqual(syms['_binman_inset_prop_size'].address, addr + 8)
6492
6493         sym_values = struct.pack('<LLL', elf.BINMAN_SYM_MAGIC_VALUE, 4, 8)
6494         expected = sym_values
6495         self.assertEqual(expected, data[:len(expected)])
6496
6497     def testOffsetFromElf(self):
6498         """Test a blob with symbols read from an ELF file"""
6499         elf_fname = self.ElfTestFile('blob_syms')
6500         TestFunctional._MakeInputFile('blob_syms', tools.read_file(elf_fname))
6501         TestFunctional._MakeInputFile('blob_syms.bin',
6502             tools.read_file(self.ElfTestFile('blob_syms.bin')))
6503
6504         data = self._DoReadFile('274_offset_from_elf.dts')
6505
6506         syms = elf.GetSymbols(elf_fname, ['binman', 'image'])
6507         base = elf.GetSymbolAddress(elf_fname, '__my_start_sym')
6508
6509         image = control.images['image']
6510         entries = image.GetEntries()
6511
6512         self.assertIn('inset', entries)
6513         inset = entries['inset']
6514
6515         self.assertEqual(base + 4, inset.offset);
6516         self.assertEqual(base + 4, inset.image_pos);
6517         self.assertEqual(4, inset.size);
6518
6519         self.assertIn('inset2', entries)
6520         inset = entries['inset2']
6521         self.assertEqual(base + 8, inset.offset);
6522         self.assertEqual(base + 8, inset.image_pos);
6523         self.assertEqual(4, inset.size);
6524
6525     def testFitAlign(self):
6526         """Test an image with an FIT with aligned external data"""
6527         data = self._DoReadFile('275_fit_align.dts')
6528         self.assertEqual(4096, len(data))
6529
6530         dtb = fdt.Fdt.FromData(data)
6531         dtb.Scan()
6532
6533         props = self._GetPropTree(dtb, ['data-position'])
6534         expected = {
6535             'u-boot:data-position': 1024,
6536             'fdt-1:data-position': 2048,
6537             'fdt-2:data-position': 3072,
6538         }
6539         self.assertEqual(expected, props)
6540
6541     def testFitFirmwareLoadables(self):
6542         """Test an image with an FIT that use fit,firmware"""
6543         if not elf.ELF_TOOLS:
6544             self.skipTest('Python elftools not available')
6545         entry_args = {
6546             'of-list': 'test-fdt1',
6547             'default-dt': 'test-fdt1',
6548             'atf-bl31-path': 'bl31.elf',
6549             'tee-os-path': 'missing.bin',
6550         }
6551         test_subdir = os.path.join(self._indir, TEST_FDT_SUBDIR)
6552         with test_util.capture_sys_output() as (stdout, stderr):
6553             data = self._DoReadFileDtb(
6554                 '276_fit_firmware_loadables.dts',
6555                 entry_args=entry_args,
6556                 extra_indirs=[test_subdir])[0]
6557
6558         dtb = fdt.Fdt.FromData(data)
6559         dtb.Scan()
6560
6561         node = dtb.GetNode('/configurations/conf-uboot-1')
6562         self.assertEqual('u-boot', node.props['firmware'].value)
6563         self.assertEqual(['atf-1', 'atf-2'],
6564                          fdt_util.GetStringList(node, 'loadables'))
6565
6566         node = dtb.GetNode('/configurations/conf-atf-1')
6567         self.assertEqual('atf-1', node.props['firmware'].value)
6568         self.assertEqual(['u-boot', 'atf-2'],
6569                          fdt_util.GetStringList(node, 'loadables'))
6570
6571         node = dtb.GetNode('/configurations/conf-missing-uboot-1')
6572         self.assertEqual('u-boot', node.props['firmware'].value)
6573         self.assertEqual(['atf-1', 'atf-2'],
6574                          fdt_util.GetStringList(node, 'loadables'))
6575
6576         node = dtb.GetNode('/configurations/conf-missing-atf-1')
6577         self.assertEqual('atf-1', node.props['firmware'].value)
6578         self.assertEqual(['u-boot', 'atf-2'],
6579                          fdt_util.GetStringList(node, 'loadables'))
6580
6581         node = dtb.GetNode('/configurations/conf-missing-tee-1')
6582         self.assertEqual('atf-1', node.props['firmware'].value)
6583         self.assertEqual(['u-boot', 'atf-2'],
6584                          fdt_util.GetStringList(node, 'loadables'))
6585
6586     def testTooldir(self):
6587         """Test that we can specify the tooldir"""
6588         with test_util.capture_sys_output() as (stdout, stderr):
6589             self.assertEqual(0, self._DoBinman('--tooldir', 'fred',
6590                                                'tool', '-l'))
6591         self.assertEqual('fred', bintool.Bintool.tooldir)
6592
6593         # Check that the toolpath is updated correctly
6594         self.assertEqual(['fred'], tools.tool_search_paths)
6595
6596         # Try with a few toolpaths; the tooldir should be at the end
6597         with test_util.capture_sys_output() as (stdout, stderr):
6598             self.assertEqual(0, self._DoBinman(
6599                 '--toolpath', 'mary', '--toolpath', 'anna', '--tooldir', 'fred',
6600                 'tool', '-l'))
6601         self.assertEqual(['mary', 'anna', 'fred'], tools.tool_search_paths)
6602
6603     def testReplaceSectionEntry(self):
6604         """Test replacing an entry in a section"""
6605         expect_data = b'w' * len(U_BOOT_DATA + COMPRESS_DATA)
6606         entry_data, expected_fdtmap, image = self._RunReplaceCmd('section/blob',
6607             expect_data, dts='241_replace_section_simple.dts')
6608         self.assertEqual(expect_data, entry_data)
6609
6610         entries = image.GetEntries()
6611         self.assertIn('section', entries)
6612         section = entries['section']
6613
6614         sect_entries = section.GetEntries()
6615         self.assertIn('blob', sect_entries)
6616         entry = sect_entries['blob']
6617         self.assertEqual(len(expect_data), entry.size)
6618
6619         fname = tools.get_output_filename('image-updated.bin')
6620         data = tools.read_file(fname)
6621
6622         new_blob_data = data[entry.image_pos:entry.image_pos + len(expect_data)]
6623         self.assertEqual(expect_data, new_blob_data)
6624
6625         self.assertEqual(U_BOOT_DATA,
6626                          data[entry.image_pos + len(expect_data):]
6627                          [:len(U_BOOT_DATA)])
6628
6629     def testReplaceSectionDeep(self):
6630         """Test replacing an entry in two levels of sections"""
6631         expect_data = b'w' * len(U_BOOT_DATA + COMPRESS_DATA)
6632         entry_data, expected_fdtmap, image = self._RunReplaceCmd(
6633             'section/section/blob', expect_data,
6634             dts='278_replace_section_deep.dts')
6635         self.assertEqual(expect_data, entry_data)
6636
6637         entries = image.GetEntries()
6638         self.assertIn('section', entries)
6639         section = entries['section']
6640
6641         subentries = section.GetEntries()
6642         self.assertIn('section', subentries)
6643         section = subentries['section']
6644
6645         sect_entries = section.GetEntries()
6646         self.assertIn('blob', sect_entries)
6647         entry = sect_entries['blob']
6648         self.assertEqual(len(expect_data), entry.size)
6649
6650         fname = tools.get_output_filename('image-updated.bin')
6651         data = tools.read_file(fname)
6652
6653         new_blob_data = data[entry.image_pos:entry.image_pos + len(expect_data)]
6654         self.assertEqual(expect_data, new_blob_data)
6655
6656         self.assertEqual(U_BOOT_DATA,
6657                          data[entry.image_pos + len(expect_data):]
6658                          [:len(U_BOOT_DATA)])
6659
6660     def testReplaceFitSibling(self):
6661         """Test an image with a FIT inside where we replace its sibling"""
6662         self._SetupSplElf()
6663         fname = TestFunctional._MakeInputFile('once', b'available once')
6664         self._DoReadFileRealDtb('277_replace_fit_sibling.dts')
6665         os.remove(fname)
6666
6667         try:
6668             tmpdir, updated_fname = self._SetupImageInTmpdir()
6669
6670             fname = os.path.join(tmpdir, 'update-blob')
6671             expected = b'w' * (len(COMPRESS_DATA + U_BOOT_DATA) + 1)
6672             tools.write_file(fname, expected)
6673
6674             self._DoBinman('replace', '-i', updated_fname, 'blob', '-f', fname)
6675             data = tools.read_file(updated_fname)
6676             start = len(U_BOOT_DTB_DATA)
6677             self.assertEqual(expected, data[start:start + len(expected)])
6678             map_fname = os.path.join(tmpdir, 'image-updated.map')
6679             self.assertFalse(os.path.exists(map_fname))
6680         finally:
6681             shutil.rmtree(tmpdir)
6682
6683     def testX509Cert(self):
6684         """Test creating an X509 certificate"""
6685         keyfile = self.TestFile('key.key')
6686         entry_args = {
6687             'keyfile': keyfile,
6688         }
6689         data = self._DoReadFileDtb('279_x509_cert.dts',
6690                                    entry_args=entry_args)[0]
6691         cert = data[:-4]
6692         self.assertEqual(U_BOOT_DATA, data[-4:])
6693
6694         # TODO: verify the signature
6695
6696     def testX509CertMissing(self):
6697         """Test that binman still produces an image if openssl is missing"""
6698         keyfile = self.TestFile('key.key')
6699         entry_args = {
6700             'keyfile': 'keyfile',
6701         }
6702         with test_util.capture_sys_output() as (_, stderr):
6703             self._DoTestFile('279_x509_cert.dts',
6704                              force_missing_bintools='openssl',
6705                              entry_args=entry_args)
6706         err = stderr.getvalue()
6707         self.assertRegex(err, "Image 'image'.*missing bintools.*: openssl")
6708
6709     def testPackRockchipTpl(self):
6710         """Test that an image with a Rockchip TPL binary can be created"""
6711         data = self._DoReadFile('291_rockchip_tpl.dts')
6712         self.assertEqual(ROCKCHIP_TPL_DATA, data[:len(ROCKCHIP_TPL_DATA)])
6713
6714     def testMkimageMissingBlobMultiple(self):
6715         """Test missing blob with mkimage entry and multiple-data-files"""
6716         with test_util.capture_sys_output() as (stdout, stderr):
6717             self._DoTestFile('292_mkimage_missing_multiple.dts', allow_missing=True)
6718         err = stderr.getvalue()
6719         self.assertIn("is missing external blobs and is non-functional", err)
6720
6721         with self.assertRaises(ValueError) as e:
6722             self._DoTestFile('292_mkimage_missing_multiple.dts', allow_missing=False)
6723         self.assertIn("not found in input path", str(e.exception))
6724
6725     def _PrepareSignEnv(self, dts='280_fit_sign.dts'):
6726         """Prepare sign environment
6727
6728         Create private and public keys, add pubkey into dtb.
6729
6730         Returns:
6731             Tuple:
6732                 FIT container
6733                 Image name
6734                 Private key
6735                 DTB
6736         """
6737         self._SetupSplElf()
6738         data = self._DoReadFileRealDtb(dts)
6739         updated_fname = tools.get_output_filename('image-updated.bin')
6740         tools.write_file(updated_fname, data)
6741         dtb = tools.get_output_filename('source.dtb')
6742         private_key = tools.get_output_filename('test_key.key')
6743         public_key = tools.get_output_filename('test_key.crt')
6744         fit = tools.get_output_filename('fit.fit')
6745         key_dir = tools.get_output_dir()
6746
6747         tools.run('openssl', 'req', '-batch' , '-newkey', 'rsa:4096',
6748                   '-sha256', '-new',  '-nodes',  '-x509', '-keyout',
6749                   private_key, '-out', public_key)
6750         tools.run('fdt_add_pubkey', '-a', 'sha256,rsa4096', '-k', key_dir,
6751                   '-n', 'test_key', '-r', 'conf', dtb)
6752
6753         return fit, updated_fname, private_key, dtb
6754
6755     def testSignSimple(self):
6756         """Test that a FIT container can be signed in image"""
6757         is_signed = False
6758         fit, fname, private_key, dtb = self._PrepareSignEnv()
6759
6760         # do sign with private key
6761         control.SignEntries(fname, None, private_key, 'sha256,rsa4096',
6762                             ['fit'])
6763         is_signed = self._CheckSign(fit, dtb)
6764
6765         self.assertEqual(is_signed, True)
6766
6767     def testSignExactFIT(self):
6768         """Test that a FIT container can be signed and replaced in image"""
6769         is_signed = False
6770         fit, fname, private_key, dtb = self._PrepareSignEnv()
6771
6772         # Make sure we propagate the toolpath, since mkimage may not be on PATH
6773         args = []
6774         if self.toolpath:
6775             for path in self.toolpath:
6776                 args += ['--toolpath', path]
6777
6778         # do sign with private key
6779         self._DoBinman(*args, 'sign', '-i', fname, '-k', private_key, '-a',
6780                        'sha256,rsa4096', '-f', fit, 'fit')
6781         is_signed = self._CheckSign(fit, dtb)
6782
6783         self.assertEqual(is_signed, True)
6784
6785     def testSignNonFit(self):
6786         """Test a non-FIT entry cannot be signed"""
6787         is_signed = False
6788         fit, fname, private_key, _ = self._PrepareSignEnv(
6789             '281_sign_non_fit.dts')
6790
6791         # do sign with private key
6792         with self.assertRaises(ValueError) as e:
6793             self._DoBinman('sign', '-i', fname, '-k', private_key, '-a',
6794                        'sha256,rsa4096', '-f', fit, 'u-boot')
6795         self.assertIn(
6796             "Node '/u-boot': Updating signatures is not supported with this entry type",
6797             str(e.exception))
6798
6799     def testSignMissingMkimage(self):
6800         """Test that FIT signing handles a missing mkimage tool"""
6801         fit, fname, private_key, _ = self._PrepareSignEnv()
6802
6803         # try to sign with a missing mkimage tool
6804         bintool.Bintool.set_missing_list(['mkimage'])
6805         with self.assertRaises(ValueError) as e:
6806             control.SignEntries(fname, None, private_key, 'sha256,rsa4096',
6807                                 ['fit'])
6808         self.assertIn("Node '/fit': Missing tool: 'mkimage'", str(e.exception))
6809
6810     def testSymbolNoWrite(self):
6811         """Test disabling of symbol writing"""
6812         self._SetupSplElf()
6813         self.checkSymbols('282_symbols_disable.dts', U_BOOT_SPL_DATA, 0x1c,
6814                           no_write_symbols=True)
6815
6816     def testSymbolNoWriteExpanded(self):
6817         """Test disabling of symbol writing in expanded entries"""
6818         entry_args = {
6819             'spl-dtb': '1',
6820         }
6821         self.checkSymbols('282_symbols_disable.dts', U_BOOT_SPL_NODTB_DATA +
6822                           U_BOOT_SPL_DTB_DATA, 0x38,
6823                           entry_args=entry_args, use_expanded=True,
6824                           no_write_symbols=True)
6825
6826     def testMkimageSpecial(self):
6827         """Test mkimage ignores special hash-1 node"""
6828         data = self._DoReadFile('283_mkimage_special.dts')
6829
6830         # Just check that the data appears in the file somewhere
6831         self.assertIn(U_BOOT_DATA, data)
6832
6833     def testFitFdtList(self):
6834         """Test an image with an FIT with the fit,fdt-list-val option"""
6835         entry_args = {
6836             'default-dt': 'test-fdt2',
6837         }
6838         data = self._DoReadFileDtb(
6839             '284_fit_fdt_list.dts',
6840             entry_args=entry_args,
6841             extra_indirs=[os.path.join(self._indir, TEST_FDT_SUBDIR)])[0]
6842         self.assertEqual(U_BOOT_NODTB_DATA, data[-len(U_BOOT_NODTB_DATA):])
6843         fit_data = data[len(U_BOOT_DATA):-len(U_BOOT_NODTB_DATA)]
6844
6845     def testSplEmptyBss(self):
6846         """Test an expanded SPL with a zero-size BSS"""
6847         # ELF file with a '__bss_size' symbol
6848         self._SetupSplElf(src_fname='bss_data_zero')
6849
6850         entry_args = {
6851             'spl-bss-pad': 'y',
6852             'spl-dtb': 'y',
6853         }
6854         data = self._DoReadFileDtb('285_spl_expand.dts',
6855                                    use_expanded=True, entry_args=entry_args)[0]
6856
6857     def testTemplate(self):
6858         """Test using a template"""
6859         TestFunctional._MakeInputFile('vga2.bin', b'#' + VGA_DATA)
6860         data = self._DoReadFile('286_template.dts')
6861         first = U_BOOT_DATA + VGA_DATA + U_BOOT_DTB_DATA
6862         second = U_BOOT_DATA + b'#' + VGA_DATA + U_BOOT_DTB_DATA
6863         self.assertEqual(U_BOOT_IMG_DATA + first + second, data)
6864
6865         dtb_fname1 = tools.get_output_filename('u-boot.dtb.tmpl1')
6866         self.assertTrue(os.path.exists(dtb_fname1))
6867         dtb = fdt.Fdt.FromData(tools.read_file(dtb_fname1))
6868         dtb.Scan()
6869         node1 = dtb.GetNode('/binman/template')
6870         self.assertTrue(node1)
6871         vga = dtb.GetNode('/binman/first/intel-vga')
6872         self.assertTrue(vga)
6873
6874         dtb_fname2 = tools.get_output_filename('u-boot.dtb.tmpl2')
6875         self.assertTrue(os.path.exists(dtb_fname2))
6876         dtb2 = fdt.Fdt.FromData(tools.read_file(dtb_fname2))
6877         dtb2.Scan()
6878         node2 = dtb2.GetNode('/binman/template')
6879         self.assertFalse(node2)
6880
6881     def testTemplateBlobMulti(self):
6882         """Test using a template with 'multiple-images' enabled"""
6883         TestFunctional._MakeInputFile('my-blob.bin', b'blob')
6884         TestFunctional._MakeInputFile('my-blob2.bin', b'other')
6885         retcode = self._DoTestFile('287_template_multi.dts')
6886
6887         self.assertEqual(0, retcode)
6888         image = control.images['image']
6889         image_fname = tools.get_output_filename('my-image.bin')
6890         data = tools.read_file(image_fname)
6891         self.assertEqual(b'blob@@@@other', data)
6892
6893     def testTemplateFit(self):
6894         """Test using a template in a FIT"""
6895         fit_data = self._DoReadFile('288_template_fit.dts')
6896         fname = os.path.join(self._indir, 'fit_data.fit')
6897         tools.write_file(fname, fit_data)
6898         out = tools.run('dumpimage', '-l', fname)
6899
6900     def testTemplateSection(self):
6901         """Test using a template in a section (not at top level)"""
6902         TestFunctional._MakeInputFile('vga2.bin', b'#' + VGA_DATA)
6903         data = self._DoReadFile('289_template_section.dts')
6904         first = U_BOOT_DATA + VGA_DATA + U_BOOT_DTB_DATA
6905         second = U_BOOT_DATA + b'#' + VGA_DATA + U_BOOT_DTB_DATA
6906         self.assertEqual(U_BOOT_IMG_DATA + first + second + first, data)
6907
6908     def testMkimageSymbols(self):
6909         """Test using mkimage to build an image with symbols in it"""
6910         self._SetupSplElf('u_boot_binman_syms')
6911         data = self._DoReadFile('290_mkimage_sym.dts')
6912
6913         image = control.images['image']
6914         entries = image.GetEntries()
6915         self.assertIn('u-boot', entries)
6916         u_boot = entries['u-boot']
6917
6918         mkim = entries['mkimage']
6919         mkim_entries = mkim.GetEntries()
6920         self.assertIn('u-boot-spl', mkim_entries)
6921         spl = mkim_entries['u-boot-spl']
6922         self.assertIn('u-boot-spl2', mkim_entries)
6923         spl2 = mkim_entries['u-boot-spl2']
6924
6925         # skip the mkimage header and the area sizes
6926         mk_data = data[mkim.offset + 0x40:]
6927         size, term = struct.unpack('>LL', mk_data[:8])
6928
6929         # There should be only one image, so check that the zero terminator is
6930         # present
6931         self.assertEqual(0, term)
6932
6933         content = mk_data[8:8 + size]
6934
6935         # The image should contain the symbols from u_boot_binman_syms.c
6936         # Note that image_pos is adjusted by the base address of the image,
6937         # which is 0x10 in our test image
6938         spl_data = content[:0x18]
6939         content = content[0x1b:]
6940
6941         # After the header is a table of offsets for each image. There should
6942         # only be one image, then a 0 terminator, so figure out the real start
6943         # of the image data
6944         base = 0x40 + 8
6945
6946         # Check symbols in both u-boot-spl and u-boot-spl2
6947         for i in range(2):
6948             vals = struct.unpack('<LLQLL', spl_data)
6949
6950             # The image should contain the symbols from u_boot_binman_syms.c
6951             # Note that image_pos is adjusted by the base address of the image,
6952             # which is 0x10 in our 'u_boot_binman_syms' test image
6953             self.assertEqual(elf.BINMAN_SYM_MAGIC_VALUE, vals[0])
6954             self.assertEqual(base, vals[1])
6955             self.assertEqual(spl2.offset, vals[2])
6956             # figure out the internal positions of its components
6957             self.assertEqual(0x10 + u_boot.image_pos, vals[3])
6958
6959             # Check that spl and spl2 are actually at the indicated positions
6960             self.assertEqual(
6961                 elf.BINMAN_SYM_MAGIC_VALUE,
6962                 struct.unpack('<I', data[spl.image_pos:spl.image_pos + 4])[0])
6963             self.assertEqual(
6964                 elf.BINMAN_SYM_MAGIC_VALUE,
6965                 struct.unpack('<I', data[spl2.image_pos:spl2.image_pos + 4])[0])
6966
6967             self.assertEqual(len(U_BOOT_DATA), vals[4])
6968
6969             # Move to next
6970             spl_data = content[:0x18]
6971
6972     def testTIBoardConfig(self):
6973         """Test that a schema validated board config file can be generated"""
6974         data = self._DoReadFile('293_ti_board_cfg.dts')
6975         self.assertEqual(TI_BOARD_CONFIG_DATA, data)
6976
6977     def testTIBoardConfigCombined(self):
6978         """Test that a schema validated combined board config file can be generated"""
6979         data = self._DoReadFile('294_ti_board_cfg_combined.dts')
6980         configlen_noheader = TI_BOARD_CONFIG_DATA * 4
6981         self.assertGreater(data, configlen_noheader)
6982
6983     def testTIBoardConfigNoDataType(self):
6984         """Test that error is thrown when data type is not supported"""
6985         with self.assertRaises(ValueError) as e:
6986             data = self._DoReadFile('295_ti_board_cfg_no_type.dts')
6987         self.assertIn("Schema validation error", str(e.exception))
6988
6989     def testPackTiSecure(self):
6990         """Test that an image with a TI secured binary can be created"""
6991         keyfile = self.TestFile('key.key')
6992         entry_args = {
6993             'keyfile': keyfile,
6994         }
6995         data = self._DoReadFileDtb('296_ti_secure.dts',
6996                                    entry_args=entry_args)[0]
6997         self.assertGreater(len(data), len(TI_UNSECURE_DATA))
6998
6999     def testPackTiSecureMissingTool(self):
7000         """Test that an image with a TI secured binary (non-functional) can be created
7001         when openssl is missing"""
7002         keyfile = self.TestFile('key.key')
7003         entry_args = {
7004             'keyfile': keyfile,
7005         }
7006         with test_util.capture_sys_output() as (_, stderr):
7007             self._DoTestFile('296_ti_secure.dts',
7008                              force_missing_bintools='openssl',
7009                              entry_args=entry_args)
7010         err = stderr.getvalue()
7011         self.assertRegex(err, "Image 'image'.*missing bintools.*: openssl")
7012
7013     def testPackTiSecureROM(self):
7014         """Test that a ROM image with a TI secured binary can be created"""
7015         keyfile = self.TestFile('key.key')
7016         entry_args = {
7017             'keyfile': keyfile,
7018         }
7019         data = self._DoReadFileDtb('297_ti_secure_rom.dts',
7020                                 entry_args=entry_args)[0]
7021         data_a = self._DoReadFileDtb('299_ti_secure_rom_a.dts',
7022                                 entry_args=entry_args)[0]
7023         data_b = self._DoReadFileDtb('300_ti_secure_rom_b.dts',
7024                                 entry_args=entry_args)[0]
7025         self.assertGreater(len(data), len(TI_UNSECURE_DATA))
7026         self.assertGreater(len(data_a), len(TI_UNSECURE_DATA))
7027         self.assertGreater(len(data_b), len(TI_UNSECURE_DATA))
7028
7029     def testPackTiSecureROMCombined(self):
7030         """Test that a ROM image with a TI secured binary can be created"""
7031         keyfile = self.TestFile('key.key')
7032         entry_args = {
7033             'keyfile': keyfile,
7034         }
7035         data = self._DoReadFileDtb('298_ti_secure_rom_combined.dts',
7036                                 entry_args=entry_args)[0]
7037         self.assertGreater(len(data), len(TI_UNSECURE_DATA))
7038
7039     def testEncryptedNoAlgo(self):
7040         """Test encrypted node with missing required properties"""
7041         with self.assertRaises(ValueError) as e:
7042             self._DoReadFileDtb('301_encrypted_no_algo.dts')
7043         self.assertIn(
7044             "Node '/binman/fit/images/u-boot/encrypted': 'encrypted' entry is missing properties: algo iv-filename",
7045             str(e.exception))
7046
7047     def testEncryptedInvalidIvfile(self):
7048         """Test encrypted node with invalid iv file"""
7049         with self.assertRaises(ValueError) as e:
7050             self._DoReadFileDtb('302_encrypted_invalid_iv_file.dts')
7051         self.assertIn("Filename 'invalid-iv-file' not found in input path",
7052                       str(e.exception))
7053
7054     def testEncryptedMissingKey(self):
7055         """Test encrypted node with missing key properties"""
7056         with self.assertRaises(ValueError) as e:
7057             self._DoReadFileDtb('303_encrypted_missing_key.dts')
7058         self.assertIn(
7059             "Node '/binman/fit/images/u-boot/encrypted': Provide either 'key-filename' or 'key-source'",
7060             str(e.exception))
7061
7062     def testEncryptedKeySource(self):
7063         """Test encrypted node with key-source property"""
7064         data = self._DoReadFileDtb('304_encrypted_key_source.dts')[0]
7065
7066         dtb = fdt.Fdt.FromData(data)
7067         dtb.Scan()
7068
7069         node = dtb.GetNode('/images/u-boot/cipher')
7070         self.assertEqual('algo-name', node.props['algo'].value)
7071         self.assertEqual('key-source-value', node.props['key-source'].value)
7072         self.assertEqual(ENCRYPTED_IV_DATA,
7073                          tools.to_bytes(''.join(node.props['iv'].value)))
7074         self.assertNotIn('key', node.props)
7075
7076     def testEncryptedKeyFile(self):
7077         """Test encrypted node with key-filename property"""
7078         data = self._DoReadFileDtb('305_encrypted_key_file.dts')[0]
7079
7080         dtb = fdt.Fdt.FromData(data)
7081         dtb.Scan()
7082
7083         node = dtb.GetNode('/images/u-boot/cipher')
7084         self.assertEqual('algo-name', node.props['algo'].value)
7085         self.assertEqual(ENCRYPTED_IV_DATA,
7086                          tools.to_bytes(''.join(node.props['iv'].value)))
7087         self.assertEqual(ENCRYPTED_KEY_DATA,
7088                          tools.to_bytes(''.join(node.props['key'].value)))
7089         self.assertNotIn('key-source', node.props)
7090
7091
7092     def testSplPubkeyDtb(self):
7093          """Test u_boot_spl_pubkey_dtb etype"""
7094          data = tools.read_file(self.TestFile("key.pem"))
7095          self._MakeInputFile("key.crt", data)
7096          self._DoReadFileRealDtb('306_spl_pubkey_dtb.dts')
7097          image = control.images['image']
7098          entries = image.GetEntries()
7099          dtb_entry = entries['u-boot-spl-pubkey-dtb']
7100          dtb_data = dtb_entry.GetData()
7101          dtb = fdt.Fdt.FromData(dtb_data)
7102          dtb.Scan()
7103
7104          signature_node = dtb.GetNode('/signature')
7105          self.assertIsNotNone(signature_node)
7106          key_node = signature_node.FindNode("key-key")
7107          self.assertIsNotNone(key_node)
7108          self.assertEqual(fdt_util.GetString(key_node, "required"),
7109                           "conf")
7110          self.assertEqual(fdt_util.GetString(key_node, "algo"),
7111                           "sha384,rsa4096")
7112          self.assertEqual(fdt_util.GetString(key_node, "key-name-hint"),
7113                           "key")
7114
7115 if __name__ == "__main__":
7116     unittest.main()