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