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