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