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