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