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