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