179326c42a3eefc6e40df04adb0ceadd9cc22408
[platform/kernel/u-boot.git] / tools / binman / ftest.py
1 # SPDX-License-Identifier: GPL-2.0+
2 # Copyright (c) 2016 Google, Inc
3 # Written by Simon Glass <sjg@chromium.org>
4 #
5 # To run a single test, change to this directory, and:
6 #
7 #    python -m unittest func_test.TestFunctional.testHelp
8
9 import collections
10 import gzip
11 import hashlib
12 from optparse import OptionParser
13 import os
14 import re
15 import shutil
16 import struct
17 import sys
18 import tempfile
19 import unittest
20
21 from binman import bintool
22 from binman import cbfs_util
23 from binman import cmdline
24 from binman import control
25 from binman import elf
26 from binman import elf_test
27 from binman import fip_util
28 from binman import fmap_util
29 from binman import state
30 from dtoc import fdt
31 from dtoc import fdt_util
32 from binman.etype import fdtmap
33 from binman.etype import image_header
34 from binman.image import Image
35 from patman import command
36 from patman import test_util
37 from patman import tools
38 from patman import tout
39
40 # Contents of test files, corresponding to different entry types
41 U_BOOT_DATA           = b'1234'
42 U_BOOT_IMG_DATA       = b'img'
43 U_BOOT_SPL_DATA       = b'56780123456789abcdefghi'
44 U_BOOT_TPL_DATA       = b'tpl9876543210fedcbazyw'
45 BLOB_DATA             = b'89'
46 ME_DATA               = b'0abcd'
47 VGA_DATA              = b'vga'
48 U_BOOT_DTB_DATA       = b'udtb'
49 U_BOOT_SPL_DTB_DATA   = b'spldtb'
50 U_BOOT_TPL_DTB_DATA   = b'tpldtb'
51 X86_START16_DATA      = b'start16'
52 X86_START16_SPL_DATA  = b'start16spl'
53 X86_START16_TPL_DATA  = b'start16tpl'
54 X86_RESET16_DATA      = b'reset16'
55 X86_RESET16_SPL_DATA  = b'reset16spl'
56 X86_RESET16_TPL_DATA  = b'reset16tpl'
57 PPC_MPC85XX_BR_DATA   = b'ppcmpc85xxbr'
58 U_BOOT_NODTB_DATA     = b'nodtb with microcode pointer somewhere in here'
59 U_BOOT_SPL_NODTB_DATA = b'splnodtb with microcode pointer somewhere in here'
60 U_BOOT_TPL_NODTB_DATA = b'tplnodtb with microcode pointer somewhere in here'
61 FSP_DATA              = b'fsp'
62 CMC_DATA              = b'cmc'
63 VBT_DATA              = b'vbt'
64 MRC_DATA              = b'mrc'
65 TEXT_DATA             = 'text'
66 TEXT_DATA2            = 'text2'
67 TEXT_DATA3            = 'text3'
68 CROS_EC_RW_DATA       = b'ecrw'
69 GBB_DATA              = b'gbbd'
70 BMPBLK_DATA           = b'bmp'
71 VBLOCK_DATA           = b'vblk'
72 FILES_DATA            = (b"sorry I'm late\nOh, don't bother apologising, I'm " +
73                          b"sorry you're alive\n")
74 COMPRESS_DATA         = b'compress xxxxxxxxxxxxxxxxxxxxxx data'
75 COMPRESS_DATA_BIG     = COMPRESS_DATA * 2
76 REFCODE_DATA          = b'refcode'
77 FSP_M_DATA            = b'fsp_m'
78 FSP_S_DATA            = b'fsp_s'
79 FSP_T_DATA            = b'fsp_t'
80 ATF_BL31_DATA         = b'bl31'
81 ATF_BL2U_DATA         = b'bl2u'
82 OPENSBI_DATA          = b'opensbi'
83 SCP_DATA              = b'scp'
84 TEST_FDT1_DATA        = b'fdt1'
85 TEST_FDT2_DATA        = b'test-fdt2'
86 ENV_DATA              = b'var1=1\nvar2="2"'
87
88 # Subdirectory of the input dir to use to put test FDTs
89 TEST_FDT_SUBDIR       = 'fdts'
90
91 # The expected size for the device tree in some tests
92 EXTRACT_DTB_SIZE = 0x3c9
93
94 # Properties expected to be in the device tree when update_dtb is used
95 BASE_DTB_PROPS = ['offset', 'size', 'image-pos']
96
97 # Extra properties expected to be in the device tree when allow-repack is used
98 REPACK_DTB_PROPS = ['orig-offset', 'orig-size']
99
100
101 class TestFunctional(unittest.TestCase):
102     """Functional tests for binman
103
104     Most of these use a sample .dts file to build an image and then check
105     that it looks correct. The sample files are in the test/ subdirectory
106     and are numbered.
107
108     For each entry type a very small test file is created using fixed
109     string contents. This makes it easy to test that things look right, and
110     debug problems.
111
112     In some cases a 'real' file must be used - these are also supplied in
113     the test/ diurectory.
114     """
115     @classmethod
116     def setUpClass(cls):
117         global entry
118         from binman import entry
119
120         # Handle the case where argv[0] is 'python'
121         cls._binman_dir = os.path.dirname(os.path.realpath(sys.argv[0]))
122         cls._binman_pathname = os.path.join(cls._binman_dir, 'binman')
123
124         # Create a temporary directory for input files
125         cls._indir = tempfile.mkdtemp(prefix='binmant.')
126
127         # Create some test files
128         TestFunctional._MakeInputFile('u-boot.bin', U_BOOT_DATA)
129         TestFunctional._MakeInputFile('u-boot.img', U_BOOT_IMG_DATA)
130         TestFunctional._MakeInputFile('spl/u-boot-spl.bin', U_BOOT_SPL_DATA)
131         TestFunctional._MakeInputFile('tpl/u-boot-tpl.bin', U_BOOT_TPL_DATA)
132         TestFunctional._MakeInputFile('blobfile', BLOB_DATA)
133         TestFunctional._MakeInputFile('me.bin', ME_DATA)
134         TestFunctional._MakeInputFile('vga.bin', VGA_DATA)
135         cls._ResetDtbs()
136
137         TestFunctional._MakeInputFile('u-boot-br.bin', PPC_MPC85XX_BR_DATA)
138
139         TestFunctional._MakeInputFile('u-boot-x86-start16.bin', X86_START16_DATA)
140         TestFunctional._MakeInputFile('spl/u-boot-x86-start16-spl.bin',
141                                       X86_START16_SPL_DATA)
142         TestFunctional._MakeInputFile('tpl/u-boot-x86-start16-tpl.bin',
143                                       X86_START16_TPL_DATA)
144
145         TestFunctional._MakeInputFile('u-boot-x86-reset16.bin',
146                                       X86_RESET16_DATA)
147         TestFunctional._MakeInputFile('spl/u-boot-x86-reset16-spl.bin',
148                                       X86_RESET16_SPL_DATA)
149         TestFunctional._MakeInputFile('tpl/u-boot-x86-reset16-tpl.bin',
150                                       X86_RESET16_TPL_DATA)
151
152         TestFunctional._MakeInputFile('u-boot-nodtb.bin', U_BOOT_NODTB_DATA)
153         TestFunctional._MakeInputFile('spl/u-boot-spl-nodtb.bin',
154                                       U_BOOT_SPL_NODTB_DATA)
155         TestFunctional._MakeInputFile('tpl/u-boot-tpl-nodtb.bin',
156                                       U_BOOT_TPL_NODTB_DATA)
157         TestFunctional._MakeInputFile('fsp.bin', FSP_DATA)
158         TestFunctional._MakeInputFile('cmc.bin', CMC_DATA)
159         TestFunctional._MakeInputFile('vbt.bin', VBT_DATA)
160         TestFunctional._MakeInputFile('mrc.bin', MRC_DATA)
161         TestFunctional._MakeInputFile('ecrw.bin', CROS_EC_RW_DATA)
162         TestFunctional._MakeInputDir('devkeys')
163         TestFunctional._MakeInputFile('bmpblk.bin', BMPBLK_DATA)
164         TestFunctional._MakeInputFile('refcode.bin', REFCODE_DATA)
165         TestFunctional._MakeInputFile('fsp_m.bin', FSP_M_DATA)
166         TestFunctional._MakeInputFile('fsp_s.bin', FSP_S_DATA)
167         TestFunctional._MakeInputFile('fsp_t.bin', FSP_T_DATA)
168
169         cls._elf_testdir = os.path.join(cls._indir, 'elftest')
170         elf_test.BuildElfTestFiles(cls._elf_testdir)
171
172         # ELF file with a '_dt_ucode_base_size' symbol
173         TestFunctional._MakeInputFile('u-boot',
174             tools.ReadFile(cls.ElfTestFile('u_boot_ucode_ptr')))
175
176         # Intel flash descriptor file
177         cls._SetupDescriptor()
178
179         shutil.copytree(cls.TestFile('files'),
180                         os.path.join(cls._indir, 'files'))
181
182         TestFunctional._MakeInputFile('compress', COMPRESS_DATA)
183         TestFunctional._MakeInputFile('compress_big', COMPRESS_DATA_BIG)
184         TestFunctional._MakeInputFile('bl31.bin', ATF_BL31_DATA)
185         TestFunctional._MakeInputFile('bl2u.bin', ATF_BL2U_DATA)
186         TestFunctional._MakeInputFile('fw_dynamic.bin', OPENSBI_DATA)
187         TestFunctional._MakeInputFile('scp.bin', SCP_DATA)
188
189         # Add a few .dtb files for testing
190         TestFunctional._MakeInputFile('%s/test-fdt1.dtb' % TEST_FDT_SUBDIR,
191                                       TEST_FDT1_DATA)
192         TestFunctional._MakeInputFile('%s/test-fdt2.dtb' % TEST_FDT_SUBDIR,
193                                       TEST_FDT2_DATA)
194
195         TestFunctional._MakeInputFile('env.txt', ENV_DATA)
196
197         # Travis-CI may have an old lz4
198         cls.have_lz4 = True
199         try:
200             tools.Run('lz4', '--no-frame-crc', '-c',
201                       os.path.join(cls._indir, 'u-boot.bin'), binary=True)
202         except:
203             cls.have_lz4 = False
204
205     @classmethod
206     def tearDownClass(cls):
207         """Remove the temporary input directory and its contents"""
208         if cls.preserve_indir:
209             print('Preserving input dir: %s' % cls._indir)
210         else:
211             if cls._indir:
212                 shutil.rmtree(cls._indir)
213         cls._indir = None
214
215     @classmethod
216     def setup_test_args(cls, preserve_indir=False, preserve_outdirs=False,
217                         toolpath=None, verbosity=None):
218         """Accept arguments controlling test execution
219
220         Args:
221             preserve_indir: Preserve the shared input directory used by all
222                 tests in this class.
223             preserve_outdir: Preserve the output directories used by tests. Each
224                 test has its own, so this is normally only useful when running a
225                 single test.
226             toolpath: ist of paths to use for tools
227         """
228         cls.preserve_indir = preserve_indir
229         cls.preserve_outdirs = preserve_outdirs
230         cls.toolpath = toolpath
231         cls.verbosity = verbosity
232
233     def _CheckLz4(self):
234         if not self.have_lz4:
235             self.skipTest('lz4 --no-frame-crc not available')
236
237     def _CleanupOutputDir(self):
238         """Remove the temporary output directory"""
239         if self.preserve_outdirs:
240             print('Preserving output dir: %s' % tools.outdir)
241         else:
242             tools._FinaliseForTest()
243
244     def setUp(self):
245         # Enable this to turn on debugging output
246         # tout.Init(tout.DEBUG)
247         command.test_result = None
248
249     def tearDown(self):
250         """Remove the temporary output directory"""
251         self._CleanupOutputDir()
252
253     def _SetupImageInTmpdir(self):
254         """Set up the output image in a new temporary directory
255
256         This is used when an image has been generated in the output directory,
257         but we want to run binman again. This will create a new output
258         directory and fail to delete the original one.
259
260         This creates a new temporary directory, copies the image to it (with a
261         new name) and removes the old output directory.
262
263         Returns:
264             Tuple:
265                 Temporary directory to use
266                 New image filename
267         """
268         image_fname = tools.GetOutputFilename('image.bin')
269         tmpdir = tempfile.mkdtemp(prefix='binman.')
270         updated_fname = os.path.join(tmpdir, 'image-updated.bin')
271         tools.WriteFile(updated_fname, tools.ReadFile(image_fname))
272         self._CleanupOutputDir()
273         return tmpdir, updated_fname
274
275     @classmethod
276     def _ResetDtbs(cls):
277         TestFunctional._MakeInputFile('u-boot.dtb', U_BOOT_DTB_DATA)
278         TestFunctional._MakeInputFile('spl/u-boot-spl.dtb', U_BOOT_SPL_DTB_DATA)
279         TestFunctional._MakeInputFile('tpl/u-boot-tpl.dtb', U_BOOT_TPL_DTB_DATA)
280
281     def _RunBinman(self, *args, **kwargs):
282         """Run binman using the command line
283
284         Args:
285             Arguments to pass, as a list of strings
286             kwargs: Arguments to pass to Command.RunPipe()
287         """
288         result = command.RunPipe([[self._binman_pathname] + list(args)],
289                 capture=True, capture_stderr=True, raise_on_error=False)
290         if result.return_code and kwargs.get('raise_on_error', True):
291             raise Exception("Error running '%s': %s" % (' '.join(args),
292                             result.stdout + result.stderr))
293         return result
294
295     def _DoBinman(self, *argv):
296         """Run binman using directly (in the same process)
297
298         Args:
299             Arguments to pass, as a list of strings
300         Returns:
301             Return value (0 for success)
302         """
303         argv = list(argv)
304         args = cmdline.ParseArgs(argv)
305         args.pager = 'binman-invalid-pager'
306         args.build_dir = self._indir
307
308         # For testing, you can force an increase in verbosity here
309         # args.verbosity = tout.DEBUG
310         return control.Binman(args)
311
312     def _DoTestFile(self, fname, debug=False, map=False, update_dtb=False,
313                     entry_args=None, images=None, use_real_dtb=False,
314                     use_expanded=False, verbosity=None, allow_missing=False,
315                     allow_fake_blobs=False, extra_indirs=None, threads=None,
316                     test_section_timeout=False, update_fdt_in_elf=None):
317         """Run binman with a given test file
318
319         Args:
320             fname: Device-tree source filename to use (e.g. 005_simple.dts)
321             debug: True to enable debugging output
322             map: True to output map files for the images
323             update_dtb: Update the offset and size of each entry in the device
324                 tree before packing it into the image
325             entry_args: Dict of entry args to supply to binman
326                 key: arg name
327                 value: value of that arg
328             images: List of image names to build
329             use_real_dtb: True to use the test file as the contents of
330                 the u-boot-dtb entry. Normally this is not needed and the
331                 test contents (the U_BOOT_DTB_DATA string) can be used.
332                 But in some test we need the real contents.
333             use_expanded: True to use expanded entries where available, e.g.
334                 'u-boot-expanded' instead of 'u-boot'
335             verbosity: Verbosity level to use (0-3, None=don't set it)
336             allow_missing: Set the '--allow-missing' flag so that missing
337                 external binaries just produce a warning instead of an error
338             allow_fake_blobs: Set the '--fake-ext-blobs' flag
339             extra_indirs: Extra input directories to add using -I
340             threads: Number of threads to use (None for default, 0 for
341                 single-threaded)
342             test_section_timeout: True to force the first time to timeout, as
343                 used in testThreadTimeout()
344             update_fdt_in_elf: Value to pass with --update-fdt-in-elf=xxx
345
346         Returns:
347             int return code, 0 on success
348         """
349         args = []
350         if debug:
351             args.append('-D')
352         if verbosity is not None:
353             args.append('-v%d' % verbosity)
354         elif self.verbosity:
355             args.append('-v%d' % self.verbosity)
356         if self.toolpath:
357             for path in self.toolpath:
358                 args += ['--toolpath', path]
359         if threads is not None:
360             args.append('-T%d' % threads)
361         if test_section_timeout:
362             args.append('--test-section-timeout')
363         args += ['build', '-p', '-I', self._indir, '-d', self.TestFile(fname)]
364         if map:
365             args.append('-m')
366         if update_dtb:
367             args.append('-u')
368         if not use_real_dtb:
369             args.append('--fake-dtb')
370         if not use_expanded:
371             args.append('--no-expanded')
372         if entry_args:
373             for arg, value in entry_args.items():
374                 args.append('-a%s=%s' % (arg, value))
375         if allow_missing:
376             args.append('-M')
377         if allow_fake_blobs:
378             args.append('--fake-ext-blobs')
379         if update_fdt_in_elf:
380             args += ['--update-fdt-in-elf', update_fdt_in_elf]
381         if images:
382             for image in images:
383                 args += ['-i', image]
384         if extra_indirs:
385             for indir in extra_indirs:
386                 args += ['-I', indir]
387         return self._DoBinman(*args)
388
389     def _SetupDtb(self, fname, outfile='u-boot.dtb'):
390         """Set up a new test device-tree file
391
392         The given file is compiled and set up as the device tree to be used
393         for ths test.
394
395         Args:
396             fname: Filename of .dts file to read
397             outfile: Output filename for compiled device-tree binary
398
399         Returns:
400             Contents of device-tree binary
401         """
402         tmpdir = tempfile.mkdtemp(prefix='binmant.')
403         dtb = fdt_util.EnsureCompiled(self.TestFile(fname), tmpdir)
404         with open(dtb, 'rb') as fd:
405             data = fd.read()
406             TestFunctional._MakeInputFile(outfile, data)
407         shutil.rmtree(tmpdir)
408         return data
409
410     def _GetDtbContentsForSplTpl(self, dtb_data, name):
411         """Create a version of the main DTB for SPL or SPL
412
413         For testing we don't actually have different versions of the DTB. With
414         U-Boot we normally run fdtgrep to remove unwanted nodes, but for tests
415         we don't normally have any unwanted nodes.
416
417         We still want the DTBs for SPL and TPL to be different though, since
418         otherwise it is confusing to know which one we are looking at. So add
419         an 'spl' or 'tpl' property to the top-level node.
420
421         Args:
422             dtb_data: dtb data to modify (this should be a value devicetree)
423             name: Name of a new property to add
424
425         Returns:
426             New dtb data with the property added
427         """
428         dtb = fdt.Fdt.FromData(dtb_data)
429         dtb.Scan()
430         dtb.GetNode('/binman').AddZeroProp(name)
431         dtb.Sync(auto_resize=True)
432         dtb.Pack()
433         return dtb.GetContents()
434
435     def _DoReadFileDtb(self, fname, use_real_dtb=False, use_expanded=False,
436                        map=False, update_dtb=False, entry_args=None,
437                        reset_dtbs=True, extra_indirs=None, threads=None):
438         """Run binman and return the resulting image
439
440         This runs binman with a given test file and then reads the resulting
441         output file. It is a shortcut function since most tests need to do
442         these steps.
443
444         Raises an assertion failure if binman returns a non-zero exit code.
445
446         Args:
447             fname: Device-tree source filename to use (e.g. 005_simple.dts)
448             use_real_dtb: True to use the test file as the contents of
449                 the u-boot-dtb entry. Normally this is not needed and the
450                 test contents (the U_BOOT_DTB_DATA string) can be used.
451                 But in some test we need the real contents.
452             use_expanded: True to use expanded entries where available, e.g.
453                 'u-boot-expanded' instead of 'u-boot'
454             map: True to output map files for the images
455             update_dtb: Update the offset and size of each entry in the device
456                 tree before packing it into the image
457             entry_args: Dict of entry args to supply to binman
458                 key: arg name
459                 value: value of that arg
460             reset_dtbs: With use_real_dtb the test dtb is overwritten by this
461                 function. If reset_dtbs is True, then the original test dtb
462                 is written back before this function finishes
463             extra_indirs: Extra input directories to add using -I
464             threads: Number of threads to use (None for default, 0 for
465                 single-threaded)
466
467         Returns:
468             Tuple:
469                 Resulting image contents
470                 Device tree contents
471                 Map data showing contents of image (or None if none)
472                 Output device tree binary filename ('u-boot.dtb' path)
473         """
474         dtb_data = None
475         # Use the compiled test file as the u-boot-dtb input
476         if use_real_dtb:
477             dtb_data = self._SetupDtb(fname)
478
479             # For testing purposes, make a copy of the DT for SPL and TPL. Add
480             # a node indicating which it is, so aid verification.
481             for name in ['spl', 'tpl']:
482                 dtb_fname = '%s/u-boot-%s.dtb' % (name, name)
483                 outfile = os.path.join(self._indir, dtb_fname)
484                 TestFunctional._MakeInputFile(dtb_fname,
485                         self._GetDtbContentsForSplTpl(dtb_data, name))
486
487         try:
488             retcode = self._DoTestFile(fname, map=map, update_dtb=update_dtb,
489                     entry_args=entry_args, use_real_dtb=use_real_dtb,
490                     use_expanded=use_expanded, extra_indirs=extra_indirs,
491                     threads=threads)
492             self.assertEqual(0, retcode)
493             out_dtb_fname = tools.GetOutputFilename('u-boot.dtb.out')
494
495             # Find the (only) image, read it and return its contents
496             image = control.images['image']
497             image_fname = tools.GetOutputFilename('image.bin')
498             self.assertTrue(os.path.exists(image_fname))
499             if map:
500                 map_fname = tools.GetOutputFilename('image.map')
501                 with open(map_fname) as fd:
502                     map_data = fd.read()
503             else:
504                 map_data = None
505             with open(image_fname, 'rb') as fd:
506                 return fd.read(), dtb_data, map_data, out_dtb_fname
507         finally:
508             # Put the test file back
509             if reset_dtbs and use_real_dtb:
510                 self._ResetDtbs()
511
512     def _DoReadFileRealDtb(self, fname):
513         """Run binman with a real .dtb file and return the resulting data
514
515         Args:
516             fname: DT source filename to use (e.g. 082_fdt_update_all.dts)
517
518         Returns:
519             Resulting image contents
520         """
521         return self._DoReadFileDtb(fname, use_real_dtb=True, update_dtb=True)[0]
522
523     def _DoReadFile(self, fname, use_real_dtb=False):
524         """Helper function which discards the device-tree binary
525
526         Args:
527             fname: Device-tree source filename to use (e.g. 005_simple.dts)
528             use_real_dtb: True to use the test file as the contents of
529                 the u-boot-dtb entry. Normally this is not needed and the
530                 test contents (the U_BOOT_DTB_DATA string) can be used.
531                 But in some test we need the real contents.
532
533         Returns:
534             Resulting image contents
535         """
536         return self._DoReadFileDtb(fname, use_real_dtb)[0]
537
538     @classmethod
539     def _MakeInputFile(cls, fname, contents):
540         """Create a new test input file, creating directories as needed
541
542         Args:
543             fname: Filename to create
544             contents: File contents to write in to the file
545         Returns:
546             Full pathname of file created
547         """
548         pathname = os.path.join(cls._indir, fname)
549         dirname = os.path.dirname(pathname)
550         if dirname and not os.path.exists(dirname):
551             os.makedirs(dirname)
552         with open(pathname, 'wb') as fd:
553             fd.write(contents)
554         return pathname
555
556     @classmethod
557     def _MakeInputDir(cls, dirname):
558         """Create a new test input directory, creating directories as needed
559
560         Args:
561             dirname: Directory name to create
562
563         Returns:
564             Full pathname of directory created
565         """
566         pathname = os.path.join(cls._indir, dirname)
567         if not os.path.exists(pathname):
568             os.makedirs(pathname)
569         return pathname
570
571     @classmethod
572     def _SetupSplElf(cls, src_fname='bss_data'):
573         """Set up an ELF file with a '_dt_ucode_base_size' symbol
574
575         Args:
576             Filename of ELF file to use as SPL
577         """
578         TestFunctional._MakeInputFile('spl/u-boot-spl',
579             tools.ReadFile(cls.ElfTestFile(src_fname)))
580
581     @classmethod
582     def _SetupTplElf(cls, src_fname='bss_data'):
583         """Set up an ELF file with a '_dt_ucode_base_size' symbol
584
585         Args:
586             Filename of ELF file to use as TPL
587         """
588         TestFunctional._MakeInputFile('tpl/u-boot-tpl',
589             tools.ReadFile(cls.ElfTestFile(src_fname)))
590
591     @classmethod
592     def _SetupDescriptor(cls):
593         with open(cls.TestFile('descriptor.bin'), 'rb') as fd:
594             TestFunctional._MakeInputFile('descriptor.bin', fd.read())
595
596     @classmethod
597     def TestFile(cls, fname):
598         return os.path.join(cls._binman_dir, 'test', fname)
599
600     @classmethod
601     def ElfTestFile(cls, fname):
602         return os.path.join(cls._elf_testdir, fname)
603
604     def AssertInList(self, grep_list, target):
605         """Assert that at least one of a list of things is in a target
606
607         Args:
608             grep_list: List of strings to check
609             target: Target string
610         """
611         for grep in grep_list:
612             if grep in target:
613                 return
614         self.fail("Error: '%s' not found in '%s'" % (grep_list, target))
615
616     def CheckNoGaps(self, entries):
617         """Check that all entries fit together without gaps
618
619         Args:
620             entries: List of entries to check
621         """
622         offset = 0
623         for entry in entries.values():
624             self.assertEqual(offset, entry.offset)
625             offset += entry.size
626
627     def GetFdtLen(self, dtb):
628         """Get the totalsize field from a device-tree binary
629
630         Args:
631             dtb: Device-tree binary contents
632
633         Returns:
634             Total size of device-tree binary, from the header
635         """
636         return struct.unpack('>L', dtb[4:8])[0]
637
638     def _GetPropTree(self, dtb, prop_names, prefix='/binman/'):
639         def AddNode(node, path):
640             if node.name != '/':
641                 path += '/' + node.name
642             for prop in node.props.values():
643                 if prop.name in prop_names:
644                     prop_path = path + ':' + prop.name
645                     tree[prop_path[len(prefix):]] = fdt_util.fdt32_to_cpu(
646                         prop.value)
647             for subnode in node.subnodes:
648                 AddNode(subnode, path)
649
650         tree = {}
651         AddNode(dtb.GetRoot(), '')
652         return tree
653
654     def testRun(self):
655         """Test a basic run with valid args"""
656         result = self._RunBinman('-h')
657
658     def testFullHelp(self):
659         """Test that the full help is displayed with -H"""
660         result = self._RunBinman('-H')
661         help_file = os.path.join(self._binman_dir, 'README.rst')
662         # Remove possible extraneous strings
663         extra = '::::::::::::::\n' + help_file + '\n::::::::::::::\n'
664         gothelp = result.stdout.replace(extra, '')
665         self.assertEqual(len(gothelp), os.path.getsize(help_file))
666         self.assertEqual(0, len(result.stderr))
667         self.assertEqual(0, result.return_code)
668
669     def testFullHelpInternal(self):
670         """Test that the full help is displayed with -H"""
671         try:
672             command.test_result = command.CommandResult()
673             result = self._DoBinman('-H')
674             help_file = os.path.join(self._binman_dir, 'README.rst')
675         finally:
676             command.test_result = None
677
678     def testHelp(self):
679         """Test that the basic help is displayed with -h"""
680         result = self._RunBinman('-h')
681         self.assertTrue(len(result.stdout) > 200)
682         self.assertEqual(0, len(result.stderr))
683         self.assertEqual(0, result.return_code)
684
685     def testBoard(self):
686         """Test that we can run it with a specific board"""
687         self._SetupDtb('005_simple.dts', 'sandbox/u-boot.dtb')
688         TestFunctional._MakeInputFile('sandbox/u-boot.bin', U_BOOT_DATA)
689         result = self._DoBinman('build', '-n', '-b', 'sandbox')
690         self.assertEqual(0, result)
691
692     def testNeedBoard(self):
693         """Test that we get an error when no board ius supplied"""
694         with self.assertRaises(ValueError) as e:
695             result = self._DoBinman('build')
696         self.assertIn("Must provide a board to process (use -b <board>)",
697                 str(e.exception))
698
699     def testMissingDt(self):
700         """Test that an invalid device-tree file generates an error"""
701         with self.assertRaises(Exception) as e:
702             self._RunBinman('build', '-d', 'missing_file')
703         # We get one error from libfdt, and a different one from fdtget.
704         self.AssertInList(["Couldn't open blob from 'missing_file'",
705                            'No such file or directory'], str(e.exception))
706
707     def testBrokenDt(self):
708         """Test that an invalid device-tree source file generates an error
709
710         Since this is a source file it should be compiled and the error
711         will come from the device-tree compiler (dtc).
712         """
713         with self.assertRaises(Exception) as e:
714             self._RunBinman('build', '-d', self.TestFile('001_invalid.dts'))
715         self.assertIn("FATAL ERROR: Unable to parse input tree",
716                 str(e.exception))
717
718     def testMissingNode(self):
719         """Test that a device tree without a 'binman' node generates an error"""
720         with self.assertRaises(Exception) as e:
721             self._DoBinman('build', '-d', self.TestFile('002_missing_node.dts'))
722         self.assertIn("does not have a 'binman' node", str(e.exception))
723
724     def testEmpty(self):
725         """Test that an empty binman node works OK (i.e. does nothing)"""
726         result = self._RunBinman('build', '-d', self.TestFile('003_empty.dts'))
727         self.assertEqual(0, len(result.stderr))
728         self.assertEqual(0, result.return_code)
729
730     def testInvalidEntry(self):
731         """Test that an invalid entry is flagged"""
732         with self.assertRaises(Exception) as e:
733             result = self._RunBinman('build', '-d',
734                                      self.TestFile('004_invalid_entry.dts'))
735         self.assertIn("Unknown entry type 'not-a-valid-type' in node "
736                 "'/binman/not-a-valid-type'", str(e.exception))
737
738     def testSimple(self):
739         """Test a simple binman with a single file"""
740         data = self._DoReadFile('005_simple.dts')
741         self.assertEqual(U_BOOT_DATA, data)
742
743     def testSimpleDebug(self):
744         """Test a simple binman run with debugging enabled"""
745         self._DoTestFile('005_simple.dts', debug=True)
746
747     def testDual(self):
748         """Test that we can handle creating two images
749
750         This also tests image padding.
751         """
752         retcode = self._DoTestFile('006_dual_image.dts')
753         self.assertEqual(0, retcode)
754
755         image = control.images['image1']
756         self.assertEqual(len(U_BOOT_DATA), image.size)
757         fname = tools.GetOutputFilename('image1.bin')
758         self.assertTrue(os.path.exists(fname))
759         with open(fname, 'rb') as fd:
760             data = fd.read()
761             self.assertEqual(U_BOOT_DATA, data)
762
763         image = control.images['image2']
764         self.assertEqual(3 + len(U_BOOT_DATA) + 5, image.size)
765         fname = tools.GetOutputFilename('image2.bin')
766         self.assertTrue(os.path.exists(fname))
767         with open(fname, 'rb') as fd:
768             data = fd.read()
769             self.assertEqual(U_BOOT_DATA, data[3:7])
770             self.assertEqual(tools.GetBytes(0, 3), data[:3])
771             self.assertEqual(tools.GetBytes(0, 5), data[7:])
772
773     def testBadAlign(self):
774         """Test that an invalid alignment value is detected"""
775         with self.assertRaises(ValueError) as e:
776             self._DoTestFile('007_bad_align.dts')
777         self.assertIn("Node '/binman/u-boot': Alignment 23 must be a power "
778                       "of two", str(e.exception))
779
780     def testPackSimple(self):
781         """Test that packing works as expected"""
782         retcode = self._DoTestFile('008_pack.dts')
783         self.assertEqual(0, retcode)
784         self.assertIn('image', control.images)
785         image = control.images['image']
786         entries = image.GetEntries()
787         self.assertEqual(5, len(entries))
788
789         # First u-boot
790         self.assertIn('u-boot', entries)
791         entry = entries['u-boot']
792         self.assertEqual(0, entry.offset)
793         self.assertEqual(len(U_BOOT_DATA), entry.size)
794
795         # Second u-boot, aligned to 16-byte boundary
796         self.assertIn('u-boot-align', entries)
797         entry = entries['u-boot-align']
798         self.assertEqual(16, entry.offset)
799         self.assertEqual(len(U_BOOT_DATA), entry.size)
800
801         # Third u-boot, size 23 bytes
802         self.assertIn('u-boot-size', entries)
803         entry = entries['u-boot-size']
804         self.assertEqual(20, entry.offset)
805         self.assertEqual(len(U_BOOT_DATA), entry.contents_size)
806         self.assertEqual(23, entry.size)
807
808         # Fourth u-boot, placed immediate after the above
809         self.assertIn('u-boot-next', entries)
810         entry = entries['u-boot-next']
811         self.assertEqual(43, entry.offset)
812         self.assertEqual(len(U_BOOT_DATA), entry.size)
813
814         # Fifth u-boot, placed at a fixed offset
815         self.assertIn('u-boot-fixed', entries)
816         entry = entries['u-boot-fixed']
817         self.assertEqual(61, entry.offset)
818         self.assertEqual(len(U_BOOT_DATA), entry.size)
819
820         self.assertEqual(65, image.size)
821
822     def testPackExtra(self):
823         """Test that extra packing feature works as expected"""
824         data, _, _, out_dtb_fname = self._DoReadFileDtb('009_pack_extra.dts',
825                                                         update_dtb=True)
826
827         self.assertIn('image', control.images)
828         image = control.images['image']
829         entries = image.GetEntries()
830         self.assertEqual(5, len(entries))
831
832         # First u-boot with padding before and after
833         self.assertIn('u-boot', entries)
834         entry = entries['u-boot']
835         self.assertEqual(0, entry.offset)
836         self.assertEqual(3, entry.pad_before)
837         self.assertEqual(3 + 5 + len(U_BOOT_DATA), entry.size)
838         self.assertEqual(U_BOOT_DATA, entry.data)
839         self.assertEqual(tools.GetBytes(0, 3) + U_BOOT_DATA +
840                          tools.GetBytes(0, 5), data[:entry.size])
841         pos = entry.size
842
843         # Second u-boot has an aligned size, but it has no effect
844         self.assertIn('u-boot-align-size-nop', entries)
845         entry = entries['u-boot-align-size-nop']
846         self.assertEqual(pos, entry.offset)
847         self.assertEqual(len(U_BOOT_DATA), entry.size)
848         self.assertEqual(U_BOOT_DATA, entry.data)
849         self.assertEqual(U_BOOT_DATA, data[pos:pos + entry.size])
850         pos += entry.size
851
852         # Third u-boot has an aligned size too
853         self.assertIn('u-boot-align-size', entries)
854         entry = entries['u-boot-align-size']
855         self.assertEqual(pos, entry.offset)
856         self.assertEqual(32, entry.size)
857         self.assertEqual(U_BOOT_DATA, entry.data)
858         self.assertEqual(U_BOOT_DATA + tools.GetBytes(0, 32 - len(U_BOOT_DATA)),
859                          data[pos:pos + entry.size])
860         pos += entry.size
861
862         # Fourth u-boot has an aligned end
863         self.assertIn('u-boot-align-end', entries)
864         entry = entries['u-boot-align-end']
865         self.assertEqual(48, entry.offset)
866         self.assertEqual(16, entry.size)
867         self.assertEqual(U_BOOT_DATA, entry.data[:len(U_BOOT_DATA)])
868         self.assertEqual(U_BOOT_DATA + tools.GetBytes(0, 16 - len(U_BOOT_DATA)),
869                          data[pos:pos + entry.size])
870         pos += entry.size
871
872         # Fifth u-boot immediately afterwards
873         self.assertIn('u-boot-align-both', entries)
874         entry = entries['u-boot-align-both']
875         self.assertEqual(64, entry.offset)
876         self.assertEqual(64, entry.size)
877         self.assertEqual(U_BOOT_DATA, entry.data[:len(U_BOOT_DATA)])
878         self.assertEqual(U_BOOT_DATA + tools.GetBytes(0, 64 - len(U_BOOT_DATA)),
879                          data[pos:pos + entry.size])
880
881         self.CheckNoGaps(entries)
882         self.assertEqual(128, image.size)
883
884         dtb = fdt.Fdt(out_dtb_fname)
885         dtb.Scan()
886         props = self._GetPropTree(dtb, ['size', 'offset', 'image-pos'])
887         expected = {
888             'image-pos': 0,
889             'offset': 0,
890             'size': 128,
891
892             'u-boot:image-pos': 0,
893             'u-boot:offset': 0,
894             'u-boot:size': 3 + 5 + len(U_BOOT_DATA),
895
896             'u-boot-align-size-nop:image-pos': 12,
897             'u-boot-align-size-nop:offset': 12,
898             'u-boot-align-size-nop:size': 4,
899
900             'u-boot-align-size:image-pos': 16,
901             'u-boot-align-size:offset': 16,
902             'u-boot-align-size:size': 32,
903
904             'u-boot-align-end:image-pos': 48,
905             'u-boot-align-end:offset': 48,
906             'u-boot-align-end:size': 16,
907
908             'u-boot-align-both:image-pos': 64,
909             'u-boot-align-both:offset': 64,
910             'u-boot-align-both:size': 64,
911             }
912         self.assertEqual(expected, props)
913
914     def testPackAlignPowerOf2(self):
915         """Test that invalid entry alignment is detected"""
916         with self.assertRaises(ValueError) as e:
917             self._DoTestFile('010_pack_align_power2.dts')
918         self.assertIn("Node '/binman/u-boot': Alignment 5 must be a power "
919                       "of two", str(e.exception))
920
921     def testPackAlignSizePowerOf2(self):
922         """Test that invalid entry size alignment is detected"""
923         with self.assertRaises(ValueError) as e:
924             self._DoTestFile('011_pack_align_size_power2.dts')
925         self.assertIn("Node '/binman/u-boot': Alignment size 55 must be a "
926                       "power of two", str(e.exception))
927
928     def testPackInvalidAlign(self):
929         """Test detection of an offset that does not match its alignment"""
930         with self.assertRaises(ValueError) as e:
931             self._DoTestFile('012_pack_inv_align.dts')
932         self.assertIn("Node '/binman/u-boot': Offset 0x5 (5) does not match "
933                       "align 0x4 (4)", str(e.exception))
934
935     def testPackInvalidSizeAlign(self):
936         """Test that invalid entry size alignment is detected"""
937         with self.assertRaises(ValueError) as e:
938             self._DoTestFile('013_pack_inv_size_align.dts')
939         self.assertIn("Node '/binman/u-boot': Size 0x5 (5) does not match "
940                       "align-size 0x4 (4)", str(e.exception))
941
942     def testPackOverlap(self):
943         """Test that overlapping regions are detected"""
944         with self.assertRaises(ValueError) as e:
945             self._DoTestFile('014_pack_overlap.dts')
946         self.assertIn("Node '/binman/u-boot-align': Offset 0x3 (3) overlaps "
947                       "with previous entry '/binman/u-boot' ending at 0x4 (4)",
948                       str(e.exception))
949
950     def testPackEntryOverflow(self):
951         """Test that entries that overflow their size are detected"""
952         with self.assertRaises(ValueError) as e:
953             self._DoTestFile('015_pack_overflow.dts')
954         self.assertIn("Node '/binman/u-boot': Entry contents size is 0x4 (4) "
955                       "but entry size is 0x3 (3)", str(e.exception))
956
957     def testPackImageOverflow(self):
958         """Test that entries which overflow the image size are detected"""
959         with self.assertRaises(ValueError) as e:
960             self._DoTestFile('016_pack_image_overflow.dts')
961         self.assertIn("Section '/binman': contents size 0x4 (4) exceeds section "
962                       "size 0x3 (3)", str(e.exception))
963
964     def testPackImageSize(self):
965         """Test that the image size can be set"""
966         retcode = self._DoTestFile('017_pack_image_size.dts')
967         self.assertEqual(0, retcode)
968         self.assertIn('image', control.images)
969         image = control.images['image']
970         self.assertEqual(7, image.size)
971
972     def testPackImageSizeAlign(self):
973         """Test that image size alignemnt works as expected"""
974         retcode = self._DoTestFile('018_pack_image_align.dts')
975         self.assertEqual(0, retcode)
976         self.assertIn('image', control.images)
977         image = control.images['image']
978         self.assertEqual(16, image.size)
979
980     def testPackInvalidImageAlign(self):
981         """Test that invalid image alignment is detected"""
982         with self.assertRaises(ValueError) as e:
983             self._DoTestFile('019_pack_inv_image_align.dts')
984         self.assertIn("Section '/binman': Size 0x7 (7) does not match "
985                       "align-size 0x8 (8)", str(e.exception))
986
987     def testPackAlignPowerOf2(self):
988         """Test that invalid image alignment is detected"""
989         with self.assertRaises(ValueError) as e:
990             self._DoTestFile('020_pack_inv_image_align_power2.dts')
991         self.assertIn("Image '/binman': Alignment size 131 must be a power of "
992                       "two", str(e.exception))
993
994     def testImagePadByte(self):
995         """Test that the image pad byte can be specified"""
996         self._SetupSplElf()
997         data = self._DoReadFile('021_image_pad.dts')
998         self.assertEqual(U_BOOT_SPL_DATA + tools.GetBytes(0xff, 1) +
999                          U_BOOT_DATA, data)
1000
1001     def testImageName(self):
1002         """Test that image files can be named"""
1003         retcode = self._DoTestFile('022_image_name.dts')
1004         self.assertEqual(0, retcode)
1005         image = control.images['image1']
1006         fname = tools.GetOutputFilename('test-name')
1007         self.assertTrue(os.path.exists(fname))
1008
1009         image = control.images['image2']
1010         fname = tools.GetOutputFilename('test-name.xx')
1011         self.assertTrue(os.path.exists(fname))
1012
1013     def testBlobFilename(self):
1014         """Test that generic blobs can be provided by filename"""
1015         data = self._DoReadFile('023_blob.dts')
1016         self.assertEqual(BLOB_DATA, data)
1017
1018     def testPackSorted(self):
1019         """Test that entries can be sorted"""
1020         self._SetupSplElf()
1021         data = self._DoReadFile('024_sorted.dts')
1022         self.assertEqual(tools.GetBytes(0, 1) + U_BOOT_SPL_DATA +
1023                          tools.GetBytes(0, 2) + U_BOOT_DATA, data)
1024
1025     def testPackZeroOffset(self):
1026         """Test that an entry at offset 0 is not given a new offset"""
1027         with self.assertRaises(ValueError) as e:
1028             self._DoTestFile('025_pack_zero_size.dts')
1029         self.assertIn("Node '/binman/u-boot-spl': Offset 0x0 (0) overlaps "
1030                       "with previous entry '/binman/u-boot' ending at 0x4 (4)",
1031                       str(e.exception))
1032
1033     def testPackUbootDtb(self):
1034         """Test that a device tree can be added to U-Boot"""
1035         data = self._DoReadFile('026_pack_u_boot_dtb.dts')
1036         self.assertEqual(U_BOOT_NODTB_DATA + U_BOOT_DTB_DATA, data)
1037
1038     def testPackX86RomNoSize(self):
1039         """Test that the end-at-4gb property requires a size property"""
1040         with self.assertRaises(ValueError) as e:
1041             self._DoTestFile('027_pack_4gb_no_size.dts')
1042         self.assertIn("Image '/binman': Section size must be provided when "
1043                       "using end-at-4gb", str(e.exception))
1044
1045     def test4gbAndSkipAtStartTogether(self):
1046         """Test that the end-at-4gb and skip-at-size property can't be used
1047         together"""
1048         with self.assertRaises(ValueError) as e:
1049             self._DoTestFile('098_4gb_and_skip_at_start_together.dts')
1050         self.assertIn("Image '/binman': Provide either 'end-at-4gb' or "
1051                       "'skip-at-start'", str(e.exception))
1052
1053     def testPackX86RomOutside(self):
1054         """Test that the end-at-4gb property checks for offset boundaries"""
1055         with self.assertRaises(ValueError) as e:
1056             self._DoTestFile('028_pack_4gb_outside.dts')
1057         self.assertIn("Node '/binman/u-boot': Offset 0x0 (0) size 0x4 (4) "
1058                       "is outside the section '/binman' starting at "
1059                       '0xffffffe0 (4294967264) of size 0x20 (32)',
1060                       str(e.exception))
1061
1062     def testPackX86Rom(self):
1063         """Test that a basic x86 ROM can be created"""
1064         self._SetupSplElf()
1065         data = self._DoReadFile('029_x86_rom.dts')
1066         self.assertEqual(U_BOOT_DATA + tools.GetBytes(0, 3) + U_BOOT_SPL_DATA +
1067                          tools.GetBytes(0, 2), data)
1068
1069     def testPackX86RomMeNoDesc(self):
1070         """Test that an invalid Intel descriptor entry is detected"""
1071         try:
1072             TestFunctional._MakeInputFile('descriptor-empty.bin', b'')
1073             with self.assertRaises(ValueError) as e:
1074                 self._DoTestFile('163_x86_rom_me_empty.dts')
1075             self.assertIn("Node '/binman/intel-descriptor': Cannot find Intel Flash Descriptor (FD) signature",
1076                           str(e.exception))
1077         finally:
1078             self._SetupDescriptor()
1079
1080     def testPackX86RomBadDesc(self):
1081         """Test that the Intel requires a descriptor entry"""
1082         with self.assertRaises(ValueError) as e:
1083             self._DoTestFile('030_x86_rom_me_no_desc.dts')
1084         self.assertIn("Node '/binman/intel-me': No offset set with "
1085                       "offset-unset: should another entry provide this correct "
1086                       "offset?", str(e.exception))
1087
1088     def testPackX86RomMe(self):
1089         """Test that an x86 ROM with an ME region can be created"""
1090         data = self._DoReadFile('031_x86_rom_me.dts')
1091         expected_desc = tools.ReadFile(self.TestFile('descriptor.bin'))
1092         if data[:0x1000] != expected_desc:
1093             self.fail('Expected descriptor binary at start of image')
1094         self.assertEqual(ME_DATA, data[0x1000:0x1000 + len(ME_DATA)])
1095
1096     def testPackVga(self):
1097         """Test that an image with a VGA binary can be created"""
1098         data = self._DoReadFile('032_intel_vga.dts')
1099         self.assertEqual(VGA_DATA, data[:len(VGA_DATA)])
1100
1101     def testPackStart16(self):
1102         """Test that an image with an x86 start16 region can be created"""
1103         data = self._DoReadFile('033_x86_start16.dts')
1104         self.assertEqual(X86_START16_DATA, data[:len(X86_START16_DATA)])
1105
1106     def testPackPowerpcMpc85xxBootpgResetvec(self):
1107         """Test that an image with powerpc-mpc85xx-bootpg-resetvec can be
1108         created"""
1109         data = self._DoReadFile('150_powerpc_mpc85xx_bootpg_resetvec.dts')
1110         self.assertEqual(PPC_MPC85XX_BR_DATA, data[:len(PPC_MPC85XX_BR_DATA)])
1111
1112     def _RunMicrocodeTest(self, dts_fname, nodtb_data, ucode_second=False):
1113         """Handle running a test for insertion of microcode
1114
1115         Args:
1116             dts_fname: Name of test .dts file
1117             nodtb_data: Data that we expect in the first section
1118             ucode_second: True if the microsecond entry is second instead of
1119                 third
1120
1121         Returns:
1122             Tuple:
1123                 Contents of first region (U-Boot or SPL)
1124                 Offset and size components of microcode pointer, as inserted
1125                     in the above (two 4-byte words)
1126         """
1127         data = self._DoReadFile(dts_fname, True)
1128
1129         # Now check the device tree has no microcode
1130         if ucode_second:
1131             ucode_content = data[len(nodtb_data):]
1132             ucode_pos = len(nodtb_data)
1133             dtb_with_ucode = ucode_content[16:]
1134             fdt_len = self.GetFdtLen(dtb_with_ucode)
1135         else:
1136             dtb_with_ucode = data[len(nodtb_data):]
1137             fdt_len = self.GetFdtLen(dtb_with_ucode)
1138             ucode_content = dtb_with_ucode[fdt_len:]
1139             ucode_pos = len(nodtb_data) + fdt_len
1140         fname = tools.GetOutputFilename('test.dtb')
1141         with open(fname, 'wb') as fd:
1142             fd.write(dtb_with_ucode)
1143         dtb = fdt.FdtScan(fname)
1144         ucode = dtb.GetNode('/microcode')
1145         self.assertTrue(ucode)
1146         for node in ucode.subnodes:
1147             self.assertFalse(node.props.get('data'))
1148
1149         # Check that the microcode appears immediately after the Fdt
1150         # This matches the concatenation of the data properties in
1151         # the /microcode/update@xxx nodes in 34_x86_ucode.dts.
1152         ucode_data = struct.pack('>4L', 0x12345678, 0x12345679, 0xabcd0000,
1153                                  0x78235609)
1154         self.assertEqual(ucode_data, ucode_content[:len(ucode_data)])
1155
1156         # Check that the microcode pointer was inserted. It should match the
1157         # expected offset and size
1158         pos_and_size = struct.pack('<2L', 0xfffffe00 + ucode_pos,
1159                                    len(ucode_data))
1160         u_boot = data[:len(nodtb_data)]
1161         return u_boot, pos_and_size
1162
1163     def testPackUbootMicrocode(self):
1164         """Test that x86 microcode can be handled correctly
1165
1166         We expect to see the following in the image, in order:
1167             u-boot-nodtb.bin with a microcode pointer inserted at the correct
1168                 place
1169             u-boot.dtb with the microcode removed
1170             the microcode
1171         """
1172         first, pos_and_size = self._RunMicrocodeTest('034_x86_ucode.dts',
1173                                                      U_BOOT_NODTB_DATA)
1174         self.assertEqual(b'nodtb with microcode' + pos_and_size +
1175                          b' somewhere in here', first)
1176
1177     def _RunPackUbootSingleMicrocode(self):
1178         """Test that x86 microcode can be handled correctly
1179
1180         We expect to see the following in the image, in order:
1181             u-boot-nodtb.bin with a microcode pointer inserted at the correct
1182                 place
1183             u-boot.dtb with the microcode
1184             an empty microcode region
1185         """
1186         # We need the libfdt library to run this test since only that allows
1187         # finding the offset of a property. This is required by
1188         # Entry_u_boot_dtb_with_ucode.ObtainContents().
1189         data = self._DoReadFile('035_x86_single_ucode.dts', True)
1190
1191         second = data[len(U_BOOT_NODTB_DATA):]
1192
1193         fdt_len = self.GetFdtLen(second)
1194         third = second[fdt_len:]
1195         second = second[:fdt_len]
1196
1197         ucode_data = struct.pack('>2L', 0x12345678, 0x12345679)
1198         self.assertIn(ucode_data, second)
1199         ucode_pos = second.find(ucode_data) + len(U_BOOT_NODTB_DATA)
1200
1201         # Check that the microcode pointer was inserted. It should match the
1202         # expected offset and size
1203         pos_and_size = struct.pack('<2L', 0xfffffe00 + ucode_pos,
1204                                    len(ucode_data))
1205         first = data[:len(U_BOOT_NODTB_DATA)]
1206         self.assertEqual(b'nodtb with microcode' + pos_and_size +
1207                          b' somewhere in here', first)
1208
1209     def testPackUbootSingleMicrocode(self):
1210         """Test that x86 microcode can be handled correctly with fdt_normal.
1211         """
1212         self._RunPackUbootSingleMicrocode()
1213
1214     def testUBootImg(self):
1215         """Test that u-boot.img can be put in a file"""
1216         data = self._DoReadFile('036_u_boot_img.dts')
1217         self.assertEqual(U_BOOT_IMG_DATA, data)
1218
1219     def testNoMicrocode(self):
1220         """Test that a missing microcode region is detected"""
1221         with self.assertRaises(ValueError) as e:
1222             self._DoReadFile('037_x86_no_ucode.dts', True)
1223         self.assertIn("Node '/binman/u-boot-dtb-with-ucode': No /microcode "
1224                       "node found in ", str(e.exception))
1225
1226     def testMicrocodeWithoutNode(self):
1227         """Test that a missing u-boot-dtb-with-ucode node is detected"""
1228         with self.assertRaises(ValueError) as e:
1229             self._DoReadFile('038_x86_ucode_missing_node.dts', True)
1230         self.assertIn("Node '/binman/u-boot-with-ucode-ptr': Cannot find "
1231                 "microcode region u-boot-dtb-with-ucode", str(e.exception))
1232
1233     def testMicrocodeWithoutNode2(self):
1234         """Test that a missing u-boot-ucode node is detected"""
1235         with self.assertRaises(ValueError) as e:
1236             self._DoReadFile('039_x86_ucode_missing_node2.dts', True)
1237         self.assertIn("Node '/binman/u-boot-with-ucode-ptr': Cannot find "
1238             "microcode region u-boot-ucode", str(e.exception))
1239
1240     def testMicrocodeWithoutPtrInElf(self):
1241         """Test that a U-Boot binary without the microcode symbol is detected"""
1242         # ELF file without a '_dt_ucode_base_size' symbol
1243         try:
1244             TestFunctional._MakeInputFile('u-boot',
1245                 tools.ReadFile(self.ElfTestFile('u_boot_no_ucode_ptr')))
1246
1247             with self.assertRaises(ValueError) as e:
1248                 self._RunPackUbootSingleMicrocode()
1249             self.assertIn("Node '/binman/u-boot-with-ucode-ptr': Cannot locate "
1250                     "_dt_ucode_base_size symbol in u-boot", str(e.exception))
1251
1252         finally:
1253             # Put the original file back
1254             TestFunctional._MakeInputFile('u-boot',
1255                 tools.ReadFile(self.ElfTestFile('u_boot_ucode_ptr')))
1256
1257     def testMicrocodeNotInImage(self):
1258         """Test that microcode must be placed within the image"""
1259         with self.assertRaises(ValueError) as e:
1260             self._DoReadFile('040_x86_ucode_not_in_image.dts', True)
1261         self.assertIn("Node '/binman/u-boot-with-ucode-ptr': Microcode "
1262                 "pointer _dt_ucode_base_size at fffffe14 is outside the "
1263                 "section ranging from 00000000 to 0000002e", str(e.exception))
1264
1265     def testWithoutMicrocode(self):
1266         """Test that we can cope with an image without microcode (e.g. qemu)"""
1267         TestFunctional._MakeInputFile('u-boot',
1268             tools.ReadFile(self.ElfTestFile('u_boot_no_ucode_ptr')))
1269         data, dtb, _, _ = self._DoReadFileDtb('044_x86_optional_ucode.dts', True)
1270
1271         # Now check the device tree has no microcode
1272         self.assertEqual(U_BOOT_NODTB_DATA, data[:len(U_BOOT_NODTB_DATA)])
1273         second = data[len(U_BOOT_NODTB_DATA):]
1274
1275         fdt_len = self.GetFdtLen(second)
1276         self.assertEqual(dtb, second[:fdt_len])
1277
1278         used_len = len(U_BOOT_NODTB_DATA) + fdt_len
1279         third = data[used_len:]
1280         self.assertEqual(tools.GetBytes(0, 0x200 - used_len), third)
1281
1282     def testUnknownPosSize(self):
1283         """Test that microcode must be placed within the image"""
1284         with self.assertRaises(ValueError) as e:
1285             self._DoReadFile('041_unknown_pos_size.dts', True)
1286         self.assertIn("Section '/binman': Unable to set offset/size for unknown "
1287                 "entry 'invalid-entry'", str(e.exception))
1288
1289     def testPackFsp(self):
1290         """Test that an image with a FSP binary can be created"""
1291         data = self._DoReadFile('042_intel_fsp.dts')
1292         self.assertEqual(FSP_DATA, data[:len(FSP_DATA)])
1293
1294     def testPackCmc(self):
1295         """Test that an image with a CMC binary can be created"""
1296         data = self._DoReadFile('043_intel_cmc.dts')
1297         self.assertEqual(CMC_DATA, data[:len(CMC_DATA)])
1298
1299     def testPackVbt(self):
1300         """Test that an image with a VBT binary can be created"""
1301         data = self._DoReadFile('046_intel_vbt.dts')
1302         self.assertEqual(VBT_DATA, data[:len(VBT_DATA)])
1303
1304     def testSplBssPad(self):
1305         """Test that we can pad SPL's BSS with zeros"""
1306         # ELF file with a '__bss_size' symbol
1307         self._SetupSplElf()
1308         data = self._DoReadFile('047_spl_bss_pad.dts')
1309         self.assertEqual(U_BOOT_SPL_DATA + tools.GetBytes(0, 10) + U_BOOT_DATA,
1310                          data)
1311
1312     def testSplBssPadMissing(self):
1313         """Test that a missing symbol is detected"""
1314         self._SetupSplElf('u_boot_ucode_ptr')
1315         with self.assertRaises(ValueError) as e:
1316             self._DoReadFile('047_spl_bss_pad.dts')
1317         self.assertIn('Expected __bss_size symbol in spl/u-boot-spl',
1318                       str(e.exception))
1319
1320     def testPackStart16Spl(self):
1321         """Test that an image with an x86 start16 SPL region can be created"""
1322         data = self._DoReadFile('048_x86_start16_spl.dts')
1323         self.assertEqual(X86_START16_SPL_DATA, data[:len(X86_START16_SPL_DATA)])
1324
1325     def _PackUbootSplMicrocode(self, dts, ucode_second=False):
1326         """Helper function for microcode tests
1327
1328         We expect to see the following in the image, in order:
1329             u-boot-spl-nodtb.bin with a microcode pointer inserted at the
1330                 correct place
1331             u-boot.dtb with the microcode removed
1332             the microcode
1333
1334         Args:
1335             dts: Device tree file to use for test
1336             ucode_second: True if the microsecond entry is second instead of
1337                 third
1338         """
1339         self._SetupSplElf('u_boot_ucode_ptr')
1340         first, pos_and_size = self._RunMicrocodeTest(dts, U_BOOT_SPL_NODTB_DATA,
1341                                                      ucode_second=ucode_second)
1342         self.assertEqual(b'splnodtb with microc' + pos_and_size +
1343                          b'ter somewhere in here', first)
1344
1345     def testPackUbootSplMicrocode(self):
1346         """Test that x86 microcode can be handled correctly in SPL"""
1347         self._PackUbootSplMicrocode('049_x86_ucode_spl.dts')
1348
1349     def testPackUbootSplMicrocodeReorder(self):
1350         """Test that order doesn't matter for microcode entries
1351
1352         This is the same as testPackUbootSplMicrocode but when we process the
1353         u-boot-ucode entry we have not yet seen the u-boot-dtb-with-ucode
1354         entry, so we reply on binman to try later.
1355         """
1356         self._PackUbootSplMicrocode('058_x86_ucode_spl_needs_retry.dts',
1357                                     ucode_second=True)
1358
1359     def testPackMrc(self):
1360         """Test that an image with an MRC binary can be created"""
1361         data = self._DoReadFile('050_intel_mrc.dts')
1362         self.assertEqual(MRC_DATA, data[:len(MRC_DATA)])
1363
1364     def testSplDtb(self):
1365         """Test that an image with spl/u-boot-spl.dtb can be created"""
1366         data = self._DoReadFile('051_u_boot_spl_dtb.dts')
1367         self.assertEqual(U_BOOT_SPL_DTB_DATA, data[:len(U_BOOT_SPL_DTB_DATA)])
1368
1369     def testSplNoDtb(self):
1370         """Test that an image with spl/u-boot-spl-nodtb.bin can be created"""
1371         self._SetupSplElf()
1372         data = self._DoReadFile('052_u_boot_spl_nodtb.dts')
1373         self.assertEqual(U_BOOT_SPL_NODTB_DATA, data[:len(U_BOOT_SPL_NODTB_DATA)])
1374
1375     def checkSymbols(self, dts, base_data, u_boot_offset, entry_args=None,
1376                      use_expanded=False):
1377         """Check the image contains the expected symbol values
1378
1379         Args:
1380             dts: Device tree file to use for test
1381             base_data: Data before and after 'u-boot' section
1382             u_boot_offset: Offset of 'u-boot' section in image
1383             entry_args: Dict of entry args to supply to binman
1384                 key: arg name
1385                 value: value of that arg
1386             use_expanded: True to use expanded entries where available, e.g.
1387                 'u-boot-expanded' instead of 'u-boot'
1388         """
1389         elf_fname = self.ElfTestFile('u_boot_binman_syms')
1390         syms = elf.GetSymbols(elf_fname, ['binman', 'image'])
1391         addr = elf.GetSymbolAddress(elf_fname, '__image_copy_start')
1392         self.assertEqual(syms['_binman_u_boot_spl_any_prop_offset'].address,
1393                          addr)
1394
1395         self._SetupSplElf('u_boot_binman_syms')
1396         data = self._DoReadFileDtb(dts, entry_args=entry_args,
1397                                    use_expanded=use_expanded)[0]
1398         # The image should contain the symbols from u_boot_binman_syms.c
1399         # Note that image_pos is adjusted by the base address of the image,
1400         # which is 0x10 in our test image
1401         sym_values = struct.pack('<LQLL', 0x00,
1402                                  u_boot_offset + len(U_BOOT_DATA),
1403                                  0x10 + u_boot_offset, 0x04)
1404         expected = (sym_values + base_data[20:] +
1405                     tools.GetBytes(0xff, 1) + U_BOOT_DATA + sym_values +
1406                     base_data[20:])
1407         self.assertEqual(expected, data)
1408
1409     def testSymbols(self):
1410         """Test binman can assign symbols embedded in U-Boot"""
1411         self.checkSymbols('053_symbols.dts', U_BOOT_SPL_DATA, 0x18)
1412
1413     def testSymbolsNoDtb(self):
1414         """Test binman can assign symbols embedded in U-Boot SPL"""
1415         self.checkSymbols('196_symbols_nodtb.dts',
1416                           U_BOOT_SPL_NODTB_DATA + U_BOOT_SPL_DTB_DATA,
1417                           0x38)
1418
1419     def testPackUnitAddress(self):
1420         """Test that we support multiple binaries with the same name"""
1421         data = self._DoReadFile('054_unit_address.dts')
1422         self.assertEqual(U_BOOT_DATA + U_BOOT_DATA, data)
1423
1424     def testSections(self):
1425         """Basic test of sections"""
1426         data = self._DoReadFile('055_sections.dts')
1427         expected = (U_BOOT_DATA + tools.GetBytes(ord('!'), 12) +
1428                     U_BOOT_DATA + tools.GetBytes(ord('a'), 12) +
1429                     U_BOOT_DATA + tools.GetBytes(ord('&'), 4))
1430         self.assertEqual(expected, data)
1431
1432     def testMap(self):
1433         """Tests outputting a map of the images"""
1434         _, _, map_data, _ = self._DoReadFileDtb('055_sections.dts', map=True)
1435         self.assertEqual('''ImagePos    Offset      Size  Name
1436 00000000  00000000  00000028  main-section
1437 00000000   00000000  00000010  section@0
1438 00000000    00000000  00000004  u-boot
1439 00000010   00000010  00000010  section@1
1440 00000010    00000000  00000004  u-boot
1441 00000020   00000020  00000004  section@2
1442 00000020    00000000  00000004  u-boot
1443 ''', map_data)
1444
1445     def testNamePrefix(self):
1446         """Tests that name prefixes are used"""
1447         _, _, map_data, _ = self._DoReadFileDtb('056_name_prefix.dts', map=True)
1448         self.assertEqual('''ImagePos    Offset      Size  Name
1449 00000000  00000000  00000028  main-section
1450 00000000   00000000  00000010  section@0
1451 00000000    00000000  00000004  ro-u-boot
1452 00000010   00000010  00000010  section@1
1453 00000010    00000000  00000004  rw-u-boot
1454 ''', map_data)
1455
1456     def testUnknownContents(self):
1457         """Test that obtaining the contents works as expected"""
1458         with self.assertRaises(ValueError) as e:
1459             self._DoReadFile('057_unknown_contents.dts', True)
1460         self.assertIn("Image '/binman': Internal error: Could not complete "
1461                 "processing of contents: remaining ["
1462                 "<binman.etype._testing.Entry__testing ", str(e.exception))
1463
1464     def testBadChangeSize(self):
1465         """Test that trying to change the size of an entry fails"""
1466         try:
1467             state.SetAllowEntryExpansion(False)
1468             with self.assertRaises(ValueError) as e:
1469                 self._DoReadFile('059_change_size.dts', True)
1470             self.assertIn("Node '/binman/_testing': Cannot update entry size from 2 to 3",
1471                           str(e.exception))
1472         finally:
1473             state.SetAllowEntryExpansion(True)
1474
1475     def testUpdateFdt(self):
1476         """Test that we can update the device tree with offset/size info"""
1477         _, _, _, out_dtb_fname = self._DoReadFileDtb('060_fdt_update.dts',
1478                                                      update_dtb=True)
1479         dtb = fdt.Fdt(out_dtb_fname)
1480         dtb.Scan()
1481         props = self._GetPropTree(dtb, BASE_DTB_PROPS + REPACK_DTB_PROPS)
1482         self.assertEqual({
1483             'image-pos': 0,
1484             'offset': 0,
1485             '_testing:offset': 32,
1486             '_testing:size': 2,
1487             '_testing:image-pos': 32,
1488             'section@0/u-boot:offset': 0,
1489             'section@0/u-boot:size': len(U_BOOT_DATA),
1490             'section@0/u-boot:image-pos': 0,
1491             'section@0:offset': 0,
1492             'section@0:size': 16,
1493             'section@0:image-pos': 0,
1494
1495             'section@1/u-boot:offset': 0,
1496             'section@1/u-boot:size': len(U_BOOT_DATA),
1497             'section@1/u-boot:image-pos': 16,
1498             'section@1:offset': 16,
1499             'section@1:size': 16,
1500             'section@1:image-pos': 16,
1501             'size': 40
1502         }, props)
1503
1504     def testUpdateFdtBad(self):
1505         """Test that we detect when ProcessFdt never completes"""
1506         with self.assertRaises(ValueError) as e:
1507             self._DoReadFileDtb('061_fdt_update_bad.dts', update_dtb=True)
1508         self.assertIn('Could not complete processing of Fdt: remaining '
1509                       '[<binman.etype._testing.Entry__testing',
1510                         str(e.exception))
1511
1512     def testEntryArgs(self):
1513         """Test passing arguments to entries from the command line"""
1514         entry_args = {
1515             'test-str-arg': 'test1',
1516             'test-int-arg': '456',
1517         }
1518         self._DoReadFileDtb('062_entry_args.dts', entry_args=entry_args)
1519         self.assertIn('image', control.images)
1520         entry = control.images['image'].GetEntries()['_testing']
1521         self.assertEqual('test0', entry.test_str_fdt)
1522         self.assertEqual('test1', entry.test_str_arg)
1523         self.assertEqual(123, entry.test_int_fdt)
1524         self.assertEqual(456, entry.test_int_arg)
1525
1526     def testEntryArgsMissing(self):
1527         """Test missing arguments and properties"""
1528         entry_args = {
1529             'test-int-arg': '456',
1530         }
1531         self._DoReadFileDtb('063_entry_args_missing.dts', entry_args=entry_args)
1532         entry = control.images['image'].GetEntries()['_testing']
1533         self.assertEqual('test0', entry.test_str_fdt)
1534         self.assertEqual(None, entry.test_str_arg)
1535         self.assertEqual(None, entry.test_int_fdt)
1536         self.assertEqual(456, entry.test_int_arg)
1537
1538     def testEntryArgsRequired(self):
1539         """Test missing arguments and properties"""
1540         entry_args = {
1541             'test-int-arg': '456',
1542         }
1543         with self.assertRaises(ValueError) as e:
1544             self._DoReadFileDtb('064_entry_args_required.dts')
1545         self.assertIn("Node '/binman/_testing': "
1546             'Missing required properties/entry args: test-str-arg, '
1547             'test-int-fdt, test-int-arg',
1548             str(e.exception))
1549
1550     def testEntryArgsInvalidFormat(self):
1551         """Test that an invalid entry-argument format is detected"""
1552         args = ['build', '-d', self.TestFile('064_entry_args_required.dts'),
1553                 '-ano-value']
1554         with self.assertRaises(ValueError) as e:
1555             self._DoBinman(*args)
1556         self.assertIn("Invalid entry arguemnt 'no-value'", str(e.exception))
1557
1558     def testEntryArgsInvalidInteger(self):
1559         """Test that an invalid entry-argument integer is detected"""
1560         entry_args = {
1561             'test-int-arg': 'abc',
1562         }
1563         with self.assertRaises(ValueError) as e:
1564             self._DoReadFileDtb('062_entry_args.dts', entry_args=entry_args)
1565         self.assertIn("Node '/binman/_testing': Cannot convert entry arg "
1566                       "'test-int-arg' (value 'abc') to integer",
1567             str(e.exception))
1568
1569     def testEntryArgsInvalidDatatype(self):
1570         """Test that an invalid entry-argument datatype is detected
1571
1572         This test could be written in entry_test.py except that it needs
1573         access to control.entry_args, which seems more than that module should
1574         be able to see.
1575         """
1576         entry_args = {
1577             'test-bad-datatype-arg': '12',
1578         }
1579         with self.assertRaises(ValueError) as e:
1580             self._DoReadFileDtb('065_entry_args_unknown_datatype.dts',
1581                                 entry_args=entry_args)
1582         self.assertIn('GetArg() internal error: Unknown data type ',
1583                       str(e.exception))
1584
1585     def testText(self):
1586         """Test for a text entry type"""
1587         entry_args = {
1588             'test-id': TEXT_DATA,
1589             'test-id2': TEXT_DATA2,
1590             'test-id3': TEXT_DATA3,
1591         }
1592         data, _, _, _ = self._DoReadFileDtb('066_text.dts',
1593                                             entry_args=entry_args)
1594         expected = (tools.ToBytes(TEXT_DATA) +
1595                     tools.GetBytes(0, 8 - len(TEXT_DATA)) +
1596                     tools.ToBytes(TEXT_DATA2) + tools.ToBytes(TEXT_DATA3) +
1597                     b'some text' + b'more text')
1598         self.assertEqual(expected, data)
1599
1600     def testEntryDocs(self):
1601         """Test for creation of entry documentation"""
1602         with test_util.capture_sys_output() as (stdout, stderr):
1603             control.WriteEntryDocs(control.GetEntryModules())
1604         self.assertTrue(len(stdout.getvalue()) > 0)
1605
1606     def testEntryDocsMissing(self):
1607         """Test handling of missing entry documentation"""
1608         with self.assertRaises(ValueError) as e:
1609             with test_util.capture_sys_output() as (stdout, stderr):
1610                 control.WriteEntryDocs(control.GetEntryModules(), 'u_boot')
1611         self.assertIn('Documentation is missing for modules: u_boot',
1612                       str(e.exception))
1613
1614     def testFmap(self):
1615         """Basic test of generation of a flashrom fmap"""
1616         data = self._DoReadFile('067_fmap.dts')
1617         fhdr, fentries = fmap_util.DecodeFmap(data[32:])
1618         expected = (U_BOOT_DATA + tools.GetBytes(ord('!'), 12) +
1619                     U_BOOT_DATA + tools.GetBytes(ord('a'), 12))
1620         self.assertEqual(expected, data[:32])
1621         self.assertEqual(b'__FMAP__', fhdr.signature)
1622         self.assertEqual(1, fhdr.ver_major)
1623         self.assertEqual(0, fhdr.ver_minor)
1624         self.assertEqual(0, fhdr.base)
1625         expect_size = fmap_util.FMAP_HEADER_LEN + fmap_util.FMAP_AREA_LEN * 5
1626         self.assertEqual(16 + 16 + expect_size, fhdr.image_size)
1627         self.assertEqual(b'FMAP', fhdr.name)
1628         self.assertEqual(5, fhdr.nareas)
1629         fiter = iter(fentries)
1630
1631         fentry = next(fiter)
1632         self.assertEqual(b'SECTION0', fentry.name)
1633         self.assertEqual(0, fentry.offset)
1634         self.assertEqual(16, fentry.size)
1635         self.assertEqual(0, fentry.flags)
1636
1637         fentry = next(fiter)
1638         self.assertEqual(b'RO_U_BOOT', fentry.name)
1639         self.assertEqual(0, fentry.offset)
1640         self.assertEqual(4, fentry.size)
1641         self.assertEqual(0, fentry.flags)
1642
1643         fentry = next(fiter)
1644         self.assertEqual(b'SECTION1', fentry.name)
1645         self.assertEqual(16, fentry.offset)
1646         self.assertEqual(16, fentry.size)
1647         self.assertEqual(0, fentry.flags)
1648
1649         fentry = next(fiter)
1650         self.assertEqual(b'RW_U_BOOT', fentry.name)
1651         self.assertEqual(16, fentry.offset)
1652         self.assertEqual(4, fentry.size)
1653         self.assertEqual(0, fentry.flags)
1654
1655         fentry = next(fiter)
1656         self.assertEqual(b'FMAP', fentry.name)
1657         self.assertEqual(32, fentry.offset)
1658         self.assertEqual(expect_size, fentry.size)
1659         self.assertEqual(0, fentry.flags)
1660
1661     def testBlobNamedByArg(self):
1662         """Test we can add a blob with the filename coming from an entry arg"""
1663         entry_args = {
1664             'cros-ec-rw-path': 'ecrw.bin',
1665         }
1666         self._DoReadFileDtb('068_blob_named_by_arg.dts', entry_args=entry_args)
1667
1668     def testFill(self):
1669         """Test for an fill entry type"""
1670         data = self._DoReadFile('069_fill.dts')
1671         expected = tools.GetBytes(0xff, 8) + tools.GetBytes(0, 8)
1672         self.assertEqual(expected, data)
1673
1674     def testFillNoSize(self):
1675         """Test for an fill entry type with no size"""
1676         with self.assertRaises(ValueError) as e:
1677             self._DoReadFile('070_fill_no_size.dts')
1678         self.assertIn("'fill' entry must have a size property",
1679                       str(e.exception))
1680
1681     def _HandleGbbCommand(self, pipe_list):
1682         """Fake calls to the futility utility"""
1683         if pipe_list[0][0] == 'futility':
1684             fname = pipe_list[0][-1]
1685             # Append our GBB data to the file, which will happen every time the
1686             # futility command is called.
1687             with open(fname, 'ab') as fd:
1688                 fd.write(GBB_DATA)
1689             return command.CommandResult()
1690
1691     def testGbb(self):
1692         """Test for the Chromium OS Google Binary Block"""
1693         command.test_result = self._HandleGbbCommand
1694         entry_args = {
1695             'keydir': 'devkeys',
1696             'bmpblk': 'bmpblk.bin',
1697         }
1698         data, _, _, _ = self._DoReadFileDtb('071_gbb.dts', entry_args=entry_args)
1699
1700         # Since futility
1701         expected = (GBB_DATA + GBB_DATA + tools.GetBytes(0, 8) +
1702                     tools.GetBytes(0, 0x2180 - 16))
1703         self.assertEqual(expected, data)
1704
1705     def testGbbTooSmall(self):
1706         """Test for the Chromium OS Google Binary Block being large enough"""
1707         with self.assertRaises(ValueError) as e:
1708             self._DoReadFileDtb('072_gbb_too_small.dts')
1709         self.assertIn("Node '/binman/gbb': GBB is too small",
1710                       str(e.exception))
1711
1712     def testGbbNoSize(self):
1713         """Test for the Chromium OS Google Binary Block having a size"""
1714         with self.assertRaises(ValueError) as e:
1715             self._DoReadFileDtb('073_gbb_no_size.dts')
1716         self.assertIn("Node '/binman/gbb': GBB must have a fixed size",
1717                       str(e.exception))
1718
1719     def _HandleVblockCommand(self, pipe_list):
1720         """Fake calls to the futility utility
1721
1722         The expected pipe is:
1723
1724            [('futility', 'vbutil_firmware', '--vblock',
1725              'vblock.vblock', '--keyblock', 'devkeys/firmware.keyblock',
1726              '--signprivate', 'devkeys/firmware_data_key.vbprivk',
1727              '--version', '1', '--fv', 'input.vblock', '--kernelkey',
1728              'devkeys/kernel_subkey.vbpubk', '--flags', '1')]
1729
1730         This writes to the output file (here, 'vblock.vblock'). If
1731         self._hash_data is False, it writes VBLOCK_DATA, else it writes a hash
1732         of the input data (here, 'input.vblock').
1733         """
1734         if pipe_list[0][0] == 'futility':
1735             fname = pipe_list[0][3]
1736             with open(fname, 'wb') as fd:
1737                 if self._hash_data:
1738                     infile = pipe_list[0][11]
1739                     m = hashlib.sha256()
1740                     data = tools.ReadFile(infile)
1741                     m.update(data)
1742                     fd.write(m.digest())
1743                 else:
1744                     fd.write(VBLOCK_DATA)
1745
1746             return command.CommandResult()
1747
1748     def testVblock(self):
1749         """Test for the Chromium OS Verified Boot Block"""
1750         self._hash_data = False
1751         command.test_result = self._HandleVblockCommand
1752         entry_args = {
1753             'keydir': 'devkeys',
1754         }
1755         data, _, _, _ = self._DoReadFileDtb('074_vblock.dts',
1756                                             entry_args=entry_args)
1757         expected = U_BOOT_DATA + VBLOCK_DATA + U_BOOT_DTB_DATA
1758         self.assertEqual(expected, data)
1759
1760     def testVblockNoContent(self):
1761         """Test we detect a vblock which has no content to sign"""
1762         with self.assertRaises(ValueError) as e:
1763             self._DoReadFile('075_vblock_no_content.dts')
1764         self.assertIn("Node '/binman/vblock': Collection must have a 'content' "
1765                       'property', str(e.exception))
1766
1767     def testVblockBadPhandle(self):
1768         """Test that we detect a vblock with an invalid phandle in contents"""
1769         with self.assertRaises(ValueError) as e:
1770             self._DoReadFile('076_vblock_bad_phandle.dts')
1771         self.assertIn("Node '/binman/vblock': Cannot find node for phandle "
1772                       '1000', str(e.exception))
1773
1774     def testVblockBadEntry(self):
1775         """Test that we detect an entry that points to a non-entry"""
1776         with self.assertRaises(ValueError) as e:
1777             self._DoReadFile('077_vblock_bad_entry.dts')
1778         self.assertIn("Node '/binman/vblock': Cannot find entry for node "
1779                       "'other'", str(e.exception))
1780
1781     def testVblockContent(self):
1782         """Test that the vblock signs the right data"""
1783         self._hash_data = True
1784         command.test_result = self._HandleVblockCommand
1785         entry_args = {
1786             'keydir': 'devkeys',
1787         }
1788         data = self._DoReadFileDtb(
1789             '189_vblock_content.dts', use_real_dtb=True, update_dtb=True,
1790             entry_args=entry_args)[0]
1791         hashlen = 32  # SHA256 hash is 32 bytes
1792         self.assertEqual(U_BOOT_DATA, data[:len(U_BOOT_DATA)])
1793         hashval = data[-hashlen:]
1794         dtb = data[len(U_BOOT_DATA):-hashlen]
1795
1796         expected_data = U_BOOT_DATA + dtb
1797
1798         # The hashval should be a hash of the dtb
1799         m = hashlib.sha256()
1800         m.update(expected_data)
1801         expected_hashval = m.digest()
1802         self.assertEqual(expected_hashval, hashval)
1803
1804     def testTpl(self):
1805         """Test that an image with TPL and its device tree can be created"""
1806         # ELF file with a '__bss_size' symbol
1807         self._SetupTplElf()
1808         data = self._DoReadFile('078_u_boot_tpl.dts')
1809         self.assertEqual(U_BOOT_TPL_DATA + U_BOOT_TPL_DTB_DATA, data)
1810
1811     def testUsesPos(self):
1812         """Test that the 'pos' property cannot be used anymore"""
1813         with self.assertRaises(ValueError) as e:
1814            data = self._DoReadFile('079_uses_pos.dts')
1815         self.assertIn("Node '/binman/u-boot': Please use 'offset' instead of "
1816                       "'pos'", str(e.exception))
1817
1818     def testFillZero(self):
1819         """Test for an fill entry type with a size of 0"""
1820         data = self._DoReadFile('080_fill_empty.dts')
1821         self.assertEqual(tools.GetBytes(0, 16), data)
1822
1823     def testTextMissing(self):
1824         """Test for a text entry type where there is no text"""
1825         with self.assertRaises(ValueError) as e:
1826             self._DoReadFileDtb('066_text.dts',)
1827         self.assertIn("Node '/binman/text': No value provided for text label "
1828                       "'test-id'", str(e.exception))
1829
1830     def testPackStart16Tpl(self):
1831         """Test that an image with an x86 start16 TPL region can be created"""
1832         data = self._DoReadFile('081_x86_start16_tpl.dts')
1833         self.assertEqual(X86_START16_TPL_DATA, data[:len(X86_START16_TPL_DATA)])
1834
1835     def testSelectImage(self):
1836         """Test that we can select which images to build"""
1837         expected = 'Skipping images: image1'
1838
1839         # We should only get the expected message in verbose mode
1840         for verbosity in (0, 2):
1841             with test_util.capture_sys_output() as (stdout, stderr):
1842                 retcode = self._DoTestFile('006_dual_image.dts',
1843                                            verbosity=verbosity,
1844                                            images=['image2'])
1845             self.assertEqual(0, retcode)
1846             if verbosity:
1847                 self.assertIn(expected, stdout.getvalue())
1848             else:
1849                 self.assertNotIn(expected, stdout.getvalue())
1850
1851             self.assertFalse(os.path.exists(tools.GetOutputFilename('image1.bin')))
1852             self.assertTrue(os.path.exists(tools.GetOutputFilename('image2.bin')))
1853             self._CleanupOutputDir()
1854
1855     def testUpdateFdtAll(self):
1856         """Test that all device trees are updated with offset/size info"""
1857         data = self._DoReadFileRealDtb('082_fdt_update_all.dts')
1858
1859         base_expected = {
1860             'section:image-pos': 0,
1861             'u-boot-tpl-dtb:size': 513,
1862             'u-boot-spl-dtb:size': 513,
1863             'u-boot-spl-dtb:offset': 493,
1864             'image-pos': 0,
1865             'section/u-boot-dtb:image-pos': 0,
1866             'u-boot-spl-dtb:image-pos': 493,
1867             'section/u-boot-dtb:size': 493,
1868             'u-boot-tpl-dtb:image-pos': 1006,
1869             'section/u-boot-dtb:offset': 0,
1870             'section:size': 493,
1871             'offset': 0,
1872             'section:offset': 0,
1873             'u-boot-tpl-dtb:offset': 1006,
1874             'size': 1519
1875         }
1876
1877         # We expect three device-tree files in the output, one after the other.
1878         # Read them in sequence. We look for an 'spl' property in the SPL tree,
1879         # and 'tpl' in the TPL tree, to make sure they are distinct from the
1880         # main U-Boot tree. All three should have the same postions and offset.
1881         start = 0
1882         for item in ['', 'spl', 'tpl']:
1883             dtb = fdt.Fdt.FromData(data[start:])
1884             dtb.Scan()
1885             props = self._GetPropTree(dtb, BASE_DTB_PROPS + REPACK_DTB_PROPS +
1886                                       ['spl', 'tpl'])
1887             expected = dict(base_expected)
1888             if item:
1889                 expected[item] = 0
1890             self.assertEqual(expected, props)
1891             start += dtb._fdt_obj.totalsize()
1892
1893     def testUpdateFdtOutput(self):
1894         """Test that output DTB files are updated"""
1895         try:
1896             data, dtb_data, _, _ = self._DoReadFileDtb('082_fdt_update_all.dts',
1897                     use_real_dtb=True, update_dtb=True, reset_dtbs=False)
1898
1899             # Unfortunately, compiling a source file always results in a file
1900             # called source.dtb (see fdt_util.EnsureCompiled()). The test
1901             # source file (e.g. test/075_fdt_update_all.dts) thus does not enter
1902             # binman as a file called u-boot.dtb. To fix this, copy the file
1903             # over to the expected place.
1904             start = 0
1905             for fname in ['u-boot.dtb.out', 'spl/u-boot-spl.dtb.out',
1906                           'tpl/u-boot-tpl.dtb.out']:
1907                 dtb = fdt.Fdt.FromData(data[start:])
1908                 size = dtb._fdt_obj.totalsize()
1909                 pathname = tools.GetOutputFilename(os.path.split(fname)[1])
1910                 outdata = tools.ReadFile(pathname)
1911                 name = os.path.split(fname)[0]
1912
1913                 if name:
1914                     orig_indata = self._GetDtbContentsForSplTpl(dtb_data, name)
1915                 else:
1916                     orig_indata = dtb_data
1917                 self.assertNotEqual(outdata, orig_indata,
1918                         "Expected output file '%s' be updated" % pathname)
1919                 self.assertEqual(outdata, data[start:start + size],
1920                         "Expected output file '%s' to match output image" %
1921                         pathname)
1922                 start += size
1923         finally:
1924             self._ResetDtbs()
1925
1926     def _decompress(self, data):
1927         return tools.Decompress(data, 'lz4')
1928
1929     def testCompress(self):
1930         """Test compression of blobs"""
1931         self._CheckLz4()
1932         data, _, _, out_dtb_fname = self._DoReadFileDtb('083_compress.dts',
1933                                             use_real_dtb=True, update_dtb=True)
1934         dtb = fdt.Fdt(out_dtb_fname)
1935         dtb.Scan()
1936         props = self._GetPropTree(dtb, ['size', 'uncomp-size'])
1937         orig = self._decompress(data)
1938         self.assertEquals(COMPRESS_DATA, orig)
1939
1940         # Do a sanity check on various fields
1941         image = control.images['image']
1942         entries = image.GetEntries()
1943         self.assertEqual(1, len(entries))
1944
1945         entry = entries['blob']
1946         self.assertEqual(COMPRESS_DATA, entry.uncomp_data)
1947         self.assertEqual(len(COMPRESS_DATA), entry.uncomp_size)
1948         orig = self._decompress(entry.data)
1949         self.assertEqual(orig, entry.uncomp_data)
1950
1951         self.assertEqual(image.data, entry.data)
1952
1953         expected = {
1954             'blob:uncomp-size': len(COMPRESS_DATA),
1955             'blob:size': len(data),
1956             'size': len(data),
1957             }
1958         self.assertEqual(expected, props)
1959
1960     def testFiles(self):
1961         """Test bringing in multiple files"""
1962         data = self._DoReadFile('084_files.dts')
1963         self.assertEqual(FILES_DATA, data)
1964
1965     def testFilesCompress(self):
1966         """Test bringing in multiple files and compressing them"""
1967         self._CheckLz4()
1968         data = self._DoReadFile('085_files_compress.dts')
1969
1970         image = control.images['image']
1971         entries = image.GetEntries()
1972         files = entries['files']
1973         entries = files._entries
1974
1975         orig = b''
1976         for i in range(1, 3):
1977             key = '%d.dat' % i
1978             start = entries[key].image_pos
1979             len = entries[key].size
1980             chunk = data[start:start + len]
1981             orig += self._decompress(chunk)
1982
1983         self.assertEqual(FILES_DATA, orig)
1984
1985     def testFilesMissing(self):
1986         """Test missing files"""
1987         with self.assertRaises(ValueError) as e:
1988             data = self._DoReadFile('086_files_none.dts')
1989         self.assertIn("Node '/binman/files': Pattern \'files/*.none\' matched "
1990                       'no files', str(e.exception))
1991
1992     def testFilesNoPattern(self):
1993         """Test missing files"""
1994         with self.assertRaises(ValueError) as e:
1995             data = self._DoReadFile('087_files_no_pattern.dts')
1996         self.assertIn("Node '/binman/files': Missing 'pattern' property",
1997                       str(e.exception))
1998
1999     def testExpandSize(self):
2000         """Test an expanding entry"""
2001         data, _, map_data, _ = self._DoReadFileDtb('088_expand_size.dts',
2002                                                    map=True)
2003         expect = (tools.GetBytes(ord('a'), 8) + U_BOOT_DATA +
2004                   MRC_DATA + tools.GetBytes(ord('b'), 1) + U_BOOT_DATA +
2005                   tools.GetBytes(ord('c'), 8) + U_BOOT_DATA +
2006                   tools.GetBytes(ord('d'), 8))
2007         self.assertEqual(expect, data)
2008         self.assertEqual('''ImagePos    Offset      Size  Name
2009 00000000  00000000  00000028  main-section
2010 00000000   00000000  00000008  fill
2011 00000008   00000008  00000004  u-boot
2012 0000000c   0000000c  00000004  section
2013 0000000c    00000000  00000003  intel-mrc
2014 00000010   00000010  00000004  u-boot2
2015 00000014   00000014  0000000c  section2
2016 00000014    00000000  00000008  fill
2017 0000001c    00000008  00000004  u-boot
2018 00000020   00000020  00000008  fill2
2019 ''', map_data)
2020
2021     def testExpandSizeBad(self):
2022         """Test an expanding entry which fails to provide contents"""
2023         with test_util.capture_sys_output() as (stdout, stderr):
2024             with self.assertRaises(ValueError) as e:
2025                 self._DoReadFileDtb('089_expand_size_bad.dts', map=True)
2026         self.assertIn("Node '/binman/_testing': Cannot obtain contents when "
2027                       'expanding entry', str(e.exception))
2028
2029     def testHash(self):
2030         """Test hashing of the contents of an entry"""
2031         _, _, _, out_dtb_fname = self._DoReadFileDtb('090_hash.dts',
2032                 use_real_dtb=True, update_dtb=True)
2033         dtb = fdt.Fdt(out_dtb_fname)
2034         dtb.Scan()
2035         hash_node = dtb.GetNode('/binman/u-boot/hash').props['value']
2036         m = hashlib.sha256()
2037         m.update(U_BOOT_DATA)
2038         self.assertEqual(m.digest(), b''.join(hash_node.value))
2039
2040     def testHashNoAlgo(self):
2041         with self.assertRaises(ValueError) as e:
2042             self._DoReadFileDtb('091_hash_no_algo.dts', update_dtb=True)
2043         self.assertIn("Node \'/binman/u-boot\': Missing \'algo\' property for "
2044                       'hash node', str(e.exception))
2045
2046     def testHashBadAlgo(self):
2047         with self.assertRaises(ValueError) as e:
2048             self._DoReadFileDtb('092_hash_bad_algo.dts', update_dtb=True)
2049         self.assertIn("Node '/binman/u-boot': Unknown hash algorithm",
2050                       str(e.exception))
2051
2052     def testHashSection(self):
2053         """Test hashing of the contents of an entry"""
2054         _, _, _, out_dtb_fname = self._DoReadFileDtb('099_hash_section.dts',
2055                 use_real_dtb=True, update_dtb=True)
2056         dtb = fdt.Fdt(out_dtb_fname)
2057         dtb.Scan()
2058         hash_node = dtb.GetNode('/binman/section/hash').props['value']
2059         m = hashlib.sha256()
2060         m.update(U_BOOT_DATA)
2061         m.update(tools.GetBytes(ord('a'), 16))
2062         self.assertEqual(m.digest(), b''.join(hash_node.value))
2063
2064     def testPackUBootTplMicrocode(self):
2065         """Test that x86 microcode can be handled correctly in TPL
2066
2067         We expect to see the following in the image, in order:
2068             u-boot-tpl-nodtb.bin with a microcode pointer inserted at the correct
2069                 place
2070             u-boot-tpl.dtb with the microcode removed
2071             the microcode
2072         """
2073         self._SetupTplElf('u_boot_ucode_ptr')
2074         first, pos_and_size = self._RunMicrocodeTest('093_x86_tpl_ucode.dts',
2075                                                      U_BOOT_TPL_NODTB_DATA)
2076         self.assertEqual(b'tplnodtb with microc' + pos_and_size +
2077                          b'ter somewhere in here', first)
2078
2079     def testFmapX86(self):
2080         """Basic test of generation of a flashrom fmap"""
2081         data = self._DoReadFile('094_fmap_x86.dts')
2082         fhdr, fentries = fmap_util.DecodeFmap(data[32:])
2083         expected = U_BOOT_DATA + MRC_DATA + tools.GetBytes(ord('a'), 32 - 7)
2084         self.assertEqual(expected, data[:32])
2085         fhdr, fentries = fmap_util.DecodeFmap(data[32:])
2086
2087         self.assertEqual(0x100, fhdr.image_size)
2088
2089         self.assertEqual(0, fentries[0].offset)
2090         self.assertEqual(4, fentries[0].size)
2091         self.assertEqual(b'U_BOOT', fentries[0].name)
2092
2093         self.assertEqual(4, fentries[1].offset)
2094         self.assertEqual(3, fentries[1].size)
2095         self.assertEqual(b'INTEL_MRC', fentries[1].name)
2096
2097         self.assertEqual(32, fentries[2].offset)
2098         self.assertEqual(fmap_util.FMAP_HEADER_LEN +
2099                          fmap_util.FMAP_AREA_LEN * 3, fentries[2].size)
2100         self.assertEqual(b'FMAP', fentries[2].name)
2101
2102     def testFmapX86Section(self):
2103         """Basic test of generation of a flashrom fmap"""
2104         data = self._DoReadFile('095_fmap_x86_section.dts')
2105         expected = U_BOOT_DATA + MRC_DATA + tools.GetBytes(ord('b'), 32 - 7)
2106         self.assertEqual(expected, data[:32])
2107         fhdr, fentries = fmap_util.DecodeFmap(data[36:])
2108
2109         self.assertEqual(0x180, fhdr.image_size)
2110         expect_size = fmap_util.FMAP_HEADER_LEN + fmap_util.FMAP_AREA_LEN * 4
2111         fiter = iter(fentries)
2112
2113         fentry = next(fiter)
2114         self.assertEqual(b'U_BOOT', fentry.name)
2115         self.assertEqual(0, fentry.offset)
2116         self.assertEqual(4, fentry.size)
2117
2118         fentry = next(fiter)
2119         self.assertEqual(b'SECTION', fentry.name)
2120         self.assertEqual(4, fentry.offset)
2121         self.assertEqual(0x20 + expect_size, fentry.size)
2122
2123         fentry = next(fiter)
2124         self.assertEqual(b'INTEL_MRC', fentry.name)
2125         self.assertEqual(4, fentry.offset)
2126         self.assertEqual(3, fentry.size)
2127
2128         fentry = next(fiter)
2129         self.assertEqual(b'FMAP', fentry.name)
2130         self.assertEqual(36, fentry.offset)
2131         self.assertEqual(expect_size, fentry.size)
2132
2133     def testElf(self):
2134         """Basic test of ELF entries"""
2135         self._SetupSplElf()
2136         self._SetupTplElf()
2137         with open(self.ElfTestFile('bss_data'), 'rb') as fd:
2138             TestFunctional._MakeInputFile('-boot', fd.read())
2139         data = self._DoReadFile('096_elf.dts')
2140
2141     def testElfStrip(self):
2142         """Basic test of ELF entries"""
2143         self._SetupSplElf()
2144         with open(self.ElfTestFile('bss_data'), 'rb') as fd:
2145             TestFunctional._MakeInputFile('-boot', fd.read())
2146         data = self._DoReadFile('097_elf_strip.dts')
2147
2148     def testPackOverlapMap(self):
2149         """Test that overlapping regions are detected"""
2150         with test_util.capture_sys_output() as (stdout, stderr):
2151             with self.assertRaises(ValueError) as e:
2152                 self._DoTestFile('014_pack_overlap.dts', map=True)
2153         map_fname = tools.GetOutputFilename('image.map')
2154         self.assertEqual("Wrote map file '%s' to show errors\n" % map_fname,
2155                          stdout.getvalue())
2156
2157         # We should not get an inmage, but there should be a map file
2158         self.assertFalse(os.path.exists(tools.GetOutputFilename('image.bin')))
2159         self.assertTrue(os.path.exists(map_fname))
2160         map_data = tools.ReadFile(map_fname, binary=False)
2161         self.assertEqual('''ImagePos    Offset      Size  Name
2162 <none>    00000000  00000008  main-section
2163 <none>     00000000  00000004  u-boot
2164 <none>     00000003  00000004  u-boot-align
2165 ''', map_data)
2166
2167     def testPackRefCode(self):
2168         """Test that an image with an Intel Reference code binary works"""
2169         data = self._DoReadFile('100_intel_refcode.dts')
2170         self.assertEqual(REFCODE_DATA, data[:len(REFCODE_DATA)])
2171
2172     def testSectionOffset(self):
2173         """Tests use of a section with an offset"""
2174         data, _, map_data, _ = self._DoReadFileDtb('101_sections_offset.dts',
2175                                                    map=True)
2176         self.assertEqual('''ImagePos    Offset      Size  Name
2177 00000000  00000000  00000038  main-section
2178 00000004   00000004  00000010  section@0
2179 00000004    00000000  00000004  u-boot
2180 00000018   00000018  00000010  section@1
2181 00000018    00000000  00000004  u-boot
2182 0000002c   0000002c  00000004  section@2
2183 0000002c    00000000  00000004  u-boot
2184 ''', map_data)
2185         self.assertEqual(data,
2186                          tools.GetBytes(0x26, 4) + U_BOOT_DATA +
2187                              tools.GetBytes(0x21, 12) +
2188                          tools.GetBytes(0x26, 4) + U_BOOT_DATA +
2189                              tools.GetBytes(0x61, 12) +
2190                          tools.GetBytes(0x26, 4) + U_BOOT_DATA +
2191                              tools.GetBytes(0x26, 8))
2192
2193     def testCbfsRaw(self):
2194         """Test base handling of a Coreboot Filesystem (CBFS)
2195
2196         The exact contents of the CBFS is verified by similar tests in
2197         cbfs_util_test.py. The tests here merely check that the files added to
2198         the CBFS can be found in the final image.
2199         """
2200         data = self._DoReadFile('102_cbfs_raw.dts')
2201         size = 0xb0
2202
2203         cbfs = cbfs_util.CbfsReader(data)
2204         self.assertEqual(size, cbfs.rom_size)
2205
2206         self.assertIn('u-boot-dtb', cbfs.files)
2207         cfile = cbfs.files['u-boot-dtb']
2208         self.assertEqual(U_BOOT_DTB_DATA, cfile.data)
2209
2210     def testCbfsArch(self):
2211         """Test on non-x86 architecture"""
2212         data = self._DoReadFile('103_cbfs_raw_ppc.dts')
2213         size = 0x100
2214
2215         cbfs = cbfs_util.CbfsReader(data)
2216         self.assertEqual(size, cbfs.rom_size)
2217
2218         self.assertIn('u-boot-dtb', cbfs.files)
2219         cfile = cbfs.files['u-boot-dtb']
2220         self.assertEqual(U_BOOT_DTB_DATA, cfile.data)
2221
2222     def testCbfsStage(self):
2223         """Tests handling of a Coreboot Filesystem (CBFS)"""
2224         if not elf.ELF_TOOLS:
2225             self.skipTest('Python elftools not available')
2226         elf_fname = os.path.join(self._indir, 'cbfs-stage.elf')
2227         elf.MakeElf(elf_fname, U_BOOT_DATA, U_BOOT_DTB_DATA)
2228         size = 0xb0
2229
2230         data = self._DoReadFile('104_cbfs_stage.dts')
2231         cbfs = cbfs_util.CbfsReader(data)
2232         self.assertEqual(size, cbfs.rom_size)
2233
2234         self.assertIn('u-boot', cbfs.files)
2235         cfile = cbfs.files['u-boot']
2236         self.assertEqual(U_BOOT_DATA + U_BOOT_DTB_DATA, cfile.data)
2237
2238     def testCbfsRawCompress(self):
2239         """Test handling of compressing raw files"""
2240         self._CheckLz4()
2241         data = self._DoReadFile('105_cbfs_raw_compress.dts')
2242         size = 0x140
2243
2244         cbfs = cbfs_util.CbfsReader(data)
2245         self.assertIn('u-boot', cbfs.files)
2246         cfile = cbfs.files['u-boot']
2247         self.assertEqual(COMPRESS_DATA, cfile.data)
2248
2249     def testCbfsBadArch(self):
2250         """Test handling of a bad architecture"""
2251         with self.assertRaises(ValueError) as e:
2252             self._DoReadFile('106_cbfs_bad_arch.dts')
2253         self.assertIn("Invalid architecture 'bad-arch'", str(e.exception))
2254
2255     def testCbfsNoSize(self):
2256         """Test handling of a missing size property"""
2257         with self.assertRaises(ValueError) as e:
2258             self._DoReadFile('107_cbfs_no_size.dts')
2259         self.assertIn('entry must have a size property', str(e.exception))
2260
2261     def testCbfsNoContents(self):
2262         """Test handling of a CBFS entry which does not provide contentsy"""
2263         with self.assertRaises(ValueError) as e:
2264             self._DoReadFile('108_cbfs_no_contents.dts')
2265         self.assertIn('Could not complete processing of contents',
2266                       str(e.exception))
2267
2268     def testCbfsBadCompress(self):
2269         """Test handling of a bad architecture"""
2270         with self.assertRaises(ValueError) as e:
2271             self._DoReadFile('109_cbfs_bad_compress.dts')
2272         self.assertIn("Invalid compression in 'u-boot': 'invalid-algo'",
2273                       str(e.exception))
2274
2275     def testCbfsNamedEntries(self):
2276         """Test handling of named entries"""
2277         data = self._DoReadFile('110_cbfs_name.dts')
2278
2279         cbfs = cbfs_util.CbfsReader(data)
2280         self.assertIn('FRED', cbfs.files)
2281         cfile1 = cbfs.files['FRED']
2282         self.assertEqual(U_BOOT_DATA, cfile1.data)
2283
2284         self.assertIn('hello', cbfs.files)
2285         cfile2 = cbfs.files['hello']
2286         self.assertEqual(U_BOOT_DTB_DATA, cfile2.data)
2287
2288     def _SetupIfwi(self, fname):
2289         """Set up to run an IFWI test
2290
2291         Args:
2292             fname: Filename of input file to provide (fitimage.bin or ifwi.bin)
2293         """
2294         self._SetupSplElf()
2295         self._SetupTplElf()
2296
2297         # Intel Integrated Firmware Image (IFWI) file
2298         with gzip.open(self.TestFile('%s.gz' % fname), 'rb') as fd:
2299             data = fd.read()
2300         TestFunctional._MakeInputFile(fname,data)
2301
2302     def _CheckIfwi(self, data):
2303         """Check that an image with an IFWI contains the correct output
2304
2305         Args:
2306             data: Conents of output file
2307         """
2308         expected_desc = tools.ReadFile(self.TestFile('descriptor.bin'))
2309         if data[:0x1000] != expected_desc:
2310             self.fail('Expected descriptor binary at start of image')
2311
2312         # We expect to find the TPL wil in subpart IBBP entry IBBL
2313         image_fname = tools.GetOutputFilename('image.bin')
2314         tpl_fname = tools.GetOutputFilename('tpl.out')
2315         tools.RunIfwiTool(image_fname, tools.CMD_EXTRACT, fname=tpl_fname,
2316                           subpart='IBBP', entry_name='IBBL')
2317
2318         tpl_data = tools.ReadFile(tpl_fname)
2319         self.assertEqual(U_BOOT_TPL_DATA, tpl_data[:len(U_BOOT_TPL_DATA)])
2320
2321     def testPackX86RomIfwi(self):
2322         """Test that an x86 ROM with Integrated Firmware Image can be created"""
2323         self._SetupIfwi('fitimage.bin')
2324         data = self._DoReadFile('111_x86_rom_ifwi.dts')
2325         self._CheckIfwi(data)
2326
2327     def testPackX86RomIfwiNoDesc(self):
2328         """Test that an x86 ROM with IFWI can be created from an ifwi.bin file"""
2329         self._SetupIfwi('ifwi.bin')
2330         data = self._DoReadFile('112_x86_rom_ifwi_nodesc.dts')
2331         self._CheckIfwi(data)
2332
2333     def testPackX86RomIfwiNoData(self):
2334         """Test that an x86 ROM with IFWI handles missing data"""
2335         self._SetupIfwi('ifwi.bin')
2336         with self.assertRaises(ValueError) as e:
2337             data = self._DoReadFile('113_x86_rom_ifwi_nodata.dts')
2338         self.assertIn('Could not complete processing of contents',
2339                       str(e.exception))
2340
2341     def testCbfsOffset(self):
2342         """Test a CBFS with files at particular offsets
2343
2344         Like all CFBS tests, this is just checking the logic that calls
2345         cbfs_util. See cbfs_util_test for fully tests (e.g. test_cbfs_offset()).
2346         """
2347         data = self._DoReadFile('114_cbfs_offset.dts')
2348         size = 0x200
2349
2350         cbfs = cbfs_util.CbfsReader(data)
2351         self.assertEqual(size, cbfs.rom_size)
2352
2353         self.assertIn('u-boot', cbfs.files)
2354         cfile = cbfs.files['u-boot']
2355         self.assertEqual(U_BOOT_DATA, cfile.data)
2356         self.assertEqual(0x40, cfile.cbfs_offset)
2357
2358         self.assertIn('u-boot-dtb', cbfs.files)
2359         cfile2 = cbfs.files['u-boot-dtb']
2360         self.assertEqual(U_BOOT_DTB_DATA, cfile2.data)
2361         self.assertEqual(0x140, cfile2.cbfs_offset)
2362
2363     def testFdtmap(self):
2364         """Test an FDT map can be inserted in the image"""
2365         data = self.data = self._DoReadFileRealDtb('115_fdtmap.dts')
2366         fdtmap_data = data[len(U_BOOT_DATA):]
2367         magic = fdtmap_data[:8]
2368         self.assertEqual(b'_FDTMAP_', magic)
2369         self.assertEqual(tools.GetBytes(0, 8), fdtmap_data[8:16])
2370
2371         fdt_data = fdtmap_data[16:]
2372         dtb = fdt.Fdt.FromData(fdt_data)
2373         dtb.Scan()
2374         props = self._GetPropTree(dtb, BASE_DTB_PROPS, prefix='/')
2375         self.assertEqual({
2376             'image-pos': 0,
2377             'offset': 0,
2378             'u-boot:offset': 0,
2379             'u-boot:size': len(U_BOOT_DATA),
2380             'u-boot:image-pos': 0,
2381             'fdtmap:image-pos': 4,
2382             'fdtmap:offset': 4,
2383             'fdtmap:size': len(fdtmap_data),
2384             'size': len(data),
2385         }, props)
2386
2387     def testFdtmapNoMatch(self):
2388         """Check handling of an FDT map when the section cannot be found"""
2389         self.data = self._DoReadFileRealDtb('115_fdtmap.dts')
2390
2391         # Mangle the section name, which should cause a mismatch between the
2392         # correct FDT path and the one expected by the section
2393         image = control.images['image']
2394         image._node.path += '-suffix'
2395         entries = image.GetEntries()
2396         fdtmap = entries['fdtmap']
2397         with self.assertRaises(ValueError) as e:
2398             fdtmap._GetFdtmap()
2399         self.assertIn("Cannot locate node for path '/binman-suffix'",
2400                       str(e.exception))
2401
2402     def testFdtmapHeader(self):
2403         """Test an FDT map and image header can be inserted in the image"""
2404         data = self.data = self._DoReadFileRealDtb('116_fdtmap_hdr.dts')
2405         fdtmap_pos = len(U_BOOT_DATA)
2406         fdtmap_data = data[fdtmap_pos:]
2407         fdt_data = fdtmap_data[16:]
2408         dtb = fdt.Fdt.FromData(fdt_data)
2409         fdt_size = dtb.GetFdtObj().totalsize()
2410         hdr_data = data[-8:]
2411         self.assertEqual(b'BinM', hdr_data[:4])
2412         offset = struct.unpack('<I', hdr_data[4:])[0] & 0xffffffff
2413         self.assertEqual(fdtmap_pos - 0x400, offset - (1 << 32))
2414
2415     def testFdtmapHeaderStart(self):
2416         """Test an image header can be inserted at the image start"""
2417         data = self.data = self._DoReadFileRealDtb('117_fdtmap_hdr_start.dts')
2418         fdtmap_pos = 0x100 + len(U_BOOT_DATA)
2419         hdr_data = data[:8]
2420         self.assertEqual(b'BinM', hdr_data[:4])
2421         offset = struct.unpack('<I', hdr_data[4:])[0]
2422         self.assertEqual(fdtmap_pos, offset)
2423
2424     def testFdtmapHeaderPos(self):
2425         """Test an image header can be inserted at a chosen position"""
2426         data = self.data = self._DoReadFileRealDtb('118_fdtmap_hdr_pos.dts')
2427         fdtmap_pos = 0x100 + len(U_BOOT_DATA)
2428         hdr_data = data[0x80:0x88]
2429         self.assertEqual(b'BinM', hdr_data[:4])
2430         offset = struct.unpack('<I', hdr_data[4:])[0]
2431         self.assertEqual(fdtmap_pos, offset)
2432
2433     def testHeaderMissingFdtmap(self):
2434         """Test an image header requires an fdtmap"""
2435         with self.assertRaises(ValueError) as e:
2436             self.data = self._DoReadFileRealDtb('119_fdtmap_hdr_missing.dts')
2437         self.assertIn("'image_header' section must have an 'fdtmap' sibling",
2438                       str(e.exception))
2439
2440     def testHeaderNoLocation(self):
2441         """Test an image header with a no specified location is detected"""
2442         with self.assertRaises(ValueError) as e:
2443             self.data = self._DoReadFileRealDtb('120_hdr_no_location.dts')
2444         self.assertIn("Invalid location 'None', expected 'start' or 'end'",
2445                       str(e.exception))
2446
2447     def testEntryExpand(self):
2448         """Test expanding an entry after it is packed"""
2449         data = self._DoReadFile('121_entry_expand.dts')
2450         self.assertEqual(b'aaa', data[:3])
2451         self.assertEqual(U_BOOT_DATA, data[3:3 + len(U_BOOT_DATA)])
2452         self.assertEqual(b'aaa', data[-3:])
2453
2454     def testEntryExpandBad(self):
2455         """Test expanding an entry after it is packed, twice"""
2456         with self.assertRaises(ValueError) as e:
2457             self._DoReadFile('122_entry_expand_twice.dts')
2458         self.assertIn("Image '/binman': Entries changed size after packing",
2459                       str(e.exception))
2460
2461     def testEntryExpandSection(self):
2462         """Test expanding an entry within a section after it is packed"""
2463         data = self._DoReadFile('123_entry_expand_section.dts')
2464         self.assertEqual(b'aaa', data[:3])
2465         self.assertEqual(U_BOOT_DATA, data[3:3 + len(U_BOOT_DATA)])
2466         self.assertEqual(b'aaa', data[-3:])
2467
2468     def testCompressDtb(self):
2469         """Test that compress of device-tree files is supported"""
2470         self._CheckLz4()
2471         data = self.data = self._DoReadFileRealDtb('124_compress_dtb.dts')
2472         self.assertEqual(U_BOOT_DATA, data[:len(U_BOOT_DATA)])
2473         comp_data = data[len(U_BOOT_DATA):]
2474         orig = self._decompress(comp_data)
2475         dtb = fdt.Fdt.FromData(orig)
2476         dtb.Scan()
2477         props = self._GetPropTree(dtb, ['size', 'uncomp-size'])
2478         expected = {
2479             'u-boot:size': len(U_BOOT_DATA),
2480             'u-boot-dtb:uncomp-size': len(orig),
2481             'u-boot-dtb:size': len(comp_data),
2482             'size': len(data),
2483             }
2484         self.assertEqual(expected, props)
2485
2486     def testCbfsUpdateFdt(self):
2487         """Test that we can update the device tree with CBFS offset/size info"""
2488         self._CheckLz4()
2489         data, _, _, out_dtb_fname = self._DoReadFileDtb('125_cbfs_update.dts',
2490                                                         update_dtb=True)
2491         dtb = fdt.Fdt(out_dtb_fname)
2492         dtb.Scan()
2493         props = self._GetPropTree(dtb, BASE_DTB_PROPS + ['uncomp-size'])
2494         del props['cbfs/u-boot:size']
2495         self.assertEqual({
2496             'offset': 0,
2497             'size': len(data),
2498             'image-pos': 0,
2499             'cbfs:offset': 0,
2500             'cbfs:size': len(data),
2501             'cbfs:image-pos': 0,
2502             'cbfs/u-boot:offset': 0x38,
2503             'cbfs/u-boot:uncomp-size': len(U_BOOT_DATA),
2504             'cbfs/u-boot:image-pos': 0x38,
2505             'cbfs/u-boot-dtb:offset': 0xb8,
2506             'cbfs/u-boot-dtb:size': len(U_BOOT_DATA),
2507             'cbfs/u-boot-dtb:image-pos': 0xb8,
2508             }, props)
2509
2510     def testCbfsBadType(self):
2511         """Test an image header with a no specified location is detected"""
2512         with self.assertRaises(ValueError) as e:
2513             self._DoReadFile('126_cbfs_bad_type.dts')
2514         self.assertIn("Unknown cbfs-type 'badtype'", str(e.exception))
2515
2516     def testList(self):
2517         """Test listing the files in an image"""
2518         self._CheckLz4()
2519         data = self._DoReadFile('127_list.dts')
2520         image = control.images['image']
2521         entries = image.BuildEntryList()
2522         self.assertEqual(7, len(entries))
2523
2524         ent = entries[0]
2525         self.assertEqual(0, ent.indent)
2526         self.assertEqual('main-section', ent.name)
2527         self.assertEqual('section', ent.etype)
2528         self.assertEqual(len(data), ent.size)
2529         self.assertEqual(0, ent.image_pos)
2530         self.assertEqual(None, ent.uncomp_size)
2531         self.assertEqual(0, ent.offset)
2532
2533         ent = entries[1]
2534         self.assertEqual(1, ent.indent)
2535         self.assertEqual('u-boot', ent.name)
2536         self.assertEqual('u-boot', ent.etype)
2537         self.assertEqual(len(U_BOOT_DATA), ent.size)
2538         self.assertEqual(0, ent.image_pos)
2539         self.assertEqual(None, ent.uncomp_size)
2540         self.assertEqual(0, ent.offset)
2541
2542         ent = entries[2]
2543         self.assertEqual(1, ent.indent)
2544         self.assertEqual('section', ent.name)
2545         self.assertEqual('section', ent.etype)
2546         section_size = ent.size
2547         self.assertEqual(0x100, ent.image_pos)
2548         self.assertEqual(None, ent.uncomp_size)
2549         self.assertEqual(0x100, ent.offset)
2550
2551         ent = entries[3]
2552         self.assertEqual(2, ent.indent)
2553         self.assertEqual('cbfs', ent.name)
2554         self.assertEqual('cbfs', ent.etype)
2555         self.assertEqual(0x400, ent.size)
2556         self.assertEqual(0x100, ent.image_pos)
2557         self.assertEqual(None, ent.uncomp_size)
2558         self.assertEqual(0, ent.offset)
2559
2560         ent = entries[4]
2561         self.assertEqual(3, ent.indent)
2562         self.assertEqual('u-boot', ent.name)
2563         self.assertEqual('u-boot', ent.etype)
2564         self.assertEqual(len(U_BOOT_DATA), ent.size)
2565         self.assertEqual(0x138, ent.image_pos)
2566         self.assertEqual(None, ent.uncomp_size)
2567         self.assertEqual(0x38, ent.offset)
2568
2569         ent = entries[5]
2570         self.assertEqual(3, ent.indent)
2571         self.assertEqual('u-boot-dtb', ent.name)
2572         self.assertEqual('text', ent.etype)
2573         self.assertGreater(len(COMPRESS_DATA), ent.size)
2574         self.assertEqual(0x178, ent.image_pos)
2575         self.assertEqual(len(COMPRESS_DATA), ent.uncomp_size)
2576         self.assertEqual(0x78, ent.offset)
2577
2578         ent = entries[6]
2579         self.assertEqual(2, ent.indent)
2580         self.assertEqual('u-boot-dtb', ent.name)
2581         self.assertEqual('u-boot-dtb', ent.etype)
2582         self.assertEqual(0x500, ent.image_pos)
2583         self.assertEqual(len(U_BOOT_DTB_DATA), ent.uncomp_size)
2584         dtb_size = ent.size
2585         # Compressing this data expands it since headers are added
2586         self.assertGreater(dtb_size, len(U_BOOT_DTB_DATA))
2587         self.assertEqual(0x400, ent.offset)
2588
2589         self.assertEqual(len(data), 0x100 + section_size)
2590         self.assertEqual(section_size, 0x400 + dtb_size)
2591
2592     def testFindFdtmap(self):
2593         """Test locating an FDT map in an image"""
2594         self._CheckLz4()
2595         data = self.data = self._DoReadFileRealDtb('128_decode_image.dts')
2596         image = control.images['image']
2597         entries = image.GetEntries()
2598         entry = entries['fdtmap']
2599         self.assertEqual(entry.image_pos, fdtmap.LocateFdtmap(data))
2600
2601     def testFindFdtmapMissing(self):
2602         """Test failing to locate an FDP map"""
2603         data = self._DoReadFile('005_simple.dts')
2604         self.assertEqual(None, fdtmap.LocateFdtmap(data))
2605
2606     def testFindImageHeader(self):
2607         """Test locating a image header"""
2608         self._CheckLz4()
2609         data = self.data = self._DoReadFileRealDtb('128_decode_image.dts')
2610         image = control.images['image']
2611         entries = image.GetEntries()
2612         entry = entries['fdtmap']
2613         # The header should point to the FDT map
2614         self.assertEqual(entry.image_pos, image_header.LocateHeaderOffset(data))
2615
2616     def testFindImageHeaderStart(self):
2617         """Test locating a image header located at the start of an image"""
2618         data = self.data = self._DoReadFileRealDtb('117_fdtmap_hdr_start.dts')
2619         image = control.images['image']
2620         entries = image.GetEntries()
2621         entry = entries['fdtmap']
2622         # The header should point to the FDT map
2623         self.assertEqual(entry.image_pos, image_header.LocateHeaderOffset(data))
2624
2625     def testFindImageHeaderMissing(self):
2626         """Test failing to locate an image header"""
2627         data = self._DoReadFile('005_simple.dts')
2628         self.assertEqual(None, image_header.LocateHeaderOffset(data))
2629
2630     def testReadImage(self):
2631         """Test reading an image and accessing its FDT map"""
2632         self._CheckLz4()
2633         data = self.data = self._DoReadFileRealDtb('128_decode_image.dts')
2634         image_fname = tools.GetOutputFilename('image.bin')
2635         orig_image = control.images['image']
2636         image = Image.FromFile(image_fname)
2637         self.assertEqual(orig_image.GetEntries().keys(),
2638                          image.GetEntries().keys())
2639
2640         orig_entry = orig_image.GetEntries()['fdtmap']
2641         entry = image.GetEntries()['fdtmap']
2642         self.assertEquals(orig_entry.offset, entry.offset)
2643         self.assertEquals(orig_entry.size, entry.size)
2644         self.assertEquals(orig_entry.image_pos, entry.image_pos)
2645
2646     def testReadImageNoHeader(self):
2647         """Test accessing an image's FDT map without an image header"""
2648         self._CheckLz4()
2649         data = self._DoReadFileRealDtb('129_decode_image_nohdr.dts')
2650         image_fname = tools.GetOutputFilename('image.bin')
2651         image = Image.FromFile(image_fname)
2652         self.assertTrue(isinstance(image, Image))
2653         self.assertEqual('image', image.image_name[-5:])
2654
2655     def testReadImageFail(self):
2656         """Test failing to read an image image's FDT map"""
2657         self._DoReadFile('005_simple.dts')
2658         image_fname = tools.GetOutputFilename('image.bin')
2659         with self.assertRaises(ValueError) as e:
2660             image = Image.FromFile(image_fname)
2661         self.assertIn("Cannot find FDT map in image", str(e.exception))
2662
2663     def testListCmd(self):
2664         """Test listing the files in an image using an Fdtmap"""
2665         self._CheckLz4()
2666         data = self._DoReadFileRealDtb('130_list_fdtmap.dts')
2667
2668         # lz4 compression size differs depending on the version
2669         image = control.images['image']
2670         entries = image.GetEntries()
2671         section_size = entries['section'].size
2672         fdt_size = entries['section'].GetEntries()['u-boot-dtb'].size
2673         fdtmap_offset = entries['fdtmap'].offset
2674
2675         try:
2676             tmpdir, updated_fname = self._SetupImageInTmpdir()
2677             with test_util.capture_sys_output() as (stdout, stderr):
2678                 self._DoBinman('ls', '-i', updated_fname)
2679         finally:
2680             shutil.rmtree(tmpdir)
2681         lines = stdout.getvalue().splitlines()
2682         expected = [
2683 'Name              Image-pos  Size  Entry-type    Offset  Uncomp-size',
2684 '----------------------------------------------------------------------',
2685 'main-section              0   c00  section            0',
2686 '  u-boot                  0     4  u-boot             0',
2687 '  section               100   %x  section          100' % section_size,
2688 '    cbfs                100   400  cbfs               0',
2689 '      u-boot            138     4  u-boot            38',
2690 '      u-boot-dtb        180   105  u-boot-dtb        80          3c9',
2691 '    u-boot-dtb          500   %x  u-boot-dtb       400          3c9' % fdt_size,
2692 '  fdtmap                %x   3bd  fdtmap           %x' %
2693         (fdtmap_offset, fdtmap_offset),
2694 '  image-header          bf8     8  image-header     bf8',
2695             ]
2696         self.assertEqual(expected, lines)
2697
2698     def testListCmdFail(self):
2699         """Test failing to list an image"""
2700         self._DoReadFile('005_simple.dts')
2701         try:
2702             tmpdir, updated_fname = self._SetupImageInTmpdir()
2703             with self.assertRaises(ValueError) as e:
2704                 self._DoBinman('ls', '-i', updated_fname)
2705         finally:
2706             shutil.rmtree(tmpdir)
2707         self.assertIn("Cannot find FDT map in image", str(e.exception))
2708
2709     def _RunListCmd(self, paths, expected):
2710         """List out entries and check the result
2711
2712         Args:
2713             paths: List of paths to pass to the list command
2714             expected: Expected list of filenames to be returned, in order
2715         """
2716         self._CheckLz4()
2717         self._DoReadFileRealDtb('130_list_fdtmap.dts')
2718         image_fname = tools.GetOutputFilename('image.bin')
2719         image = Image.FromFile(image_fname)
2720         lines = image.GetListEntries(paths)[1]
2721         files = [line[0].strip() for line in lines[1:]]
2722         self.assertEqual(expected, files)
2723
2724     def testListCmdSection(self):
2725         """Test listing the files in a section"""
2726         self._RunListCmd(['section'],
2727             ['section', 'cbfs', 'u-boot', 'u-boot-dtb', 'u-boot-dtb'])
2728
2729     def testListCmdFile(self):
2730         """Test listing a particular file"""
2731         self._RunListCmd(['*u-boot-dtb'], ['u-boot-dtb', 'u-boot-dtb'])
2732
2733     def testListCmdWildcard(self):
2734         """Test listing a wildcarded file"""
2735         self._RunListCmd(['*boot*'],
2736             ['u-boot', 'u-boot', 'u-boot-dtb', 'u-boot-dtb'])
2737
2738     def testListCmdWildcardMulti(self):
2739         """Test listing a wildcarded file"""
2740         self._RunListCmd(['*cb*', '*head*'],
2741             ['cbfs', 'u-boot', 'u-boot-dtb', 'image-header'])
2742
2743     def testListCmdEmpty(self):
2744         """Test listing a wildcarded file"""
2745         self._RunListCmd(['nothing'], [])
2746
2747     def testListCmdPath(self):
2748         """Test listing the files in a sub-entry of a section"""
2749         self._RunListCmd(['section/cbfs'], ['cbfs', 'u-boot', 'u-boot-dtb'])
2750
2751     def _RunExtractCmd(self, entry_name, decomp=True):
2752         """Extract an entry from an image
2753
2754         Args:
2755             entry_name: Entry name to extract
2756             decomp: True to decompress the data if compressed, False to leave
2757                 it in its raw uncompressed format
2758
2759         Returns:
2760             data from entry
2761         """
2762         self._CheckLz4()
2763         self._DoReadFileRealDtb('130_list_fdtmap.dts')
2764         image_fname = tools.GetOutputFilename('image.bin')
2765         return control.ReadEntry(image_fname, entry_name, decomp)
2766
2767     def testExtractSimple(self):
2768         """Test extracting a single file"""
2769         data = self._RunExtractCmd('u-boot')
2770         self.assertEqual(U_BOOT_DATA, data)
2771
2772     def testExtractSection(self):
2773         """Test extracting the files in a section"""
2774         data = self._RunExtractCmd('section')
2775         cbfs_data = data[:0x400]
2776         cbfs = cbfs_util.CbfsReader(cbfs_data)
2777         self.assertEqual(['u-boot', 'u-boot-dtb', ''], list(cbfs.files.keys()))
2778         dtb_data = data[0x400:]
2779         dtb = self._decompress(dtb_data)
2780         self.assertEqual(EXTRACT_DTB_SIZE, len(dtb))
2781
2782     def testExtractCompressed(self):
2783         """Test extracting compressed data"""
2784         data = self._RunExtractCmd('section/u-boot-dtb')
2785         self.assertEqual(EXTRACT_DTB_SIZE, len(data))
2786
2787     def testExtractRaw(self):
2788         """Test extracting compressed data without decompressing it"""
2789         data = self._RunExtractCmd('section/u-boot-dtb', decomp=False)
2790         dtb = self._decompress(data)
2791         self.assertEqual(EXTRACT_DTB_SIZE, len(dtb))
2792
2793     def testExtractCbfs(self):
2794         """Test extracting CBFS data"""
2795         data = self._RunExtractCmd('section/cbfs/u-boot')
2796         self.assertEqual(U_BOOT_DATA, data)
2797
2798     def testExtractCbfsCompressed(self):
2799         """Test extracting CBFS compressed data"""
2800         data = self._RunExtractCmd('section/cbfs/u-boot-dtb')
2801         self.assertEqual(EXTRACT_DTB_SIZE, len(data))
2802
2803     def testExtractCbfsRaw(self):
2804         """Test extracting CBFS compressed data without decompressing it"""
2805         data = self._RunExtractCmd('section/cbfs/u-boot-dtb', decomp=False)
2806         dtb = tools.Decompress(data, 'lzma', with_header=False)
2807         self.assertEqual(EXTRACT_DTB_SIZE, len(dtb))
2808
2809     def testExtractBadEntry(self):
2810         """Test extracting a bad section path"""
2811         with self.assertRaises(ValueError) as e:
2812             self._RunExtractCmd('section/does-not-exist')
2813         self.assertIn("Entry 'does-not-exist' not found in '/section'",
2814                       str(e.exception))
2815
2816     def testExtractMissingFile(self):
2817         """Test extracting file that does not exist"""
2818         with self.assertRaises(IOError) as e:
2819             control.ReadEntry('missing-file', 'name')
2820
2821     def testExtractBadFile(self):
2822         """Test extracting an invalid file"""
2823         fname = os.path.join(self._indir, 'badfile')
2824         tools.WriteFile(fname, b'')
2825         with self.assertRaises(ValueError) as e:
2826             control.ReadEntry(fname, 'name')
2827
2828     def testExtractCmd(self):
2829         """Test extracting a file fron an image on the command line"""
2830         self._CheckLz4()
2831         self._DoReadFileRealDtb('130_list_fdtmap.dts')
2832         fname = os.path.join(self._indir, 'output.extact')
2833         try:
2834             tmpdir, updated_fname = self._SetupImageInTmpdir()
2835             with test_util.capture_sys_output() as (stdout, stderr):
2836                 self._DoBinman('extract', '-i', updated_fname, 'u-boot',
2837                                '-f', fname)
2838         finally:
2839             shutil.rmtree(tmpdir)
2840         data = tools.ReadFile(fname)
2841         self.assertEqual(U_BOOT_DATA, data)
2842
2843     def testExtractOneEntry(self):
2844         """Test extracting a single entry fron an image """
2845         self._CheckLz4()
2846         self._DoReadFileRealDtb('130_list_fdtmap.dts')
2847         image_fname = tools.GetOutputFilename('image.bin')
2848         fname = os.path.join(self._indir, 'output.extact')
2849         control.ExtractEntries(image_fname, fname, None, ['u-boot'])
2850         data = tools.ReadFile(fname)
2851         self.assertEqual(U_BOOT_DATA, data)
2852
2853     def _CheckExtractOutput(self, decomp):
2854         """Helper to test file output with and without decompression
2855
2856         Args:
2857             decomp: True to decompress entry data, False to output it raw
2858         """
2859         def _CheckPresent(entry_path, expect_data, expect_size=None):
2860             """Check and remove expected file
2861
2862             This checks the data/size of a file and removes the file both from
2863             the outfiles set and from the output directory. Once all files are
2864             processed, both the set and directory should be empty.
2865
2866             Args:
2867                 entry_path: Entry path
2868                 expect_data: Data to expect in file, or None to skip check
2869                 expect_size: Size of data to expect in file, or None to skip
2870             """
2871             path = os.path.join(outdir, entry_path)
2872             data = tools.ReadFile(path)
2873             os.remove(path)
2874             if expect_data:
2875                 self.assertEqual(expect_data, data)
2876             elif expect_size:
2877                 self.assertEqual(expect_size, len(data))
2878             outfiles.remove(path)
2879
2880         def _CheckDirPresent(name):
2881             """Remove expected directory
2882
2883             This gives an error if the directory does not exist as expected
2884
2885             Args:
2886                 name: Name of directory to remove
2887             """
2888             path = os.path.join(outdir, name)
2889             os.rmdir(path)
2890
2891         self._DoReadFileRealDtb('130_list_fdtmap.dts')
2892         image_fname = tools.GetOutputFilename('image.bin')
2893         outdir = os.path.join(self._indir, 'extract')
2894         einfos = control.ExtractEntries(image_fname, None, outdir, [], decomp)
2895
2896         # Create a set of all file that were output (should be 9)
2897         outfiles = set()
2898         for root, dirs, files in os.walk(outdir):
2899             outfiles |= set([os.path.join(root, fname) for fname in files])
2900         self.assertEqual(9, len(outfiles))
2901         self.assertEqual(9, len(einfos))
2902
2903         image = control.images['image']
2904         entries = image.GetEntries()
2905
2906         # Check the 9 files in various ways
2907         section = entries['section']
2908         section_entries = section.GetEntries()
2909         cbfs_entries = section_entries['cbfs'].GetEntries()
2910         _CheckPresent('u-boot', U_BOOT_DATA)
2911         _CheckPresent('section/cbfs/u-boot', U_BOOT_DATA)
2912         dtb_len = EXTRACT_DTB_SIZE
2913         if not decomp:
2914             dtb_len = cbfs_entries['u-boot-dtb'].size
2915         _CheckPresent('section/cbfs/u-boot-dtb', None, dtb_len)
2916         if not decomp:
2917             dtb_len = section_entries['u-boot-dtb'].size
2918         _CheckPresent('section/u-boot-dtb', None, dtb_len)
2919
2920         fdtmap = entries['fdtmap']
2921         _CheckPresent('fdtmap', fdtmap.data)
2922         hdr = entries['image-header']
2923         _CheckPresent('image-header', hdr.data)
2924
2925         _CheckPresent('section/root', section.data)
2926         cbfs = section_entries['cbfs']
2927         _CheckPresent('section/cbfs/root', cbfs.data)
2928         data = tools.ReadFile(image_fname)
2929         _CheckPresent('root', data)
2930
2931         # There should be no files left. Remove all the directories to check.
2932         # If there are any files/dirs remaining, one of these checks will fail.
2933         self.assertEqual(0, len(outfiles))
2934         _CheckDirPresent('section/cbfs')
2935         _CheckDirPresent('section')
2936         _CheckDirPresent('')
2937         self.assertFalse(os.path.exists(outdir))
2938
2939     def testExtractAllEntries(self):
2940         """Test extracting all entries"""
2941         self._CheckLz4()
2942         self._CheckExtractOutput(decomp=True)
2943
2944     def testExtractAllEntriesRaw(self):
2945         """Test extracting all entries without decompressing them"""
2946         self._CheckLz4()
2947         self._CheckExtractOutput(decomp=False)
2948
2949     def testExtractSelectedEntries(self):
2950         """Test extracting some entries"""
2951         self._CheckLz4()
2952         self._DoReadFileRealDtb('130_list_fdtmap.dts')
2953         image_fname = tools.GetOutputFilename('image.bin')
2954         outdir = os.path.join(self._indir, 'extract')
2955         einfos = control.ExtractEntries(image_fname, None, outdir,
2956                                         ['*cb*', '*head*'])
2957
2958         # File output is tested by testExtractAllEntries(), so just check that
2959         # the expected entries are selected
2960         names = [einfo.name for einfo in einfos]
2961         self.assertEqual(names,
2962                          ['cbfs', 'u-boot', 'u-boot-dtb', 'image-header'])
2963
2964     def testExtractNoEntryPaths(self):
2965         """Test extracting some entries"""
2966         self._CheckLz4()
2967         self._DoReadFileRealDtb('130_list_fdtmap.dts')
2968         image_fname = tools.GetOutputFilename('image.bin')
2969         with self.assertRaises(ValueError) as e:
2970             control.ExtractEntries(image_fname, 'fname', None, [])
2971         self.assertIn('Must specify an entry path to write with -f',
2972                       str(e.exception))
2973
2974     def testExtractTooManyEntryPaths(self):
2975         """Test extracting some entries"""
2976         self._CheckLz4()
2977         self._DoReadFileRealDtb('130_list_fdtmap.dts')
2978         image_fname = tools.GetOutputFilename('image.bin')
2979         with self.assertRaises(ValueError) as e:
2980             control.ExtractEntries(image_fname, 'fname', None, ['a', 'b'])
2981         self.assertIn('Must specify exactly one entry path to write with -f',
2982                       str(e.exception))
2983
2984     def testPackAlignSection(self):
2985         """Test that sections can have alignment"""
2986         self._DoReadFile('131_pack_align_section.dts')
2987
2988         self.assertIn('image', control.images)
2989         image = control.images['image']
2990         entries = image.GetEntries()
2991         self.assertEqual(3, len(entries))
2992
2993         # First u-boot
2994         self.assertIn('u-boot', entries)
2995         entry = entries['u-boot']
2996         self.assertEqual(0, entry.offset)
2997         self.assertEqual(0, entry.image_pos)
2998         self.assertEqual(len(U_BOOT_DATA), entry.contents_size)
2999         self.assertEqual(len(U_BOOT_DATA), entry.size)
3000
3001         # Section0
3002         self.assertIn('section0', entries)
3003         section0 = entries['section0']
3004         self.assertEqual(0x10, section0.offset)
3005         self.assertEqual(0x10, section0.image_pos)
3006         self.assertEqual(len(U_BOOT_DATA), section0.size)
3007
3008         # Second u-boot
3009         section_entries = section0.GetEntries()
3010         self.assertIn('u-boot', section_entries)
3011         entry = section_entries['u-boot']
3012         self.assertEqual(0, entry.offset)
3013         self.assertEqual(0x10, entry.image_pos)
3014         self.assertEqual(len(U_BOOT_DATA), entry.contents_size)
3015         self.assertEqual(len(U_BOOT_DATA), entry.size)
3016
3017         # Section1
3018         self.assertIn('section1', entries)
3019         section1 = entries['section1']
3020         self.assertEqual(0x14, section1.offset)
3021         self.assertEqual(0x14, section1.image_pos)
3022         self.assertEqual(0x20, section1.size)
3023
3024         # Second u-boot
3025         section_entries = section1.GetEntries()
3026         self.assertIn('u-boot', section_entries)
3027         entry = section_entries['u-boot']
3028         self.assertEqual(0, entry.offset)
3029         self.assertEqual(0x14, entry.image_pos)
3030         self.assertEqual(len(U_BOOT_DATA), entry.contents_size)
3031         self.assertEqual(len(U_BOOT_DATA), entry.size)
3032
3033         # Section2
3034         self.assertIn('section2', section_entries)
3035         section2 = section_entries['section2']
3036         self.assertEqual(0x4, section2.offset)
3037         self.assertEqual(0x18, section2.image_pos)
3038         self.assertEqual(4, section2.size)
3039
3040         # Third u-boot
3041         section_entries = section2.GetEntries()
3042         self.assertIn('u-boot', section_entries)
3043         entry = section_entries['u-boot']
3044         self.assertEqual(0, entry.offset)
3045         self.assertEqual(0x18, entry.image_pos)
3046         self.assertEqual(len(U_BOOT_DATA), entry.contents_size)
3047         self.assertEqual(len(U_BOOT_DATA), entry.size)
3048
3049     def _RunReplaceCmd(self, entry_name, data, decomp=True, allow_resize=True,
3050                        dts='132_replace.dts'):
3051         """Replace an entry in an image
3052
3053         This writes the entry data to update it, then opens the updated file and
3054         returns the value that it now finds there.
3055
3056         Args:
3057             entry_name: Entry name to replace
3058             data: Data to replace it with
3059             decomp: True to compress the data if needed, False if data is
3060                 already compressed so should be used as is
3061             allow_resize: True to allow entries to change size, False to raise
3062                 an exception
3063
3064         Returns:
3065             Tuple:
3066                 data from entry
3067                 data from fdtmap (excluding header)
3068                 Image object that was modified
3069         """
3070         dtb_data = self._DoReadFileDtb(dts, use_real_dtb=True,
3071                                        update_dtb=True)[1]
3072
3073         self.assertIn('image', control.images)
3074         image = control.images['image']
3075         entries = image.GetEntries()
3076         orig_dtb_data = entries['u-boot-dtb'].data
3077         orig_fdtmap_data = entries['fdtmap'].data
3078
3079         image_fname = tools.GetOutputFilename('image.bin')
3080         updated_fname = tools.GetOutputFilename('image-updated.bin')
3081         tools.WriteFile(updated_fname, tools.ReadFile(image_fname))
3082         image = control.WriteEntry(updated_fname, entry_name, data, decomp,
3083                                    allow_resize)
3084         data = control.ReadEntry(updated_fname, entry_name, decomp)
3085
3086         # The DT data should not change unless resized:
3087         if not allow_resize:
3088             new_dtb_data = entries['u-boot-dtb'].data
3089             self.assertEqual(new_dtb_data, orig_dtb_data)
3090             new_fdtmap_data = entries['fdtmap'].data
3091             self.assertEqual(new_fdtmap_data, orig_fdtmap_data)
3092
3093         return data, orig_fdtmap_data[fdtmap.FDTMAP_HDR_LEN:], image
3094
3095     def testReplaceSimple(self):
3096         """Test replacing a single file"""
3097         expected = b'x' * len(U_BOOT_DATA)
3098         data, expected_fdtmap, _ = self._RunReplaceCmd('u-boot', expected,
3099                                                     allow_resize=False)
3100         self.assertEqual(expected, data)
3101
3102         # Test that the state looks right. There should be an FDT for the fdtmap
3103         # that we jsut read back in, and it should match what we find in the
3104         # 'control' tables. Checking for an FDT that does not exist should
3105         # return None.
3106         path, fdtmap = state.GetFdtContents('fdtmap')
3107         self.assertIsNotNone(path)
3108         self.assertEqual(expected_fdtmap, fdtmap)
3109
3110         dtb = state.GetFdtForEtype('fdtmap')
3111         self.assertEqual(dtb.GetContents(), fdtmap)
3112
3113         missing_path, missing_fdtmap = state.GetFdtContents('missing')
3114         self.assertIsNone(missing_path)
3115         self.assertIsNone(missing_fdtmap)
3116
3117         missing_dtb = state.GetFdtForEtype('missing')
3118         self.assertIsNone(missing_dtb)
3119
3120         self.assertEqual('/binman', state.fdt_path_prefix)
3121
3122     def testReplaceResizeFail(self):
3123         """Test replacing a file by something larger"""
3124         expected = U_BOOT_DATA + b'x'
3125         with self.assertRaises(ValueError) as e:
3126             self._RunReplaceCmd('u-boot', expected, allow_resize=False,
3127                                 dts='139_replace_repack.dts')
3128         self.assertIn("Node '/u-boot': Entry data size does not match, but resize is disabled",
3129                       str(e.exception))
3130
3131     def testReplaceMulti(self):
3132         """Test replacing entry data where multiple images are generated"""
3133         data = self._DoReadFileDtb('133_replace_multi.dts', use_real_dtb=True,
3134                                    update_dtb=True)[0]
3135         expected = b'x' * len(U_BOOT_DATA)
3136         updated_fname = tools.GetOutputFilename('image-updated.bin')
3137         tools.WriteFile(updated_fname, data)
3138         entry_name = 'u-boot'
3139         control.WriteEntry(updated_fname, entry_name, expected,
3140                            allow_resize=False)
3141         data = control.ReadEntry(updated_fname, entry_name)
3142         self.assertEqual(expected, data)
3143
3144         # Check the state looks right.
3145         self.assertEqual('/binman/image', state.fdt_path_prefix)
3146
3147         # Now check we can write the first image
3148         image_fname = tools.GetOutputFilename('first-image.bin')
3149         updated_fname = tools.GetOutputFilename('first-updated.bin')
3150         tools.WriteFile(updated_fname, tools.ReadFile(image_fname))
3151         entry_name = 'u-boot'
3152         control.WriteEntry(updated_fname, entry_name, expected,
3153                            allow_resize=False)
3154         data = control.ReadEntry(updated_fname, entry_name)
3155         self.assertEqual(expected, data)
3156
3157         # Check the state looks right.
3158         self.assertEqual('/binman/first-image', state.fdt_path_prefix)
3159
3160     def testUpdateFdtAllRepack(self):
3161         """Test that all device trees are updated with offset/size info"""
3162         data = self._DoReadFileRealDtb('134_fdt_update_all_repack.dts')
3163         SECTION_SIZE = 0x300
3164         DTB_SIZE = 602
3165         FDTMAP_SIZE = 608
3166         base_expected = {
3167             'offset': 0,
3168             'size': SECTION_SIZE + DTB_SIZE * 2 + FDTMAP_SIZE,
3169             'image-pos': 0,
3170             'section:offset': 0,
3171             'section:size': SECTION_SIZE,
3172             'section:image-pos': 0,
3173             'section/u-boot-dtb:offset': 4,
3174             'section/u-boot-dtb:size': 636,
3175             'section/u-boot-dtb:image-pos': 4,
3176             'u-boot-spl-dtb:offset': SECTION_SIZE,
3177             'u-boot-spl-dtb:size': DTB_SIZE,
3178             'u-boot-spl-dtb:image-pos': SECTION_SIZE,
3179             'u-boot-tpl-dtb:offset': SECTION_SIZE + DTB_SIZE,
3180             'u-boot-tpl-dtb:image-pos': SECTION_SIZE + DTB_SIZE,
3181             'u-boot-tpl-dtb:size': DTB_SIZE,
3182             'fdtmap:offset': SECTION_SIZE + DTB_SIZE * 2,
3183             'fdtmap:size': FDTMAP_SIZE,
3184             'fdtmap:image-pos': SECTION_SIZE + DTB_SIZE * 2,
3185         }
3186         main_expected = {
3187             'section:orig-size': SECTION_SIZE,
3188             'section/u-boot-dtb:orig-offset': 4,
3189         }
3190
3191         # We expect three device-tree files in the output, with the first one
3192         # within a fixed-size section.
3193         # Read them in sequence. We look for an 'spl' property in the SPL tree,
3194         # and 'tpl' in the TPL tree, to make sure they are distinct from the
3195         # main U-Boot tree. All three should have the same positions and offset
3196         # except that the main tree should include the main_expected properties
3197         start = 4
3198         for item in ['', 'spl', 'tpl', None]:
3199             if item is None:
3200                 start += 16  # Move past fdtmap header
3201             dtb = fdt.Fdt.FromData(data[start:])
3202             dtb.Scan()
3203             props = self._GetPropTree(dtb,
3204                 BASE_DTB_PROPS + REPACK_DTB_PROPS + ['spl', 'tpl'],
3205                 prefix='/' if item is None else '/binman/')
3206             expected = dict(base_expected)
3207             if item:
3208                 expected[item] = 0
3209             else:
3210                 # Main DTB and fdtdec should include the 'orig-' properties
3211                 expected.update(main_expected)
3212             # Helpful for debugging:
3213             #for prop in sorted(props):
3214                 #print('prop %s %s %s' % (prop, props[prop], expected[prop]))
3215             self.assertEqual(expected, props)
3216             if item == '':
3217                 start = SECTION_SIZE
3218             else:
3219                 start += dtb._fdt_obj.totalsize()
3220
3221     def testFdtmapHeaderMiddle(self):
3222         """Test an FDT map in the middle of an image when it should be at end"""
3223         with self.assertRaises(ValueError) as e:
3224             self._DoReadFileRealDtb('135_fdtmap_hdr_middle.dts')
3225         self.assertIn("Invalid sibling order 'middle' for image-header: Must be at 'end' to match location",
3226                       str(e.exception))
3227
3228     def testFdtmapHeaderStartBad(self):
3229         """Test an FDT map in middle of an image when it should be at start"""
3230         with self.assertRaises(ValueError) as e:
3231             self._DoReadFileRealDtb('136_fdtmap_hdr_startbad.dts')
3232         self.assertIn("Invalid sibling order 'end' for image-header: Must be at 'start' to match location",
3233                       str(e.exception))
3234
3235     def testFdtmapHeaderEndBad(self):
3236         """Test an FDT map at the start of an image when it should be at end"""
3237         with self.assertRaises(ValueError) as e:
3238             self._DoReadFileRealDtb('137_fdtmap_hdr_endbad.dts')
3239         self.assertIn("Invalid sibling order 'start' for image-header: Must be at 'end' to match location",
3240                       str(e.exception))
3241
3242     def testFdtmapHeaderNoSize(self):
3243         """Test an image header at the end of an image with undefined size"""
3244         self._DoReadFileRealDtb('138_fdtmap_hdr_nosize.dts')
3245
3246     def testReplaceResize(self):
3247         """Test replacing a single file in an entry with a larger file"""
3248         expected = U_BOOT_DATA + b'x'
3249         data, _, image = self._RunReplaceCmd('u-boot', expected,
3250                                              dts='139_replace_repack.dts')
3251         self.assertEqual(expected, data)
3252
3253         entries = image.GetEntries()
3254         dtb_data = entries['u-boot-dtb'].data
3255         dtb = fdt.Fdt.FromData(dtb_data)
3256         dtb.Scan()
3257
3258         # The u-boot section should now be larger in the dtb
3259         node = dtb.GetNode('/binman/u-boot')
3260         self.assertEqual(len(expected), fdt_util.GetInt(node, 'size'))
3261
3262         # Same for the fdtmap
3263         fdata = entries['fdtmap'].data
3264         fdtb = fdt.Fdt.FromData(fdata[fdtmap.FDTMAP_HDR_LEN:])
3265         fdtb.Scan()
3266         fnode = fdtb.GetNode('/u-boot')
3267         self.assertEqual(len(expected), fdt_util.GetInt(fnode, 'size'))
3268
3269     def testReplaceResizeNoRepack(self):
3270         """Test replacing an entry with a larger file when not allowed"""
3271         expected = U_BOOT_DATA + b'x'
3272         with self.assertRaises(ValueError) as e:
3273             self._RunReplaceCmd('u-boot', expected)
3274         self.assertIn('Entry data size does not match, but allow-repack is not present for this image',
3275                       str(e.exception))
3276
3277     def testEntryShrink(self):
3278         """Test contracting an entry after it is packed"""
3279         try:
3280             state.SetAllowEntryContraction(True)
3281             data = self._DoReadFileDtb('140_entry_shrink.dts',
3282                                        update_dtb=True)[0]
3283         finally:
3284             state.SetAllowEntryContraction(False)
3285         self.assertEqual(b'a', data[:1])
3286         self.assertEqual(U_BOOT_DATA, data[1:1 + len(U_BOOT_DATA)])
3287         self.assertEqual(b'a', data[-1:])
3288
3289     def testEntryShrinkFail(self):
3290         """Test not being allowed to contract an entry after it is packed"""
3291         data = self._DoReadFileDtb('140_entry_shrink.dts', update_dtb=True)[0]
3292
3293         # In this case there is a spare byte at the end of the data. The size of
3294         # the contents is only 1 byte but we still have the size before it
3295         # shrunk.
3296         self.assertEqual(b'a\0', data[:2])
3297         self.assertEqual(U_BOOT_DATA, data[2:2 + len(U_BOOT_DATA)])
3298         self.assertEqual(b'a\0', data[-2:])
3299
3300     def testDescriptorOffset(self):
3301         """Test that the Intel descriptor is always placed at at the start"""
3302         data = self._DoReadFileDtb('141_descriptor_offset.dts')
3303         image = control.images['image']
3304         entries = image.GetEntries()
3305         desc = entries['intel-descriptor']
3306         self.assertEqual(0xff800000, desc.offset);
3307         self.assertEqual(0xff800000, desc.image_pos);
3308
3309     def testReplaceCbfs(self):
3310         """Test replacing a single file in CBFS without changing the size"""
3311         self._CheckLz4()
3312         expected = b'x' * len(U_BOOT_DATA)
3313         data = self._DoReadFileRealDtb('142_replace_cbfs.dts')
3314         updated_fname = tools.GetOutputFilename('image-updated.bin')
3315         tools.WriteFile(updated_fname, data)
3316         entry_name = 'section/cbfs/u-boot'
3317         control.WriteEntry(updated_fname, entry_name, expected,
3318                            allow_resize=True)
3319         data = control.ReadEntry(updated_fname, entry_name)
3320         self.assertEqual(expected, data)
3321
3322     def testReplaceResizeCbfs(self):
3323         """Test replacing a single file in CBFS with one of a different size"""
3324         self._CheckLz4()
3325         expected = U_BOOT_DATA + b'x'
3326         data = self._DoReadFileRealDtb('142_replace_cbfs.dts')
3327         updated_fname = tools.GetOutputFilename('image-updated.bin')
3328         tools.WriteFile(updated_fname, data)
3329         entry_name = 'section/cbfs/u-boot'
3330         control.WriteEntry(updated_fname, entry_name, expected,
3331                            allow_resize=True)
3332         data = control.ReadEntry(updated_fname, entry_name)
3333         self.assertEqual(expected, data)
3334
3335     def _SetupForReplace(self):
3336         """Set up some files to use to replace entries
3337
3338         This generates an image, copies it to a new file, extracts all the files
3339         in it and updates some of them
3340
3341         Returns:
3342             List
3343                 Image filename
3344                 Output directory
3345                 Expected values for updated entries, each a string
3346         """
3347         data = self._DoReadFileRealDtb('143_replace_all.dts')
3348
3349         updated_fname = tools.GetOutputFilename('image-updated.bin')
3350         tools.WriteFile(updated_fname, data)
3351
3352         outdir = os.path.join(self._indir, 'extract')
3353         einfos = control.ExtractEntries(updated_fname, None, outdir, [])
3354
3355         expected1 = b'x' + U_BOOT_DATA + b'y'
3356         u_boot_fname1 = os.path.join(outdir, 'u-boot')
3357         tools.WriteFile(u_boot_fname1, expected1)
3358
3359         expected2 = b'a' + U_BOOT_DATA + b'b'
3360         u_boot_fname2 = os.path.join(outdir, 'u-boot2')
3361         tools.WriteFile(u_boot_fname2, expected2)
3362
3363         expected_text = b'not the same text'
3364         text_fname = os.path.join(outdir, 'text')
3365         tools.WriteFile(text_fname, expected_text)
3366
3367         dtb_fname = os.path.join(outdir, 'u-boot-dtb')
3368         dtb = fdt.FdtScan(dtb_fname)
3369         node = dtb.GetNode('/binman/text')
3370         node.AddString('my-property', 'the value')
3371         dtb.Sync(auto_resize=True)
3372         dtb.Flush()
3373
3374         return updated_fname, outdir, expected1, expected2, expected_text
3375
3376     def _CheckReplaceMultiple(self, entry_paths):
3377         """Handle replacing the contents of multiple entries
3378
3379         Args:
3380             entry_paths: List of entry paths to replace
3381
3382         Returns:
3383             List
3384                 Dict of entries in the image:
3385                     key: Entry name
3386                     Value: Entry object
3387             Expected values for updated entries, each a string
3388         """
3389         updated_fname, outdir, expected1, expected2, expected_text = (
3390             self._SetupForReplace())
3391         control.ReplaceEntries(updated_fname, None, outdir, entry_paths)
3392
3393         image = Image.FromFile(updated_fname)
3394         image.LoadData()
3395         return image.GetEntries(), expected1, expected2, expected_text
3396
3397     def testReplaceAll(self):
3398         """Test replacing the contents of all entries"""
3399         entries, expected1, expected2, expected_text = (
3400             self._CheckReplaceMultiple([]))
3401         data = entries['u-boot'].data
3402         self.assertEqual(expected1, data)
3403
3404         data = entries['u-boot2'].data
3405         self.assertEqual(expected2, data)
3406
3407         data = entries['text'].data
3408         self.assertEqual(expected_text, data)
3409
3410         # Check that the device tree is updated
3411         data = entries['u-boot-dtb'].data
3412         dtb = fdt.Fdt.FromData(data)
3413         dtb.Scan()
3414         node = dtb.GetNode('/binman/text')
3415         self.assertEqual('the value', node.props['my-property'].value)
3416
3417     def testReplaceSome(self):
3418         """Test replacing the contents of a few entries"""
3419         entries, expected1, expected2, expected_text = (
3420             self._CheckReplaceMultiple(['u-boot2', 'text']))
3421
3422         # This one should not change
3423         data = entries['u-boot'].data
3424         self.assertEqual(U_BOOT_DATA, data)
3425
3426         data = entries['u-boot2'].data
3427         self.assertEqual(expected2, data)
3428
3429         data = entries['text'].data
3430         self.assertEqual(expected_text, data)
3431
3432     def testReplaceCmd(self):
3433         """Test replacing a file fron an image on the command line"""
3434         self._DoReadFileRealDtb('143_replace_all.dts')
3435
3436         try:
3437             tmpdir, updated_fname = self._SetupImageInTmpdir()
3438
3439             fname = os.path.join(tmpdir, 'update-u-boot.bin')
3440             expected = b'x' * len(U_BOOT_DATA)
3441             tools.WriteFile(fname, expected)
3442
3443             self._DoBinman('replace', '-i', updated_fname, 'u-boot', '-f', fname)
3444             data = tools.ReadFile(updated_fname)
3445             self.assertEqual(expected, data[:len(expected)])
3446             map_fname = os.path.join(tmpdir, 'image-updated.map')
3447             self.assertFalse(os.path.exists(map_fname))
3448         finally:
3449             shutil.rmtree(tmpdir)
3450
3451     def testReplaceCmdSome(self):
3452         """Test replacing some files fron an image on the command line"""
3453         updated_fname, outdir, expected1, expected2, expected_text = (
3454             self._SetupForReplace())
3455
3456         self._DoBinman('replace', '-i', updated_fname, '-I', outdir,
3457                        'u-boot2', 'text')
3458
3459         tools.PrepareOutputDir(None)
3460         image = Image.FromFile(updated_fname)
3461         image.LoadData()
3462         entries = image.GetEntries()
3463
3464         # This one should not change
3465         data = entries['u-boot'].data
3466         self.assertEqual(U_BOOT_DATA, data)
3467
3468         data = entries['u-boot2'].data
3469         self.assertEqual(expected2, data)
3470
3471         data = entries['text'].data
3472         self.assertEqual(expected_text, data)
3473
3474     def testReplaceMissing(self):
3475         """Test replacing entries where the file is missing"""
3476         updated_fname, outdir, expected1, expected2, expected_text = (
3477             self._SetupForReplace())
3478
3479         # Remove one of the files, to generate a warning
3480         u_boot_fname1 = os.path.join(outdir, 'u-boot')
3481         os.remove(u_boot_fname1)
3482
3483         with test_util.capture_sys_output() as (stdout, stderr):
3484             control.ReplaceEntries(updated_fname, None, outdir, [])
3485         self.assertIn("Skipping entry '/u-boot' from missing file",
3486                       stderr.getvalue())
3487
3488     def testReplaceCmdMap(self):
3489         """Test replacing a file fron an image on the command line"""
3490         self._DoReadFileRealDtb('143_replace_all.dts')
3491
3492         try:
3493             tmpdir, updated_fname = self._SetupImageInTmpdir()
3494
3495             fname = os.path.join(self._indir, 'update-u-boot.bin')
3496             expected = b'x' * len(U_BOOT_DATA)
3497             tools.WriteFile(fname, expected)
3498
3499             self._DoBinman('replace', '-i', updated_fname, 'u-boot',
3500                            '-f', fname, '-m')
3501             map_fname = os.path.join(tmpdir, 'image-updated.map')
3502             self.assertTrue(os.path.exists(map_fname))
3503         finally:
3504             shutil.rmtree(tmpdir)
3505
3506     def testReplaceNoEntryPaths(self):
3507         """Test replacing an entry without an entry path"""
3508         self._DoReadFileRealDtb('143_replace_all.dts')
3509         image_fname = tools.GetOutputFilename('image.bin')
3510         with self.assertRaises(ValueError) as e:
3511             control.ReplaceEntries(image_fname, 'fname', None, [])
3512         self.assertIn('Must specify an entry path to read with -f',
3513                       str(e.exception))
3514
3515     def testReplaceTooManyEntryPaths(self):
3516         """Test extracting some entries"""
3517         self._DoReadFileRealDtb('143_replace_all.dts')
3518         image_fname = tools.GetOutputFilename('image.bin')
3519         with self.assertRaises(ValueError) as e:
3520             control.ReplaceEntries(image_fname, 'fname', None, ['a', 'b'])
3521         self.assertIn('Must specify exactly one entry path to write with -f',
3522                       str(e.exception))
3523
3524     def testPackReset16(self):
3525         """Test that an image with an x86 reset16 region can be created"""
3526         data = self._DoReadFile('144_x86_reset16.dts')
3527         self.assertEqual(X86_RESET16_DATA, data[:len(X86_RESET16_DATA)])
3528
3529     def testPackReset16Spl(self):
3530         """Test that an image with an x86 reset16-spl region can be created"""
3531         data = self._DoReadFile('145_x86_reset16_spl.dts')
3532         self.assertEqual(X86_RESET16_SPL_DATA, data[:len(X86_RESET16_SPL_DATA)])
3533
3534     def testPackReset16Tpl(self):
3535         """Test that an image with an x86 reset16-tpl region can be created"""
3536         data = self._DoReadFile('146_x86_reset16_tpl.dts')
3537         self.assertEqual(X86_RESET16_TPL_DATA, data[:len(X86_RESET16_TPL_DATA)])
3538
3539     def testPackIntelFit(self):
3540         """Test that an image with an Intel FIT and pointer can be created"""
3541         data = self._DoReadFile('147_intel_fit.dts')
3542         self.assertEqual(U_BOOT_DATA, data[:len(U_BOOT_DATA)])
3543         fit = data[16:32];
3544         self.assertEqual(b'_FIT_   \x01\x00\x00\x00\x00\x01\x80}' , fit)
3545         ptr = struct.unpack('<i', data[0x40:0x44])[0]
3546
3547         image = control.images['image']
3548         entries = image.GetEntries()
3549         expected_ptr = entries['intel-fit'].image_pos - (1 << 32)
3550         self.assertEqual(expected_ptr, ptr)
3551
3552     def testPackIntelFitMissing(self):
3553         """Test detection of a FIT pointer with not FIT region"""
3554         with self.assertRaises(ValueError) as e:
3555             self._DoReadFile('148_intel_fit_missing.dts')
3556         self.assertIn("'intel-fit-ptr' section must have an 'intel-fit' sibling",
3557                       str(e.exception))
3558
3559     def _CheckSymbolsTplSection(self, dts, expected_vals):
3560         data = self._DoReadFile(dts)
3561         sym_values = struct.pack('<LQLL', *expected_vals)
3562         upto1 = 4 + len(U_BOOT_SPL_DATA)
3563         expected1 = tools.GetBytes(0xff, 4) + sym_values + U_BOOT_SPL_DATA[20:]
3564         self.assertEqual(expected1, data[:upto1])
3565
3566         upto2 = upto1 + 1 + len(U_BOOT_SPL_DATA)
3567         expected2 = tools.GetBytes(0xff, 1) + sym_values + U_BOOT_SPL_DATA[20:]
3568         self.assertEqual(expected2, data[upto1:upto2])
3569
3570         upto3 = 0x34 + len(U_BOOT_DATA)
3571         expected3 = tools.GetBytes(0xff, 1) + U_BOOT_DATA
3572         self.assertEqual(expected3, data[upto2:upto3])
3573
3574         expected4 = sym_values + U_BOOT_TPL_DATA[20:]
3575         self.assertEqual(expected4, data[upto3:upto3 + len(U_BOOT_TPL_DATA)])
3576
3577     def testSymbolsTplSection(self):
3578         """Test binman can assign symbols embedded in U-Boot TPL in a section"""
3579         self._SetupSplElf('u_boot_binman_syms')
3580         self._SetupTplElf('u_boot_binman_syms')
3581         self._CheckSymbolsTplSection('149_symbols_tpl.dts',
3582                                      [0x04, 0x1c, 0x10 + 0x34, 0x04])
3583
3584     def testSymbolsTplSectionX86(self):
3585         """Test binman can assign symbols in a section with end-at-4gb"""
3586         self._SetupSplElf('u_boot_binman_syms_x86')
3587         self._SetupTplElf('u_boot_binman_syms_x86')
3588         self._CheckSymbolsTplSection('155_symbols_tpl_x86.dts',
3589                                      [0xffffff04, 0xffffff1c, 0xffffff34,
3590                                       0x04])
3591
3592     def testPackX86RomIfwiSectiom(self):
3593         """Test that a section can be placed in an IFWI region"""
3594         self._SetupIfwi('fitimage.bin')
3595         data = self._DoReadFile('151_x86_rom_ifwi_section.dts')
3596         self._CheckIfwi(data)
3597
3598     def testPackFspM(self):
3599         """Test that an image with a FSP memory-init binary can be created"""
3600         data = self._DoReadFile('152_intel_fsp_m.dts')
3601         self.assertEqual(FSP_M_DATA, data[:len(FSP_M_DATA)])
3602
3603     def testPackFspS(self):
3604         """Test that an image with a FSP silicon-init binary can be created"""
3605         data = self._DoReadFile('153_intel_fsp_s.dts')
3606         self.assertEqual(FSP_S_DATA, data[:len(FSP_S_DATA)])
3607
3608     def testPackFspT(self):
3609         """Test that an image with a FSP temp-ram-init binary can be created"""
3610         data = self._DoReadFile('154_intel_fsp_t.dts')
3611         self.assertEqual(FSP_T_DATA, data[:len(FSP_T_DATA)])
3612
3613     def testMkimage(self):
3614         """Test using mkimage to build an image"""
3615         data = self._DoReadFile('156_mkimage.dts')
3616
3617         # Just check that the data appears in the file somewhere
3618         self.assertIn(U_BOOT_SPL_DATA, data)
3619
3620     def testExtblob(self):
3621         """Test an image with an external blob"""
3622         data = self._DoReadFile('157_blob_ext.dts')
3623         self.assertEqual(REFCODE_DATA, data)
3624
3625     def testExtblobMissing(self):
3626         """Test an image with a missing external blob"""
3627         with self.assertRaises(ValueError) as e:
3628             self._DoReadFile('158_blob_ext_missing.dts')
3629         self.assertIn("Filename 'missing-file' not found in input path",
3630                       str(e.exception))
3631
3632     def testExtblobMissingOk(self):
3633         """Test an image with an missing external blob that is allowed"""
3634         with test_util.capture_sys_output() as (stdout, stderr):
3635             self._DoTestFile('158_blob_ext_missing.dts', allow_missing=True)
3636         err = stderr.getvalue()
3637         self.assertRegex(err, "Image 'main-section'.*missing.*: blob-ext")
3638
3639     def testExtblobMissingOkSect(self):
3640         """Test an image with an missing external blob that is allowed"""
3641         with test_util.capture_sys_output() as (stdout, stderr):
3642             self._DoTestFile('159_blob_ext_missing_sect.dts',
3643                              allow_missing=True)
3644         err = stderr.getvalue()
3645         self.assertRegex(err, "Image 'main-section'.*missing.*: "
3646                          "blob-ext blob-ext2")
3647
3648     def testPackX86RomMeMissingDesc(self):
3649         """Test that an missing Intel descriptor entry is allowed"""
3650         with test_util.capture_sys_output() as (stdout, stderr):
3651             self._DoTestFile('164_x86_rom_me_missing.dts', allow_missing=True)
3652         err = stderr.getvalue()
3653         self.assertRegex(err,
3654                          "Image 'main-section'.*missing.*: intel-descriptor")
3655
3656     def testPackX86RomMissingIfwi(self):
3657         """Test that an x86 ROM with Integrated Firmware Image can be created"""
3658         self._SetupIfwi('fitimage.bin')
3659         pathname = os.path.join(self._indir, 'fitimage.bin')
3660         os.remove(pathname)
3661         with test_util.capture_sys_output() as (stdout, stderr):
3662             self._DoTestFile('111_x86_rom_ifwi.dts', allow_missing=True)
3663         err = stderr.getvalue()
3664         self.assertRegex(err, "Image 'main-section'.*missing.*: intel-ifwi")
3665
3666     def testPackOverlap(self):
3667         """Test that zero-size overlapping regions are ignored"""
3668         self._DoTestFile('160_pack_overlap_zero.dts')
3669
3670     def testSimpleFit(self):
3671         """Test an image with a FIT inside"""
3672         data = self._DoReadFile('161_fit.dts')
3673         self.assertEqual(U_BOOT_DATA, data[:len(U_BOOT_DATA)])
3674         self.assertEqual(U_BOOT_NODTB_DATA, data[-len(U_BOOT_NODTB_DATA):])
3675         fit_data = data[len(U_BOOT_DATA):-len(U_BOOT_NODTB_DATA)]
3676
3677         # The data should be inside the FIT
3678         dtb = fdt.Fdt.FromData(fit_data)
3679         dtb.Scan()
3680         fnode = dtb.GetNode('/images/kernel')
3681         self.assertIn('data', fnode.props)
3682
3683         fname = os.path.join(self._indir, 'fit_data.fit')
3684         tools.WriteFile(fname, fit_data)
3685         out = tools.Run('dumpimage', '-l', fname)
3686
3687         # Check a few features to make sure the plumbing works. We don't need
3688         # to test the operation of mkimage or dumpimage here. First convert the
3689         # output into a dict where the keys are the fields printed by dumpimage
3690         # and the values are a list of values for each field
3691         lines = out.splitlines()
3692
3693         # Converts "Compression:  gzip compressed" into two groups:
3694         # 'Compression' and 'gzip compressed'
3695         re_line = re.compile(r'^ *([^:]*)(?:: *(.*))?$')
3696         vals = collections.defaultdict(list)
3697         for line in lines:
3698             mat = re_line.match(line)
3699             vals[mat.group(1)].append(mat.group(2))
3700
3701         self.assertEquals('FIT description: test-desc', lines[0])
3702         self.assertIn('Created:', lines[1])
3703         self.assertIn('Image 0 (kernel)', vals)
3704         self.assertIn('Hash value', vals)
3705         data_sizes = vals.get('Data Size')
3706         self.assertIsNotNone(data_sizes)
3707         self.assertEqual(2, len(data_sizes))
3708         # Format is "4 Bytes = 0.00 KiB = 0.00 MiB" so take the first word
3709         self.assertEqual(len(U_BOOT_DATA), int(data_sizes[0].split()[0]))
3710         self.assertEqual(len(U_BOOT_SPL_DTB_DATA), int(data_sizes[1].split()[0]))
3711
3712     def testFitExternal(self):
3713         """Test an image with an FIT with external images"""
3714         data = self._DoReadFile('162_fit_external.dts')
3715         fit_data = data[len(U_BOOT_DATA):-2]  # _testing is 2 bytes
3716
3717         # Size of the external-data region as set up by mkimage
3718         external_data_size = len(U_BOOT_DATA) + 2
3719         expected_size = (len(U_BOOT_DATA) + 0x400 +
3720                          tools.Align(external_data_size, 4) +
3721                          len(U_BOOT_NODTB_DATA))
3722
3723         # The data should be outside the FIT
3724         dtb = fdt.Fdt.FromData(fit_data)
3725         dtb.Scan()
3726         fnode = dtb.GetNode('/images/kernel')
3727         self.assertNotIn('data', fnode.props)
3728         self.assertEqual(len(U_BOOT_DATA),
3729                          fdt_util.fdt32_to_cpu(fnode.props['data-size'].value))
3730         fit_pos = 0x400;
3731         self.assertEqual(
3732             fit_pos,
3733             fdt_util.fdt32_to_cpu(fnode.props['data-position'].value))
3734
3735         self.assertEquals(expected_size, len(data))
3736         actual_pos = len(U_BOOT_DATA) + fit_pos
3737         self.assertEqual(U_BOOT_DATA + b'aa',
3738                          data[actual_pos:actual_pos + external_data_size])
3739
3740     def testSectionIgnoreHashSignature(self):
3741         """Test that sections ignore hash, signature nodes for its data"""
3742         data = self._DoReadFile('165_section_ignore_hash_signature.dts')
3743         expected = (U_BOOT_DATA + U_BOOT_DATA)
3744         self.assertEqual(expected, data)
3745
3746     def testPadInSections(self):
3747         """Test pad-before, pad-after for entries in sections"""
3748         data, _, _, out_dtb_fname = self._DoReadFileDtb(
3749             '166_pad_in_sections.dts', update_dtb=True)
3750         expected = (U_BOOT_DATA + tools.GetBytes(ord('!'), 12) +
3751                     U_BOOT_DATA + tools.GetBytes(ord('!'), 6) +
3752                     U_BOOT_DATA)
3753         self.assertEqual(expected, data)
3754
3755         dtb = fdt.Fdt(out_dtb_fname)
3756         dtb.Scan()
3757         props = self._GetPropTree(dtb, ['size', 'image-pos', 'offset'])
3758         expected = {
3759             'image-pos': 0,
3760             'offset': 0,
3761             'size': 12 + 6 + 3 * len(U_BOOT_DATA),
3762
3763             'section:image-pos': 0,
3764             'section:offset': 0,
3765             'section:size': 12 + 6 + 3 * len(U_BOOT_DATA),
3766
3767             'section/before:image-pos': 0,
3768             'section/before:offset': 0,
3769             'section/before:size': len(U_BOOT_DATA),
3770
3771             'section/u-boot:image-pos': 4,
3772             'section/u-boot:offset': 4,
3773             'section/u-boot:size': 12 + len(U_BOOT_DATA) + 6,
3774
3775             'section/after:image-pos': 26,
3776             'section/after:offset': 26,
3777             'section/after:size': len(U_BOOT_DATA),
3778             }
3779         self.assertEqual(expected, props)
3780
3781     def testFitImageSubentryAlignment(self):
3782         """Test relative alignability of FIT image subentries"""
3783         entry_args = {
3784             'test-id': TEXT_DATA,
3785         }
3786         data, _, _, _ = self._DoReadFileDtb('167_fit_image_subentry_alignment.dts',
3787                                             entry_args=entry_args)
3788         dtb = fdt.Fdt.FromData(data)
3789         dtb.Scan()
3790
3791         node = dtb.GetNode('/images/kernel')
3792         data = dtb.GetProps(node)["data"].bytes
3793         align_pad = 0x10 - (len(U_BOOT_SPL_DATA) % 0x10)
3794         expected = (tools.GetBytes(0, 0x20) + U_BOOT_SPL_DATA +
3795                     tools.GetBytes(0, align_pad) + U_BOOT_DATA)
3796         self.assertEqual(expected, data)
3797
3798         node = dtb.GetNode('/images/fdt-1')
3799         data = dtb.GetProps(node)["data"].bytes
3800         expected = (U_BOOT_SPL_DTB_DATA + tools.GetBytes(0, 20) +
3801                     tools.ToBytes(TEXT_DATA) + tools.GetBytes(0, 30) +
3802                     U_BOOT_DTB_DATA)
3803         self.assertEqual(expected, data)
3804
3805     def testFitExtblobMissingOk(self):
3806         """Test a FIT with a missing external blob that is allowed"""
3807         with test_util.capture_sys_output() as (stdout, stderr):
3808             self._DoTestFile('168_fit_missing_blob.dts',
3809                              allow_missing=True)
3810         err = stderr.getvalue()
3811         self.assertRegex(err, "Image 'main-section'.*missing.*: atf-bl31")
3812
3813     def testBlobNamedByArgMissing(self):
3814         """Test handling of a missing entry arg"""
3815         with self.assertRaises(ValueError) as e:
3816             self._DoReadFile('068_blob_named_by_arg.dts')
3817         self.assertIn("Missing required properties/entry args: cros-ec-rw-path",
3818                       str(e.exception))
3819
3820     def testPackBl31(self):
3821         """Test that an image with an ATF BL31 binary can be created"""
3822         data = self._DoReadFile('169_atf_bl31.dts')
3823         self.assertEqual(ATF_BL31_DATA, data[:len(ATF_BL31_DATA)])
3824
3825     def testPackScp(self):
3826         """Test that an image with an SCP binary can be created"""
3827         data = self._DoReadFile('172_scp.dts')
3828         self.assertEqual(SCP_DATA, data[:len(SCP_DATA)])
3829
3830     def testFitFdt(self):
3831         """Test an image with an FIT with multiple FDT images"""
3832         def _CheckFdt(seq, expected_data):
3833             """Check the FDT nodes
3834
3835             Args:
3836                 seq: Sequence number to check (0 or 1)
3837                 expected_data: Expected contents of 'data' property
3838             """
3839             name = 'fdt-%d' % seq
3840             fnode = dtb.GetNode('/images/%s' % name)
3841             self.assertIsNotNone(fnode)
3842             self.assertEqual({'description','type', 'compression', 'data'},
3843                              set(fnode.props.keys()))
3844             self.assertEqual(expected_data, fnode.props['data'].bytes)
3845             self.assertEqual('fdt-test-fdt%d.dtb' % seq,
3846                              fnode.props['description'].value)
3847
3848         def _CheckConfig(seq, expected_data):
3849             """Check the configuration nodes
3850
3851             Args:
3852                 seq: Sequence number to check (0 or 1)
3853                 expected_data: Expected contents of 'data' property
3854             """
3855             cnode = dtb.GetNode('/configurations')
3856             self.assertIn('default', cnode.props)
3857             self.assertEqual('config-2', cnode.props['default'].value)
3858
3859             name = 'config-%d' % seq
3860             fnode = dtb.GetNode('/configurations/%s' % name)
3861             self.assertIsNotNone(fnode)
3862             self.assertEqual({'description','firmware', 'loadables', 'fdt'},
3863                              set(fnode.props.keys()))
3864             self.assertEqual('conf-test-fdt%d.dtb' % seq,
3865                              fnode.props['description'].value)
3866             self.assertEqual('fdt-%d' % seq, fnode.props['fdt'].value)
3867
3868         entry_args = {
3869             'of-list': 'test-fdt1 test-fdt2',
3870             'default-dt': 'test-fdt2',
3871         }
3872         data = self._DoReadFileDtb(
3873             '170_fit_fdt.dts',
3874             entry_args=entry_args,
3875             extra_indirs=[os.path.join(self._indir, TEST_FDT_SUBDIR)])[0]
3876         self.assertEqual(U_BOOT_NODTB_DATA, data[-len(U_BOOT_NODTB_DATA):])
3877         fit_data = data[len(U_BOOT_DATA):-len(U_BOOT_NODTB_DATA)]
3878
3879         dtb = fdt.Fdt.FromData(fit_data)
3880         dtb.Scan()
3881         fnode = dtb.GetNode('/images/kernel')
3882         self.assertIn('data', fnode.props)
3883
3884         # Check all the properties in fdt-1 and fdt-2
3885         _CheckFdt(1, TEST_FDT1_DATA)
3886         _CheckFdt(2, TEST_FDT2_DATA)
3887
3888         # Check configurations
3889         _CheckConfig(1, TEST_FDT1_DATA)
3890         _CheckConfig(2, TEST_FDT2_DATA)
3891
3892     def testFitFdtMissingList(self):
3893         """Test handling of a missing 'of-list' entry arg"""
3894         with self.assertRaises(ValueError) as e:
3895             self._DoReadFile('170_fit_fdt.dts')
3896         self.assertIn("Generator node requires 'of-list' entry argument",
3897                       str(e.exception))
3898
3899     def testFitFdtEmptyList(self):
3900         """Test handling of an empty 'of-list' entry arg"""
3901         entry_args = {
3902             'of-list': '',
3903         }
3904         data = self._DoReadFileDtb('170_fit_fdt.dts', entry_args=entry_args)[0]
3905
3906     def testFitFdtMissingProp(self):
3907         """Test handling of a missing 'fit,fdt-list' property"""
3908         with self.assertRaises(ValueError) as e:
3909             self._DoReadFile('171_fit_fdt_missing_prop.dts')
3910         self.assertIn("Generator node requires 'fit,fdt-list' property",
3911                       str(e.exception))
3912
3913     def testFitFdtEmptyList(self):
3914         """Test handling of an empty 'of-list' entry arg"""
3915         entry_args = {
3916             'of-list': '',
3917         }
3918         data = self._DoReadFileDtb('170_fit_fdt.dts', entry_args=entry_args)[0]
3919
3920     def testFitFdtMissing(self):
3921         """Test handling of a missing 'default-dt' entry arg"""
3922         entry_args = {
3923             'of-list': 'test-fdt1 test-fdt2',
3924         }
3925         with self.assertRaises(ValueError) as e:
3926             self._DoReadFileDtb(
3927                 '170_fit_fdt.dts',
3928                 entry_args=entry_args,
3929                 extra_indirs=[os.path.join(self._indir, TEST_FDT_SUBDIR)])[0]
3930         self.assertIn("Generated 'default' node requires default-dt entry argument",
3931                       str(e.exception))
3932
3933     def testFitFdtNotInList(self):
3934         """Test handling of a default-dt that is not in the of-list"""
3935         entry_args = {
3936             'of-list': 'test-fdt1 test-fdt2',
3937             'default-dt': 'test-fdt3',
3938         }
3939         with self.assertRaises(ValueError) as e:
3940             self._DoReadFileDtb(
3941                 '170_fit_fdt.dts',
3942                 entry_args=entry_args,
3943                 extra_indirs=[os.path.join(self._indir, TEST_FDT_SUBDIR)])[0]
3944         self.assertIn("default-dt entry argument 'test-fdt3' not found in fdt list: test-fdt1, test-fdt2",
3945                       str(e.exception))
3946
3947     def testFitExtblobMissingHelp(self):
3948         """Test display of help messages when an external blob is missing"""
3949         control.missing_blob_help = control._ReadMissingBlobHelp()
3950         control.missing_blob_help['wibble'] = 'Wibble test'
3951         control.missing_blob_help['another'] = 'Another test'
3952         with test_util.capture_sys_output() as (stdout, stderr):
3953             self._DoTestFile('168_fit_missing_blob.dts',
3954                              allow_missing=True)
3955         err = stderr.getvalue()
3956
3957         # We can get the tag from the name, the type or the missing-msg
3958         # property. Check all three.
3959         self.assertIn('You may need to build ARM Trusted', err)
3960         self.assertIn('Wibble test', err)
3961         self.assertIn('Another test', err)
3962
3963     def testMissingBlob(self):
3964         """Test handling of a blob containing a missing file"""
3965         with self.assertRaises(ValueError) as e:
3966             self._DoTestFile('173_missing_blob.dts', allow_missing=True)
3967         self.assertIn("Filename 'missing' not found in input path",
3968                       str(e.exception))
3969
3970     def testEnvironment(self):
3971         """Test adding a U-Boot environment"""
3972         data = self._DoReadFile('174_env.dts')
3973         self.assertEqual(U_BOOT_DATA, data[:len(U_BOOT_DATA)])
3974         self.assertEqual(U_BOOT_NODTB_DATA, data[-len(U_BOOT_NODTB_DATA):])
3975         env = data[len(U_BOOT_DATA):-len(U_BOOT_NODTB_DATA)]
3976         self.assertEqual(b'\x1b\x97\x22\x7c\x01var1=1\0var2="2"\0\0\xff\xff',
3977                          env)
3978
3979     def testEnvironmentNoSize(self):
3980         """Test that a missing 'size' property is detected"""
3981         with self.assertRaises(ValueError) as e:
3982             self._DoTestFile('175_env_no_size.dts')
3983         self.assertIn("'u-boot-env' entry must have a size property",
3984                       str(e.exception))
3985
3986     def testEnvironmentTooSmall(self):
3987         """Test handling of an environment that does not fit"""
3988         with self.assertRaises(ValueError) as e:
3989             self._DoTestFile('176_env_too_small.dts')
3990
3991         # checksum, start byte, environment with \0 terminator, final \0
3992         need = 4 + 1 + len(ENV_DATA) + 1 + 1
3993         short = need - 0x8
3994         self.assertIn("too small to hold data (need %#x more bytes)" % short,
3995                       str(e.exception))
3996
3997     def testSkipAtStart(self):
3998         """Test handling of skip-at-start section"""
3999         data = self._DoReadFile('177_skip_at_start.dts')
4000         self.assertEqual(U_BOOT_DATA, data)
4001
4002         image = control.images['image']
4003         entries = image.GetEntries()
4004         section = entries['section']
4005         self.assertEqual(0, section.offset)
4006         self.assertEqual(len(U_BOOT_DATA), section.size)
4007         self.assertEqual(U_BOOT_DATA, section.GetData())
4008
4009         entry = section.GetEntries()['u-boot']
4010         self.assertEqual(16, entry.offset)
4011         self.assertEqual(len(U_BOOT_DATA), entry.size)
4012         self.assertEqual(U_BOOT_DATA, entry.data)
4013
4014     def testSkipAtStartPad(self):
4015         """Test handling of skip-at-start section with padded entry"""
4016         data = self._DoReadFile('178_skip_at_start_pad.dts')
4017         before = tools.GetBytes(0, 8)
4018         after = tools.GetBytes(0, 4)
4019         all = before + U_BOOT_DATA + after
4020         self.assertEqual(all, data)
4021
4022         image = control.images['image']
4023         entries = image.GetEntries()
4024         section = entries['section']
4025         self.assertEqual(0, section.offset)
4026         self.assertEqual(len(all), section.size)
4027         self.assertEqual(all, section.GetData())
4028
4029         entry = section.GetEntries()['u-boot']
4030         self.assertEqual(16, entry.offset)
4031         self.assertEqual(len(all), entry.size)
4032         self.assertEqual(U_BOOT_DATA, entry.data)
4033
4034     def testSkipAtStartSectionPad(self):
4035         """Test handling of skip-at-start section with padding"""
4036         data = self._DoReadFile('179_skip_at_start_section_pad.dts')
4037         before = tools.GetBytes(0, 8)
4038         after = tools.GetBytes(0, 4)
4039         all = before + U_BOOT_DATA + after
4040         self.assertEqual(all, data)
4041
4042         image = control.images['image']
4043         entries = image.GetEntries()
4044         section = entries['section']
4045         self.assertEqual(0, section.offset)
4046         self.assertEqual(len(all), section.size)
4047         self.assertEqual(U_BOOT_DATA, section.data)
4048         self.assertEqual(all, section.GetPaddedData())
4049
4050         entry = section.GetEntries()['u-boot']
4051         self.assertEqual(16, entry.offset)
4052         self.assertEqual(len(U_BOOT_DATA), entry.size)
4053         self.assertEqual(U_BOOT_DATA, entry.data)
4054
4055     def testSectionPad(self):
4056         """Testing padding with sections"""
4057         data = self._DoReadFile('180_section_pad.dts')
4058         expected = (tools.GetBytes(ord('&'), 3) +
4059                     tools.GetBytes(ord('!'), 5) +
4060                     U_BOOT_DATA +
4061                     tools.GetBytes(ord('!'), 1) +
4062                     tools.GetBytes(ord('&'), 2))
4063         self.assertEqual(expected, data)
4064
4065     def testSectionAlign(self):
4066         """Testing alignment with sections"""
4067         data = self._DoReadFileDtb('181_section_align.dts', map=True)[0]
4068         expected = (b'\0' +                         # fill section
4069                     tools.GetBytes(ord('&'), 1) +   # padding to section align
4070                     b'\0' +                         # fill section
4071                     tools.GetBytes(ord('!'), 3) +   # padding to u-boot align
4072                     U_BOOT_DATA +
4073                     tools.GetBytes(ord('!'), 4) +   # padding to u-boot size
4074                     tools.GetBytes(ord('!'), 4))    # padding to section size
4075         self.assertEqual(expected, data)
4076
4077     def testCompressImage(self):
4078         """Test compression of the entire image"""
4079         self._CheckLz4()
4080         data, _, _, out_dtb_fname = self._DoReadFileDtb(
4081             '182_compress_image.dts', use_real_dtb=True, update_dtb=True)
4082         dtb = fdt.Fdt(out_dtb_fname)
4083         dtb.Scan()
4084         props = self._GetPropTree(dtb, ['offset', 'image-pos', 'size',
4085                                         'uncomp-size'])
4086         orig = self._decompress(data)
4087         self.assertEquals(COMPRESS_DATA + U_BOOT_DATA, orig)
4088
4089         # Do a sanity check on various fields
4090         image = control.images['image']
4091         entries = image.GetEntries()
4092         self.assertEqual(2, len(entries))
4093
4094         entry = entries['blob']
4095         self.assertEqual(COMPRESS_DATA, entry.data)
4096         self.assertEqual(len(COMPRESS_DATA), entry.size)
4097
4098         entry = entries['u-boot']
4099         self.assertEqual(U_BOOT_DATA, entry.data)
4100         self.assertEqual(len(U_BOOT_DATA), entry.size)
4101
4102         self.assertEqual(len(data), image.size)
4103         self.assertEqual(COMPRESS_DATA + U_BOOT_DATA, image.uncomp_data)
4104         self.assertEqual(len(COMPRESS_DATA + U_BOOT_DATA), image.uncomp_size)
4105         orig = self._decompress(image.data)
4106         self.assertEqual(orig, image.uncomp_data)
4107
4108         expected = {
4109             'blob:offset': 0,
4110             'blob:size': len(COMPRESS_DATA),
4111             'u-boot:offset': len(COMPRESS_DATA),
4112             'u-boot:size': len(U_BOOT_DATA),
4113             'uncomp-size': len(COMPRESS_DATA + U_BOOT_DATA),
4114             'offset': 0,
4115             'image-pos': 0,
4116             'size': len(data),
4117             }
4118         self.assertEqual(expected, props)
4119
4120     def testCompressImageLess(self):
4121         """Test compression where compression reduces the image size"""
4122         self._CheckLz4()
4123         data, _, _, out_dtb_fname = self._DoReadFileDtb(
4124             '183_compress_image_less.dts', use_real_dtb=True, update_dtb=True)
4125         dtb = fdt.Fdt(out_dtb_fname)
4126         dtb.Scan()
4127         props = self._GetPropTree(dtb, ['offset', 'image-pos', 'size',
4128                                         'uncomp-size'])
4129         orig = self._decompress(data)
4130
4131         self.assertEquals(COMPRESS_DATA + COMPRESS_DATA + U_BOOT_DATA, orig)
4132
4133         # Do a sanity check on various fields
4134         image = control.images['image']
4135         entries = image.GetEntries()
4136         self.assertEqual(2, len(entries))
4137
4138         entry = entries['blob']
4139         self.assertEqual(COMPRESS_DATA_BIG, entry.data)
4140         self.assertEqual(len(COMPRESS_DATA_BIG), entry.size)
4141
4142         entry = entries['u-boot']
4143         self.assertEqual(U_BOOT_DATA, entry.data)
4144         self.assertEqual(len(U_BOOT_DATA), entry.size)
4145
4146         self.assertEqual(len(data), image.size)
4147         self.assertEqual(COMPRESS_DATA_BIG + U_BOOT_DATA, image.uncomp_data)
4148         self.assertEqual(len(COMPRESS_DATA_BIG + U_BOOT_DATA),
4149                          image.uncomp_size)
4150         orig = self._decompress(image.data)
4151         self.assertEqual(orig, image.uncomp_data)
4152
4153         expected = {
4154             'blob:offset': 0,
4155             'blob:size': len(COMPRESS_DATA_BIG),
4156             'u-boot:offset': len(COMPRESS_DATA_BIG),
4157             'u-boot:size': len(U_BOOT_DATA),
4158             'uncomp-size': len(COMPRESS_DATA_BIG + U_BOOT_DATA),
4159             'offset': 0,
4160             'image-pos': 0,
4161             'size': len(data),
4162             }
4163         self.assertEqual(expected, props)
4164
4165     def testCompressSectionSize(self):
4166         """Test compression of a section with a fixed size"""
4167         self._CheckLz4()
4168         data, _, _, out_dtb_fname = self._DoReadFileDtb(
4169             '184_compress_section_size.dts', use_real_dtb=True, update_dtb=True)
4170         dtb = fdt.Fdt(out_dtb_fname)
4171         dtb.Scan()
4172         props = self._GetPropTree(dtb, ['offset', 'image-pos', 'size',
4173                                         'uncomp-size'])
4174         orig = self._decompress(data)
4175         self.assertEquals(COMPRESS_DATA + U_BOOT_DATA, orig)
4176         expected = {
4177             'section/blob:offset': 0,
4178             'section/blob:size': len(COMPRESS_DATA),
4179             'section/u-boot:offset': len(COMPRESS_DATA),
4180             'section/u-boot:size': len(U_BOOT_DATA),
4181             'section:offset': 0,
4182             'section:image-pos': 0,
4183             'section:uncomp-size': len(COMPRESS_DATA + U_BOOT_DATA),
4184             'section:size': 0x30,
4185             'offset': 0,
4186             'image-pos': 0,
4187             'size': 0x30,
4188             }
4189         self.assertEqual(expected, props)
4190
4191     def testCompressSection(self):
4192         """Test compression of a section with no fixed size"""
4193         self._CheckLz4()
4194         data, _, _, out_dtb_fname = self._DoReadFileDtb(
4195             '185_compress_section.dts', use_real_dtb=True, update_dtb=True)
4196         dtb = fdt.Fdt(out_dtb_fname)
4197         dtb.Scan()
4198         props = self._GetPropTree(dtb, ['offset', 'image-pos', 'size',
4199                                         'uncomp-size'])
4200         orig = self._decompress(data)
4201         self.assertEquals(COMPRESS_DATA + U_BOOT_DATA, orig)
4202         expected = {
4203             'section/blob:offset': 0,
4204             'section/blob:size': len(COMPRESS_DATA),
4205             'section/u-boot:offset': len(COMPRESS_DATA),
4206             'section/u-boot:size': len(U_BOOT_DATA),
4207             'section:offset': 0,
4208             'section:image-pos': 0,
4209             'section:uncomp-size': len(COMPRESS_DATA + U_BOOT_DATA),
4210             'section:size': len(data),
4211             'offset': 0,
4212             'image-pos': 0,
4213             'size': len(data),
4214             }
4215         self.assertEqual(expected, props)
4216
4217     def testCompressExtra(self):
4218         """Test compression of a section with no fixed size"""
4219         self._CheckLz4()
4220         data, _, _, out_dtb_fname = self._DoReadFileDtb(
4221             '186_compress_extra.dts', use_real_dtb=True, update_dtb=True)
4222         dtb = fdt.Fdt(out_dtb_fname)
4223         dtb.Scan()
4224         props = self._GetPropTree(dtb, ['offset', 'image-pos', 'size',
4225                                         'uncomp-size'])
4226
4227         base = data[len(U_BOOT_DATA):]
4228         self.assertEquals(U_BOOT_DATA, base[:len(U_BOOT_DATA)])
4229         rest = base[len(U_BOOT_DATA):]
4230
4231         # Check compressed data
4232         section1 = self._decompress(rest)
4233         expect1 = tools.Compress(COMPRESS_DATA + U_BOOT_DATA, 'lz4')
4234         self.assertEquals(expect1, rest[:len(expect1)])
4235         self.assertEquals(COMPRESS_DATA + U_BOOT_DATA, section1)
4236         rest1 = rest[len(expect1):]
4237
4238         section2 = self._decompress(rest1)
4239         expect2 = tools.Compress(COMPRESS_DATA + COMPRESS_DATA, 'lz4')
4240         self.assertEquals(expect2, rest1[:len(expect2)])
4241         self.assertEquals(COMPRESS_DATA + COMPRESS_DATA, section2)
4242         rest2 = rest1[len(expect2):]
4243
4244         expect_size = (len(U_BOOT_DATA) + len(U_BOOT_DATA) + len(expect1) +
4245                        len(expect2) + len(U_BOOT_DATA))
4246         #self.assertEquals(expect_size, len(data))
4247
4248         #self.assertEquals(U_BOOT_DATA, rest2)
4249
4250         self.maxDiff = None
4251         expected = {
4252             'u-boot:offset': 0,
4253             'u-boot:image-pos': 0,
4254             'u-boot:size': len(U_BOOT_DATA),
4255
4256             'base:offset': len(U_BOOT_DATA),
4257             'base:image-pos': len(U_BOOT_DATA),
4258             'base:size': len(data) - len(U_BOOT_DATA),
4259             'base/u-boot:offset': 0,
4260             'base/u-boot:image-pos': len(U_BOOT_DATA),
4261             'base/u-boot:size': len(U_BOOT_DATA),
4262             'base/u-boot2:offset': len(U_BOOT_DATA) + len(expect1) +
4263                 len(expect2),
4264             'base/u-boot2:image-pos': len(U_BOOT_DATA) * 2 + len(expect1) +
4265                 len(expect2),
4266             'base/u-boot2:size': len(U_BOOT_DATA),
4267
4268             'base/section:offset': len(U_BOOT_DATA),
4269             'base/section:image-pos': len(U_BOOT_DATA) * 2,
4270             'base/section:size': len(expect1),
4271             'base/section:uncomp-size': len(COMPRESS_DATA + U_BOOT_DATA),
4272             'base/section/blob:offset': 0,
4273             'base/section/blob:size': len(COMPRESS_DATA),
4274             'base/section/u-boot:offset': len(COMPRESS_DATA),
4275             'base/section/u-boot:size': len(U_BOOT_DATA),
4276
4277             'base/section2:offset': len(U_BOOT_DATA) + len(expect1),
4278             'base/section2:image-pos': len(U_BOOT_DATA) * 2 + len(expect1),
4279             'base/section2:size': len(expect2),
4280             'base/section2:uncomp-size': len(COMPRESS_DATA + COMPRESS_DATA),
4281             'base/section2/blob:offset': 0,
4282             'base/section2/blob:size': len(COMPRESS_DATA),
4283             'base/section2/blob2:offset': len(COMPRESS_DATA),
4284             'base/section2/blob2:size': len(COMPRESS_DATA),
4285
4286             'offset': 0,
4287             'image-pos': 0,
4288             'size': len(data),
4289             }
4290         self.assertEqual(expected, props)
4291
4292     def testSymbolsSubsection(self):
4293         """Test binman can assign symbols from a subsection"""
4294         self.checkSymbols('187_symbols_sub.dts', U_BOOT_SPL_DATA, 0x18)
4295
4296     def testReadImageEntryArg(self):
4297         """Test reading an image that would need an entry arg to generate"""
4298         entry_args = {
4299             'cros-ec-rw-path': 'ecrw.bin',
4300         }
4301         data = self.data = self._DoReadFileDtb(
4302             '188_image_entryarg.dts',use_real_dtb=True, update_dtb=True,
4303             entry_args=entry_args)
4304
4305         image_fname = tools.GetOutputFilename('image.bin')
4306         orig_image = control.images['image']
4307
4308         # This should not generate an error about the missing 'cros-ec-rw-path'
4309         # since we are reading the image from a file. Compare with
4310         # testEntryArgsRequired()
4311         image = Image.FromFile(image_fname)
4312         self.assertEqual(orig_image.GetEntries().keys(),
4313                          image.GetEntries().keys())
4314
4315     def testFilesAlign(self):
4316         """Test alignment with files"""
4317         data = self._DoReadFile('190_files_align.dts')
4318
4319         # The first string is 15 bytes so will align to 16
4320         expect = FILES_DATA[:15] + b'\0' + FILES_DATA[15:]
4321         self.assertEqual(expect, data)
4322
4323     def testReadImageSkip(self):
4324         """Test reading an image and accessing its FDT map"""
4325         data = self.data = self._DoReadFileRealDtb('191_read_image_skip.dts')
4326         image_fname = tools.GetOutputFilename('image.bin')
4327         orig_image = control.images['image']
4328         image = Image.FromFile(image_fname)
4329         self.assertEqual(orig_image.GetEntries().keys(),
4330                          image.GetEntries().keys())
4331
4332         orig_entry = orig_image.GetEntries()['fdtmap']
4333         entry = image.GetEntries()['fdtmap']
4334         self.assertEqual(orig_entry.offset, entry.offset)
4335         self.assertEqual(orig_entry.size, entry.size)
4336         self.assertEqual(16, entry.image_pos)
4337
4338         u_boot = image.GetEntries()['section'].GetEntries()['u-boot']
4339
4340         self.assertEquals(U_BOOT_DATA, u_boot.ReadData())
4341
4342     def testTplNoDtb(self):
4343         """Test that an image with tpl/u-boot-tpl-nodtb.bin can be created"""
4344         self._SetupTplElf()
4345         data = self._DoReadFile('192_u_boot_tpl_nodtb.dts')
4346         self.assertEqual(U_BOOT_TPL_NODTB_DATA,
4347                          data[:len(U_BOOT_TPL_NODTB_DATA)])
4348
4349     def testTplBssPad(self):
4350         """Test that we can pad TPL's BSS with zeros"""
4351         # ELF file with a '__bss_size' symbol
4352         self._SetupTplElf()
4353         data = self._DoReadFile('193_tpl_bss_pad.dts')
4354         self.assertEqual(U_BOOT_TPL_DATA + tools.GetBytes(0, 10) + U_BOOT_DATA,
4355                          data)
4356
4357     def testTplBssPadMissing(self):
4358         """Test that a missing symbol is detected"""
4359         self._SetupTplElf('u_boot_ucode_ptr')
4360         with self.assertRaises(ValueError) as e:
4361             self._DoReadFile('193_tpl_bss_pad.dts')
4362         self.assertIn('Expected __bss_size symbol in tpl/u-boot-tpl',
4363                       str(e.exception))
4364
4365     def checkDtbSizes(self, data, pad_len, start):
4366         """Check the size arguments in a dtb embedded in an image
4367
4368         Args:
4369             data: The image data
4370             pad_len: Length of the pad section in the image, in bytes
4371             start: Start offset of the devicetree to examine, within the image
4372
4373         Returns:
4374             Size of the devicetree in bytes
4375         """
4376         dtb_data = data[start:]
4377         dtb = fdt.Fdt.FromData(dtb_data)
4378         fdt_size = dtb.GetFdtObj().totalsize()
4379         dtb.Scan()
4380         props = self._GetPropTree(dtb, 'size')
4381         self.assertEqual({
4382             'size': len(data),
4383             'u-boot-spl/u-boot-spl-bss-pad:size': pad_len,
4384             'u-boot-spl/u-boot-spl-dtb:size': 801,
4385             'u-boot-spl/u-boot-spl-nodtb:size': len(U_BOOT_SPL_NODTB_DATA),
4386             'u-boot-spl:size': 860,
4387             'u-boot-tpl:size': len(U_BOOT_TPL_DATA),
4388             'u-boot/u-boot-dtb:size': 781,
4389             'u-boot/u-boot-nodtb:size': len(U_BOOT_NODTB_DATA),
4390             'u-boot:size': 827,
4391             }, props)
4392         return fdt_size
4393
4394     def testExpanded(self):
4395         """Test that an expanded entry type is selected when needed"""
4396         self._SetupSplElf()
4397         self._SetupTplElf()
4398
4399         # SPL has a devicetree, TPL does not
4400         entry_args = {
4401             'spl-dtb': '1',
4402             'spl-bss-pad': 'y',
4403             'tpl-dtb': '',
4404         }
4405         self._DoReadFileDtb('194_fdt_incl.dts', use_expanded=True,
4406                             entry_args=entry_args)
4407         image = control.images['image']
4408         entries = image.GetEntries()
4409         self.assertEqual(3, len(entries))
4410
4411         # First, u-boot, which should be expanded into u-boot-nodtb and dtb
4412         self.assertIn('u-boot', entries)
4413         entry = entries['u-boot']
4414         self.assertEqual('u-boot-expanded', entry.etype)
4415         subent = entry.GetEntries()
4416         self.assertEqual(2, len(subent))
4417         self.assertIn('u-boot-nodtb', subent)
4418         self.assertIn('u-boot-dtb', subent)
4419
4420         # Second, u-boot-spl, which should be expanded into three parts
4421         self.assertIn('u-boot-spl', entries)
4422         entry = entries['u-boot-spl']
4423         self.assertEqual('u-boot-spl-expanded', entry.etype)
4424         subent = entry.GetEntries()
4425         self.assertEqual(3, len(subent))
4426         self.assertIn('u-boot-spl-nodtb', subent)
4427         self.assertIn('u-boot-spl-bss-pad', subent)
4428         self.assertIn('u-boot-spl-dtb', subent)
4429
4430         # Third, u-boot-tpl, which should be not be expanded, since TPL has no
4431         # devicetree
4432         self.assertIn('u-boot-tpl', entries)
4433         entry = entries['u-boot-tpl']
4434         self.assertEqual('u-boot-tpl', entry.etype)
4435         self.assertEqual(None, entry.GetEntries())
4436
4437     def testExpandedTpl(self):
4438         """Test that an expanded entry type is selected for TPL when needed"""
4439         self._SetupTplElf()
4440
4441         entry_args = {
4442             'tpl-bss-pad': 'y',
4443             'tpl-dtb': 'y',
4444         }
4445         self._DoReadFileDtb('195_fdt_incl_tpl.dts', use_expanded=True,
4446                             entry_args=entry_args)
4447         image = control.images['image']
4448         entries = image.GetEntries()
4449         self.assertEqual(1, len(entries))
4450
4451         # We only have u-boot-tpl, which be expanded
4452         self.assertIn('u-boot-tpl', entries)
4453         entry = entries['u-boot-tpl']
4454         self.assertEqual('u-boot-tpl-expanded', entry.etype)
4455         subent = entry.GetEntries()
4456         self.assertEqual(3, len(subent))
4457         self.assertIn('u-boot-tpl-nodtb', subent)
4458         self.assertIn('u-boot-tpl-bss-pad', subent)
4459         self.assertIn('u-boot-tpl-dtb', subent)
4460
4461     def testExpandedNoPad(self):
4462         """Test an expanded entry without BSS pad enabled"""
4463         self._SetupSplElf()
4464         self._SetupTplElf()
4465
4466         # SPL has a devicetree, TPL does not
4467         entry_args = {
4468             'spl-dtb': 'something',
4469             'spl-bss-pad': 'n',
4470             'tpl-dtb': '',
4471         }
4472         self._DoReadFileDtb('194_fdt_incl.dts', use_expanded=True,
4473                             entry_args=entry_args)
4474         image = control.images['image']
4475         entries = image.GetEntries()
4476
4477         # Just check u-boot-spl, which should be expanded into two parts
4478         self.assertIn('u-boot-spl', entries)
4479         entry = entries['u-boot-spl']
4480         self.assertEqual('u-boot-spl-expanded', entry.etype)
4481         subent = entry.GetEntries()
4482         self.assertEqual(2, len(subent))
4483         self.assertIn('u-boot-spl-nodtb', subent)
4484         self.assertIn('u-boot-spl-dtb', subent)
4485
4486     def testExpandedTplNoPad(self):
4487         """Test that an expanded entry type with padding disabled in TPL"""
4488         self._SetupTplElf()
4489
4490         entry_args = {
4491             'tpl-bss-pad': '',
4492             'tpl-dtb': 'y',
4493         }
4494         self._DoReadFileDtb('195_fdt_incl_tpl.dts', use_expanded=True,
4495                             entry_args=entry_args)
4496         image = control.images['image']
4497         entries = image.GetEntries()
4498         self.assertEqual(1, len(entries))
4499
4500         # We only have u-boot-tpl, which be expanded
4501         self.assertIn('u-boot-tpl', entries)
4502         entry = entries['u-boot-tpl']
4503         self.assertEqual('u-boot-tpl-expanded', entry.etype)
4504         subent = entry.GetEntries()
4505         self.assertEqual(2, len(subent))
4506         self.assertIn('u-boot-tpl-nodtb', subent)
4507         self.assertIn('u-boot-tpl-dtb', subent)
4508
4509     def testFdtInclude(self):
4510         """Test that an Fdt is update within all binaries"""
4511         self._SetupSplElf()
4512         self._SetupTplElf()
4513
4514         # SPL has a devicetree, TPL does not
4515         self.maxDiff = None
4516         entry_args = {
4517             'spl-dtb': '1',
4518             'spl-bss-pad': 'y',
4519             'tpl-dtb': '',
4520         }
4521         # Build the image. It includes two separate devicetree binaries, each
4522         # with their own contents, but all contain the binman definition.
4523         data = self._DoReadFileDtb(
4524             '194_fdt_incl.dts', use_real_dtb=True, use_expanded=True,
4525             update_dtb=True, entry_args=entry_args)[0]
4526         pad_len = 10
4527
4528         # Check the U-Boot dtb
4529         start = len(U_BOOT_NODTB_DATA)
4530         fdt_size = self.checkDtbSizes(data, pad_len, start)
4531
4532         # Now check SPL
4533         start += fdt_size + len(U_BOOT_SPL_NODTB_DATA) + pad_len
4534         fdt_size = self.checkDtbSizes(data, pad_len, start)
4535
4536         # TPL has no devicetree
4537         start += fdt_size + len(U_BOOT_TPL_DATA)
4538         self.assertEqual(len(data), start)
4539
4540     def testSymbolsExpanded(self):
4541         """Test binman can assign symbols in expanded entries"""
4542         entry_args = {
4543             'spl-dtb': '1',
4544         }
4545         self.checkSymbols('197_symbols_expand.dts', U_BOOT_SPL_NODTB_DATA +
4546                           U_BOOT_SPL_DTB_DATA, 0x38,
4547                           entry_args=entry_args, use_expanded=True)
4548
4549     def testCollection(self):
4550         """Test a collection"""
4551         data = self._DoReadFile('198_collection.dts')
4552         self.assertEqual(U_BOOT_NODTB_DATA + U_BOOT_DTB_DATA +
4553                          tools.GetBytes(0xff, 2) + U_BOOT_NODTB_DATA +
4554                          tools.GetBytes(0xfe, 3) + U_BOOT_DTB_DATA,
4555                          data)
4556
4557     def testCollectionSection(self):
4558         """Test a collection where a section must be built first"""
4559         # Sections never have their contents when GetData() is called, but when
4560         # BuildSectionData() is called with required=True, a section will force
4561         # building the contents, producing an error is anything is still
4562         # missing.
4563         data = self._DoReadFile('199_collection_section.dts')
4564         section = U_BOOT_NODTB_DATA + U_BOOT_DTB_DATA
4565         self.assertEqual(section + U_BOOT_DATA + tools.GetBytes(0xff, 2) +
4566                          section + tools.GetBytes(0xfe, 3) + U_BOOT_DATA,
4567                          data)
4568
4569     def testAlignDefault(self):
4570         """Test that default alignment works on sections"""
4571         data = self._DoReadFile('200_align_default.dts')
4572         expected = (U_BOOT_DATA + tools.GetBytes(0, 8 - len(U_BOOT_DATA)) +
4573                     U_BOOT_DATA)
4574         # Special alignment for section
4575         expected += tools.GetBytes(0, 32 - len(expected))
4576         # No alignment within the nested section
4577         expected += U_BOOT_DATA + U_BOOT_NODTB_DATA;
4578         # Now the final piece, which should be default-aligned
4579         expected += tools.GetBytes(0, 88 - len(expected)) + U_BOOT_NODTB_DATA
4580         self.assertEqual(expected, data)
4581
4582     def testPackOpenSBI(self):
4583         """Test that an image with an OpenSBI binary can be created"""
4584         data = self._DoReadFile('201_opensbi.dts')
4585         self.assertEqual(OPENSBI_DATA, data[:len(OPENSBI_DATA)])
4586
4587     def testSectionsSingleThread(self):
4588         """Test sections without multithreading"""
4589         data = self._DoReadFileDtb('055_sections.dts', threads=0)[0]
4590         expected = (U_BOOT_DATA + tools.GetBytes(ord('!'), 12) +
4591                     U_BOOT_DATA + tools.GetBytes(ord('a'), 12) +
4592                     U_BOOT_DATA + tools.GetBytes(ord('&'), 4))
4593         self.assertEqual(expected, data)
4594
4595     def testThreadTimeout(self):
4596         """Test handling a thread that takes too long"""
4597         with self.assertRaises(ValueError) as e:
4598             self._DoTestFile('202_section_timeout.dts',
4599                              test_section_timeout=True)
4600         self.assertIn("Timed out obtaining contents", str(e.exception))
4601
4602     def testTiming(self):
4603         """Test output of timing information"""
4604         data = self._DoReadFile('055_sections.dts')
4605         with test_util.capture_sys_output() as (stdout, stderr):
4606             state.TimingShow()
4607         self.assertIn('read:', stdout.getvalue())
4608         self.assertIn('compress:', stdout.getvalue())
4609
4610     def testUpdateFdtInElf(self):
4611         """Test that we can update the devicetree in an ELF file"""
4612         infile = elf_fname = self.ElfTestFile('u_boot_binman_embed')
4613         outfile = os.path.join(self._indir, 'u-boot.out')
4614         begin_sym = 'dtb_embed_begin'
4615         end_sym = 'dtb_embed_end'
4616         retcode = self._DoTestFile(
4617             '060_fdt_update.dts', update_dtb=True,
4618             update_fdt_in_elf=','.join([infile,outfile,begin_sym,end_sym]))
4619         self.assertEqual(0, retcode)
4620
4621         # Check that the output file does in fact contact a dtb with the binman
4622         # definition in the correct place
4623         syms = elf.GetSymbolFileOffset(infile,
4624                                        ['dtb_embed_begin', 'dtb_embed_end'])
4625         data = tools.ReadFile(outfile)
4626         dtb_data = data[syms['dtb_embed_begin'].offset:
4627                         syms['dtb_embed_end'].offset]
4628
4629         dtb = fdt.Fdt.FromData(dtb_data)
4630         dtb.Scan()
4631         props = self._GetPropTree(dtb, BASE_DTB_PROPS + REPACK_DTB_PROPS)
4632         self.assertEqual({
4633             'image-pos': 0,
4634             'offset': 0,
4635             '_testing:offset': 32,
4636             '_testing:size': 2,
4637             '_testing:image-pos': 32,
4638             'section@0/u-boot:offset': 0,
4639             'section@0/u-boot:size': len(U_BOOT_DATA),
4640             'section@0/u-boot:image-pos': 0,
4641             'section@0:offset': 0,
4642             'section@0:size': 16,
4643             'section@0:image-pos': 0,
4644
4645             'section@1/u-boot:offset': 0,
4646             'section@1/u-boot:size': len(U_BOOT_DATA),
4647             'section@1/u-boot:image-pos': 16,
4648             'section@1:offset': 16,
4649             'section@1:size': 16,
4650             'section@1:image-pos': 16,
4651             'size': 40
4652         }, props)
4653
4654     def testUpdateFdtInElfInvalid(self):
4655         """Test that invalid args are detected with --update-fdt-in-elf"""
4656         with self.assertRaises(ValueError) as e:
4657             self._DoTestFile('060_fdt_update.dts', update_fdt_in_elf='fred')
4658         self.assertIn("Invalid args ['fred'] to --update-fdt-in-elf",
4659                       str(e.exception))
4660
4661     def testUpdateFdtInElfNoSyms(self):
4662         """Test that missing symbols are detected with --update-fdt-in-elf"""
4663         infile = elf_fname = self.ElfTestFile('u_boot_binman_embed')
4664         outfile = ''
4665         begin_sym = 'wrong_begin'
4666         end_sym = 'wrong_end'
4667         with self.assertRaises(ValueError) as e:
4668             self._DoTestFile(
4669                 '060_fdt_update.dts',
4670                 update_fdt_in_elf=','.join([infile,outfile,begin_sym,end_sym]))
4671         self.assertIn("Expected two symbols 'wrong_begin' and 'wrong_end': got 0:",
4672                       str(e.exception))
4673
4674     def testUpdateFdtInElfTooSmall(self):
4675         """Test that an over-large dtb is detected with --update-fdt-in-elf"""
4676         infile = elf_fname = self.ElfTestFile('u_boot_binman_embed_sm')
4677         outfile = os.path.join(self._indir, 'u-boot.out')
4678         begin_sym = 'dtb_embed_begin'
4679         end_sym = 'dtb_embed_end'
4680         with self.assertRaises(ValueError) as e:
4681             self._DoTestFile(
4682                 '060_fdt_update.dts', update_dtb=True,
4683                 update_fdt_in_elf=','.join([infile,outfile,begin_sym,end_sym]))
4684         self.assertRegex(
4685             str(e.exception),
4686             "Not enough space in '.*u_boot_binman_embed_sm' for data length.*")
4687
4688     def testVersion(self):
4689         """Test we can get the binman version"""
4690         version = '(unreleased)'
4691         self.assertEqual(version, state.GetVersion(self._indir))
4692
4693         with self.assertRaises(SystemExit):
4694             with test_util.capture_sys_output() as (_, stderr):
4695                 self._DoBinman('-V')
4696         self.assertEqual('Binman %s\n' % version, stderr.getvalue())
4697
4698         # Try running the tool too, just to be safe
4699         result = self._RunBinman('-V')
4700         self.assertEqual('Binman %s\n' % version, result.stderr)
4701
4702         # Set up a version file to make sure that works
4703         version = 'v2025.01-rc2'
4704         tools.WriteFile(os.path.join(self._indir, 'version'), version,
4705                         binary=False)
4706         self.assertEqual(version, state.GetVersion(self._indir))
4707
4708     def testAltFormat(self):
4709         """Test that alternative formats can be used to extract"""
4710         self._DoReadFileRealDtb('213_fdtmap_alt_format.dts')
4711
4712         try:
4713             tmpdir, updated_fname = self._SetupImageInTmpdir()
4714             with test_util.capture_sys_output() as (stdout, _):
4715                 self._DoBinman('extract', '-i', updated_fname, '-F', 'list')
4716             self.assertEqual(
4717                 '''Flag (-F)   Entry type            Description
4718 fdt         fdtmap                Extract the devicetree blob from the fdtmap
4719 ''',
4720                 stdout.getvalue())
4721
4722             dtb = os.path.join(tmpdir, 'fdt.dtb')
4723             self._DoBinman('extract', '-i', updated_fname, '-F', 'fdt', '-f',
4724                            dtb, 'fdtmap')
4725
4726             # Check that we can read it and it can be scanning, meaning it does
4727             # not have a 16-byte fdtmap header
4728             data = tools.ReadFile(dtb)
4729             dtb = fdt.Fdt.FromData(data)
4730             dtb.Scan()
4731
4732             # Now check u-boot which has no alt_format
4733             fname = os.path.join(tmpdir, 'fdt.dtb')
4734             self._DoBinman('extract', '-i', updated_fname, '-F', 'dummy',
4735                            '-f', fname, 'u-boot')
4736             data = tools.ReadFile(fname)
4737             self.assertEqual(U_BOOT_DATA, data)
4738
4739         finally:
4740             shutil.rmtree(tmpdir)
4741
4742     def testExtblobList(self):
4743         """Test an image with an external blob list"""
4744         data = self._DoReadFile('215_blob_ext_list.dts')
4745         self.assertEqual(REFCODE_DATA + FSP_M_DATA, data)
4746
4747     def testExtblobListMissing(self):
4748         """Test an image with a missing external blob"""
4749         with self.assertRaises(ValueError) as e:
4750             self._DoReadFile('216_blob_ext_list_missing.dts')
4751         self.assertIn("Filename 'missing-file' not found in input path",
4752                       str(e.exception))
4753
4754     def testExtblobListMissingOk(self):
4755         """Test an image with an missing external blob that is allowed"""
4756         with test_util.capture_sys_output() as (stdout, stderr):
4757             self._DoTestFile('216_blob_ext_list_missing.dts',
4758                              allow_missing=True)
4759         err = stderr.getvalue()
4760         self.assertRegex(err, "Image 'main-section'.*missing.*: blob-ext")
4761
4762     def testFip(self):
4763         """Basic test of generation of an ARM Firmware Image Package (FIP)"""
4764         data = self._DoReadFile('203_fip.dts')
4765         hdr, fents = fip_util.decode_fip(data)
4766         self.assertEqual(fip_util.HEADER_MAGIC, hdr.name)
4767         self.assertEqual(fip_util.HEADER_SERIAL, hdr.serial)
4768         self.assertEqual(0x123, hdr.flags)
4769
4770         self.assertEqual(2, len(fents))
4771
4772         fent = fents[0]
4773         self.assertEqual(
4774             bytes([0x47,  0xd4, 0x08, 0x6d, 0x4c, 0xfe, 0x98, 0x46,
4775                   0x9b, 0x95, 0x29, 0x50, 0xcb, 0xbd, 0x5a, 0x0]), fent.uuid)
4776         self.assertEqual('soc-fw', fent.fip_type)
4777         self.assertEqual(0x88, fent.offset)
4778         self.assertEqual(len(ATF_BL31_DATA), fent.size)
4779         self.assertEqual(0x123456789abcdef, fent.flags)
4780         self.assertEqual(ATF_BL31_DATA, fent.data)
4781         self.assertEqual(True, fent.valid)
4782
4783         fent = fents[1]
4784         self.assertEqual(
4785             bytes([0x65, 0x92, 0x27, 0x03, 0x2f, 0x74, 0xe6, 0x44,
4786              0x8d, 0xff, 0x57, 0x9a, 0xc1, 0xff, 0x06, 0x10]), fent.uuid)
4787         self.assertEqual('scp-fwu-cfg', fent.fip_type)
4788         self.assertEqual(0x8c, fent.offset)
4789         self.assertEqual(len(ATF_BL31_DATA), fent.size)
4790         self.assertEqual(0, fent.flags)
4791         self.assertEqual(ATF_BL2U_DATA, fent.data)
4792         self.assertEqual(True, fent.valid)
4793
4794     def testFipOther(self):
4795         """Basic FIP with something that isn't a external blob"""
4796         data = self._DoReadFile('204_fip_other.dts')
4797         hdr, fents = fip_util.decode_fip(data)
4798
4799         self.assertEqual(2, len(fents))
4800         fent = fents[1]
4801         self.assertEqual('rot-cert', fent.fip_type)
4802         self.assertEqual(b'aa', fent.data)
4803
4804     def testFipOther(self):
4805         """Basic FIP with something that isn't a external blob"""
4806         data = self._DoReadFile('204_fip_other.dts')
4807         hdr, fents = fip_util.decode_fip(data)
4808
4809         self.assertEqual(2, len(fents))
4810         fent = fents[1]
4811         self.assertEqual('rot-cert', fent.fip_type)
4812         self.assertEqual(b'aa', fent.data)
4813
4814     def testFipNoType(self):
4815         """FIP with an entry of an unknown type"""
4816         with self.assertRaises(ValueError) as e:
4817             self._DoReadFile('205_fip_no_type.dts')
4818         self.assertIn("Must provide a fip-type (node name 'u-boot' is not a known FIP type)",
4819                       str(e.exception))
4820
4821     def testFipUuid(self):
4822         """Basic FIP with a manual uuid"""
4823         data = self._DoReadFile('206_fip_uuid.dts')
4824         hdr, fents = fip_util.decode_fip(data)
4825
4826         self.assertEqual(2, len(fents))
4827         fent = fents[1]
4828         self.assertEqual(None, fent.fip_type)
4829         self.assertEqual(
4830             bytes([0xfc, 0x65, 0x13, 0x92, 0x4a, 0x5b, 0x11, 0xec,
4831                    0x94, 0x35, 0xff, 0x2d, 0x1c, 0xfc, 0x79, 0x9c]),
4832             fent.uuid)
4833         self.assertEqual(U_BOOT_DATA, fent.data)
4834
4835     def testFipLs(self):
4836         """Test listing a FIP"""
4837         data = self._DoReadFileRealDtb('207_fip_ls.dts')
4838         hdr, fents = fip_util.decode_fip(data)
4839
4840         try:
4841             tmpdir, updated_fname = self._SetupImageInTmpdir()
4842             with test_util.capture_sys_output() as (stdout, stderr):
4843                 self._DoBinman('ls', '-i', updated_fname)
4844         finally:
4845             shutil.rmtree(tmpdir)
4846         lines = stdout.getvalue().splitlines()
4847         expected = [
4848 'Name          Image-pos  Size  Entry-type  Offset  Uncomp-size',
4849 '----------------------------------------------------------------',
4850 'main-section          0   2d3  section          0',
4851 '  atf-fip             0    90  atf-fip          0',
4852 '    soc-fw           88     4  blob-ext        88',
4853 '    u-boot           8c     4  u-boot          8c',
4854 '  fdtmap             90   243  fdtmap          90',
4855 ]
4856         self.assertEqual(expected, lines)
4857
4858         image = control.images['image']
4859         entries = image.GetEntries()
4860         fdtmap = entries['fdtmap']
4861
4862         fdtmap_data = data[fdtmap.image_pos:fdtmap.image_pos + fdtmap.size]
4863         magic = fdtmap_data[:8]
4864         self.assertEqual(b'_FDTMAP_', magic)
4865         self.assertEqual(tools.GetBytes(0, 8), fdtmap_data[8:16])
4866
4867         fdt_data = fdtmap_data[16:]
4868         dtb = fdt.Fdt.FromData(fdt_data)
4869         dtb.Scan()
4870         props = self._GetPropTree(dtb, BASE_DTB_PROPS, prefix='/')
4871         self.assertEqual({
4872             'atf-fip/soc-fw:image-pos': 136,
4873             'atf-fip/soc-fw:offset': 136,
4874             'atf-fip/soc-fw:size': 4,
4875             'atf-fip/u-boot:image-pos': 140,
4876             'atf-fip/u-boot:offset': 140,
4877             'atf-fip/u-boot:size': 4,
4878             'atf-fip:image-pos': 0,
4879             'atf-fip:offset': 0,
4880             'atf-fip:size': 144,
4881             'image-pos': 0,
4882             'offset': 0,
4883             'fdtmap:image-pos': fdtmap.image_pos,
4884             'fdtmap:offset': fdtmap.offset,
4885             'fdtmap:size': len(fdtmap_data),
4886             'size': len(data),
4887         }, props)
4888
4889     def testFipExtractOneEntry(self):
4890         """Test extracting a single entry fron an FIP"""
4891         self._DoReadFileRealDtb('207_fip_ls.dts')
4892         image_fname = tools.GetOutputFilename('image.bin')
4893         fname = os.path.join(self._indir, 'output.extact')
4894         control.ExtractEntries(image_fname, fname, None, ['atf-fip/u-boot'])
4895         data = tools.ReadFile(fname)
4896         self.assertEqual(U_BOOT_DATA, data)
4897
4898     def testFipReplace(self):
4899         """Test replacing a single file in a FIP"""
4900         expected = U_BOOT_DATA + tools.GetBytes(0x78, 50)
4901         data = self._DoReadFileRealDtb('208_fip_replace.dts')
4902         updated_fname = tools.GetOutputFilename('image-updated.bin')
4903         tools.WriteFile(updated_fname, data)
4904         entry_name = 'atf-fip/u-boot'
4905         control.WriteEntry(updated_fname, entry_name, expected,
4906                            allow_resize=True)
4907         actual = control.ReadEntry(updated_fname, entry_name)
4908         self.assertEqual(expected, actual)
4909
4910         new_data = tools.ReadFile(updated_fname)
4911         hdr, fents = fip_util.decode_fip(new_data)
4912
4913         self.assertEqual(2, len(fents))
4914
4915         # Check that the FIP entry is updated
4916         fent = fents[1]
4917         self.assertEqual(0x8c, fent.offset)
4918         self.assertEqual(len(expected), fent.size)
4919         self.assertEqual(0, fent.flags)
4920         self.assertEqual(expected, fent.data)
4921         self.assertEqual(True, fent.valid)
4922
4923     def testFipMissing(self):
4924         with test_util.capture_sys_output() as (stdout, stderr):
4925             self._DoTestFile('209_fip_missing.dts', allow_missing=True)
4926         err = stderr.getvalue()
4927         self.assertRegex(err, "Image 'main-section'.*missing.*: rmm-fw")
4928
4929     def testFipSize(self):
4930         """Test a FIP with a size property"""
4931         data = self._DoReadFile('210_fip_size.dts')
4932         self.assertEqual(0x100 + len(U_BOOT_DATA), len(data))
4933         hdr, fents = fip_util.decode_fip(data)
4934         self.assertEqual(fip_util.HEADER_MAGIC, hdr.name)
4935         self.assertEqual(fip_util.HEADER_SERIAL, hdr.serial)
4936
4937         self.assertEqual(1, len(fents))
4938
4939         fent = fents[0]
4940         self.assertEqual('soc-fw', fent.fip_type)
4941         self.assertEqual(0x60, fent.offset)
4942         self.assertEqual(len(ATF_BL31_DATA), fent.size)
4943         self.assertEqual(ATF_BL31_DATA, fent.data)
4944         self.assertEqual(True, fent.valid)
4945
4946         rest = data[0x60 + len(ATF_BL31_DATA):0x100]
4947         self.assertEqual(tools.GetBytes(0xff, len(rest)), rest)
4948
4949     def testFipBadAlign(self):
4950         """Test that an invalid alignment value in a FIP is detected"""
4951         with self.assertRaises(ValueError) as e:
4952             self._DoTestFile('211_fip_bad_align.dts')
4953         self.assertIn(
4954             "Node \'/binman/atf-fip\': FIP alignment 31 must be a power of two",
4955             str(e.exception))
4956
4957     def testFipCollection(self):
4958         """Test using a FIP in a collection"""
4959         data = self._DoReadFile('212_fip_collection.dts')
4960         entry1 = control.images['image'].GetEntries()['collection']
4961         data1 = data[:entry1.size]
4962         hdr1, fents2 = fip_util.decode_fip(data1)
4963
4964         entry2 = control.images['image'].GetEntries()['atf-fip']
4965         data2 = data[entry2.offset:entry2.offset + entry2.size]
4966         hdr1, fents2 = fip_util.decode_fip(data2)
4967
4968         # The 'collection' entry should have U-Boot included at the end
4969         self.assertEqual(entry1.size - len(U_BOOT_DATA), entry2.size)
4970         self.assertEqual(data1, data2 + U_BOOT_DATA)
4971         self.assertEqual(U_BOOT_DATA, data1[-4:])
4972
4973         # There should be a U-Boot after the final FIP
4974         self.assertEqual(U_BOOT_DATA, data[-4:])
4975
4976     def testFakeBlob(self):
4977         """Test handling of faking an external blob"""
4978         with test_util.capture_sys_output() as (stdout, stderr):
4979             self._DoTestFile('217_fake_blob.dts', allow_missing=True,
4980                              allow_fake_blobs=True)
4981         err = stderr.getvalue()
4982         self.assertRegex(
4983             err,
4984             "Image '.*' has faked external blobs and is non-functional: .*")
4985
4986     def testExtblobListFaked(self):
4987         """Test an extblob with missing external blob that are faked"""
4988         with test_util.capture_sys_output() as (stdout, stderr):
4989             self._DoTestFile('216_blob_ext_list_missing.dts',
4990                              allow_fake_blobs=True)
4991         err = stderr.getvalue()
4992         self.assertRegex(err, "Image 'main-section'.*faked.*: blob-ext-list")
4993
4994
4995 if __name__ == "__main__":
4996     unittest.main()