binman: Support adding sections to FMAPs
[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         self._SetupSplElf()
1345         data = self._DoReadFile('052_u_boot_spl_nodtb.dts')
1346         self.assertEqual(U_BOOT_SPL_NODTB_DATA, data[:len(U_BOOT_SPL_NODTB_DATA)])
1347
1348     def checkSymbols(self, dts, base_data, u_boot_offset, entry_args=None,
1349                      use_expanded=False):
1350         """Check the image contains the expected symbol values
1351
1352         Args:
1353             dts: Device tree file to use for test
1354             base_data: Data before and after 'u-boot' section
1355             u_boot_offset: Offset of 'u-boot' section in image
1356             entry_args: Dict of entry args to supply to binman
1357                 key: arg name
1358                 value: value of that arg
1359             use_expanded: True to use expanded entries where available, e.g.
1360                 'u-boot-expanded' instead of 'u-boot'
1361         """
1362         elf_fname = self.ElfTestFile('u_boot_binman_syms')
1363         syms = elf.GetSymbols(elf_fname, ['binman', 'image'])
1364         addr = elf.GetSymbolAddress(elf_fname, '__image_copy_start')
1365         self.assertEqual(syms['_binman_u_boot_spl_any_prop_offset'].address,
1366                          addr)
1367
1368         self._SetupSplElf('u_boot_binman_syms')
1369         data = self._DoReadFileDtb(dts, entry_args=entry_args,
1370                                    use_expanded=use_expanded)[0]
1371         # The image should contain the symbols from u_boot_binman_syms.c
1372         # Note that image_pos is adjusted by the base address of the image,
1373         # which is 0x10 in our test image
1374         sym_values = struct.pack('<LQLL', 0x00,
1375                                  u_boot_offset + len(U_BOOT_DATA),
1376                                  0x10 + u_boot_offset, 0x04)
1377         expected = (sym_values + base_data[20:] +
1378                     tools.GetBytes(0xff, 1) + U_BOOT_DATA + sym_values +
1379                     base_data[20:])
1380         self.assertEqual(expected, data)
1381
1382     def testSymbols(self):
1383         """Test binman can assign symbols embedded in U-Boot"""
1384         self.checkSymbols('053_symbols.dts', U_BOOT_SPL_DATA, 0x18)
1385
1386     def testSymbolsNoDtb(self):
1387         """Test binman can assign symbols embedded in U-Boot SPL"""
1388         self.checkSymbols('196_symbols_nodtb.dts',
1389                           U_BOOT_SPL_NODTB_DATA + U_BOOT_SPL_DTB_DATA,
1390                           0x38)
1391
1392     def testPackUnitAddress(self):
1393         """Test that we support multiple binaries with the same name"""
1394         data = self._DoReadFile('054_unit_address.dts')
1395         self.assertEqual(U_BOOT_DATA + U_BOOT_DATA, data)
1396
1397     def testSections(self):
1398         """Basic test of sections"""
1399         data = self._DoReadFile('055_sections.dts')
1400         expected = (U_BOOT_DATA + tools.GetBytes(ord('!'), 12) +
1401                     U_BOOT_DATA + tools.GetBytes(ord('a'), 12) +
1402                     U_BOOT_DATA + tools.GetBytes(ord('&'), 4))
1403         self.assertEqual(expected, data)
1404
1405     def testMap(self):
1406         """Tests outputting a map of the images"""
1407         _, _, map_data, _ = self._DoReadFileDtb('055_sections.dts', map=True)
1408         self.assertEqual('''ImagePos    Offset      Size  Name
1409 00000000  00000000  00000028  main-section
1410 00000000   00000000  00000010  section@0
1411 00000000    00000000  00000004  u-boot
1412 00000010   00000010  00000010  section@1
1413 00000010    00000000  00000004  u-boot
1414 00000020   00000020  00000004  section@2
1415 00000020    00000000  00000004  u-boot
1416 ''', map_data)
1417
1418     def testNamePrefix(self):
1419         """Tests that name prefixes are used"""
1420         _, _, map_data, _ = self._DoReadFileDtb('056_name_prefix.dts', map=True)
1421         self.assertEqual('''ImagePos    Offset      Size  Name
1422 00000000  00000000  00000028  main-section
1423 00000000   00000000  00000010  section@0
1424 00000000    00000000  00000004  ro-u-boot
1425 00000010   00000010  00000010  section@1
1426 00000010    00000000  00000004  rw-u-boot
1427 ''', map_data)
1428
1429     def testUnknownContents(self):
1430         """Test that obtaining the contents works as expected"""
1431         with self.assertRaises(ValueError) as e:
1432             self._DoReadFile('057_unknown_contents.dts', True)
1433         self.assertIn("Image '/binman': Internal error: Could not complete "
1434                 "processing of contents: remaining ["
1435                 "<binman.etype._testing.Entry__testing ", str(e.exception))
1436
1437     def testBadChangeSize(self):
1438         """Test that trying to change the size of an entry fails"""
1439         try:
1440             state.SetAllowEntryExpansion(False)
1441             with self.assertRaises(ValueError) as e:
1442                 self._DoReadFile('059_change_size.dts', True)
1443             self.assertIn("Node '/binman/_testing': Cannot update entry size from 2 to 3",
1444                           str(e.exception))
1445         finally:
1446             state.SetAllowEntryExpansion(True)
1447
1448     def testUpdateFdt(self):
1449         """Test that we can update the device tree with offset/size info"""
1450         _, _, _, out_dtb_fname = self._DoReadFileDtb('060_fdt_update.dts',
1451                                                      update_dtb=True)
1452         dtb = fdt.Fdt(out_dtb_fname)
1453         dtb.Scan()
1454         props = self._GetPropTree(dtb, BASE_DTB_PROPS + REPACK_DTB_PROPS)
1455         self.assertEqual({
1456             'image-pos': 0,
1457             'offset': 0,
1458             '_testing:offset': 32,
1459             '_testing:size': 2,
1460             '_testing:image-pos': 32,
1461             'section@0/u-boot:offset': 0,
1462             'section@0/u-boot:size': len(U_BOOT_DATA),
1463             'section@0/u-boot:image-pos': 0,
1464             'section@0:offset': 0,
1465             'section@0:size': 16,
1466             'section@0:image-pos': 0,
1467
1468             'section@1/u-boot:offset': 0,
1469             'section@1/u-boot:size': len(U_BOOT_DATA),
1470             'section@1/u-boot:image-pos': 16,
1471             'section@1:offset': 16,
1472             'section@1:size': 16,
1473             'section@1:image-pos': 16,
1474             'size': 40
1475         }, props)
1476
1477     def testUpdateFdtBad(self):
1478         """Test that we detect when ProcessFdt never completes"""
1479         with self.assertRaises(ValueError) as e:
1480             self._DoReadFileDtb('061_fdt_update_bad.dts', update_dtb=True)
1481         self.assertIn('Could not complete processing of Fdt: remaining '
1482                       '[<binman.etype._testing.Entry__testing',
1483                         str(e.exception))
1484
1485     def testEntryArgs(self):
1486         """Test passing arguments to entries from the command line"""
1487         entry_args = {
1488             'test-str-arg': 'test1',
1489             'test-int-arg': '456',
1490         }
1491         self._DoReadFileDtb('062_entry_args.dts', entry_args=entry_args)
1492         self.assertIn('image', control.images)
1493         entry = control.images['image'].GetEntries()['_testing']
1494         self.assertEqual('test0', entry.test_str_fdt)
1495         self.assertEqual('test1', entry.test_str_arg)
1496         self.assertEqual(123, entry.test_int_fdt)
1497         self.assertEqual(456, entry.test_int_arg)
1498
1499     def testEntryArgsMissing(self):
1500         """Test missing arguments and properties"""
1501         entry_args = {
1502             'test-int-arg': '456',
1503         }
1504         self._DoReadFileDtb('063_entry_args_missing.dts', entry_args=entry_args)
1505         entry = control.images['image'].GetEntries()['_testing']
1506         self.assertEqual('test0', entry.test_str_fdt)
1507         self.assertEqual(None, entry.test_str_arg)
1508         self.assertEqual(None, entry.test_int_fdt)
1509         self.assertEqual(456, entry.test_int_arg)
1510
1511     def testEntryArgsRequired(self):
1512         """Test missing arguments and properties"""
1513         entry_args = {
1514             'test-int-arg': '456',
1515         }
1516         with self.assertRaises(ValueError) as e:
1517             self._DoReadFileDtb('064_entry_args_required.dts')
1518         self.assertIn("Node '/binman/_testing': "
1519             'Missing required properties/entry args: test-str-arg, '
1520             'test-int-fdt, test-int-arg',
1521             str(e.exception))
1522
1523     def testEntryArgsInvalidFormat(self):
1524         """Test that an invalid entry-argument format is detected"""
1525         args = ['build', '-d', self.TestFile('064_entry_args_required.dts'),
1526                 '-ano-value']
1527         with self.assertRaises(ValueError) as e:
1528             self._DoBinman(*args)
1529         self.assertIn("Invalid entry arguemnt 'no-value'", str(e.exception))
1530
1531     def testEntryArgsInvalidInteger(self):
1532         """Test that an invalid entry-argument integer is detected"""
1533         entry_args = {
1534             'test-int-arg': 'abc',
1535         }
1536         with self.assertRaises(ValueError) as e:
1537             self._DoReadFileDtb('062_entry_args.dts', entry_args=entry_args)
1538         self.assertIn("Node '/binman/_testing': Cannot convert entry arg "
1539                       "'test-int-arg' (value 'abc') to integer",
1540             str(e.exception))
1541
1542     def testEntryArgsInvalidDatatype(self):
1543         """Test that an invalid entry-argument datatype is detected
1544
1545         This test could be written in entry_test.py except that it needs
1546         access to control.entry_args, which seems more than that module should
1547         be able to see.
1548         """
1549         entry_args = {
1550             'test-bad-datatype-arg': '12',
1551         }
1552         with self.assertRaises(ValueError) as e:
1553             self._DoReadFileDtb('065_entry_args_unknown_datatype.dts',
1554                                 entry_args=entry_args)
1555         self.assertIn('GetArg() internal error: Unknown data type ',
1556                       str(e.exception))
1557
1558     def testText(self):
1559         """Test for a text entry type"""
1560         entry_args = {
1561             'test-id': TEXT_DATA,
1562             'test-id2': TEXT_DATA2,
1563             'test-id3': TEXT_DATA3,
1564         }
1565         data, _, _, _ = self._DoReadFileDtb('066_text.dts',
1566                                             entry_args=entry_args)
1567         expected = (tools.ToBytes(TEXT_DATA) +
1568                     tools.GetBytes(0, 8 - len(TEXT_DATA)) +
1569                     tools.ToBytes(TEXT_DATA2) + tools.ToBytes(TEXT_DATA3) +
1570                     b'some text' + b'more text')
1571         self.assertEqual(expected, data)
1572
1573     def testEntryDocs(self):
1574         """Test for creation of entry documentation"""
1575         with test_util.capture_sys_output() as (stdout, stderr):
1576             control.WriteEntryDocs(control.GetEntryModules())
1577         self.assertTrue(len(stdout.getvalue()) > 0)
1578
1579     def testEntryDocsMissing(self):
1580         """Test handling of missing entry documentation"""
1581         with self.assertRaises(ValueError) as e:
1582             with test_util.capture_sys_output() as (stdout, stderr):
1583                 control.WriteEntryDocs(control.GetEntryModules(), 'u_boot')
1584         self.assertIn('Documentation is missing for modules: u_boot',
1585                       str(e.exception))
1586
1587     def testFmap(self):
1588         """Basic test of generation of a flashrom fmap"""
1589         data = self._DoReadFile('067_fmap.dts')
1590         fhdr, fentries = fmap_util.DecodeFmap(data[32:])
1591         expected = (U_BOOT_DATA + tools.GetBytes(ord('!'), 12) +
1592                     U_BOOT_DATA + tools.GetBytes(ord('a'), 12))
1593         self.assertEqual(expected, data[:32])
1594         self.assertEqual(b'__FMAP__', fhdr.signature)
1595         self.assertEqual(1, fhdr.ver_major)
1596         self.assertEqual(0, fhdr.ver_minor)
1597         self.assertEqual(0, fhdr.base)
1598         expect_size = fmap_util.FMAP_HEADER_LEN + fmap_util.FMAP_AREA_LEN * 5
1599         self.assertEqual(16 + 16 + expect_size, fhdr.image_size)
1600         self.assertEqual(b'FMAP', fhdr.name)
1601         self.assertEqual(5, fhdr.nareas)
1602         fiter = iter(fentries)
1603
1604         fentry = next(fiter)
1605         self.assertEqual(b'SECTION0', fentry.name)
1606         self.assertEqual(0, fentry.offset)
1607         self.assertEqual(16, fentry.size)
1608         self.assertEqual(0, fentry.flags)
1609
1610         fentry = next(fiter)
1611         self.assertEqual(b'RO_U_BOOT', fentry.name)
1612         self.assertEqual(0, fentry.offset)
1613         self.assertEqual(4, fentry.size)
1614         self.assertEqual(0, fentry.flags)
1615
1616         fentry = next(fiter)
1617         self.assertEqual(b'SECTION1', fentry.name)
1618         self.assertEqual(16, fentry.offset)
1619         self.assertEqual(16, fentry.size)
1620         self.assertEqual(0, fentry.flags)
1621
1622         fentry = next(fiter)
1623         self.assertEqual(b'RW_U_BOOT', fentry.name)
1624         self.assertEqual(16, fentry.offset)
1625         self.assertEqual(4, fentry.size)
1626         self.assertEqual(0, fentry.flags)
1627
1628         fentry = next(fiter)
1629         self.assertEqual(b'FMAP', fentry.name)
1630         self.assertEqual(32, fentry.offset)
1631         self.assertEqual(expect_size, fentry.size)
1632         self.assertEqual(0, fentry.flags)
1633
1634     def testBlobNamedByArg(self):
1635         """Test we can add a blob with the filename coming from an entry arg"""
1636         entry_args = {
1637             'cros-ec-rw-path': 'ecrw.bin',
1638         }
1639         self._DoReadFileDtb('068_blob_named_by_arg.dts', entry_args=entry_args)
1640
1641     def testFill(self):
1642         """Test for an fill entry type"""
1643         data = self._DoReadFile('069_fill.dts')
1644         expected = tools.GetBytes(0xff, 8) + tools.GetBytes(0, 8)
1645         self.assertEqual(expected, data)
1646
1647     def testFillNoSize(self):
1648         """Test for an fill entry type with no size"""
1649         with self.assertRaises(ValueError) as e:
1650             self._DoReadFile('070_fill_no_size.dts')
1651         self.assertIn("'fill' entry must have a size property",
1652                       str(e.exception))
1653
1654     def _HandleGbbCommand(self, pipe_list):
1655         """Fake calls to the futility utility"""
1656         if pipe_list[0][0] == 'futility':
1657             fname = pipe_list[0][-1]
1658             # Append our GBB data to the file, which will happen every time the
1659             # futility command is called.
1660             with open(fname, 'ab') as fd:
1661                 fd.write(GBB_DATA)
1662             return command.CommandResult()
1663
1664     def testGbb(self):
1665         """Test for the Chromium OS Google Binary Block"""
1666         command.test_result = self._HandleGbbCommand
1667         entry_args = {
1668             'keydir': 'devkeys',
1669             'bmpblk': 'bmpblk.bin',
1670         }
1671         data, _, _, _ = self._DoReadFileDtb('071_gbb.dts', entry_args=entry_args)
1672
1673         # Since futility
1674         expected = (GBB_DATA + GBB_DATA + tools.GetBytes(0, 8) +
1675                     tools.GetBytes(0, 0x2180 - 16))
1676         self.assertEqual(expected, data)
1677
1678     def testGbbTooSmall(self):
1679         """Test for the Chromium OS Google Binary Block being large enough"""
1680         with self.assertRaises(ValueError) as e:
1681             self._DoReadFileDtb('072_gbb_too_small.dts')
1682         self.assertIn("Node '/binman/gbb': GBB is too small",
1683                       str(e.exception))
1684
1685     def testGbbNoSize(self):
1686         """Test for the Chromium OS Google Binary Block having a size"""
1687         with self.assertRaises(ValueError) as e:
1688             self._DoReadFileDtb('073_gbb_no_size.dts')
1689         self.assertIn("Node '/binman/gbb': GBB must have a fixed size",
1690                       str(e.exception))
1691
1692     def _HandleVblockCommand(self, pipe_list):
1693         """Fake calls to the futility utility
1694
1695         The expected pipe is:
1696
1697            [('futility', 'vbutil_firmware', '--vblock',
1698              'vblock.vblock', '--keyblock', 'devkeys/firmware.keyblock',
1699              '--signprivate', 'devkeys/firmware_data_key.vbprivk',
1700              '--version', '1', '--fv', 'input.vblock', '--kernelkey',
1701              'devkeys/kernel_subkey.vbpubk', '--flags', '1')]
1702
1703         This writes to the output file (here, 'vblock.vblock'). If
1704         self._hash_data is False, it writes VBLOCK_DATA, else it writes a hash
1705         of the input data (here, 'input.vblock').
1706         """
1707         if pipe_list[0][0] == 'futility':
1708             fname = pipe_list[0][3]
1709             with open(fname, 'wb') as fd:
1710                 if self._hash_data:
1711                     infile = pipe_list[0][11]
1712                     m = hashlib.sha256()
1713                     data = tools.ReadFile(infile)
1714                     m.update(data)
1715                     fd.write(m.digest())
1716                 else:
1717                     fd.write(VBLOCK_DATA)
1718
1719             return command.CommandResult()
1720
1721     def testVblock(self):
1722         """Test for the Chromium OS Verified Boot Block"""
1723         self._hash_data = False
1724         command.test_result = self._HandleVblockCommand
1725         entry_args = {
1726             'keydir': 'devkeys',
1727         }
1728         data, _, _, _ = self._DoReadFileDtb('074_vblock.dts',
1729                                             entry_args=entry_args)
1730         expected = U_BOOT_DATA + VBLOCK_DATA + U_BOOT_DTB_DATA
1731         self.assertEqual(expected, data)
1732
1733     def testVblockNoContent(self):
1734         """Test we detect a vblock which has no content to sign"""
1735         with self.assertRaises(ValueError) as e:
1736             self._DoReadFile('075_vblock_no_content.dts')
1737         self.assertIn("Node '/binman/vblock': Collection must have a 'content' "
1738                       'property', str(e.exception))
1739
1740     def testVblockBadPhandle(self):
1741         """Test that we detect a vblock with an invalid phandle in contents"""
1742         with self.assertRaises(ValueError) as e:
1743             self._DoReadFile('076_vblock_bad_phandle.dts')
1744         self.assertIn("Node '/binman/vblock': Cannot find node for phandle "
1745                       '1000', str(e.exception))
1746
1747     def testVblockBadEntry(self):
1748         """Test that we detect an entry that points to a non-entry"""
1749         with self.assertRaises(ValueError) as e:
1750             self._DoReadFile('077_vblock_bad_entry.dts')
1751         self.assertIn("Node '/binman/vblock': Cannot find entry for node "
1752                       "'other'", str(e.exception))
1753
1754     def testVblockContent(self):
1755         """Test that the vblock signs the right data"""
1756         self._hash_data = True
1757         command.test_result = self._HandleVblockCommand
1758         entry_args = {
1759             'keydir': 'devkeys',
1760         }
1761         data = self._DoReadFileDtb(
1762             '189_vblock_content.dts', use_real_dtb=True, update_dtb=True,
1763             entry_args=entry_args)[0]
1764         hashlen = 32  # SHA256 hash is 32 bytes
1765         self.assertEqual(U_BOOT_DATA, data[:len(U_BOOT_DATA)])
1766         hashval = data[-hashlen:]
1767         dtb = data[len(U_BOOT_DATA):-hashlen]
1768
1769         expected_data = U_BOOT_DATA + dtb
1770
1771         # The hashval should be a hash of the dtb
1772         m = hashlib.sha256()
1773         m.update(expected_data)
1774         expected_hashval = m.digest()
1775         self.assertEqual(expected_hashval, hashval)
1776
1777     def testTpl(self):
1778         """Test that an image with TPL and its device tree can be created"""
1779         # ELF file with a '__bss_size' symbol
1780         self._SetupTplElf()
1781         data = self._DoReadFile('078_u_boot_tpl.dts')
1782         self.assertEqual(U_BOOT_TPL_DATA + U_BOOT_TPL_DTB_DATA, data)
1783
1784     def testUsesPos(self):
1785         """Test that the 'pos' property cannot be used anymore"""
1786         with self.assertRaises(ValueError) as e:
1787            data = self._DoReadFile('079_uses_pos.dts')
1788         self.assertIn("Node '/binman/u-boot': Please use 'offset' instead of "
1789                       "'pos'", str(e.exception))
1790
1791     def testFillZero(self):
1792         """Test for an fill entry type with a size of 0"""
1793         data = self._DoReadFile('080_fill_empty.dts')
1794         self.assertEqual(tools.GetBytes(0, 16), data)
1795
1796     def testTextMissing(self):
1797         """Test for a text entry type where there is no text"""
1798         with self.assertRaises(ValueError) as e:
1799             self._DoReadFileDtb('066_text.dts',)
1800         self.assertIn("Node '/binman/text': No value provided for text label "
1801                       "'test-id'", str(e.exception))
1802
1803     def testPackStart16Tpl(self):
1804         """Test that an image with an x86 start16 TPL region can be created"""
1805         data = self._DoReadFile('081_x86_start16_tpl.dts')
1806         self.assertEqual(X86_START16_TPL_DATA, data[:len(X86_START16_TPL_DATA)])
1807
1808     def testSelectImage(self):
1809         """Test that we can select which images to build"""
1810         expected = 'Skipping images: image1'
1811
1812         # We should only get the expected message in verbose mode
1813         for verbosity in (0, 2):
1814             with test_util.capture_sys_output() as (stdout, stderr):
1815                 retcode = self._DoTestFile('006_dual_image.dts',
1816                                            verbosity=verbosity,
1817                                            images=['image2'])
1818             self.assertEqual(0, retcode)
1819             if verbosity:
1820                 self.assertIn(expected, stdout.getvalue())
1821             else:
1822                 self.assertNotIn(expected, stdout.getvalue())
1823
1824             self.assertFalse(os.path.exists(tools.GetOutputFilename('image1.bin')))
1825             self.assertTrue(os.path.exists(tools.GetOutputFilename('image2.bin')))
1826             self._CleanupOutputDir()
1827
1828     def testUpdateFdtAll(self):
1829         """Test that all device trees are updated with offset/size info"""
1830         data = self._DoReadFileRealDtb('082_fdt_update_all.dts')
1831
1832         base_expected = {
1833             'section:image-pos': 0,
1834             'u-boot-tpl-dtb:size': 513,
1835             'u-boot-spl-dtb:size': 513,
1836             'u-boot-spl-dtb:offset': 493,
1837             'image-pos': 0,
1838             'section/u-boot-dtb:image-pos': 0,
1839             'u-boot-spl-dtb:image-pos': 493,
1840             'section/u-boot-dtb:size': 493,
1841             'u-boot-tpl-dtb:image-pos': 1006,
1842             'section/u-boot-dtb:offset': 0,
1843             'section:size': 493,
1844             'offset': 0,
1845             'section:offset': 0,
1846             'u-boot-tpl-dtb:offset': 1006,
1847             'size': 1519
1848         }
1849
1850         # We expect three device-tree files in the output, one after the other.
1851         # Read them in sequence. We look for an 'spl' property in the SPL tree,
1852         # and 'tpl' in the TPL tree, to make sure they are distinct from the
1853         # main U-Boot tree. All three should have the same postions and offset.
1854         start = 0
1855         for item in ['', 'spl', 'tpl']:
1856             dtb = fdt.Fdt.FromData(data[start:])
1857             dtb.Scan()
1858             props = self._GetPropTree(dtb, BASE_DTB_PROPS + REPACK_DTB_PROPS +
1859                                       ['spl', 'tpl'])
1860             expected = dict(base_expected)
1861             if item:
1862                 expected[item] = 0
1863             self.assertEqual(expected, props)
1864             start += dtb._fdt_obj.totalsize()
1865
1866     def testUpdateFdtOutput(self):
1867         """Test that output DTB files are updated"""
1868         try:
1869             data, dtb_data, _, _ = self._DoReadFileDtb('082_fdt_update_all.dts',
1870                     use_real_dtb=True, update_dtb=True, reset_dtbs=False)
1871
1872             # Unfortunately, compiling a source file always results in a file
1873             # called source.dtb (see fdt_util.EnsureCompiled()). The test
1874             # source file (e.g. test/075_fdt_update_all.dts) thus does not enter
1875             # binman as a file called u-boot.dtb. To fix this, copy the file
1876             # over to the expected place.
1877             start = 0
1878             for fname in ['u-boot.dtb.out', 'spl/u-boot-spl.dtb.out',
1879                           'tpl/u-boot-tpl.dtb.out']:
1880                 dtb = fdt.Fdt.FromData(data[start:])
1881                 size = dtb._fdt_obj.totalsize()
1882                 pathname = tools.GetOutputFilename(os.path.split(fname)[1])
1883                 outdata = tools.ReadFile(pathname)
1884                 name = os.path.split(fname)[0]
1885
1886                 if name:
1887                     orig_indata = self._GetDtbContentsForSplTpl(dtb_data, name)
1888                 else:
1889                     orig_indata = dtb_data
1890                 self.assertNotEqual(outdata, orig_indata,
1891                         "Expected output file '%s' be updated" % pathname)
1892                 self.assertEqual(outdata, data[start:start + size],
1893                         "Expected output file '%s' to match output image" %
1894                         pathname)
1895                 start += size
1896         finally:
1897             self._ResetDtbs()
1898
1899     def _decompress(self, data):
1900         return tools.Decompress(data, 'lz4')
1901
1902     def testCompress(self):
1903         """Test compression of blobs"""
1904         self._CheckLz4()
1905         data, _, _, out_dtb_fname = self._DoReadFileDtb('083_compress.dts',
1906                                             use_real_dtb=True, update_dtb=True)
1907         dtb = fdt.Fdt(out_dtb_fname)
1908         dtb.Scan()
1909         props = self._GetPropTree(dtb, ['size', 'uncomp-size'])
1910         orig = self._decompress(data)
1911         self.assertEquals(COMPRESS_DATA, orig)
1912
1913         # Do a sanity check on various fields
1914         image = control.images['image']
1915         entries = image.GetEntries()
1916         self.assertEqual(1, len(entries))
1917
1918         entry = entries['blob']
1919         self.assertEqual(COMPRESS_DATA, entry.uncomp_data)
1920         self.assertEqual(len(COMPRESS_DATA), entry.uncomp_size)
1921         orig = self._decompress(entry.data)
1922         self.assertEqual(orig, entry.uncomp_data)
1923
1924         self.assertEqual(image.data, entry.data)
1925
1926         expected = {
1927             'blob:uncomp-size': len(COMPRESS_DATA),
1928             'blob:size': len(data),
1929             'size': len(data),
1930             }
1931         self.assertEqual(expected, props)
1932
1933     def testFiles(self):
1934         """Test bringing in multiple files"""
1935         data = self._DoReadFile('084_files.dts')
1936         self.assertEqual(FILES_DATA, data)
1937
1938     def testFilesCompress(self):
1939         """Test bringing in multiple files and compressing them"""
1940         self._CheckLz4()
1941         data = self._DoReadFile('085_files_compress.dts')
1942
1943         image = control.images['image']
1944         entries = image.GetEntries()
1945         files = entries['files']
1946         entries = files._entries
1947
1948         orig = b''
1949         for i in range(1, 3):
1950             key = '%d.dat' % i
1951             start = entries[key].image_pos
1952             len = entries[key].size
1953             chunk = data[start:start + len]
1954             orig += self._decompress(chunk)
1955
1956         self.assertEqual(FILES_DATA, orig)
1957
1958     def testFilesMissing(self):
1959         """Test missing files"""
1960         with self.assertRaises(ValueError) as e:
1961             data = self._DoReadFile('086_files_none.dts')
1962         self.assertIn("Node '/binman/files': Pattern \'files/*.none\' matched "
1963                       'no files', str(e.exception))
1964
1965     def testFilesNoPattern(self):
1966         """Test missing files"""
1967         with self.assertRaises(ValueError) as e:
1968             data = self._DoReadFile('087_files_no_pattern.dts')
1969         self.assertIn("Node '/binman/files': Missing 'pattern' property",
1970                       str(e.exception))
1971
1972     def testExpandSize(self):
1973         """Test an expanding entry"""
1974         data, _, map_data, _ = self._DoReadFileDtb('088_expand_size.dts',
1975                                                    map=True)
1976         expect = (tools.GetBytes(ord('a'), 8) + U_BOOT_DATA +
1977                   MRC_DATA + tools.GetBytes(ord('b'), 1) + U_BOOT_DATA +
1978                   tools.GetBytes(ord('c'), 8) + U_BOOT_DATA +
1979                   tools.GetBytes(ord('d'), 8))
1980         self.assertEqual(expect, data)
1981         self.assertEqual('''ImagePos    Offset      Size  Name
1982 00000000  00000000  00000028  main-section
1983 00000000   00000000  00000008  fill
1984 00000008   00000008  00000004  u-boot
1985 0000000c   0000000c  00000004  section
1986 0000000c    00000000  00000003  intel-mrc
1987 00000010   00000010  00000004  u-boot2
1988 00000014   00000014  0000000c  section2
1989 00000014    00000000  00000008  fill
1990 0000001c    00000008  00000004  u-boot
1991 00000020   00000020  00000008  fill2
1992 ''', map_data)
1993
1994     def testExpandSizeBad(self):
1995         """Test an expanding entry which fails to provide contents"""
1996         with test_util.capture_sys_output() as (stdout, stderr):
1997             with self.assertRaises(ValueError) as e:
1998                 self._DoReadFileDtb('089_expand_size_bad.dts', map=True)
1999         self.assertIn("Node '/binman/_testing': Cannot obtain contents when "
2000                       'expanding entry', str(e.exception))
2001
2002     def testHash(self):
2003         """Test hashing of the contents of an entry"""
2004         _, _, _, out_dtb_fname = self._DoReadFileDtb('090_hash.dts',
2005                 use_real_dtb=True, update_dtb=True)
2006         dtb = fdt.Fdt(out_dtb_fname)
2007         dtb.Scan()
2008         hash_node = dtb.GetNode('/binman/u-boot/hash').props['value']
2009         m = hashlib.sha256()
2010         m.update(U_BOOT_DATA)
2011         self.assertEqual(m.digest(), b''.join(hash_node.value))
2012
2013     def testHashNoAlgo(self):
2014         with self.assertRaises(ValueError) as e:
2015             self._DoReadFileDtb('091_hash_no_algo.dts', update_dtb=True)
2016         self.assertIn("Node \'/binman/u-boot\': Missing \'algo\' property for "
2017                       'hash node', str(e.exception))
2018
2019     def testHashBadAlgo(self):
2020         with self.assertRaises(ValueError) as e:
2021             self._DoReadFileDtb('092_hash_bad_algo.dts', update_dtb=True)
2022         self.assertIn("Node '/binman/u-boot': Unknown hash algorithm",
2023                       str(e.exception))
2024
2025     def testHashSection(self):
2026         """Test hashing of the contents of an entry"""
2027         _, _, _, out_dtb_fname = self._DoReadFileDtb('099_hash_section.dts',
2028                 use_real_dtb=True, update_dtb=True)
2029         dtb = fdt.Fdt(out_dtb_fname)
2030         dtb.Scan()
2031         hash_node = dtb.GetNode('/binman/section/hash').props['value']
2032         m = hashlib.sha256()
2033         m.update(U_BOOT_DATA)
2034         m.update(tools.GetBytes(ord('a'), 16))
2035         self.assertEqual(m.digest(), b''.join(hash_node.value))
2036
2037     def testPackUBootTplMicrocode(self):
2038         """Test that x86 microcode can be handled correctly in TPL
2039
2040         We expect to see the following in the image, in order:
2041             u-boot-tpl-nodtb.bin with a microcode pointer inserted at the correct
2042                 place
2043             u-boot-tpl.dtb with the microcode removed
2044             the microcode
2045         """
2046         self._SetupTplElf('u_boot_ucode_ptr')
2047         first, pos_and_size = self._RunMicrocodeTest('093_x86_tpl_ucode.dts',
2048                                                      U_BOOT_TPL_NODTB_DATA)
2049         self.assertEqual(b'tplnodtb with microc' + pos_and_size +
2050                          b'ter somewhere in here', first)
2051
2052     def testFmapX86(self):
2053         """Basic test of generation of a flashrom fmap"""
2054         data = self._DoReadFile('094_fmap_x86.dts')
2055         fhdr, fentries = fmap_util.DecodeFmap(data[32:])
2056         expected = U_BOOT_DATA + MRC_DATA + tools.GetBytes(ord('a'), 32 - 7)
2057         self.assertEqual(expected, data[:32])
2058         fhdr, fentries = fmap_util.DecodeFmap(data[32:])
2059
2060         self.assertEqual(0x100, fhdr.image_size)
2061
2062         self.assertEqual(0, fentries[0].offset)
2063         self.assertEqual(4, fentries[0].size)
2064         self.assertEqual(b'U_BOOT', fentries[0].name)
2065
2066         self.assertEqual(4, fentries[1].offset)
2067         self.assertEqual(3, fentries[1].size)
2068         self.assertEqual(b'INTEL_MRC', fentries[1].name)
2069
2070         self.assertEqual(32, fentries[2].offset)
2071         self.assertEqual(fmap_util.FMAP_HEADER_LEN +
2072                          fmap_util.FMAP_AREA_LEN * 3, fentries[2].size)
2073         self.assertEqual(b'FMAP', fentries[2].name)
2074
2075     def testFmapX86Section(self):
2076         """Basic test of generation of a flashrom fmap"""
2077         data = self._DoReadFile('095_fmap_x86_section.dts')
2078         expected = U_BOOT_DATA + MRC_DATA + tools.GetBytes(ord('b'), 32 - 7)
2079         self.assertEqual(expected, data[:32])
2080         fhdr, fentries = fmap_util.DecodeFmap(data[36:])
2081
2082         self.assertEqual(0x180, fhdr.image_size)
2083         expect_size = fmap_util.FMAP_HEADER_LEN + fmap_util.FMAP_AREA_LEN * 4
2084         fiter = iter(fentries)
2085
2086         fentry = next(fiter)
2087         self.assertEqual(b'U_BOOT', fentry.name)
2088         self.assertEqual(0, fentry.offset)
2089         self.assertEqual(4, fentry.size)
2090
2091         fentry = next(fiter)
2092         self.assertEqual(b'SECTION', fentry.name)
2093         self.assertEqual(4, fentry.offset)
2094         self.assertEqual(0x20 + expect_size, fentry.size)
2095
2096         fentry = next(fiter)
2097         self.assertEqual(b'INTEL_MRC', fentry.name)
2098         self.assertEqual(4, fentry.offset)
2099         self.assertEqual(3, fentry.size)
2100
2101         fentry = next(fiter)
2102         self.assertEqual(b'FMAP', fentry.name)
2103         self.assertEqual(36, fentry.offset)
2104         self.assertEqual(expect_size, fentry.size)
2105
2106     def testElf(self):
2107         """Basic test of ELF entries"""
2108         self._SetupSplElf()
2109         self._SetupTplElf()
2110         with open(self.ElfTestFile('bss_data'), 'rb') as fd:
2111             TestFunctional._MakeInputFile('-boot', fd.read())
2112         data = self._DoReadFile('096_elf.dts')
2113
2114     def testElfStrip(self):
2115         """Basic test of ELF entries"""
2116         self._SetupSplElf()
2117         with open(self.ElfTestFile('bss_data'), 'rb') as fd:
2118             TestFunctional._MakeInputFile('-boot', fd.read())
2119         data = self._DoReadFile('097_elf_strip.dts')
2120
2121     def testPackOverlapMap(self):
2122         """Test that overlapping regions are detected"""
2123         with test_util.capture_sys_output() as (stdout, stderr):
2124             with self.assertRaises(ValueError) as e:
2125                 self._DoTestFile('014_pack_overlap.dts', map=True)
2126         map_fname = tools.GetOutputFilename('image.map')
2127         self.assertEqual("Wrote map file '%s' to show errors\n" % map_fname,
2128                          stdout.getvalue())
2129
2130         # We should not get an inmage, but there should be a map file
2131         self.assertFalse(os.path.exists(tools.GetOutputFilename('image.bin')))
2132         self.assertTrue(os.path.exists(map_fname))
2133         map_data = tools.ReadFile(map_fname, binary=False)
2134         self.assertEqual('''ImagePos    Offset      Size  Name
2135 <none>    00000000  00000008  main-section
2136 <none>     00000000  00000004  u-boot
2137 <none>     00000003  00000004  u-boot-align
2138 ''', map_data)
2139
2140     def testPackRefCode(self):
2141         """Test that an image with an Intel Reference code binary works"""
2142         data = self._DoReadFile('100_intel_refcode.dts')
2143         self.assertEqual(REFCODE_DATA, data[:len(REFCODE_DATA)])
2144
2145     def testSectionOffset(self):
2146         """Tests use of a section with an offset"""
2147         data, _, map_data, _ = self._DoReadFileDtb('101_sections_offset.dts',
2148                                                    map=True)
2149         self.assertEqual('''ImagePos    Offset      Size  Name
2150 00000000  00000000  00000038  main-section
2151 00000004   00000004  00000010  section@0
2152 00000004    00000000  00000004  u-boot
2153 00000018   00000018  00000010  section@1
2154 00000018    00000000  00000004  u-boot
2155 0000002c   0000002c  00000004  section@2
2156 0000002c    00000000  00000004  u-boot
2157 ''', map_data)
2158         self.assertEqual(data,
2159                          tools.GetBytes(0x26, 4) + U_BOOT_DATA +
2160                              tools.GetBytes(0x21, 12) +
2161                          tools.GetBytes(0x26, 4) + U_BOOT_DATA +
2162                              tools.GetBytes(0x61, 12) +
2163                          tools.GetBytes(0x26, 4) + U_BOOT_DATA +
2164                              tools.GetBytes(0x26, 8))
2165
2166     def testCbfsRaw(self):
2167         """Test base handling of a Coreboot Filesystem (CBFS)
2168
2169         The exact contents of the CBFS is verified by similar tests in
2170         cbfs_util_test.py. The tests here merely check that the files added to
2171         the CBFS can be found in the final image.
2172         """
2173         data = self._DoReadFile('102_cbfs_raw.dts')
2174         size = 0xb0
2175
2176         cbfs = cbfs_util.CbfsReader(data)
2177         self.assertEqual(size, cbfs.rom_size)
2178
2179         self.assertIn('u-boot-dtb', cbfs.files)
2180         cfile = cbfs.files['u-boot-dtb']
2181         self.assertEqual(U_BOOT_DTB_DATA, cfile.data)
2182
2183     def testCbfsArch(self):
2184         """Test on non-x86 architecture"""
2185         data = self._DoReadFile('103_cbfs_raw_ppc.dts')
2186         size = 0x100
2187
2188         cbfs = cbfs_util.CbfsReader(data)
2189         self.assertEqual(size, cbfs.rom_size)
2190
2191         self.assertIn('u-boot-dtb', cbfs.files)
2192         cfile = cbfs.files['u-boot-dtb']
2193         self.assertEqual(U_BOOT_DTB_DATA, cfile.data)
2194
2195     def testCbfsStage(self):
2196         """Tests handling of a Coreboot Filesystem (CBFS)"""
2197         if not elf.ELF_TOOLS:
2198             self.skipTest('Python elftools not available')
2199         elf_fname = os.path.join(self._indir, 'cbfs-stage.elf')
2200         elf.MakeElf(elf_fname, U_BOOT_DATA, U_BOOT_DTB_DATA)
2201         size = 0xb0
2202
2203         data = self._DoReadFile('104_cbfs_stage.dts')
2204         cbfs = cbfs_util.CbfsReader(data)
2205         self.assertEqual(size, cbfs.rom_size)
2206
2207         self.assertIn('u-boot', cbfs.files)
2208         cfile = cbfs.files['u-boot']
2209         self.assertEqual(U_BOOT_DATA + U_BOOT_DTB_DATA, cfile.data)
2210
2211     def testCbfsRawCompress(self):
2212         """Test handling of compressing raw files"""
2213         self._CheckLz4()
2214         data = self._DoReadFile('105_cbfs_raw_compress.dts')
2215         size = 0x140
2216
2217         cbfs = cbfs_util.CbfsReader(data)
2218         self.assertIn('u-boot', cbfs.files)
2219         cfile = cbfs.files['u-boot']
2220         self.assertEqual(COMPRESS_DATA, cfile.data)
2221
2222     def testCbfsBadArch(self):
2223         """Test handling of a bad architecture"""
2224         with self.assertRaises(ValueError) as e:
2225             self._DoReadFile('106_cbfs_bad_arch.dts')
2226         self.assertIn("Invalid architecture 'bad-arch'", str(e.exception))
2227
2228     def testCbfsNoSize(self):
2229         """Test handling of a missing size property"""
2230         with self.assertRaises(ValueError) as e:
2231             self._DoReadFile('107_cbfs_no_size.dts')
2232         self.assertIn('entry must have a size property', str(e.exception))
2233
2234     def testCbfsNoCOntents(self):
2235         """Test handling of a CBFS entry which does not provide contentsy"""
2236         with self.assertRaises(ValueError) as e:
2237             self._DoReadFile('108_cbfs_no_contents.dts')
2238         self.assertIn('Could not complete processing of contents',
2239                       str(e.exception))
2240
2241     def testCbfsBadCompress(self):
2242         """Test handling of a bad architecture"""
2243         with self.assertRaises(ValueError) as e:
2244             self._DoReadFile('109_cbfs_bad_compress.dts')
2245         self.assertIn("Invalid compression in 'u-boot': 'invalid-algo'",
2246                       str(e.exception))
2247
2248     def testCbfsNamedEntries(self):
2249         """Test handling of named entries"""
2250         data = self._DoReadFile('110_cbfs_name.dts')
2251
2252         cbfs = cbfs_util.CbfsReader(data)
2253         self.assertIn('FRED', cbfs.files)
2254         cfile1 = cbfs.files['FRED']
2255         self.assertEqual(U_BOOT_DATA, cfile1.data)
2256
2257         self.assertIn('hello', cbfs.files)
2258         cfile2 = cbfs.files['hello']
2259         self.assertEqual(U_BOOT_DTB_DATA, cfile2.data)
2260
2261     def _SetupIfwi(self, fname):
2262         """Set up to run an IFWI test
2263
2264         Args:
2265             fname: Filename of input file to provide (fitimage.bin or ifwi.bin)
2266         """
2267         self._SetupSplElf()
2268         self._SetupTplElf()
2269
2270         # Intel Integrated Firmware Image (IFWI) file
2271         with gzip.open(self.TestFile('%s.gz' % fname), 'rb') as fd:
2272             data = fd.read()
2273         TestFunctional._MakeInputFile(fname,data)
2274
2275     def _CheckIfwi(self, data):
2276         """Check that an image with an IFWI contains the correct output
2277
2278         Args:
2279             data: Conents of output file
2280         """
2281         expected_desc = tools.ReadFile(self.TestFile('descriptor.bin'))
2282         if data[:0x1000] != expected_desc:
2283             self.fail('Expected descriptor binary at start of image')
2284
2285         # We expect to find the TPL wil in subpart IBBP entry IBBL
2286         image_fname = tools.GetOutputFilename('image.bin')
2287         tpl_fname = tools.GetOutputFilename('tpl.out')
2288         tools.RunIfwiTool(image_fname, tools.CMD_EXTRACT, fname=tpl_fname,
2289                           subpart='IBBP', entry_name='IBBL')
2290
2291         tpl_data = tools.ReadFile(tpl_fname)
2292         self.assertEqual(U_BOOT_TPL_DATA, tpl_data[:len(U_BOOT_TPL_DATA)])
2293
2294     def testPackX86RomIfwi(self):
2295         """Test that an x86 ROM with Integrated Firmware Image can be created"""
2296         self._SetupIfwi('fitimage.bin')
2297         data = self._DoReadFile('111_x86_rom_ifwi.dts')
2298         self._CheckIfwi(data)
2299
2300     def testPackX86RomIfwiNoDesc(self):
2301         """Test that an x86 ROM with IFWI can be created from an ifwi.bin file"""
2302         self._SetupIfwi('ifwi.bin')
2303         data = self._DoReadFile('112_x86_rom_ifwi_nodesc.dts')
2304         self._CheckIfwi(data)
2305
2306     def testPackX86RomIfwiNoData(self):
2307         """Test that an x86 ROM with IFWI handles missing data"""
2308         self._SetupIfwi('ifwi.bin')
2309         with self.assertRaises(ValueError) as e:
2310             data = self._DoReadFile('113_x86_rom_ifwi_nodata.dts')
2311         self.assertIn('Could not complete processing of contents',
2312                       str(e.exception))
2313
2314     def testCbfsOffset(self):
2315         """Test a CBFS with files at particular offsets
2316
2317         Like all CFBS tests, this is just checking the logic that calls
2318         cbfs_util. See cbfs_util_test for fully tests (e.g. test_cbfs_offset()).
2319         """
2320         data = self._DoReadFile('114_cbfs_offset.dts')
2321         size = 0x200
2322
2323         cbfs = cbfs_util.CbfsReader(data)
2324         self.assertEqual(size, cbfs.rom_size)
2325
2326         self.assertIn('u-boot', cbfs.files)
2327         cfile = cbfs.files['u-boot']
2328         self.assertEqual(U_BOOT_DATA, cfile.data)
2329         self.assertEqual(0x40, cfile.cbfs_offset)
2330
2331         self.assertIn('u-boot-dtb', cbfs.files)
2332         cfile2 = cbfs.files['u-boot-dtb']
2333         self.assertEqual(U_BOOT_DTB_DATA, cfile2.data)
2334         self.assertEqual(0x140, cfile2.cbfs_offset)
2335
2336     def testFdtmap(self):
2337         """Test an FDT map can be inserted in the image"""
2338         data = self.data = self._DoReadFileRealDtb('115_fdtmap.dts')
2339         fdtmap_data = data[len(U_BOOT_DATA):]
2340         magic = fdtmap_data[:8]
2341         self.assertEqual(b'_FDTMAP_', magic)
2342         self.assertEqual(tools.GetBytes(0, 8), fdtmap_data[8:16])
2343
2344         fdt_data = fdtmap_data[16:]
2345         dtb = fdt.Fdt.FromData(fdt_data)
2346         dtb.Scan()
2347         props = self._GetPropTree(dtb, BASE_DTB_PROPS, prefix='/')
2348         self.assertEqual({
2349             'image-pos': 0,
2350             'offset': 0,
2351             'u-boot:offset': 0,
2352             'u-boot:size': len(U_BOOT_DATA),
2353             'u-boot:image-pos': 0,
2354             'fdtmap:image-pos': 4,
2355             'fdtmap:offset': 4,
2356             'fdtmap:size': len(fdtmap_data),
2357             'size': len(data),
2358         }, props)
2359
2360     def testFdtmapNoMatch(self):
2361         """Check handling of an FDT map when the section cannot be found"""
2362         self.data = self._DoReadFileRealDtb('115_fdtmap.dts')
2363
2364         # Mangle the section name, which should cause a mismatch between the
2365         # correct FDT path and the one expected by the section
2366         image = control.images['image']
2367         image._node.path += '-suffix'
2368         entries = image.GetEntries()
2369         fdtmap = entries['fdtmap']
2370         with self.assertRaises(ValueError) as e:
2371             fdtmap._GetFdtmap()
2372         self.assertIn("Cannot locate node for path '/binman-suffix'",
2373                       str(e.exception))
2374
2375     def testFdtmapHeader(self):
2376         """Test an FDT map and image header can be inserted in the image"""
2377         data = self.data = self._DoReadFileRealDtb('116_fdtmap_hdr.dts')
2378         fdtmap_pos = len(U_BOOT_DATA)
2379         fdtmap_data = data[fdtmap_pos:]
2380         fdt_data = fdtmap_data[16:]
2381         dtb = fdt.Fdt.FromData(fdt_data)
2382         fdt_size = dtb.GetFdtObj().totalsize()
2383         hdr_data = data[-8:]
2384         self.assertEqual(b'BinM', hdr_data[:4])
2385         offset = struct.unpack('<I', hdr_data[4:])[0] & 0xffffffff
2386         self.assertEqual(fdtmap_pos - 0x400, offset - (1 << 32))
2387
2388     def testFdtmapHeaderStart(self):
2389         """Test an image header can be inserted at the image start"""
2390         data = self.data = self._DoReadFileRealDtb('117_fdtmap_hdr_start.dts')
2391         fdtmap_pos = 0x100 + len(U_BOOT_DATA)
2392         hdr_data = data[:8]
2393         self.assertEqual(b'BinM', hdr_data[:4])
2394         offset = struct.unpack('<I', hdr_data[4:])[0]
2395         self.assertEqual(fdtmap_pos, offset)
2396
2397     def testFdtmapHeaderPos(self):
2398         """Test an image header can be inserted at a chosen position"""
2399         data = self.data = self._DoReadFileRealDtb('118_fdtmap_hdr_pos.dts')
2400         fdtmap_pos = 0x100 + len(U_BOOT_DATA)
2401         hdr_data = data[0x80:0x88]
2402         self.assertEqual(b'BinM', hdr_data[:4])
2403         offset = struct.unpack('<I', hdr_data[4:])[0]
2404         self.assertEqual(fdtmap_pos, offset)
2405
2406     def testHeaderMissingFdtmap(self):
2407         """Test an image header requires an fdtmap"""
2408         with self.assertRaises(ValueError) as e:
2409             self.data = self._DoReadFileRealDtb('119_fdtmap_hdr_missing.dts')
2410         self.assertIn("'image_header' section must have an 'fdtmap' sibling",
2411                       str(e.exception))
2412
2413     def testHeaderNoLocation(self):
2414         """Test an image header with a no specified location is detected"""
2415         with self.assertRaises(ValueError) as e:
2416             self.data = self._DoReadFileRealDtb('120_hdr_no_location.dts')
2417         self.assertIn("Invalid location 'None', expected 'start' or 'end'",
2418                       str(e.exception))
2419
2420     def testEntryExpand(self):
2421         """Test expanding an entry after it is packed"""
2422         data = self._DoReadFile('121_entry_expand.dts')
2423         self.assertEqual(b'aaa', data[:3])
2424         self.assertEqual(U_BOOT_DATA, data[3:3 + len(U_BOOT_DATA)])
2425         self.assertEqual(b'aaa', data[-3:])
2426
2427     def testEntryExpandBad(self):
2428         """Test expanding an entry after it is packed, twice"""
2429         with self.assertRaises(ValueError) as e:
2430             self._DoReadFile('122_entry_expand_twice.dts')
2431         self.assertIn("Image '/binman': Entries changed size after packing",
2432                       str(e.exception))
2433
2434     def testEntryExpandSection(self):
2435         """Test expanding an entry within a section after it is packed"""
2436         data = self._DoReadFile('123_entry_expand_section.dts')
2437         self.assertEqual(b'aaa', data[:3])
2438         self.assertEqual(U_BOOT_DATA, data[3:3 + len(U_BOOT_DATA)])
2439         self.assertEqual(b'aaa', data[-3:])
2440
2441     def testCompressDtb(self):
2442         """Test that compress of device-tree files is supported"""
2443         self._CheckLz4()
2444         data = self.data = self._DoReadFileRealDtb('124_compress_dtb.dts')
2445         self.assertEqual(U_BOOT_DATA, data[:len(U_BOOT_DATA)])
2446         comp_data = data[len(U_BOOT_DATA):]
2447         orig = self._decompress(comp_data)
2448         dtb = fdt.Fdt.FromData(orig)
2449         dtb.Scan()
2450         props = self._GetPropTree(dtb, ['size', 'uncomp-size'])
2451         expected = {
2452             'u-boot:size': len(U_BOOT_DATA),
2453             'u-boot-dtb:uncomp-size': len(orig),
2454             'u-boot-dtb:size': len(comp_data),
2455             'size': len(data),
2456             }
2457         self.assertEqual(expected, props)
2458
2459     def testCbfsUpdateFdt(self):
2460         """Test that we can update the device tree with CBFS offset/size info"""
2461         self._CheckLz4()
2462         data, _, _, out_dtb_fname = self._DoReadFileDtb('125_cbfs_update.dts',
2463                                                         update_dtb=True)
2464         dtb = fdt.Fdt(out_dtb_fname)
2465         dtb.Scan()
2466         props = self._GetPropTree(dtb, BASE_DTB_PROPS + ['uncomp-size'])
2467         del props['cbfs/u-boot:size']
2468         self.assertEqual({
2469             'offset': 0,
2470             'size': len(data),
2471             'image-pos': 0,
2472             'cbfs:offset': 0,
2473             'cbfs:size': len(data),
2474             'cbfs:image-pos': 0,
2475             'cbfs/u-boot:offset': 0x38,
2476             'cbfs/u-boot:uncomp-size': len(U_BOOT_DATA),
2477             'cbfs/u-boot:image-pos': 0x38,
2478             'cbfs/u-boot-dtb:offset': 0xb8,
2479             'cbfs/u-boot-dtb:size': len(U_BOOT_DATA),
2480             'cbfs/u-boot-dtb:image-pos': 0xb8,
2481             }, props)
2482
2483     def testCbfsBadType(self):
2484         """Test an image header with a no specified location is detected"""
2485         with self.assertRaises(ValueError) as e:
2486             self._DoReadFile('126_cbfs_bad_type.dts')
2487         self.assertIn("Unknown cbfs-type 'badtype'", str(e.exception))
2488
2489     def testList(self):
2490         """Test listing the files in an image"""
2491         self._CheckLz4()
2492         data = self._DoReadFile('127_list.dts')
2493         image = control.images['image']
2494         entries = image.BuildEntryList()
2495         self.assertEqual(7, len(entries))
2496
2497         ent = entries[0]
2498         self.assertEqual(0, ent.indent)
2499         self.assertEqual('main-section', ent.name)
2500         self.assertEqual('section', ent.etype)
2501         self.assertEqual(len(data), ent.size)
2502         self.assertEqual(0, ent.image_pos)
2503         self.assertEqual(None, ent.uncomp_size)
2504         self.assertEqual(0, ent.offset)
2505
2506         ent = entries[1]
2507         self.assertEqual(1, ent.indent)
2508         self.assertEqual('u-boot', ent.name)
2509         self.assertEqual('u-boot', ent.etype)
2510         self.assertEqual(len(U_BOOT_DATA), ent.size)
2511         self.assertEqual(0, ent.image_pos)
2512         self.assertEqual(None, ent.uncomp_size)
2513         self.assertEqual(0, ent.offset)
2514
2515         ent = entries[2]
2516         self.assertEqual(1, ent.indent)
2517         self.assertEqual('section', ent.name)
2518         self.assertEqual('section', ent.etype)
2519         section_size = ent.size
2520         self.assertEqual(0x100, ent.image_pos)
2521         self.assertEqual(None, ent.uncomp_size)
2522         self.assertEqual(0x100, ent.offset)
2523
2524         ent = entries[3]
2525         self.assertEqual(2, ent.indent)
2526         self.assertEqual('cbfs', ent.name)
2527         self.assertEqual('cbfs', ent.etype)
2528         self.assertEqual(0x400, ent.size)
2529         self.assertEqual(0x100, ent.image_pos)
2530         self.assertEqual(None, ent.uncomp_size)
2531         self.assertEqual(0, ent.offset)
2532
2533         ent = entries[4]
2534         self.assertEqual(3, ent.indent)
2535         self.assertEqual('u-boot', ent.name)
2536         self.assertEqual('u-boot', ent.etype)
2537         self.assertEqual(len(U_BOOT_DATA), ent.size)
2538         self.assertEqual(0x138, ent.image_pos)
2539         self.assertEqual(None, ent.uncomp_size)
2540         self.assertEqual(0x38, ent.offset)
2541
2542         ent = entries[5]
2543         self.assertEqual(3, ent.indent)
2544         self.assertEqual('u-boot-dtb', ent.name)
2545         self.assertEqual('text', ent.etype)
2546         self.assertGreater(len(COMPRESS_DATA), ent.size)
2547         self.assertEqual(0x178, ent.image_pos)
2548         self.assertEqual(len(COMPRESS_DATA), ent.uncomp_size)
2549         self.assertEqual(0x78, ent.offset)
2550
2551         ent = entries[6]
2552         self.assertEqual(2, ent.indent)
2553         self.assertEqual('u-boot-dtb', ent.name)
2554         self.assertEqual('u-boot-dtb', ent.etype)
2555         self.assertEqual(0x500, ent.image_pos)
2556         self.assertEqual(len(U_BOOT_DTB_DATA), ent.uncomp_size)
2557         dtb_size = ent.size
2558         # Compressing this data expands it since headers are added
2559         self.assertGreater(dtb_size, len(U_BOOT_DTB_DATA))
2560         self.assertEqual(0x400, ent.offset)
2561
2562         self.assertEqual(len(data), 0x100 + section_size)
2563         self.assertEqual(section_size, 0x400 + dtb_size)
2564
2565     def testFindFdtmap(self):
2566         """Test locating an FDT map in an image"""
2567         self._CheckLz4()
2568         data = self.data = self._DoReadFileRealDtb('128_decode_image.dts')
2569         image = control.images['image']
2570         entries = image.GetEntries()
2571         entry = entries['fdtmap']
2572         self.assertEqual(entry.image_pos, fdtmap.LocateFdtmap(data))
2573
2574     def testFindFdtmapMissing(self):
2575         """Test failing to locate an FDP map"""
2576         data = self._DoReadFile('005_simple.dts')
2577         self.assertEqual(None, fdtmap.LocateFdtmap(data))
2578
2579     def testFindImageHeader(self):
2580         """Test locating a image header"""
2581         self._CheckLz4()
2582         data = self.data = self._DoReadFileRealDtb('128_decode_image.dts')
2583         image = control.images['image']
2584         entries = image.GetEntries()
2585         entry = entries['fdtmap']
2586         # The header should point to the FDT map
2587         self.assertEqual(entry.image_pos, image_header.LocateHeaderOffset(data))
2588
2589     def testFindImageHeaderStart(self):
2590         """Test locating a image header located at the start of an image"""
2591         data = self.data = self._DoReadFileRealDtb('117_fdtmap_hdr_start.dts')
2592         image = control.images['image']
2593         entries = image.GetEntries()
2594         entry = entries['fdtmap']
2595         # The header should point to the FDT map
2596         self.assertEqual(entry.image_pos, image_header.LocateHeaderOffset(data))
2597
2598     def testFindImageHeaderMissing(self):
2599         """Test failing to locate an image header"""
2600         data = self._DoReadFile('005_simple.dts')
2601         self.assertEqual(None, image_header.LocateHeaderOffset(data))
2602
2603     def testReadImage(self):
2604         """Test reading an image and accessing its FDT map"""
2605         self._CheckLz4()
2606         data = self.data = self._DoReadFileRealDtb('128_decode_image.dts')
2607         image_fname = tools.GetOutputFilename('image.bin')
2608         orig_image = control.images['image']
2609         image = Image.FromFile(image_fname)
2610         self.assertEqual(orig_image.GetEntries().keys(),
2611                          image.GetEntries().keys())
2612
2613         orig_entry = orig_image.GetEntries()['fdtmap']
2614         entry = image.GetEntries()['fdtmap']
2615         self.assertEquals(orig_entry.offset, entry.offset)
2616         self.assertEquals(orig_entry.size, entry.size)
2617         self.assertEquals(orig_entry.image_pos, entry.image_pos)
2618
2619     def testReadImageNoHeader(self):
2620         """Test accessing an image's FDT map without an image header"""
2621         self._CheckLz4()
2622         data = self._DoReadFileRealDtb('129_decode_image_nohdr.dts')
2623         image_fname = tools.GetOutputFilename('image.bin')
2624         image = Image.FromFile(image_fname)
2625         self.assertTrue(isinstance(image, Image))
2626         self.assertEqual('image', image.image_name[-5:])
2627
2628     def testReadImageFail(self):
2629         """Test failing to read an image image's FDT map"""
2630         self._DoReadFile('005_simple.dts')
2631         image_fname = tools.GetOutputFilename('image.bin')
2632         with self.assertRaises(ValueError) as e:
2633             image = Image.FromFile(image_fname)
2634         self.assertIn("Cannot find FDT map in image", str(e.exception))
2635
2636     def testListCmd(self):
2637         """Test listing the files in an image using an Fdtmap"""
2638         self._CheckLz4()
2639         data = self._DoReadFileRealDtb('130_list_fdtmap.dts')
2640
2641         # lz4 compression size differs depending on the version
2642         image = control.images['image']
2643         entries = image.GetEntries()
2644         section_size = entries['section'].size
2645         fdt_size = entries['section'].GetEntries()['u-boot-dtb'].size
2646         fdtmap_offset = entries['fdtmap'].offset
2647
2648         try:
2649             tmpdir, updated_fname = self._SetupImageInTmpdir()
2650             with test_util.capture_sys_output() as (stdout, stderr):
2651                 self._DoBinman('ls', '-i', updated_fname)
2652         finally:
2653             shutil.rmtree(tmpdir)
2654         lines = stdout.getvalue().splitlines()
2655         expected = [
2656 'Name              Image-pos  Size  Entry-type    Offset  Uncomp-size',
2657 '----------------------------------------------------------------------',
2658 'main-section              0   c00  section            0',
2659 '  u-boot                  0     4  u-boot             0',
2660 '  section               100   %x  section          100' % section_size,
2661 '    cbfs                100   400  cbfs               0',
2662 '      u-boot            138     4  u-boot            38',
2663 '      u-boot-dtb        180   105  u-boot-dtb        80          3c9',
2664 '    u-boot-dtb          500   %x  u-boot-dtb       400          3c9' % fdt_size,
2665 '  fdtmap                %x   3bd  fdtmap           %x' %
2666         (fdtmap_offset, fdtmap_offset),
2667 '  image-header          bf8     8  image-header     bf8',
2668             ]
2669         self.assertEqual(expected, lines)
2670
2671     def testListCmdFail(self):
2672         """Test failing to list an image"""
2673         self._DoReadFile('005_simple.dts')
2674         try:
2675             tmpdir, updated_fname = self._SetupImageInTmpdir()
2676             with self.assertRaises(ValueError) as e:
2677                 self._DoBinman('ls', '-i', updated_fname)
2678         finally:
2679             shutil.rmtree(tmpdir)
2680         self.assertIn("Cannot find FDT map in image", str(e.exception))
2681
2682     def _RunListCmd(self, paths, expected):
2683         """List out entries and check the result
2684
2685         Args:
2686             paths: List of paths to pass to the list command
2687             expected: Expected list of filenames to be returned, in order
2688         """
2689         self._CheckLz4()
2690         self._DoReadFileRealDtb('130_list_fdtmap.dts')
2691         image_fname = tools.GetOutputFilename('image.bin')
2692         image = Image.FromFile(image_fname)
2693         lines = image.GetListEntries(paths)[1]
2694         files = [line[0].strip() for line in lines[1:]]
2695         self.assertEqual(expected, files)
2696
2697     def testListCmdSection(self):
2698         """Test listing the files in a section"""
2699         self._RunListCmd(['section'],
2700             ['section', 'cbfs', 'u-boot', 'u-boot-dtb', 'u-boot-dtb'])
2701
2702     def testListCmdFile(self):
2703         """Test listing a particular file"""
2704         self._RunListCmd(['*u-boot-dtb'], ['u-boot-dtb', 'u-boot-dtb'])
2705
2706     def testListCmdWildcard(self):
2707         """Test listing a wildcarded file"""
2708         self._RunListCmd(['*boot*'],
2709             ['u-boot', 'u-boot', 'u-boot-dtb', 'u-boot-dtb'])
2710
2711     def testListCmdWildcardMulti(self):
2712         """Test listing a wildcarded file"""
2713         self._RunListCmd(['*cb*', '*head*'],
2714             ['cbfs', 'u-boot', 'u-boot-dtb', 'image-header'])
2715
2716     def testListCmdEmpty(self):
2717         """Test listing a wildcarded file"""
2718         self._RunListCmd(['nothing'], [])
2719
2720     def testListCmdPath(self):
2721         """Test listing the files in a sub-entry of a section"""
2722         self._RunListCmd(['section/cbfs'], ['cbfs', 'u-boot', 'u-boot-dtb'])
2723
2724     def _RunExtractCmd(self, entry_name, decomp=True):
2725         """Extract an entry from an image
2726
2727         Args:
2728             entry_name: Entry name to extract
2729             decomp: True to decompress the data if compressed, False to leave
2730                 it in its raw uncompressed format
2731
2732         Returns:
2733             data from entry
2734         """
2735         self._CheckLz4()
2736         self._DoReadFileRealDtb('130_list_fdtmap.dts')
2737         image_fname = tools.GetOutputFilename('image.bin')
2738         return control.ReadEntry(image_fname, entry_name, decomp)
2739
2740     def testExtractSimple(self):
2741         """Test extracting a single file"""
2742         data = self._RunExtractCmd('u-boot')
2743         self.assertEqual(U_BOOT_DATA, data)
2744
2745     def testExtractSection(self):
2746         """Test extracting the files in a section"""
2747         data = self._RunExtractCmd('section')
2748         cbfs_data = data[:0x400]
2749         cbfs = cbfs_util.CbfsReader(cbfs_data)
2750         self.assertEqual(['u-boot', 'u-boot-dtb', ''], list(cbfs.files.keys()))
2751         dtb_data = data[0x400:]
2752         dtb = self._decompress(dtb_data)
2753         self.assertEqual(EXTRACT_DTB_SIZE, len(dtb))
2754
2755     def testExtractCompressed(self):
2756         """Test extracting compressed data"""
2757         data = self._RunExtractCmd('section/u-boot-dtb')
2758         self.assertEqual(EXTRACT_DTB_SIZE, len(data))
2759
2760     def testExtractRaw(self):
2761         """Test extracting compressed data without decompressing it"""
2762         data = self._RunExtractCmd('section/u-boot-dtb', decomp=False)
2763         dtb = self._decompress(data)
2764         self.assertEqual(EXTRACT_DTB_SIZE, len(dtb))
2765
2766     def testExtractCbfs(self):
2767         """Test extracting CBFS data"""
2768         data = self._RunExtractCmd('section/cbfs/u-boot')
2769         self.assertEqual(U_BOOT_DATA, data)
2770
2771     def testExtractCbfsCompressed(self):
2772         """Test extracting CBFS compressed data"""
2773         data = self._RunExtractCmd('section/cbfs/u-boot-dtb')
2774         self.assertEqual(EXTRACT_DTB_SIZE, len(data))
2775
2776     def testExtractCbfsRaw(self):
2777         """Test extracting CBFS compressed data without decompressing it"""
2778         data = self._RunExtractCmd('section/cbfs/u-boot-dtb', decomp=False)
2779         dtb = tools.Decompress(data, 'lzma', with_header=False)
2780         self.assertEqual(EXTRACT_DTB_SIZE, len(dtb))
2781
2782     def testExtractBadEntry(self):
2783         """Test extracting a bad section path"""
2784         with self.assertRaises(ValueError) as e:
2785             self._RunExtractCmd('section/does-not-exist')
2786         self.assertIn("Entry 'does-not-exist' not found in '/section'",
2787                       str(e.exception))
2788
2789     def testExtractMissingFile(self):
2790         """Test extracting file that does not exist"""
2791         with self.assertRaises(IOError) as e:
2792             control.ReadEntry('missing-file', 'name')
2793
2794     def testExtractBadFile(self):
2795         """Test extracting an invalid file"""
2796         fname = os.path.join(self._indir, 'badfile')
2797         tools.WriteFile(fname, b'')
2798         with self.assertRaises(ValueError) as e:
2799             control.ReadEntry(fname, 'name')
2800
2801     def testExtractCmd(self):
2802         """Test extracting a file fron an image on the command line"""
2803         self._CheckLz4()
2804         self._DoReadFileRealDtb('130_list_fdtmap.dts')
2805         fname = os.path.join(self._indir, 'output.extact')
2806         try:
2807             tmpdir, updated_fname = self._SetupImageInTmpdir()
2808             with test_util.capture_sys_output() as (stdout, stderr):
2809                 self._DoBinman('extract', '-i', updated_fname, 'u-boot',
2810                                '-f', fname)
2811         finally:
2812             shutil.rmtree(tmpdir)
2813         data = tools.ReadFile(fname)
2814         self.assertEqual(U_BOOT_DATA, data)
2815
2816     def testExtractOneEntry(self):
2817         """Test extracting a single entry fron an image """
2818         self._CheckLz4()
2819         self._DoReadFileRealDtb('130_list_fdtmap.dts')
2820         image_fname = tools.GetOutputFilename('image.bin')
2821         fname = os.path.join(self._indir, 'output.extact')
2822         control.ExtractEntries(image_fname, fname, None, ['u-boot'])
2823         data = tools.ReadFile(fname)
2824         self.assertEqual(U_BOOT_DATA, data)
2825
2826     def _CheckExtractOutput(self, decomp):
2827         """Helper to test file output with and without decompression
2828
2829         Args:
2830             decomp: True to decompress entry data, False to output it raw
2831         """
2832         def _CheckPresent(entry_path, expect_data, expect_size=None):
2833             """Check and remove expected file
2834
2835             This checks the data/size of a file and removes the file both from
2836             the outfiles set and from the output directory. Once all files are
2837             processed, both the set and directory should be empty.
2838
2839             Args:
2840                 entry_path: Entry path
2841                 expect_data: Data to expect in file, or None to skip check
2842                 expect_size: Size of data to expect in file, or None to skip
2843             """
2844             path = os.path.join(outdir, entry_path)
2845             data = tools.ReadFile(path)
2846             os.remove(path)
2847             if expect_data:
2848                 self.assertEqual(expect_data, data)
2849             elif expect_size:
2850                 self.assertEqual(expect_size, len(data))
2851             outfiles.remove(path)
2852
2853         def _CheckDirPresent(name):
2854             """Remove expected directory
2855
2856             This gives an error if the directory does not exist as expected
2857
2858             Args:
2859                 name: Name of directory to remove
2860             """
2861             path = os.path.join(outdir, name)
2862             os.rmdir(path)
2863
2864         self._DoReadFileRealDtb('130_list_fdtmap.dts')
2865         image_fname = tools.GetOutputFilename('image.bin')
2866         outdir = os.path.join(self._indir, 'extract')
2867         einfos = control.ExtractEntries(image_fname, None, outdir, [], decomp)
2868
2869         # Create a set of all file that were output (should be 9)
2870         outfiles = set()
2871         for root, dirs, files in os.walk(outdir):
2872             outfiles |= set([os.path.join(root, fname) for fname in files])
2873         self.assertEqual(9, len(outfiles))
2874         self.assertEqual(9, len(einfos))
2875
2876         image = control.images['image']
2877         entries = image.GetEntries()
2878
2879         # Check the 9 files in various ways
2880         section = entries['section']
2881         section_entries = section.GetEntries()
2882         cbfs_entries = section_entries['cbfs'].GetEntries()
2883         _CheckPresent('u-boot', U_BOOT_DATA)
2884         _CheckPresent('section/cbfs/u-boot', U_BOOT_DATA)
2885         dtb_len = EXTRACT_DTB_SIZE
2886         if not decomp:
2887             dtb_len = cbfs_entries['u-boot-dtb'].size
2888         _CheckPresent('section/cbfs/u-boot-dtb', None, dtb_len)
2889         if not decomp:
2890             dtb_len = section_entries['u-boot-dtb'].size
2891         _CheckPresent('section/u-boot-dtb', None, dtb_len)
2892
2893         fdtmap = entries['fdtmap']
2894         _CheckPresent('fdtmap', fdtmap.data)
2895         hdr = entries['image-header']
2896         _CheckPresent('image-header', hdr.data)
2897
2898         _CheckPresent('section/root', section.data)
2899         cbfs = section_entries['cbfs']
2900         _CheckPresent('section/cbfs/root', cbfs.data)
2901         data = tools.ReadFile(image_fname)
2902         _CheckPresent('root', data)
2903
2904         # There should be no files left. Remove all the directories to check.
2905         # If there are any files/dirs remaining, one of these checks will fail.
2906         self.assertEqual(0, len(outfiles))
2907         _CheckDirPresent('section/cbfs')
2908         _CheckDirPresent('section')
2909         _CheckDirPresent('')
2910         self.assertFalse(os.path.exists(outdir))
2911
2912     def testExtractAllEntries(self):
2913         """Test extracting all entries"""
2914         self._CheckLz4()
2915         self._CheckExtractOutput(decomp=True)
2916
2917     def testExtractAllEntriesRaw(self):
2918         """Test extracting all entries without decompressing them"""
2919         self._CheckLz4()
2920         self._CheckExtractOutput(decomp=False)
2921
2922     def testExtractSelectedEntries(self):
2923         """Test extracting some entries"""
2924         self._CheckLz4()
2925         self._DoReadFileRealDtb('130_list_fdtmap.dts')
2926         image_fname = tools.GetOutputFilename('image.bin')
2927         outdir = os.path.join(self._indir, 'extract')
2928         einfos = control.ExtractEntries(image_fname, None, outdir,
2929                                         ['*cb*', '*head*'])
2930
2931         # File output is tested by testExtractAllEntries(), so just check that
2932         # the expected entries are selected
2933         names = [einfo.name for einfo in einfos]
2934         self.assertEqual(names,
2935                          ['cbfs', 'u-boot', 'u-boot-dtb', 'image-header'])
2936
2937     def testExtractNoEntryPaths(self):
2938         """Test extracting some entries"""
2939         self._CheckLz4()
2940         self._DoReadFileRealDtb('130_list_fdtmap.dts')
2941         image_fname = tools.GetOutputFilename('image.bin')
2942         with self.assertRaises(ValueError) as e:
2943             control.ExtractEntries(image_fname, 'fname', None, [])
2944         self.assertIn('Must specify an entry path to write with -f',
2945                       str(e.exception))
2946
2947     def testExtractTooManyEntryPaths(self):
2948         """Test extracting some entries"""
2949         self._CheckLz4()
2950         self._DoReadFileRealDtb('130_list_fdtmap.dts')
2951         image_fname = tools.GetOutputFilename('image.bin')
2952         with self.assertRaises(ValueError) as e:
2953             control.ExtractEntries(image_fname, 'fname', None, ['a', 'b'])
2954         self.assertIn('Must specify exactly one entry path to write with -f',
2955                       str(e.exception))
2956
2957     def testPackAlignSection(self):
2958         """Test that sections can have alignment"""
2959         self._DoReadFile('131_pack_align_section.dts')
2960
2961         self.assertIn('image', control.images)
2962         image = control.images['image']
2963         entries = image.GetEntries()
2964         self.assertEqual(3, len(entries))
2965
2966         # First u-boot
2967         self.assertIn('u-boot', entries)
2968         entry = entries['u-boot']
2969         self.assertEqual(0, entry.offset)
2970         self.assertEqual(0, entry.image_pos)
2971         self.assertEqual(len(U_BOOT_DATA), entry.contents_size)
2972         self.assertEqual(len(U_BOOT_DATA), entry.size)
2973
2974         # Section0
2975         self.assertIn('section0', entries)
2976         section0 = entries['section0']
2977         self.assertEqual(0x10, section0.offset)
2978         self.assertEqual(0x10, section0.image_pos)
2979         self.assertEqual(len(U_BOOT_DATA), section0.size)
2980
2981         # Second u-boot
2982         section_entries = section0.GetEntries()
2983         self.assertIn('u-boot', section_entries)
2984         entry = section_entries['u-boot']
2985         self.assertEqual(0, entry.offset)
2986         self.assertEqual(0x10, entry.image_pos)
2987         self.assertEqual(len(U_BOOT_DATA), entry.contents_size)
2988         self.assertEqual(len(U_BOOT_DATA), entry.size)
2989
2990         # Section1
2991         self.assertIn('section1', entries)
2992         section1 = entries['section1']
2993         self.assertEqual(0x14, section1.offset)
2994         self.assertEqual(0x14, section1.image_pos)
2995         self.assertEqual(0x20, section1.size)
2996
2997         # Second u-boot
2998         section_entries = section1.GetEntries()
2999         self.assertIn('u-boot', section_entries)
3000         entry = section_entries['u-boot']
3001         self.assertEqual(0, entry.offset)
3002         self.assertEqual(0x14, entry.image_pos)
3003         self.assertEqual(len(U_BOOT_DATA), entry.contents_size)
3004         self.assertEqual(len(U_BOOT_DATA), entry.size)
3005
3006         # Section2
3007         self.assertIn('section2', section_entries)
3008         section2 = section_entries['section2']
3009         self.assertEqual(0x4, section2.offset)
3010         self.assertEqual(0x18, section2.image_pos)
3011         self.assertEqual(4, section2.size)
3012
3013         # Third u-boot
3014         section_entries = section2.GetEntries()
3015         self.assertIn('u-boot', section_entries)
3016         entry = section_entries['u-boot']
3017         self.assertEqual(0, entry.offset)
3018         self.assertEqual(0x18, entry.image_pos)
3019         self.assertEqual(len(U_BOOT_DATA), entry.contents_size)
3020         self.assertEqual(len(U_BOOT_DATA), entry.size)
3021
3022     def _RunReplaceCmd(self, entry_name, data, decomp=True, allow_resize=True,
3023                        dts='132_replace.dts'):
3024         """Replace an entry in an image
3025
3026         This writes the entry data to update it, then opens the updated file and
3027         returns the value that it now finds there.
3028
3029         Args:
3030             entry_name: Entry name to replace
3031             data: Data to replace it with
3032             decomp: True to compress the data if needed, False if data is
3033                 already compressed so should be used as is
3034             allow_resize: True to allow entries to change size, False to raise
3035                 an exception
3036
3037         Returns:
3038             Tuple:
3039                 data from entry
3040                 data from fdtmap (excluding header)
3041                 Image object that was modified
3042         """
3043         dtb_data = self._DoReadFileDtb(dts, use_real_dtb=True,
3044                                        update_dtb=True)[1]
3045
3046         self.assertIn('image', control.images)
3047         image = control.images['image']
3048         entries = image.GetEntries()
3049         orig_dtb_data = entries['u-boot-dtb'].data
3050         orig_fdtmap_data = entries['fdtmap'].data
3051
3052         image_fname = tools.GetOutputFilename('image.bin')
3053         updated_fname = tools.GetOutputFilename('image-updated.bin')
3054         tools.WriteFile(updated_fname, tools.ReadFile(image_fname))
3055         image = control.WriteEntry(updated_fname, entry_name, data, decomp,
3056                                    allow_resize)
3057         data = control.ReadEntry(updated_fname, entry_name, decomp)
3058
3059         # The DT data should not change unless resized:
3060         if not allow_resize:
3061             new_dtb_data = entries['u-boot-dtb'].data
3062             self.assertEqual(new_dtb_data, orig_dtb_data)
3063             new_fdtmap_data = entries['fdtmap'].data
3064             self.assertEqual(new_fdtmap_data, orig_fdtmap_data)
3065
3066         return data, orig_fdtmap_data[fdtmap.FDTMAP_HDR_LEN:], image
3067
3068     def testReplaceSimple(self):
3069         """Test replacing a single file"""
3070         expected = b'x' * len(U_BOOT_DATA)
3071         data, expected_fdtmap, _ = self._RunReplaceCmd('u-boot', expected,
3072                                                     allow_resize=False)
3073         self.assertEqual(expected, data)
3074
3075         # Test that the state looks right. There should be an FDT for the fdtmap
3076         # that we jsut read back in, and it should match what we find in the
3077         # 'control' tables. Checking for an FDT that does not exist should
3078         # return None.
3079         path, fdtmap = state.GetFdtContents('fdtmap')
3080         self.assertIsNotNone(path)
3081         self.assertEqual(expected_fdtmap, fdtmap)
3082
3083         dtb = state.GetFdtForEtype('fdtmap')
3084         self.assertEqual(dtb.GetContents(), fdtmap)
3085
3086         missing_path, missing_fdtmap = state.GetFdtContents('missing')
3087         self.assertIsNone(missing_path)
3088         self.assertIsNone(missing_fdtmap)
3089
3090         missing_dtb = state.GetFdtForEtype('missing')
3091         self.assertIsNone(missing_dtb)
3092
3093         self.assertEqual('/binman', state.fdt_path_prefix)
3094
3095     def testReplaceResizeFail(self):
3096         """Test replacing a file by something larger"""
3097         expected = U_BOOT_DATA + b'x'
3098         with self.assertRaises(ValueError) as e:
3099             self._RunReplaceCmd('u-boot', expected, allow_resize=False,
3100                                 dts='139_replace_repack.dts')
3101         self.assertIn("Node '/u-boot': Entry data size does not match, but resize is disabled",
3102                       str(e.exception))
3103
3104     def testReplaceMulti(self):
3105         """Test replacing entry data where multiple images are generated"""
3106         data = self._DoReadFileDtb('133_replace_multi.dts', use_real_dtb=True,
3107                                    update_dtb=True)[0]
3108         expected = b'x' * len(U_BOOT_DATA)
3109         updated_fname = tools.GetOutputFilename('image-updated.bin')
3110         tools.WriteFile(updated_fname, data)
3111         entry_name = 'u-boot'
3112         control.WriteEntry(updated_fname, entry_name, expected,
3113                            allow_resize=False)
3114         data = control.ReadEntry(updated_fname, entry_name)
3115         self.assertEqual(expected, data)
3116
3117         # Check the state looks right.
3118         self.assertEqual('/binman/image', state.fdt_path_prefix)
3119
3120         # Now check we can write the first image
3121         image_fname = tools.GetOutputFilename('first-image.bin')
3122         updated_fname = tools.GetOutputFilename('first-updated.bin')
3123         tools.WriteFile(updated_fname, tools.ReadFile(image_fname))
3124         entry_name = 'u-boot'
3125         control.WriteEntry(updated_fname, entry_name, expected,
3126                            allow_resize=False)
3127         data = control.ReadEntry(updated_fname, entry_name)
3128         self.assertEqual(expected, data)
3129
3130         # Check the state looks right.
3131         self.assertEqual('/binman/first-image', state.fdt_path_prefix)
3132
3133     def testUpdateFdtAllRepack(self):
3134         """Test that all device trees are updated with offset/size info"""
3135         data = self._DoReadFileRealDtb('134_fdt_update_all_repack.dts')
3136         SECTION_SIZE = 0x300
3137         DTB_SIZE = 602
3138         FDTMAP_SIZE = 608
3139         base_expected = {
3140             'offset': 0,
3141             'size': SECTION_SIZE + DTB_SIZE * 2 + FDTMAP_SIZE,
3142             'image-pos': 0,
3143             'section:offset': 0,
3144             'section:size': SECTION_SIZE,
3145             'section:image-pos': 0,
3146             'section/u-boot-dtb:offset': 4,
3147             'section/u-boot-dtb:size': 636,
3148             'section/u-boot-dtb:image-pos': 4,
3149             'u-boot-spl-dtb:offset': SECTION_SIZE,
3150             'u-boot-spl-dtb:size': DTB_SIZE,
3151             'u-boot-spl-dtb:image-pos': SECTION_SIZE,
3152             'u-boot-tpl-dtb:offset': SECTION_SIZE + DTB_SIZE,
3153             'u-boot-tpl-dtb:image-pos': SECTION_SIZE + DTB_SIZE,
3154             'u-boot-tpl-dtb:size': DTB_SIZE,
3155             'fdtmap:offset': SECTION_SIZE + DTB_SIZE * 2,
3156             'fdtmap:size': FDTMAP_SIZE,
3157             'fdtmap:image-pos': SECTION_SIZE + DTB_SIZE * 2,
3158         }
3159         main_expected = {
3160             'section:orig-size': SECTION_SIZE,
3161             'section/u-boot-dtb:orig-offset': 4,
3162         }
3163
3164         # We expect three device-tree files in the output, with the first one
3165         # within a fixed-size section.
3166         # Read them in sequence. We look for an 'spl' property in the SPL tree,
3167         # and 'tpl' in the TPL tree, to make sure they are distinct from the
3168         # main U-Boot tree. All three should have the same positions and offset
3169         # except that the main tree should include the main_expected properties
3170         start = 4
3171         for item in ['', 'spl', 'tpl', None]:
3172             if item is None:
3173                 start += 16  # Move past fdtmap header
3174             dtb = fdt.Fdt.FromData(data[start:])
3175             dtb.Scan()
3176             props = self._GetPropTree(dtb,
3177                 BASE_DTB_PROPS + REPACK_DTB_PROPS + ['spl', 'tpl'],
3178                 prefix='/' if item is None else '/binman/')
3179             expected = dict(base_expected)
3180             if item:
3181                 expected[item] = 0
3182             else:
3183                 # Main DTB and fdtdec should include the 'orig-' properties
3184                 expected.update(main_expected)
3185             # Helpful for debugging:
3186             #for prop in sorted(props):
3187                 #print('prop %s %s %s' % (prop, props[prop], expected[prop]))
3188             self.assertEqual(expected, props)
3189             if item == '':
3190                 start = SECTION_SIZE
3191             else:
3192                 start += dtb._fdt_obj.totalsize()
3193
3194     def testFdtmapHeaderMiddle(self):
3195         """Test an FDT map in the middle of an image when it should be at end"""
3196         with self.assertRaises(ValueError) as e:
3197             self._DoReadFileRealDtb('135_fdtmap_hdr_middle.dts')
3198         self.assertIn("Invalid sibling order 'middle' for image-header: Must be at 'end' to match location",
3199                       str(e.exception))
3200
3201     def testFdtmapHeaderStartBad(self):
3202         """Test an FDT map in middle of an image when it should be at start"""
3203         with self.assertRaises(ValueError) as e:
3204             self._DoReadFileRealDtb('136_fdtmap_hdr_startbad.dts')
3205         self.assertIn("Invalid sibling order 'end' for image-header: Must be at 'start' to match location",
3206                       str(e.exception))
3207
3208     def testFdtmapHeaderEndBad(self):
3209         """Test an FDT map at the start of an image when it should be at end"""
3210         with self.assertRaises(ValueError) as e:
3211             self._DoReadFileRealDtb('137_fdtmap_hdr_endbad.dts')
3212         self.assertIn("Invalid sibling order 'start' for image-header: Must be at 'end' to match location",
3213                       str(e.exception))
3214
3215     def testFdtmapHeaderNoSize(self):
3216         """Test an image header at the end of an image with undefined size"""
3217         self._DoReadFileRealDtb('138_fdtmap_hdr_nosize.dts')
3218
3219     def testReplaceResize(self):
3220         """Test replacing a single file in an entry with a larger file"""
3221         expected = U_BOOT_DATA + b'x'
3222         data, _, image = self._RunReplaceCmd('u-boot', expected,
3223                                              dts='139_replace_repack.dts')
3224         self.assertEqual(expected, data)
3225
3226         entries = image.GetEntries()
3227         dtb_data = entries['u-boot-dtb'].data
3228         dtb = fdt.Fdt.FromData(dtb_data)
3229         dtb.Scan()
3230
3231         # The u-boot section should now be larger in the dtb
3232         node = dtb.GetNode('/binman/u-boot')
3233         self.assertEqual(len(expected), fdt_util.GetInt(node, 'size'))
3234
3235         # Same for the fdtmap
3236         fdata = entries['fdtmap'].data
3237         fdtb = fdt.Fdt.FromData(fdata[fdtmap.FDTMAP_HDR_LEN:])
3238         fdtb.Scan()
3239         fnode = fdtb.GetNode('/u-boot')
3240         self.assertEqual(len(expected), fdt_util.GetInt(fnode, 'size'))
3241
3242     def testReplaceResizeNoRepack(self):
3243         """Test replacing an entry with a larger file when not allowed"""
3244         expected = U_BOOT_DATA + b'x'
3245         with self.assertRaises(ValueError) as e:
3246             self._RunReplaceCmd('u-boot', expected)
3247         self.assertIn('Entry data size does not match, but allow-repack is not present for this image',
3248                       str(e.exception))
3249
3250     def testEntryShrink(self):
3251         """Test contracting an entry after it is packed"""
3252         try:
3253             state.SetAllowEntryContraction(True)
3254             data = self._DoReadFileDtb('140_entry_shrink.dts',
3255                                        update_dtb=True)[0]
3256         finally:
3257             state.SetAllowEntryContraction(False)
3258         self.assertEqual(b'a', data[:1])
3259         self.assertEqual(U_BOOT_DATA, data[1:1 + len(U_BOOT_DATA)])
3260         self.assertEqual(b'a', data[-1:])
3261
3262     def testEntryShrinkFail(self):
3263         """Test not being allowed to contract an entry after it is packed"""
3264         data = self._DoReadFileDtb('140_entry_shrink.dts', update_dtb=True)[0]
3265
3266         # In this case there is a spare byte at the end of the data. The size of
3267         # the contents is only 1 byte but we still have the size before it
3268         # shrunk.
3269         self.assertEqual(b'a\0', data[:2])
3270         self.assertEqual(U_BOOT_DATA, data[2:2 + len(U_BOOT_DATA)])
3271         self.assertEqual(b'a\0', data[-2:])
3272
3273     def testDescriptorOffset(self):
3274         """Test that the Intel descriptor is always placed at at the start"""
3275         data = self._DoReadFileDtb('141_descriptor_offset.dts')
3276         image = control.images['image']
3277         entries = image.GetEntries()
3278         desc = entries['intel-descriptor']
3279         self.assertEqual(0xff800000, desc.offset);
3280         self.assertEqual(0xff800000, desc.image_pos);
3281
3282     def testReplaceCbfs(self):
3283         """Test replacing a single file in CBFS without changing the size"""
3284         self._CheckLz4()
3285         expected = b'x' * len(U_BOOT_DATA)
3286         data = self._DoReadFileRealDtb('142_replace_cbfs.dts')
3287         updated_fname = tools.GetOutputFilename('image-updated.bin')
3288         tools.WriteFile(updated_fname, data)
3289         entry_name = 'section/cbfs/u-boot'
3290         control.WriteEntry(updated_fname, entry_name, expected,
3291                            allow_resize=True)
3292         data = control.ReadEntry(updated_fname, entry_name)
3293         self.assertEqual(expected, data)
3294
3295     def testReplaceResizeCbfs(self):
3296         """Test replacing a single file in CBFS with one of a different size"""
3297         self._CheckLz4()
3298         expected = U_BOOT_DATA + b'x'
3299         data = self._DoReadFileRealDtb('142_replace_cbfs.dts')
3300         updated_fname = tools.GetOutputFilename('image-updated.bin')
3301         tools.WriteFile(updated_fname, data)
3302         entry_name = 'section/cbfs/u-boot'
3303         control.WriteEntry(updated_fname, entry_name, expected,
3304                            allow_resize=True)
3305         data = control.ReadEntry(updated_fname, entry_name)
3306         self.assertEqual(expected, data)
3307
3308     def _SetupForReplace(self):
3309         """Set up some files to use to replace entries
3310
3311         This generates an image, copies it to a new file, extracts all the files
3312         in it and updates some of them
3313
3314         Returns:
3315             List
3316                 Image filename
3317                 Output directory
3318                 Expected values for updated entries, each a string
3319         """
3320         data = self._DoReadFileRealDtb('143_replace_all.dts')
3321
3322         updated_fname = tools.GetOutputFilename('image-updated.bin')
3323         tools.WriteFile(updated_fname, data)
3324
3325         outdir = os.path.join(self._indir, 'extract')
3326         einfos = control.ExtractEntries(updated_fname, None, outdir, [])
3327
3328         expected1 = b'x' + U_BOOT_DATA + b'y'
3329         u_boot_fname1 = os.path.join(outdir, 'u-boot')
3330         tools.WriteFile(u_boot_fname1, expected1)
3331
3332         expected2 = b'a' + U_BOOT_DATA + b'b'
3333         u_boot_fname2 = os.path.join(outdir, 'u-boot2')
3334         tools.WriteFile(u_boot_fname2, expected2)
3335
3336         expected_text = b'not the same text'
3337         text_fname = os.path.join(outdir, 'text')
3338         tools.WriteFile(text_fname, expected_text)
3339
3340         dtb_fname = os.path.join(outdir, 'u-boot-dtb')
3341         dtb = fdt.FdtScan(dtb_fname)
3342         node = dtb.GetNode('/binman/text')
3343         node.AddString('my-property', 'the value')
3344         dtb.Sync(auto_resize=True)
3345         dtb.Flush()
3346
3347         return updated_fname, outdir, expected1, expected2, expected_text
3348
3349     def _CheckReplaceMultiple(self, entry_paths):
3350         """Handle replacing the contents of multiple entries
3351
3352         Args:
3353             entry_paths: List of entry paths to replace
3354
3355         Returns:
3356             List
3357                 Dict of entries in the image:
3358                     key: Entry name
3359                     Value: Entry object
3360             Expected values for updated entries, each a string
3361         """
3362         updated_fname, outdir, expected1, expected2, expected_text = (
3363             self._SetupForReplace())
3364         control.ReplaceEntries(updated_fname, None, outdir, entry_paths)
3365
3366         image = Image.FromFile(updated_fname)
3367         image.LoadData()
3368         return image.GetEntries(), expected1, expected2, expected_text
3369
3370     def testReplaceAll(self):
3371         """Test replacing the contents of all entries"""
3372         entries, expected1, expected2, expected_text = (
3373             self._CheckReplaceMultiple([]))
3374         data = entries['u-boot'].data
3375         self.assertEqual(expected1, data)
3376
3377         data = entries['u-boot2'].data
3378         self.assertEqual(expected2, data)
3379
3380         data = entries['text'].data
3381         self.assertEqual(expected_text, data)
3382
3383         # Check that the device tree is updated
3384         data = entries['u-boot-dtb'].data
3385         dtb = fdt.Fdt.FromData(data)
3386         dtb.Scan()
3387         node = dtb.GetNode('/binman/text')
3388         self.assertEqual('the value', node.props['my-property'].value)
3389
3390     def testReplaceSome(self):
3391         """Test replacing the contents of a few entries"""
3392         entries, expected1, expected2, expected_text = (
3393             self._CheckReplaceMultiple(['u-boot2', 'text']))
3394
3395         # This one should not change
3396         data = entries['u-boot'].data
3397         self.assertEqual(U_BOOT_DATA, data)
3398
3399         data = entries['u-boot2'].data
3400         self.assertEqual(expected2, data)
3401
3402         data = entries['text'].data
3403         self.assertEqual(expected_text, data)
3404
3405     def testReplaceCmd(self):
3406         """Test replacing a file fron an image on the command line"""
3407         self._DoReadFileRealDtb('143_replace_all.dts')
3408
3409         try:
3410             tmpdir, updated_fname = self._SetupImageInTmpdir()
3411
3412             fname = os.path.join(tmpdir, 'update-u-boot.bin')
3413             expected = b'x' * len(U_BOOT_DATA)
3414             tools.WriteFile(fname, expected)
3415
3416             self._DoBinman('replace', '-i', updated_fname, 'u-boot', '-f', fname)
3417             data = tools.ReadFile(updated_fname)
3418             self.assertEqual(expected, data[:len(expected)])
3419             map_fname = os.path.join(tmpdir, 'image-updated.map')
3420             self.assertFalse(os.path.exists(map_fname))
3421         finally:
3422             shutil.rmtree(tmpdir)
3423
3424     def testReplaceCmdSome(self):
3425         """Test replacing some files fron an image on the command line"""
3426         updated_fname, outdir, expected1, expected2, expected_text = (
3427             self._SetupForReplace())
3428
3429         self._DoBinman('replace', '-i', updated_fname, '-I', outdir,
3430                        'u-boot2', 'text')
3431
3432         tools.PrepareOutputDir(None)
3433         image = Image.FromFile(updated_fname)
3434         image.LoadData()
3435         entries = image.GetEntries()
3436
3437         # This one should not change
3438         data = entries['u-boot'].data
3439         self.assertEqual(U_BOOT_DATA, data)
3440
3441         data = entries['u-boot2'].data
3442         self.assertEqual(expected2, data)
3443
3444         data = entries['text'].data
3445         self.assertEqual(expected_text, data)
3446
3447     def testReplaceMissing(self):
3448         """Test replacing entries where the file is missing"""
3449         updated_fname, outdir, expected1, expected2, expected_text = (
3450             self._SetupForReplace())
3451
3452         # Remove one of the files, to generate a warning
3453         u_boot_fname1 = os.path.join(outdir, 'u-boot')
3454         os.remove(u_boot_fname1)
3455
3456         with test_util.capture_sys_output() as (stdout, stderr):
3457             control.ReplaceEntries(updated_fname, None, outdir, [])
3458         self.assertIn("Skipping entry '/u-boot' from missing file",
3459                       stderr.getvalue())
3460
3461     def testReplaceCmdMap(self):
3462         """Test replacing a file fron an image on the command line"""
3463         self._DoReadFileRealDtb('143_replace_all.dts')
3464
3465         try:
3466             tmpdir, updated_fname = self._SetupImageInTmpdir()
3467
3468             fname = os.path.join(self._indir, 'update-u-boot.bin')
3469             expected = b'x' * len(U_BOOT_DATA)
3470             tools.WriteFile(fname, expected)
3471
3472             self._DoBinman('replace', '-i', updated_fname, 'u-boot',
3473                            '-f', fname, '-m')
3474             map_fname = os.path.join(tmpdir, 'image-updated.map')
3475             self.assertTrue(os.path.exists(map_fname))
3476         finally:
3477             shutil.rmtree(tmpdir)
3478
3479     def testReplaceNoEntryPaths(self):
3480         """Test replacing an entry without an entry path"""
3481         self._DoReadFileRealDtb('143_replace_all.dts')
3482         image_fname = tools.GetOutputFilename('image.bin')
3483         with self.assertRaises(ValueError) as e:
3484             control.ReplaceEntries(image_fname, 'fname', None, [])
3485         self.assertIn('Must specify an entry path to read with -f',
3486                       str(e.exception))
3487
3488     def testReplaceTooManyEntryPaths(self):
3489         """Test extracting some entries"""
3490         self._DoReadFileRealDtb('143_replace_all.dts')
3491         image_fname = tools.GetOutputFilename('image.bin')
3492         with self.assertRaises(ValueError) as e:
3493             control.ReplaceEntries(image_fname, 'fname', None, ['a', 'b'])
3494         self.assertIn('Must specify exactly one entry path to write with -f',
3495                       str(e.exception))
3496
3497     def testPackReset16(self):
3498         """Test that an image with an x86 reset16 region can be created"""
3499         data = self._DoReadFile('144_x86_reset16.dts')
3500         self.assertEqual(X86_RESET16_DATA, data[:len(X86_RESET16_DATA)])
3501
3502     def testPackReset16Spl(self):
3503         """Test that an image with an x86 reset16-spl region can be created"""
3504         data = self._DoReadFile('145_x86_reset16_spl.dts')
3505         self.assertEqual(X86_RESET16_SPL_DATA, data[:len(X86_RESET16_SPL_DATA)])
3506
3507     def testPackReset16Tpl(self):
3508         """Test that an image with an x86 reset16-tpl region can be created"""
3509         data = self._DoReadFile('146_x86_reset16_tpl.dts')
3510         self.assertEqual(X86_RESET16_TPL_DATA, data[:len(X86_RESET16_TPL_DATA)])
3511
3512     def testPackIntelFit(self):
3513         """Test that an image with an Intel FIT and pointer can be created"""
3514         data = self._DoReadFile('147_intel_fit.dts')
3515         self.assertEqual(U_BOOT_DATA, data[:len(U_BOOT_DATA)])
3516         fit = data[16:32];
3517         self.assertEqual(b'_FIT_   \x01\x00\x00\x00\x00\x01\x80}' , fit)
3518         ptr = struct.unpack('<i', data[0x40:0x44])[0]
3519
3520         image = control.images['image']
3521         entries = image.GetEntries()
3522         expected_ptr = entries['intel-fit'].image_pos - (1 << 32)
3523         self.assertEqual(expected_ptr, ptr)
3524
3525     def testPackIntelFitMissing(self):
3526         """Test detection of a FIT pointer with not FIT region"""
3527         with self.assertRaises(ValueError) as e:
3528             self._DoReadFile('148_intel_fit_missing.dts')
3529         self.assertIn("'intel-fit-ptr' section must have an 'intel-fit' sibling",
3530                       str(e.exception))
3531
3532     def _CheckSymbolsTplSection(self, dts, expected_vals):
3533         data = self._DoReadFile(dts)
3534         sym_values = struct.pack('<LQLL', *expected_vals)
3535         upto1 = 4 + len(U_BOOT_SPL_DATA)
3536         expected1 = tools.GetBytes(0xff, 4) + sym_values + U_BOOT_SPL_DATA[20:]
3537         self.assertEqual(expected1, data[:upto1])
3538
3539         upto2 = upto1 + 1 + len(U_BOOT_SPL_DATA)
3540         expected2 = tools.GetBytes(0xff, 1) + sym_values + U_BOOT_SPL_DATA[20:]
3541         self.assertEqual(expected2, data[upto1:upto2])
3542
3543         upto3 = 0x34 + len(U_BOOT_DATA)
3544         expected3 = tools.GetBytes(0xff, 1) + U_BOOT_DATA
3545         self.assertEqual(expected3, data[upto2:upto3])
3546
3547         expected4 = sym_values + U_BOOT_TPL_DATA[20:]
3548         self.assertEqual(expected4, data[upto3:upto3 + len(U_BOOT_TPL_DATA)])
3549
3550     def testSymbolsTplSection(self):
3551         """Test binman can assign symbols embedded in U-Boot TPL in a section"""
3552         self._SetupSplElf('u_boot_binman_syms')
3553         self._SetupTplElf('u_boot_binman_syms')
3554         self._CheckSymbolsTplSection('149_symbols_tpl.dts',
3555                                      [0x04, 0x1c, 0x10 + 0x34, 0x04])
3556
3557     def testSymbolsTplSectionX86(self):
3558         """Test binman can assign symbols in a section with end-at-4gb"""
3559         self._SetupSplElf('u_boot_binman_syms_x86')
3560         self._SetupTplElf('u_boot_binman_syms_x86')
3561         self._CheckSymbolsTplSection('155_symbols_tpl_x86.dts',
3562                                      [0xffffff04, 0xffffff1c, 0xffffff34,
3563                                       0x04])
3564
3565     def testPackX86RomIfwiSectiom(self):
3566         """Test that a section can be placed in an IFWI region"""
3567         self._SetupIfwi('fitimage.bin')
3568         data = self._DoReadFile('151_x86_rom_ifwi_section.dts')
3569         self._CheckIfwi(data)
3570
3571     def testPackFspM(self):
3572         """Test that an image with a FSP memory-init binary can be created"""
3573         data = self._DoReadFile('152_intel_fsp_m.dts')
3574         self.assertEqual(FSP_M_DATA, data[:len(FSP_M_DATA)])
3575
3576     def testPackFspS(self):
3577         """Test that an image with a FSP silicon-init binary can be created"""
3578         data = self._DoReadFile('153_intel_fsp_s.dts')
3579         self.assertEqual(FSP_S_DATA, data[:len(FSP_S_DATA)])
3580
3581     def testPackFspT(self):
3582         """Test that an image with a FSP temp-ram-init binary can be created"""
3583         data = self._DoReadFile('154_intel_fsp_t.dts')
3584         self.assertEqual(FSP_T_DATA, data[:len(FSP_T_DATA)])
3585
3586     def testMkimage(self):
3587         """Test using mkimage to build an image"""
3588         data = self._DoReadFile('156_mkimage.dts')
3589
3590         # Just check that the data appears in the file somewhere
3591         self.assertIn(U_BOOT_SPL_DATA, data)
3592
3593     def testExtblob(self):
3594         """Test an image with an external blob"""
3595         data = self._DoReadFile('157_blob_ext.dts')
3596         self.assertEqual(REFCODE_DATA, data)
3597
3598     def testExtblobMissing(self):
3599         """Test an image with a missing external blob"""
3600         with self.assertRaises(ValueError) as e:
3601             self._DoReadFile('158_blob_ext_missing.dts')
3602         self.assertIn("Filename 'missing-file' not found in input path",
3603                       str(e.exception))
3604
3605     def testExtblobMissingOk(self):
3606         """Test an image with an missing external blob that is allowed"""
3607         with test_util.capture_sys_output() as (stdout, stderr):
3608             self._DoTestFile('158_blob_ext_missing.dts', allow_missing=True)
3609         err = stderr.getvalue()
3610         self.assertRegex(err, "Image 'main-section'.*missing.*: blob-ext")
3611
3612     def testExtblobMissingOkSect(self):
3613         """Test an image with an missing external blob that is allowed"""
3614         with test_util.capture_sys_output() as (stdout, stderr):
3615             self._DoTestFile('159_blob_ext_missing_sect.dts',
3616                              allow_missing=True)
3617         err = stderr.getvalue()
3618         self.assertRegex(err, "Image 'main-section'.*missing.*: "
3619                          "blob-ext blob-ext2")
3620
3621     def testPackX86RomMeMissingDesc(self):
3622         """Test that an missing Intel descriptor entry is allowed"""
3623         with test_util.capture_sys_output() as (stdout, stderr):
3624             self._DoTestFile('164_x86_rom_me_missing.dts', allow_missing=True)
3625         err = stderr.getvalue()
3626         self.assertRegex(err,
3627                          "Image 'main-section'.*missing.*: intel-descriptor")
3628
3629     def testPackX86RomMissingIfwi(self):
3630         """Test that an x86 ROM with Integrated Firmware Image can be created"""
3631         self._SetupIfwi('fitimage.bin')
3632         pathname = os.path.join(self._indir, 'fitimage.bin')
3633         os.remove(pathname)
3634         with test_util.capture_sys_output() as (stdout, stderr):
3635             self._DoTestFile('111_x86_rom_ifwi.dts', allow_missing=True)
3636         err = stderr.getvalue()
3637         self.assertRegex(err, "Image 'main-section'.*missing.*: intel-ifwi")
3638
3639     def testPackOverlap(self):
3640         """Test that zero-size overlapping regions are ignored"""
3641         self._DoTestFile('160_pack_overlap_zero.dts')
3642
3643     def testSimpleFit(self):
3644         """Test an image with a FIT inside"""
3645         data = self._DoReadFile('161_fit.dts')
3646         self.assertEqual(U_BOOT_DATA, data[:len(U_BOOT_DATA)])
3647         self.assertEqual(U_BOOT_NODTB_DATA, data[-len(U_BOOT_NODTB_DATA):])
3648         fit_data = data[len(U_BOOT_DATA):-len(U_BOOT_NODTB_DATA)]
3649
3650         # The data should be inside the FIT
3651         dtb = fdt.Fdt.FromData(fit_data)
3652         dtb.Scan()
3653         fnode = dtb.GetNode('/images/kernel')
3654         self.assertIn('data', fnode.props)
3655
3656         fname = os.path.join(self._indir, 'fit_data.fit')
3657         tools.WriteFile(fname, fit_data)
3658         out = tools.Run('dumpimage', '-l', fname)
3659
3660         # Check a few features to make sure the plumbing works. We don't need
3661         # to test the operation of mkimage or dumpimage here. First convert the
3662         # output into a dict where the keys are the fields printed by dumpimage
3663         # and the values are a list of values for each field
3664         lines = out.splitlines()
3665
3666         # Converts "Compression:  gzip compressed" into two groups:
3667         # 'Compression' and 'gzip compressed'
3668         re_line = re.compile(r'^ *([^:]*)(?:: *(.*))?$')
3669         vals = collections.defaultdict(list)
3670         for line in lines:
3671             mat = re_line.match(line)
3672             vals[mat.group(1)].append(mat.group(2))
3673
3674         self.assertEquals('FIT description: test-desc', lines[0])
3675         self.assertIn('Created:', lines[1])
3676         self.assertIn('Image 0 (kernel)', vals)
3677         self.assertIn('Hash value', vals)
3678         data_sizes = vals.get('Data Size')
3679         self.assertIsNotNone(data_sizes)
3680         self.assertEqual(2, len(data_sizes))
3681         # Format is "4 Bytes = 0.00 KiB = 0.00 MiB" so take the first word
3682         self.assertEqual(len(U_BOOT_DATA), int(data_sizes[0].split()[0]))
3683         self.assertEqual(len(U_BOOT_SPL_DTB_DATA), int(data_sizes[1].split()[0]))
3684
3685     def testFitExternal(self):
3686         """Test an image with an FIT with external images"""
3687         data = self._DoReadFile('162_fit_external.dts')
3688         fit_data = data[len(U_BOOT_DATA):-2]  # _testing is 2 bytes
3689
3690         # The data should be outside the FIT
3691         dtb = fdt.Fdt.FromData(fit_data)
3692         dtb.Scan()
3693         fnode = dtb.GetNode('/images/kernel')
3694         self.assertNotIn('data', fnode.props)
3695
3696     def testSectionIgnoreHashSignature(self):
3697         """Test that sections ignore hash, signature nodes for its data"""
3698         data = self._DoReadFile('165_section_ignore_hash_signature.dts')
3699         expected = (U_BOOT_DATA + U_BOOT_DATA)
3700         self.assertEqual(expected, data)
3701
3702     def testPadInSections(self):
3703         """Test pad-before, pad-after for entries in sections"""
3704         data, _, _, out_dtb_fname = self._DoReadFileDtb(
3705             '166_pad_in_sections.dts', update_dtb=True)
3706         expected = (U_BOOT_DATA + tools.GetBytes(ord('!'), 12) +
3707                     U_BOOT_DATA + tools.GetBytes(ord('!'), 6) +
3708                     U_BOOT_DATA)
3709         self.assertEqual(expected, data)
3710
3711         dtb = fdt.Fdt(out_dtb_fname)
3712         dtb.Scan()
3713         props = self._GetPropTree(dtb, ['size', 'image-pos', 'offset'])
3714         expected = {
3715             'image-pos': 0,
3716             'offset': 0,
3717             'size': 12 + 6 + 3 * len(U_BOOT_DATA),
3718
3719             'section:image-pos': 0,
3720             'section:offset': 0,
3721             'section:size': 12 + 6 + 3 * len(U_BOOT_DATA),
3722
3723             'section/before:image-pos': 0,
3724             'section/before:offset': 0,
3725             'section/before:size': len(U_BOOT_DATA),
3726
3727             'section/u-boot:image-pos': 4,
3728             'section/u-boot:offset': 4,
3729             'section/u-boot:size': 12 + len(U_BOOT_DATA) + 6,
3730
3731             'section/after:image-pos': 26,
3732             'section/after:offset': 26,
3733             'section/after:size': len(U_BOOT_DATA),
3734             }
3735         self.assertEqual(expected, props)
3736
3737     def testFitImageSubentryAlignment(self):
3738         """Test relative alignability of FIT image subentries"""
3739         entry_args = {
3740             'test-id': TEXT_DATA,
3741         }
3742         data, _, _, _ = self._DoReadFileDtb('167_fit_image_subentry_alignment.dts',
3743                                             entry_args=entry_args)
3744         dtb = fdt.Fdt.FromData(data)
3745         dtb.Scan()
3746
3747         node = dtb.GetNode('/images/kernel')
3748         data = dtb.GetProps(node)["data"].bytes
3749         align_pad = 0x10 - (len(U_BOOT_SPL_DATA) % 0x10)
3750         expected = (tools.GetBytes(0, 0x20) + U_BOOT_SPL_DATA +
3751                     tools.GetBytes(0, align_pad) + U_BOOT_DATA)
3752         self.assertEqual(expected, data)
3753
3754         node = dtb.GetNode('/images/fdt-1')
3755         data = dtb.GetProps(node)["data"].bytes
3756         expected = (U_BOOT_SPL_DTB_DATA + tools.GetBytes(0, 20) +
3757                     tools.ToBytes(TEXT_DATA) + tools.GetBytes(0, 30) +
3758                     U_BOOT_DTB_DATA)
3759         self.assertEqual(expected, data)
3760
3761     def testFitExtblobMissingOk(self):
3762         """Test a FIT with a missing external blob that is allowed"""
3763         with test_util.capture_sys_output() as (stdout, stderr):
3764             self._DoTestFile('168_fit_missing_blob.dts',
3765                              allow_missing=True)
3766         err = stderr.getvalue()
3767         self.assertRegex(err, "Image 'main-section'.*missing.*: atf-bl31")
3768
3769     def testBlobNamedByArgMissing(self):
3770         """Test handling of a missing entry arg"""
3771         with self.assertRaises(ValueError) as e:
3772             self._DoReadFile('068_blob_named_by_arg.dts')
3773         self.assertIn("Missing required properties/entry args: cros-ec-rw-path",
3774                       str(e.exception))
3775
3776     def testPackBl31(self):
3777         """Test that an image with an ATF BL31 binary can be created"""
3778         data = self._DoReadFile('169_atf_bl31.dts')
3779         self.assertEqual(ATF_BL31_DATA, data[:len(ATF_BL31_DATA)])
3780
3781     def testPackScp(self):
3782         """Test that an image with an SCP binary can be created"""
3783         data = self._DoReadFile('172_scp.dts')
3784         self.assertEqual(SCP_DATA, data[:len(SCP_DATA)])
3785
3786     def testFitFdt(self):
3787         """Test an image with an FIT with multiple FDT images"""
3788         def _CheckFdt(seq, expected_data):
3789             """Check the FDT nodes
3790
3791             Args:
3792                 seq: Sequence number to check (0 or 1)
3793                 expected_data: Expected contents of 'data' property
3794             """
3795             name = 'fdt-%d' % seq
3796             fnode = dtb.GetNode('/images/%s' % name)
3797             self.assertIsNotNone(fnode)
3798             self.assertEqual({'description','type', 'compression', 'data'},
3799                              set(fnode.props.keys()))
3800             self.assertEqual(expected_data, fnode.props['data'].bytes)
3801             self.assertEqual('fdt-test-fdt%d.dtb' % seq,
3802                              fnode.props['description'].value)
3803
3804         def _CheckConfig(seq, expected_data):
3805             """Check the configuration nodes
3806
3807             Args:
3808                 seq: Sequence number to check (0 or 1)
3809                 expected_data: Expected contents of 'data' property
3810             """
3811             cnode = dtb.GetNode('/configurations')
3812             self.assertIn('default', cnode.props)
3813             self.assertEqual('config-2', cnode.props['default'].value)
3814
3815             name = 'config-%d' % seq
3816             fnode = dtb.GetNode('/configurations/%s' % name)
3817             self.assertIsNotNone(fnode)
3818             self.assertEqual({'description','firmware', 'loadables', 'fdt'},
3819                              set(fnode.props.keys()))
3820             self.assertEqual('conf-test-fdt%d.dtb' % seq,
3821                              fnode.props['description'].value)
3822             self.assertEqual('fdt-%d' % seq, fnode.props['fdt'].value)
3823
3824         entry_args = {
3825             'of-list': 'test-fdt1 test-fdt2',
3826             'default-dt': 'test-fdt2',
3827         }
3828         data = self._DoReadFileDtb(
3829             '172_fit_fdt.dts',
3830             entry_args=entry_args,
3831             extra_indirs=[os.path.join(self._indir, TEST_FDT_SUBDIR)])[0]
3832         self.assertEqual(U_BOOT_NODTB_DATA, data[-len(U_BOOT_NODTB_DATA):])
3833         fit_data = data[len(U_BOOT_DATA):-len(U_BOOT_NODTB_DATA)]
3834
3835         dtb = fdt.Fdt.FromData(fit_data)
3836         dtb.Scan()
3837         fnode = dtb.GetNode('/images/kernel')
3838         self.assertIn('data', fnode.props)
3839
3840         # Check all the properties in fdt-1 and fdt-2
3841         _CheckFdt(1, TEST_FDT1_DATA)
3842         _CheckFdt(2, TEST_FDT2_DATA)
3843
3844         # Check configurations
3845         _CheckConfig(1, TEST_FDT1_DATA)
3846         _CheckConfig(2, TEST_FDT2_DATA)
3847
3848     def testFitFdtMissingList(self):
3849         """Test handling of a missing 'of-list' entry arg"""
3850         with self.assertRaises(ValueError) as e:
3851             self._DoReadFile('172_fit_fdt.dts')
3852         self.assertIn("Generator node requires 'of-list' entry argument",
3853                       str(e.exception))
3854
3855     def testFitFdtEmptyList(self):
3856         """Test handling of an empty 'of-list' entry arg"""
3857         entry_args = {
3858             'of-list': '',
3859         }
3860         data = self._DoReadFileDtb('170_fit_fdt.dts', entry_args=entry_args)[0]
3861
3862     def testFitFdtMissingProp(self):
3863         """Test handling of a missing 'fit,fdt-list' property"""
3864         with self.assertRaises(ValueError) as e:
3865             self._DoReadFile('171_fit_fdt_missing_prop.dts')
3866         self.assertIn("Generator node requires 'fit,fdt-list' property",
3867                       str(e.exception))
3868
3869     def testFitFdtEmptyList(self):
3870         """Test handling of an empty 'of-list' entry arg"""
3871         entry_args = {
3872             'of-list': '',
3873         }
3874         data = self._DoReadFileDtb('172_fit_fdt.dts', entry_args=entry_args)[0]
3875
3876     def testFitFdtMissing(self):
3877         """Test handling of a missing 'default-dt' entry arg"""
3878         entry_args = {
3879             'of-list': 'test-fdt1 test-fdt2',
3880         }
3881         with self.assertRaises(ValueError) as e:
3882             self._DoReadFileDtb(
3883                 '172_fit_fdt.dts',
3884                 entry_args=entry_args,
3885                 extra_indirs=[os.path.join(self._indir, TEST_FDT_SUBDIR)])[0]
3886         self.assertIn("Generated 'default' node requires default-dt entry argument",
3887                       str(e.exception))
3888
3889     def testFitFdtNotInList(self):
3890         """Test handling of a default-dt that is not in the of-list"""
3891         entry_args = {
3892             'of-list': 'test-fdt1 test-fdt2',
3893             'default-dt': 'test-fdt3',
3894         }
3895         with self.assertRaises(ValueError) as e:
3896             self._DoReadFileDtb(
3897                 '172_fit_fdt.dts',
3898                 entry_args=entry_args,
3899                 extra_indirs=[os.path.join(self._indir, TEST_FDT_SUBDIR)])[0]
3900         self.assertIn("default-dt entry argument 'test-fdt3' not found in fdt list: test-fdt1, test-fdt2",
3901                       str(e.exception))
3902
3903     def testFitExtblobMissingHelp(self):
3904         """Test display of help messages when an external blob is missing"""
3905         control.missing_blob_help = control._ReadMissingBlobHelp()
3906         control.missing_blob_help['wibble'] = 'Wibble test'
3907         control.missing_blob_help['another'] = 'Another test'
3908         with test_util.capture_sys_output() as (stdout, stderr):
3909             self._DoTestFile('168_fit_missing_blob.dts',
3910                              allow_missing=True)
3911         err = stderr.getvalue()
3912
3913         # We can get the tag from the name, the type or the missing-msg
3914         # property. Check all three.
3915         self.assertIn('You may need to build ARM Trusted', err)
3916         self.assertIn('Wibble test', err)
3917         self.assertIn('Another test', err)
3918
3919     def testMissingBlob(self):
3920         """Test handling of a blob containing a missing file"""
3921         with self.assertRaises(ValueError) as e:
3922             self._DoTestFile('173_missing_blob.dts', allow_missing=True)
3923         self.assertIn("Filename 'missing' not found in input path",
3924                       str(e.exception))
3925
3926     def testEnvironment(self):
3927         """Test adding a U-Boot environment"""
3928         data = self._DoReadFile('174_env.dts')
3929         self.assertEqual(U_BOOT_DATA, data[:len(U_BOOT_DATA)])
3930         self.assertEqual(U_BOOT_NODTB_DATA, data[-len(U_BOOT_NODTB_DATA):])
3931         env = data[len(U_BOOT_DATA):-len(U_BOOT_NODTB_DATA)]
3932         self.assertEqual(b'\x1b\x97\x22\x7c\x01var1=1\0var2="2"\0\0\xff\xff',
3933                          env)
3934
3935     def testEnvironmentNoSize(self):
3936         """Test that a missing 'size' property is detected"""
3937         with self.assertRaises(ValueError) as e:
3938             self._DoTestFile('175_env_no_size.dts')
3939         self.assertIn("'u-boot-env' entry must have a size property",
3940                       str(e.exception))
3941
3942     def testEnvironmentTooSmall(self):
3943         """Test handling of an environment that does not fit"""
3944         with self.assertRaises(ValueError) as e:
3945             self._DoTestFile('176_env_too_small.dts')
3946
3947         # checksum, start byte, environment with \0 terminator, final \0
3948         need = 4 + 1 + len(ENV_DATA) + 1 + 1
3949         short = need - 0x8
3950         self.assertIn("too small to hold data (need %#x more bytes)" % short,
3951                       str(e.exception))
3952
3953     def testSkipAtStart(self):
3954         """Test handling of skip-at-start section"""
3955         data = self._DoReadFile('177_skip_at_start.dts')
3956         self.assertEqual(U_BOOT_DATA, data)
3957
3958         image = control.images['image']
3959         entries = image.GetEntries()
3960         section = entries['section']
3961         self.assertEqual(0, section.offset)
3962         self.assertEqual(len(U_BOOT_DATA), section.size)
3963         self.assertEqual(U_BOOT_DATA, section.GetData())
3964
3965         entry = section.GetEntries()['u-boot']
3966         self.assertEqual(16, entry.offset)
3967         self.assertEqual(len(U_BOOT_DATA), entry.size)
3968         self.assertEqual(U_BOOT_DATA, entry.data)
3969
3970     def testSkipAtStartPad(self):
3971         """Test handling of skip-at-start section with padded entry"""
3972         data = self._DoReadFile('178_skip_at_start_pad.dts')
3973         before = tools.GetBytes(0, 8)
3974         after = tools.GetBytes(0, 4)
3975         all = before + U_BOOT_DATA + after
3976         self.assertEqual(all, data)
3977
3978         image = control.images['image']
3979         entries = image.GetEntries()
3980         section = entries['section']
3981         self.assertEqual(0, section.offset)
3982         self.assertEqual(len(all), section.size)
3983         self.assertEqual(all, section.GetData())
3984
3985         entry = section.GetEntries()['u-boot']
3986         self.assertEqual(16, entry.offset)
3987         self.assertEqual(len(all), entry.size)
3988         self.assertEqual(U_BOOT_DATA, entry.data)
3989
3990     def testSkipAtStartSectionPad(self):
3991         """Test handling of skip-at-start section with padding"""
3992         data = self._DoReadFile('179_skip_at_start_section_pad.dts')
3993         before = tools.GetBytes(0, 8)
3994         after = tools.GetBytes(0, 4)
3995         all = before + U_BOOT_DATA + after
3996         self.assertEqual(all, data)
3997
3998         image = control.images['image']
3999         entries = image.GetEntries()
4000         section = entries['section']
4001         self.assertEqual(0, section.offset)
4002         self.assertEqual(len(all), section.size)
4003         self.assertEqual(U_BOOT_DATA, section.data)
4004         self.assertEqual(all, section.GetPaddedData())
4005
4006         entry = section.GetEntries()['u-boot']
4007         self.assertEqual(16, entry.offset)
4008         self.assertEqual(len(U_BOOT_DATA), entry.size)
4009         self.assertEqual(U_BOOT_DATA, entry.data)
4010
4011     def testSectionPad(self):
4012         """Testing padding with sections"""
4013         data = self._DoReadFile('180_section_pad.dts')
4014         expected = (tools.GetBytes(ord('&'), 3) +
4015                     tools.GetBytes(ord('!'), 5) +
4016                     U_BOOT_DATA +
4017                     tools.GetBytes(ord('!'), 1) +
4018                     tools.GetBytes(ord('&'), 2))
4019         self.assertEqual(expected, data)
4020
4021     def testSectionAlign(self):
4022         """Testing alignment with sections"""
4023         data = self._DoReadFileDtb('181_section_align.dts', map=True)[0]
4024         expected = (b'\0' +                         # fill section
4025                     tools.GetBytes(ord('&'), 1) +   # padding to section align
4026                     b'\0' +                         # fill section
4027                     tools.GetBytes(ord('!'), 3) +   # padding to u-boot align
4028                     U_BOOT_DATA +
4029                     tools.GetBytes(ord('!'), 4) +   # padding to u-boot size
4030                     tools.GetBytes(ord('!'), 4))    # padding to section size
4031         self.assertEqual(expected, data)
4032
4033     def testCompressImage(self):
4034         """Test compression of the entire image"""
4035         self._CheckLz4()
4036         data, _, _, out_dtb_fname = self._DoReadFileDtb(
4037             '182_compress_image.dts', use_real_dtb=True, update_dtb=True)
4038         dtb = fdt.Fdt(out_dtb_fname)
4039         dtb.Scan()
4040         props = self._GetPropTree(dtb, ['offset', 'image-pos', 'size',
4041                                         'uncomp-size'])
4042         orig = self._decompress(data)
4043         self.assertEquals(COMPRESS_DATA + U_BOOT_DATA, orig)
4044
4045         # Do a sanity check on various fields
4046         image = control.images['image']
4047         entries = image.GetEntries()
4048         self.assertEqual(2, len(entries))
4049
4050         entry = entries['blob']
4051         self.assertEqual(COMPRESS_DATA, entry.data)
4052         self.assertEqual(len(COMPRESS_DATA), entry.size)
4053
4054         entry = entries['u-boot']
4055         self.assertEqual(U_BOOT_DATA, entry.data)
4056         self.assertEqual(len(U_BOOT_DATA), entry.size)
4057
4058         self.assertEqual(len(data), image.size)
4059         self.assertEqual(COMPRESS_DATA + U_BOOT_DATA, image.uncomp_data)
4060         self.assertEqual(len(COMPRESS_DATA + U_BOOT_DATA), image.uncomp_size)
4061         orig = self._decompress(image.data)
4062         self.assertEqual(orig, image.uncomp_data)
4063
4064         expected = {
4065             'blob:offset': 0,
4066             'blob:size': len(COMPRESS_DATA),
4067             'u-boot:offset': len(COMPRESS_DATA),
4068             'u-boot:size': len(U_BOOT_DATA),
4069             'uncomp-size': len(COMPRESS_DATA + U_BOOT_DATA),
4070             'offset': 0,
4071             'image-pos': 0,
4072             'size': len(data),
4073             }
4074         self.assertEqual(expected, props)
4075
4076     def testCompressImageLess(self):
4077         """Test compression where compression reduces the image size"""
4078         self._CheckLz4()
4079         data, _, _, out_dtb_fname = self._DoReadFileDtb(
4080             '183_compress_image_less.dts', use_real_dtb=True, update_dtb=True)
4081         dtb = fdt.Fdt(out_dtb_fname)
4082         dtb.Scan()
4083         props = self._GetPropTree(dtb, ['offset', 'image-pos', 'size',
4084                                         'uncomp-size'])
4085         orig = self._decompress(data)
4086
4087         self.assertEquals(COMPRESS_DATA + COMPRESS_DATA + U_BOOT_DATA, orig)
4088
4089         # Do a sanity check on various fields
4090         image = control.images['image']
4091         entries = image.GetEntries()
4092         self.assertEqual(2, len(entries))
4093
4094         entry = entries['blob']
4095         self.assertEqual(COMPRESS_DATA_BIG, entry.data)
4096         self.assertEqual(len(COMPRESS_DATA_BIG), entry.size)
4097
4098         entry = entries['u-boot']
4099         self.assertEqual(U_BOOT_DATA, entry.data)
4100         self.assertEqual(len(U_BOOT_DATA), entry.size)
4101
4102         self.assertEqual(len(data), image.size)
4103         self.assertEqual(COMPRESS_DATA_BIG + U_BOOT_DATA, image.uncomp_data)
4104         self.assertEqual(len(COMPRESS_DATA_BIG + U_BOOT_DATA),
4105                          image.uncomp_size)
4106         orig = self._decompress(image.data)
4107         self.assertEqual(orig, image.uncomp_data)
4108
4109         expected = {
4110             'blob:offset': 0,
4111             'blob:size': len(COMPRESS_DATA_BIG),
4112             'u-boot:offset': len(COMPRESS_DATA_BIG),
4113             'u-boot:size': len(U_BOOT_DATA),
4114             'uncomp-size': len(COMPRESS_DATA_BIG + U_BOOT_DATA),
4115             'offset': 0,
4116             'image-pos': 0,
4117             'size': len(data),
4118             }
4119         self.assertEqual(expected, props)
4120
4121     def testCompressSectionSize(self):
4122         """Test compression of a section with a fixed size"""
4123         self._CheckLz4()
4124         data, _, _, out_dtb_fname = self._DoReadFileDtb(
4125             '184_compress_section_size.dts', use_real_dtb=True, update_dtb=True)
4126         dtb = fdt.Fdt(out_dtb_fname)
4127         dtb.Scan()
4128         props = self._GetPropTree(dtb, ['offset', 'image-pos', 'size',
4129                                         'uncomp-size'])
4130         orig = self._decompress(data)
4131         self.assertEquals(COMPRESS_DATA + U_BOOT_DATA, orig)
4132         expected = {
4133             'section/blob:offset': 0,
4134             'section/blob:size': len(COMPRESS_DATA),
4135             'section/u-boot:offset': len(COMPRESS_DATA),
4136             'section/u-boot:size': len(U_BOOT_DATA),
4137             'section:offset': 0,
4138             'section:image-pos': 0,
4139             'section:uncomp-size': len(COMPRESS_DATA + U_BOOT_DATA),
4140             'section:size': 0x30,
4141             'offset': 0,
4142             'image-pos': 0,
4143             'size': 0x30,
4144             }
4145         self.assertEqual(expected, props)
4146
4147     def testCompressSection(self):
4148         """Test compression of a section with no fixed size"""
4149         self._CheckLz4()
4150         data, _, _, out_dtb_fname = self._DoReadFileDtb(
4151             '185_compress_section.dts', use_real_dtb=True, update_dtb=True)
4152         dtb = fdt.Fdt(out_dtb_fname)
4153         dtb.Scan()
4154         props = self._GetPropTree(dtb, ['offset', 'image-pos', 'size',
4155                                         'uncomp-size'])
4156         orig = self._decompress(data)
4157         self.assertEquals(COMPRESS_DATA + U_BOOT_DATA, orig)
4158         expected = {
4159             'section/blob:offset': 0,
4160             'section/blob:size': len(COMPRESS_DATA),
4161             'section/u-boot:offset': len(COMPRESS_DATA),
4162             'section/u-boot:size': len(U_BOOT_DATA),
4163             'section:offset': 0,
4164             'section:image-pos': 0,
4165             'section:uncomp-size': len(COMPRESS_DATA + U_BOOT_DATA),
4166             'section:size': len(data),
4167             'offset': 0,
4168             'image-pos': 0,
4169             'size': len(data),
4170             }
4171         self.assertEqual(expected, props)
4172
4173     def testCompressExtra(self):
4174         """Test compression of a section with no fixed size"""
4175         self._CheckLz4()
4176         data, _, _, out_dtb_fname = self._DoReadFileDtb(
4177             '186_compress_extra.dts', use_real_dtb=True, update_dtb=True)
4178         dtb = fdt.Fdt(out_dtb_fname)
4179         dtb.Scan()
4180         props = self._GetPropTree(dtb, ['offset', 'image-pos', 'size',
4181                                         'uncomp-size'])
4182
4183         base = data[len(U_BOOT_DATA):]
4184         self.assertEquals(U_BOOT_DATA, base[:len(U_BOOT_DATA)])
4185         rest = base[len(U_BOOT_DATA):]
4186
4187         # Check compressed data
4188         section1 = self._decompress(rest)
4189         expect1 = tools.Compress(COMPRESS_DATA + U_BOOT_DATA, 'lz4')
4190         self.assertEquals(expect1, rest[:len(expect1)])
4191         self.assertEquals(COMPRESS_DATA + U_BOOT_DATA, section1)
4192         rest1 = rest[len(expect1):]
4193
4194         section2 = self._decompress(rest1)
4195         expect2 = tools.Compress(COMPRESS_DATA + COMPRESS_DATA, 'lz4')
4196         self.assertEquals(expect2, rest1[:len(expect2)])
4197         self.assertEquals(COMPRESS_DATA + COMPRESS_DATA, section2)
4198         rest2 = rest1[len(expect2):]
4199
4200         expect_size = (len(U_BOOT_DATA) + len(U_BOOT_DATA) + len(expect1) +
4201                        len(expect2) + len(U_BOOT_DATA))
4202         #self.assertEquals(expect_size, len(data))
4203
4204         #self.assertEquals(U_BOOT_DATA, rest2)
4205
4206         self.maxDiff = None
4207         expected = {
4208             'u-boot:offset': 0,
4209             'u-boot:image-pos': 0,
4210             'u-boot:size': len(U_BOOT_DATA),
4211
4212             'base:offset': len(U_BOOT_DATA),
4213             'base:image-pos': len(U_BOOT_DATA),
4214             'base:size': len(data) - len(U_BOOT_DATA),
4215             'base/u-boot:offset': 0,
4216             'base/u-boot:image-pos': len(U_BOOT_DATA),
4217             'base/u-boot:size': len(U_BOOT_DATA),
4218             'base/u-boot2:offset': len(U_BOOT_DATA) + len(expect1) +
4219                 len(expect2),
4220             'base/u-boot2:image-pos': len(U_BOOT_DATA) * 2 + len(expect1) +
4221                 len(expect2),
4222             'base/u-boot2:size': len(U_BOOT_DATA),
4223
4224             'base/section:offset': len(U_BOOT_DATA),
4225             'base/section:image-pos': len(U_BOOT_DATA) * 2,
4226             'base/section:size': len(expect1),
4227             'base/section:uncomp-size': len(COMPRESS_DATA + U_BOOT_DATA),
4228             'base/section/blob:offset': 0,
4229             'base/section/blob:size': len(COMPRESS_DATA),
4230             'base/section/u-boot:offset': len(COMPRESS_DATA),
4231             'base/section/u-boot:size': len(U_BOOT_DATA),
4232
4233             'base/section2:offset': len(U_BOOT_DATA) + len(expect1),
4234             'base/section2:image-pos': len(U_BOOT_DATA) * 2 + len(expect1),
4235             'base/section2:size': len(expect2),
4236             'base/section2:uncomp-size': len(COMPRESS_DATA + COMPRESS_DATA),
4237             'base/section2/blob:offset': 0,
4238             'base/section2/blob:size': len(COMPRESS_DATA),
4239             'base/section2/blob2:offset': len(COMPRESS_DATA),
4240             'base/section2/blob2:size': len(COMPRESS_DATA),
4241
4242             'offset': 0,
4243             'image-pos': 0,
4244             'size': len(data),
4245             }
4246         self.assertEqual(expected, props)
4247
4248     def testSymbolsSubsection(self):
4249         """Test binman can assign symbols from a subsection"""
4250         self.checkSymbols('187_symbols_sub.dts', U_BOOT_SPL_DATA, 0x18)
4251
4252     def testReadImageEntryArg(self):
4253         """Test reading an image that would need an entry arg to generate"""
4254         entry_args = {
4255             'cros-ec-rw-path': 'ecrw.bin',
4256         }
4257         data = self.data = self._DoReadFileDtb(
4258             '188_image_entryarg.dts',use_real_dtb=True, update_dtb=True,
4259             entry_args=entry_args)
4260
4261         image_fname = tools.GetOutputFilename('image.bin')
4262         orig_image = control.images['image']
4263
4264         # This should not generate an error about the missing 'cros-ec-rw-path'
4265         # since we are reading the image from a file. Compare with
4266         # testEntryArgsRequired()
4267         image = Image.FromFile(image_fname)
4268         self.assertEqual(orig_image.GetEntries().keys(),
4269                          image.GetEntries().keys())
4270
4271     def testFilesAlign(self):
4272         """Test alignment with files"""
4273         data = self._DoReadFile('190_files_align.dts')
4274
4275         # The first string is 15 bytes so will align to 16
4276         expect = FILES_DATA[:15] + b'\0' + FILES_DATA[15:]
4277         self.assertEqual(expect, data)
4278
4279     def testReadImageSkip(self):
4280         """Test reading an image and accessing its FDT map"""
4281         data = self.data = self._DoReadFileRealDtb('191_read_image_skip.dts')
4282         image_fname = tools.GetOutputFilename('image.bin')
4283         orig_image = control.images['image']
4284         image = Image.FromFile(image_fname)
4285         self.assertEqual(orig_image.GetEntries().keys(),
4286                          image.GetEntries().keys())
4287
4288         orig_entry = orig_image.GetEntries()['fdtmap']
4289         entry = image.GetEntries()['fdtmap']
4290         self.assertEqual(orig_entry.offset, entry.offset)
4291         self.assertEqual(orig_entry.size, entry.size)
4292         self.assertEqual(16, entry.image_pos)
4293
4294         u_boot = image.GetEntries()['section'].GetEntries()['u-boot']
4295
4296         self.assertEquals(U_BOOT_DATA, u_boot.ReadData())
4297
4298     def testTplNoDtb(self):
4299         """Test that an image with tpl/u-boot-tpl-nodtb.bin can be created"""
4300         self._SetupTplElf()
4301         data = self._DoReadFile('192_u_boot_tpl_nodtb.dts')
4302         self.assertEqual(U_BOOT_TPL_NODTB_DATA,
4303                          data[:len(U_BOOT_TPL_NODTB_DATA)])
4304
4305     def testTplBssPad(self):
4306         """Test that we can pad TPL's BSS with zeros"""
4307         # ELF file with a '__bss_size' symbol
4308         self._SetupTplElf()
4309         data = self._DoReadFile('193_tpl_bss_pad.dts')
4310         self.assertEqual(U_BOOT_TPL_DATA + tools.GetBytes(0, 10) + U_BOOT_DATA,
4311                          data)
4312
4313     def testTplBssPadMissing(self):
4314         """Test that a missing symbol is detected"""
4315         self._SetupTplElf('u_boot_ucode_ptr')
4316         with self.assertRaises(ValueError) as e:
4317             self._DoReadFile('193_tpl_bss_pad.dts')
4318         self.assertIn('Expected __bss_size symbol in tpl/u-boot-tpl',
4319                       str(e.exception))
4320
4321     def checkDtbSizes(self, data, pad_len, start):
4322         """Check the size arguments in a dtb embedded in an image
4323
4324         Args:
4325             data: The image data
4326             pad_len: Length of the pad section in the image, in bytes
4327             start: Start offset of the devicetree to examine, within the image
4328
4329         Returns:
4330             Size of the devicetree in bytes
4331         """
4332         dtb_data = data[start:]
4333         dtb = fdt.Fdt.FromData(dtb_data)
4334         fdt_size = dtb.GetFdtObj().totalsize()
4335         dtb.Scan()
4336         props = self._GetPropTree(dtb, 'size')
4337         self.assertEqual({
4338             'size': len(data),
4339             'u-boot-spl/u-boot-spl-bss-pad:size': pad_len,
4340             'u-boot-spl/u-boot-spl-dtb:size': 801,
4341             'u-boot-spl/u-boot-spl-nodtb:size': len(U_BOOT_SPL_NODTB_DATA),
4342             'u-boot-spl:size': 860,
4343             'u-boot-tpl:size': len(U_BOOT_TPL_DATA),
4344             'u-boot/u-boot-dtb:size': 781,
4345             'u-boot/u-boot-nodtb:size': len(U_BOOT_NODTB_DATA),
4346             'u-boot:size': 827,
4347             }, props)
4348         return fdt_size
4349
4350     def testExpanded(self):
4351         """Test that an expanded entry type is selected when needed"""
4352         self._SetupSplElf()
4353         self._SetupTplElf()
4354
4355         # SPL has a devicetree, TPL does not
4356         entry_args = {
4357             'spl-dtb': '1',
4358             'spl-bss-pad': 'y',
4359             'tpl-dtb': '',
4360         }
4361         self._DoReadFileDtb('194_fdt_incl.dts', use_expanded=True,
4362                             entry_args=entry_args)
4363         image = control.images['image']
4364         entries = image.GetEntries()
4365         self.assertEqual(3, len(entries))
4366
4367         # First, u-boot, which should be expanded into u-boot-nodtb and dtb
4368         self.assertIn('u-boot', entries)
4369         entry = entries['u-boot']
4370         self.assertEqual('u-boot-expanded', entry.etype)
4371         subent = entry.GetEntries()
4372         self.assertEqual(2, len(subent))
4373         self.assertIn('u-boot-nodtb', subent)
4374         self.assertIn('u-boot-dtb', subent)
4375
4376         # Second, u-boot-spl, which should be expanded into three parts
4377         self.assertIn('u-boot-spl', entries)
4378         entry = entries['u-boot-spl']
4379         self.assertEqual('u-boot-spl-expanded', entry.etype)
4380         subent = entry.GetEntries()
4381         self.assertEqual(3, len(subent))
4382         self.assertIn('u-boot-spl-nodtb', subent)
4383         self.assertIn('u-boot-spl-bss-pad', subent)
4384         self.assertIn('u-boot-spl-dtb', subent)
4385
4386         # Third, u-boot-tpl, which should be not be expanded, since TPL has no
4387         # devicetree
4388         self.assertIn('u-boot-tpl', entries)
4389         entry = entries['u-boot-tpl']
4390         self.assertEqual('u-boot-tpl', entry.etype)
4391         self.assertEqual(None, entry.GetEntries())
4392
4393     def testExpandedTpl(self):
4394         """Test that an expanded entry type is selected for TPL when needed"""
4395         self._SetupTplElf()
4396
4397         entry_args = {
4398             'tpl-bss-pad': 'y',
4399             'tpl-dtb': 'y',
4400         }
4401         self._DoReadFileDtb('195_fdt_incl_tpl.dts', use_expanded=True,
4402                             entry_args=entry_args)
4403         image = control.images['image']
4404         entries = image.GetEntries()
4405         self.assertEqual(1, len(entries))
4406
4407         # We only have u-boot-tpl, which be expanded
4408         self.assertIn('u-boot-tpl', entries)
4409         entry = entries['u-boot-tpl']
4410         self.assertEqual('u-boot-tpl-expanded', entry.etype)
4411         subent = entry.GetEntries()
4412         self.assertEqual(3, len(subent))
4413         self.assertIn('u-boot-tpl-nodtb', subent)
4414         self.assertIn('u-boot-tpl-bss-pad', subent)
4415         self.assertIn('u-boot-tpl-dtb', subent)
4416
4417     def testExpandedNoPad(self):
4418         """Test an expanded entry without BSS pad enabled"""
4419         self._SetupSplElf()
4420         self._SetupTplElf()
4421
4422         # SPL has a devicetree, TPL does not
4423         entry_args = {
4424             'spl-dtb': 'something',
4425             'spl-bss-pad': 'n',
4426             'tpl-dtb': '',
4427         }
4428         self._DoReadFileDtb('194_fdt_incl.dts', use_expanded=True,
4429                             entry_args=entry_args)
4430         image = control.images['image']
4431         entries = image.GetEntries()
4432
4433         # Just check u-boot-spl, which should be expanded into two parts
4434         self.assertIn('u-boot-spl', entries)
4435         entry = entries['u-boot-spl']
4436         self.assertEqual('u-boot-spl-expanded', entry.etype)
4437         subent = entry.GetEntries()
4438         self.assertEqual(2, len(subent))
4439         self.assertIn('u-boot-spl-nodtb', subent)
4440         self.assertIn('u-boot-spl-dtb', subent)
4441
4442     def testExpandedTplNoPad(self):
4443         """Test that an expanded entry type with padding disabled in TPL"""
4444         self._SetupTplElf()
4445
4446         entry_args = {
4447             'tpl-bss-pad': '',
4448             'tpl-dtb': 'y',
4449         }
4450         self._DoReadFileDtb('195_fdt_incl_tpl.dts', use_expanded=True,
4451                             entry_args=entry_args)
4452         image = control.images['image']
4453         entries = image.GetEntries()
4454         self.assertEqual(1, len(entries))
4455
4456         # We only have u-boot-tpl, which be expanded
4457         self.assertIn('u-boot-tpl', entries)
4458         entry = entries['u-boot-tpl']
4459         self.assertEqual('u-boot-tpl-expanded', entry.etype)
4460         subent = entry.GetEntries()
4461         self.assertEqual(2, len(subent))
4462         self.assertIn('u-boot-tpl-nodtb', subent)
4463         self.assertIn('u-boot-tpl-dtb', subent)
4464
4465     def testFdtInclude(self):
4466         """Test that an Fdt is update within all binaries"""
4467         self._SetupSplElf()
4468         self._SetupTplElf()
4469
4470         # SPL has a devicetree, TPL does not
4471         self.maxDiff = None
4472         entry_args = {
4473             'spl-dtb': '1',
4474             'spl-bss-pad': 'y',
4475             'tpl-dtb': '',
4476         }
4477         # Build the image. It includes two separate devicetree binaries, each
4478         # with their own contents, but all contain the binman definition.
4479         data = self._DoReadFileDtb(
4480             '194_fdt_incl.dts', use_real_dtb=True, use_expanded=True,
4481             update_dtb=True, entry_args=entry_args)[0]
4482         pad_len = 10
4483
4484         # Check the U-Boot dtb
4485         start = len(U_BOOT_NODTB_DATA)
4486         fdt_size = self.checkDtbSizes(data, pad_len, start)
4487
4488         # Now check SPL
4489         start += fdt_size + len(U_BOOT_SPL_NODTB_DATA) + pad_len
4490         fdt_size = self.checkDtbSizes(data, pad_len, start)
4491
4492         # TPL has no devicetree
4493         start += fdt_size + len(U_BOOT_TPL_DATA)
4494         self.assertEqual(len(data), start)
4495
4496     def testSymbolsExpanded(self):
4497         """Test binman can assign symbols in expanded entries"""
4498         entry_args = {
4499             'spl-dtb': '1',
4500         }
4501         self.checkSymbols('197_symbols_expand.dts', U_BOOT_SPL_NODTB_DATA +
4502                           U_BOOT_SPL_DTB_DATA, 0x38,
4503                           entry_args=entry_args, use_expanded=True)
4504
4505     def testCollection(self):
4506         """Test a collection"""
4507         data = self._DoReadFile('198_collection.dts')
4508         self.assertEqual(U_BOOT_NODTB_DATA + U_BOOT_DTB_DATA +
4509                          tools.GetBytes(0xff, 2) + U_BOOT_NODTB_DATA +
4510                          tools.GetBytes(0xfe, 3) + U_BOOT_DTB_DATA,
4511                          data)
4512
4513     def testCollectionSection(self):
4514         """Test a collection where a section must be built first"""
4515         # Sections never have their contents when GetData() is called, but when
4516         # _BuildSectionData() is called with required=True, a section will force
4517         # building the contents, producing an error is anything is still
4518         # missing.
4519         data = self._DoReadFile('199_collection_section.dts')
4520         section = U_BOOT_NODTB_DATA + U_BOOT_DTB_DATA
4521         self.assertEqual(section + U_BOOT_DATA + tools.GetBytes(0xff, 2) +
4522                          section + tools.GetBytes(0xfe, 3) + U_BOOT_DATA,
4523                          data)
4524
4525     def testAlignDefault(self):
4526         """Test that default alignment works on sections"""
4527         data = self._DoReadFile('200_align_default.dts')
4528         expected = (U_BOOT_DATA + tools.GetBytes(0, 8 - len(U_BOOT_DATA)) +
4529                     U_BOOT_DATA)
4530         # Special alignment for section
4531         expected += tools.GetBytes(0, 32 - len(expected))
4532         # No alignment within the nested section
4533         expected += U_BOOT_DATA + U_BOOT_NODTB_DATA;
4534         # Now the final piece, which should be default-aligned
4535         expected += tools.GetBytes(0, 88 - len(expected)) + U_BOOT_NODTB_DATA
4536         self.assertEqual(expected, data)
4537
4538 if __name__ == "__main__":
4539     unittest.main()