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