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