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