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