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