872b8554440d6a3799a4d0652b1a1e570109addb
[oweals/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 from __future__ import print_function
10
11 import hashlib
12 from optparse import OptionParser
13 import os
14 import shutil
15 import struct
16 import sys
17 import tempfile
18 import unittest
19
20 import binman
21 import cbfs_util
22 import cmdline
23 import command
24 import control
25 import elf
26 import elf_test
27 import fdt
28 from etype import fdtmap
29 from etype import image_header
30 import fdt_util
31 import fmap_util
32 import test_util
33 import gzip
34 from image import Image
35 import state
36 import tools
37 import tout
38
39 # Contents of test files, corresponding to different entry types
40 U_BOOT_DATA           = b'1234'
41 U_BOOT_IMG_DATA       = b'img'
42 U_BOOT_SPL_DATA       = b'56780123456789abcdefghi'
43 U_BOOT_TPL_DATA       = b'tpl9876543210fedcbazyw'
44 BLOB_DATA             = b'89'
45 ME_DATA               = b'0abcd'
46 VGA_DATA              = b'vga'
47 U_BOOT_DTB_DATA       = b'udtb'
48 U_BOOT_SPL_DTB_DATA   = b'spldtb'
49 U_BOOT_TPL_DTB_DATA   = b'tpldtb'
50 X86_START16_DATA      = b'start16'
51 X86_START16_SPL_DATA  = b'start16spl'
52 X86_START16_TPL_DATA  = b'start16tpl'
53 X86_RESET16_DATA      = b'reset16'
54 X86_RESET16_SPL_DATA  = b'reset16spl'
55 X86_RESET16_TPL_DATA  = b'reset16tpl'
56 PPC_MPC85XX_BR_DATA   = b'ppcmpc85xxbr'
57 U_BOOT_NODTB_DATA     = b'nodtb with microcode pointer somewhere in here'
58 U_BOOT_SPL_NODTB_DATA = b'splnodtb with microcode pointer somewhere in here'
59 U_BOOT_TPL_NODTB_DATA = b'tplnodtb with microcode pointer somewhere in here'
60 FSP_DATA              = b'fsp'
61 CMC_DATA              = b'cmc'
62 VBT_DATA              = b'vbt'
63 MRC_DATA              = b'mrc'
64 TEXT_DATA             = 'text'
65 TEXT_DATA2            = 'text2'
66 TEXT_DATA3            = 'text3'
67 CROS_EC_RW_DATA       = b'ecrw'
68 GBB_DATA              = b'gbbd'
69 BMPBLK_DATA           = b'bmp'
70 VBLOCK_DATA           = b'vblk'
71 FILES_DATA            = (b"sorry I'm late\nOh, don't bother apologising, I'm " +
72                          b"sorry you're alive\n")
73 COMPRESS_DATA         = b'compress xxxxxxxxxxxxxxxxxxxxxx data'
74 REFCODE_DATA          = b'refcode'
75 FSP_M_DATA            = b'fsp_m'
76 FSP_S_DATA            = b'fsp_s'
77 FSP_T_DATA            = b'fsp_t'
78
79 # The expected size for the device tree in some tests
80 EXTRACT_DTB_SIZE = 0x3c9
81
82 # Properties expected to be in the device tree when update_dtb is used
83 BASE_DTB_PROPS = ['offset', 'size', 'image-pos']
84
85 # Extra properties expected to be in the device tree when allow-repack is used
86 REPACK_DTB_PROPS = ['orig-offset', 'orig-size']
87
88
89 class TestFunctional(unittest.TestCase):
90     """Functional tests for binman
91
92     Most of these use a sample .dts file to build an image and then check
93     that it looks correct. The sample files are in the test/ subdirectory
94     and are numbered.
95
96     For each entry type a very small test file is created using fixed
97     string contents. This makes it easy to test that things look right, and
98     debug problems.
99
100     In some cases a 'real' file must be used - these are also supplied in
101     the test/ diurectory.
102     """
103     @classmethod
104     def setUpClass(cls):
105         global entry
106         import entry
107
108         # Handle the case where argv[0] is 'python'
109         cls._binman_dir = os.path.dirname(os.path.realpath(sys.argv[0]))
110         cls._binman_pathname = os.path.join(cls._binman_dir, 'binman')
111
112         # Create a temporary directory for input files
113         cls._indir = tempfile.mkdtemp(prefix='binmant.')
114
115         # Create some test files
116         TestFunctional._MakeInputFile('u-boot.bin', U_BOOT_DATA)
117         TestFunctional._MakeInputFile('u-boot.img', U_BOOT_IMG_DATA)
118         TestFunctional._MakeInputFile('spl/u-boot-spl.bin', U_BOOT_SPL_DATA)
119         TestFunctional._MakeInputFile('tpl/u-boot-tpl.bin', U_BOOT_TPL_DATA)
120         TestFunctional._MakeInputFile('blobfile', BLOB_DATA)
121         TestFunctional._MakeInputFile('me.bin', ME_DATA)
122         TestFunctional._MakeInputFile('vga.bin', VGA_DATA)
123         cls._ResetDtbs()
124
125         TestFunctional._MakeInputFile('u-boot-br.bin', PPC_MPC85XX_BR_DATA)
126
127         TestFunctional._MakeInputFile('u-boot-x86-start16.bin', X86_START16_DATA)
128         TestFunctional._MakeInputFile('spl/u-boot-x86-start16-spl.bin',
129                                       X86_START16_SPL_DATA)
130         TestFunctional._MakeInputFile('tpl/u-boot-x86-start16-tpl.bin',
131                                       X86_START16_TPL_DATA)
132
133         TestFunctional._MakeInputFile('u-boot-x86-reset16.bin',
134                                       X86_RESET16_DATA)
135         TestFunctional._MakeInputFile('spl/u-boot-x86-reset16-spl.bin',
136                                       X86_RESET16_SPL_DATA)
137         TestFunctional._MakeInputFile('tpl/u-boot-x86-reset16-tpl.bin',
138                                       X86_RESET16_TPL_DATA)
139
140         TestFunctional._MakeInputFile('u-boot-nodtb.bin', U_BOOT_NODTB_DATA)
141         TestFunctional._MakeInputFile('spl/u-boot-spl-nodtb.bin',
142                                       U_BOOT_SPL_NODTB_DATA)
143         TestFunctional._MakeInputFile('tpl/u-boot-tpl-nodtb.bin',
144                                       U_BOOT_TPL_NODTB_DATA)
145         TestFunctional._MakeInputFile('fsp.bin', FSP_DATA)
146         TestFunctional._MakeInputFile('cmc.bin', CMC_DATA)
147         TestFunctional._MakeInputFile('vbt.bin', VBT_DATA)
148         TestFunctional._MakeInputFile('mrc.bin', MRC_DATA)
149         TestFunctional._MakeInputFile('ecrw.bin', CROS_EC_RW_DATA)
150         TestFunctional._MakeInputDir('devkeys')
151         TestFunctional._MakeInputFile('bmpblk.bin', BMPBLK_DATA)
152         TestFunctional._MakeInputFile('refcode.bin', REFCODE_DATA)
153         TestFunctional._MakeInputFile('fsp_m.bin', FSP_M_DATA)
154         TestFunctional._MakeInputFile('fsp_s.bin', FSP_S_DATA)
155         TestFunctional._MakeInputFile('fsp_t.bin', FSP_T_DATA)
156
157         cls._elf_testdir = os.path.join(cls._indir, 'elftest')
158         elf_test.BuildElfTestFiles(cls._elf_testdir)
159
160         # ELF file with a '_dt_ucode_base_size' symbol
161         TestFunctional._MakeInputFile('u-boot',
162             tools.ReadFile(cls.ElfTestFile('u_boot_ucode_ptr')))
163
164         # Intel flash descriptor file
165         with open(cls.TestFile('descriptor.bin'), 'rb') as fd:
166             TestFunctional._MakeInputFile('descriptor.bin', fd.read())
167
168         shutil.copytree(cls.TestFile('files'),
169                         os.path.join(cls._indir, 'files'))
170
171         TestFunctional._MakeInputFile('compress', COMPRESS_DATA)
172
173         # Travis-CI may have an old lz4
174         cls.have_lz4 = True
175         try:
176             tools.Run('lz4', '--no-frame-crc', '-c',
177                       os.path.join(cls._indir, 'u-boot.bin'), binary=True)
178         except:
179             cls.have_lz4 = False
180
181     @classmethod
182     def tearDownClass(cls):
183         """Remove the temporary input directory and its contents"""
184         if cls.preserve_indir:
185             print('Preserving input dir: %s' % cls._indir)
186         else:
187             if cls._indir:
188                 shutil.rmtree(cls._indir)
189         cls._indir = None
190
191     @classmethod
192     def setup_test_args(cls, preserve_indir=False, preserve_outdirs=False,
193                         toolpath=None, verbosity=None):
194         """Accept arguments controlling test execution
195
196         Args:
197             preserve_indir: Preserve the shared input directory used by all
198                 tests in this class.
199             preserve_outdir: Preserve the output directories used by tests. Each
200                 test has its own, so this is normally only useful when running a
201                 single test.
202             toolpath: ist of paths to use for tools
203         """
204         cls.preserve_indir = preserve_indir
205         cls.preserve_outdirs = preserve_outdirs
206         cls.toolpath = toolpath
207         cls.verbosity = verbosity
208
209     def _CheckLz4(self):
210         if not self.have_lz4:
211             self.skipTest('lz4 --no-frame-crc not available')
212
213     def _CleanupOutputDir(self):
214         """Remove the temporary output directory"""
215         if self.preserve_outdirs:
216             print('Preserving output dir: %s' % tools.outdir)
217         else:
218             tools._FinaliseForTest()
219
220     def setUp(self):
221         # Enable this to turn on debugging output
222         # tout.Init(tout.DEBUG)
223         command.test_result = None
224
225     def tearDown(self):
226         """Remove the temporary output directory"""
227         self._CleanupOutputDir()
228
229     def _SetupImageInTmpdir(self):
230         """Set up the output image in a new temporary directory
231
232         This is used when an image has been generated in the output directory,
233         but we want to run binman again. This will create a new output
234         directory and fail to delete the original one.
235
236         This creates a new temporary directory, copies the image to it (with a
237         new name) and removes the old output directory.
238
239         Returns:
240             Tuple:
241                 Temporary directory to use
242                 New image filename
243         """
244         image_fname = tools.GetOutputFilename('image.bin')
245         tmpdir = tempfile.mkdtemp(prefix='binman.')
246         updated_fname = os.path.join(tmpdir, 'image-updated.bin')
247         tools.WriteFile(updated_fname, tools.ReadFile(image_fname))
248         self._CleanupOutputDir()
249         return tmpdir, updated_fname
250
251     @classmethod
252     def _ResetDtbs(cls):
253         TestFunctional._MakeInputFile('u-boot.dtb', U_BOOT_DTB_DATA)
254         TestFunctional._MakeInputFile('spl/u-boot-spl.dtb', U_BOOT_SPL_DTB_DATA)
255         TestFunctional._MakeInputFile('tpl/u-boot-tpl.dtb', U_BOOT_TPL_DTB_DATA)
256
257     def _RunBinman(self, *args, **kwargs):
258         """Run binman using the command line
259
260         Args:
261             Arguments to pass, as a list of strings
262             kwargs: Arguments to pass to Command.RunPipe()
263         """
264         result = command.RunPipe([[self._binman_pathname] + list(args)],
265                 capture=True, capture_stderr=True, raise_on_error=False)
266         if result.return_code and kwargs.get('raise_on_error', True):
267             raise Exception("Error running '%s': %s" % (' '.join(args),
268                             result.stdout + result.stderr))
269         return result
270
271     def _DoBinman(self, *argv):
272         """Run binman using directly (in the same process)
273
274         Args:
275             Arguments to pass, as a list of strings
276         Returns:
277             Return value (0 for success)
278         """
279         argv = list(argv)
280         args = cmdline.ParseArgs(argv)
281         args.pager = 'binman-invalid-pager'
282         args.build_dir = self._indir
283
284         # For testing, you can force an increase in verbosity here
285         # args.verbosity = tout.DEBUG
286         return control.Binman(args)
287
288     def _DoTestFile(self, fname, debug=False, map=False, update_dtb=False,
289                     entry_args=None, images=None, use_real_dtb=False,
290                     verbosity=None):
291         """Run binman with a given test file
292
293         Args:
294             fname: Device-tree source filename to use (e.g. 005_simple.dts)
295             debug: True to enable debugging output
296             map: True to output map files for the images
297             update_dtb: Update the offset and size of each entry in the device
298                 tree before packing it into the image
299             entry_args: Dict of entry args to supply to binman
300                 key: arg name
301                 value: value of that arg
302             images: List of image names to build
303         """
304         args = []
305         if debug:
306             args.append('-D')
307         if verbosity is not None:
308             args.append('-v%d' % verbosity)
309         elif self.verbosity:
310             args.append('-v%d' % self.verbosity)
311         if self.toolpath:
312             for path in self.toolpath:
313                 args += ['--toolpath', path]
314         args += ['build', '-p', '-I', self._indir, '-d', self.TestFile(fname)]
315         if map:
316             args.append('-m')
317         if update_dtb:
318             args.append('-u')
319         if not use_real_dtb:
320             args.append('--fake-dtb')
321         if entry_args:
322             for arg, value in entry_args.items():
323                 args.append('-a%s=%s' % (arg, value))
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 [<_testing.Entry__testing ",
1294                 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                       '[<_testing.Entry__testing', str(e.exception))
1342
1343     def testEntryArgs(self):
1344         """Test passing arguments to entries from the command line"""
1345         entry_args = {
1346             'test-str-arg': 'test1',
1347             'test-int-arg': '456',
1348         }
1349         self._DoReadFileDtb('062_entry_args.dts', entry_args=entry_args)
1350         self.assertIn('image', control.images)
1351         entry = control.images['image'].GetEntries()['_testing']
1352         self.assertEqual('test0', entry.test_str_fdt)
1353         self.assertEqual('test1', entry.test_str_arg)
1354         self.assertEqual(123, entry.test_int_fdt)
1355         self.assertEqual(456, entry.test_int_arg)
1356
1357     def testEntryArgsMissing(self):
1358         """Test missing arguments and properties"""
1359         entry_args = {
1360             'test-int-arg': '456',
1361         }
1362         self._DoReadFileDtb('063_entry_args_missing.dts', entry_args=entry_args)
1363         entry = control.images['image'].GetEntries()['_testing']
1364         self.assertEqual('test0', entry.test_str_fdt)
1365         self.assertEqual(None, entry.test_str_arg)
1366         self.assertEqual(None, entry.test_int_fdt)
1367         self.assertEqual(456, entry.test_int_arg)
1368
1369     def testEntryArgsRequired(self):
1370         """Test missing arguments and properties"""
1371         entry_args = {
1372             'test-int-arg': '456',
1373         }
1374         with self.assertRaises(ValueError) as e:
1375             self._DoReadFileDtb('064_entry_args_required.dts')
1376         self.assertIn("Node '/binman/_testing': Missing required "
1377             'properties/entry args: test-str-arg, test-int-fdt, test-int-arg',
1378             str(e.exception))
1379
1380     def testEntryArgsInvalidFormat(self):
1381         """Test that an invalid entry-argument format is detected"""
1382         args = ['build', '-d', self.TestFile('064_entry_args_required.dts'),
1383                 '-ano-value']
1384         with self.assertRaises(ValueError) as e:
1385             self._DoBinman(*args)
1386         self.assertIn("Invalid entry arguemnt 'no-value'", str(e.exception))
1387
1388     def testEntryArgsInvalidInteger(self):
1389         """Test that an invalid entry-argument integer is detected"""
1390         entry_args = {
1391             'test-int-arg': 'abc',
1392         }
1393         with self.assertRaises(ValueError) as e:
1394             self._DoReadFileDtb('062_entry_args.dts', entry_args=entry_args)
1395         self.assertIn("Node '/binman/_testing': Cannot convert entry arg "
1396                       "'test-int-arg' (value 'abc') to integer",
1397             str(e.exception))
1398
1399     def testEntryArgsInvalidDatatype(self):
1400         """Test that an invalid entry-argument datatype is detected
1401
1402         This test could be written in entry_test.py except that it needs
1403         access to control.entry_args, which seems more than that module should
1404         be able to see.
1405         """
1406         entry_args = {
1407             'test-bad-datatype-arg': '12',
1408         }
1409         with self.assertRaises(ValueError) as e:
1410             self._DoReadFileDtb('065_entry_args_unknown_datatype.dts',
1411                                 entry_args=entry_args)
1412         self.assertIn('GetArg() internal error: Unknown data type ',
1413                       str(e.exception))
1414
1415     def testText(self):
1416         """Test for a text entry type"""
1417         entry_args = {
1418             'test-id': TEXT_DATA,
1419             'test-id2': TEXT_DATA2,
1420             'test-id3': TEXT_DATA3,
1421         }
1422         data, _, _, _ = self._DoReadFileDtb('066_text.dts',
1423                                             entry_args=entry_args)
1424         expected = (tools.ToBytes(TEXT_DATA) +
1425                     tools.GetBytes(0, 8 - len(TEXT_DATA)) +
1426                     tools.ToBytes(TEXT_DATA2) + tools.ToBytes(TEXT_DATA3) +
1427                     b'some text' + b'more text')
1428         self.assertEqual(expected, data)
1429
1430     def testEntryDocs(self):
1431         """Test for creation of entry documentation"""
1432         with test_util.capture_sys_output() as (stdout, stderr):
1433             control.WriteEntryDocs(binman.GetEntryModules())
1434         self.assertTrue(len(stdout.getvalue()) > 0)
1435
1436     def testEntryDocsMissing(self):
1437         """Test handling of missing entry documentation"""
1438         with self.assertRaises(ValueError) as e:
1439             with test_util.capture_sys_output() as (stdout, stderr):
1440                 control.WriteEntryDocs(binman.GetEntryModules(), 'u_boot')
1441         self.assertIn('Documentation is missing for modules: u_boot',
1442                       str(e.exception))
1443
1444     def testFmap(self):
1445         """Basic test of generation of a flashrom fmap"""
1446         data = self._DoReadFile('067_fmap.dts')
1447         fhdr, fentries = fmap_util.DecodeFmap(data[32:])
1448         expected = (U_BOOT_DATA + tools.GetBytes(ord('!'), 12) +
1449                     U_BOOT_DATA + tools.GetBytes(ord('a'), 12))
1450         self.assertEqual(expected, data[:32])
1451         self.assertEqual(b'__FMAP__', fhdr.signature)
1452         self.assertEqual(1, fhdr.ver_major)
1453         self.assertEqual(0, fhdr.ver_minor)
1454         self.assertEqual(0, fhdr.base)
1455         self.assertEqual(16 + 16 +
1456                          fmap_util.FMAP_HEADER_LEN +
1457                          fmap_util.FMAP_AREA_LEN * 3, fhdr.image_size)
1458         self.assertEqual(b'FMAP', fhdr.name)
1459         self.assertEqual(3, fhdr.nareas)
1460         for fentry in fentries:
1461             self.assertEqual(0, fentry.flags)
1462
1463         self.assertEqual(0, fentries[0].offset)
1464         self.assertEqual(4, fentries[0].size)
1465         self.assertEqual(b'RO_U_BOOT', fentries[0].name)
1466
1467         self.assertEqual(16, fentries[1].offset)
1468         self.assertEqual(4, fentries[1].size)
1469         self.assertEqual(b'RW_U_BOOT', fentries[1].name)
1470
1471         self.assertEqual(32, fentries[2].offset)
1472         self.assertEqual(fmap_util.FMAP_HEADER_LEN +
1473                          fmap_util.FMAP_AREA_LEN * 3, fentries[2].size)
1474         self.assertEqual(b'FMAP', fentries[2].name)
1475
1476     def testBlobNamedByArg(self):
1477         """Test we can add a blob with the filename coming from an entry arg"""
1478         entry_args = {
1479             'cros-ec-rw-path': 'ecrw.bin',
1480         }
1481         data, _, _, _ = self._DoReadFileDtb('068_blob_named_by_arg.dts',
1482                                             entry_args=entry_args)
1483
1484     def testFill(self):
1485         """Test for an fill entry type"""
1486         data = self._DoReadFile('069_fill.dts')
1487         expected = tools.GetBytes(0xff, 8) + tools.GetBytes(0, 8)
1488         self.assertEqual(expected, data)
1489
1490     def testFillNoSize(self):
1491         """Test for an fill entry type with no size"""
1492         with self.assertRaises(ValueError) as e:
1493             self._DoReadFile('070_fill_no_size.dts')
1494         self.assertIn("'fill' entry must have a size property",
1495                       str(e.exception))
1496
1497     def _HandleGbbCommand(self, pipe_list):
1498         """Fake calls to the futility utility"""
1499         if pipe_list[0][0] == 'futility':
1500             fname = pipe_list[0][-1]
1501             # Append our GBB data to the file, which will happen every time the
1502             # futility command is called.
1503             with open(fname, 'ab') as fd:
1504                 fd.write(GBB_DATA)
1505             return command.CommandResult()
1506
1507     def testGbb(self):
1508         """Test for the Chromium OS Google Binary Block"""
1509         command.test_result = self._HandleGbbCommand
1510         entry_args = {
1511             'keydir': 'devkeys',
1512             'bmpblk': 'bmpblk.bin',
1513         }
1514         data, _, _, _ = self._DoReadFileDtb('071_gbb.dts', entry_args=entry_args)
1515
1516         # Since futility
1517         expected = (GBB_DATA + GBB_DATA + tools.GetBytes(0, 8) +
1518                     tools.GetBytes(0, 0x2180 - 16))
1519         self.assertEqual(expected, data)
1520
1521     def testGbbTooSmall(self):
1522         """Test for the Chromium OS Google Binary Block being large enough"""
1523         with self.assertRaises(ValueError) as e:
1524             self._DoReadFileDtb('072_gbb_too_small.dts')
1525         self.assertIn("Node '/binman/gbb': GBB is too small",
1526                       str(e.exception))
1527
1528     def testGbbNoSize(self):
1529         """Test for the Chromium OS Google Binary Block having a size"""
1530         with self.assertRaises(ValueError) as e:
1531             self._DoReadFileDtb('073_gbb_no_size.dts')
1532         self.assertIn("Node '/binman/gbb': GBB must have a fixed size",
1533                       str(e.exception))
1534
1535     def _HandleVblockCommand(self, pipe_list):
1536         """Fake calls to the futility utility"""
1537         if pipe_list[0][0] == 'futility':
1538             fname = pipe_list[0][3]
1539             with open(fname, 'wb') as fd:
1540                 fd.write(VBLOCK_DATA)
1541             return command.CommandResult()
1542
1543     def testVblock(self):
1544         """Test for the Chromium OS Verified Boot Block"""
1545         command.test_result = self._HandleVblockCommand
1546         entry_args = {
1547             'keydir': 'devkeys',
1548         }
1549         data, _, _, _ = self._DoReadFileDtb('074_vblock.dts',
1550                                             entry_args=entry_args)
1551         expected = U_BOOT_DATA + VBLOCK_DATA + U_BOOT_DTB_DATA
1552         self.assertEqual(expected, data)
1553
1554     def testVblockNoContent(self):
1555         """Test we detect a vblock which has no content to sign"""
1556         with self.assertRaises(ValueError) as e:
1557             self._DoReadFile('075_vblock_no_content.dts')
1558         self.assertIn("Node '/binman/vblock': Vblock must have a 'content' "
1559                       'property', str(e.exception))
1560
1561     def testVblockBadPhandle(self):
1562         """Test that we detect a vblock with an invalid phandle in contents"""
1563         with self.assertRaises(ValueError) as e:
1564             self._DoReadFile('076_vblock_bad_phandle.dts')
1565         self.assertIn("Node '/binman/vblock': Cannot find node for phandle "
1566                       '1000', str(e.exception))
1567
1568     def testVblockBadEntry(self):
1569         """Test that we detect an entry that points to a non-entry"""
1570         with self.assertRaises(ValueError) as e:
1571             self._DoReadFile('077_vblock_bad_entry.dts')
1572         self.assertIn("Node '/binman/vblock': Cannot find entry for node "
1573                       "'other'", str(e.exception))
1574
1575     def testTpl(self):
1576         """Test that an image with TPL and its device tree can be created"""
1577         # ELF file with a '__bss_size' symbol
1578         self._SetupTplElf()
1579         data = self._DoReadFile('078_u_boot_tpl.dts')
1580         self.assertEqual(U_BOOT_TPL_DATA + U_BOOT_TPL_DTB_DATA, data)
1581
1582     def testUsesPos(self):
1583         """Test that the 'pos' property cannot be used anymore"""
1584         with self.assertRaises(ValueError) as e:
1585            data = self._DoReadFile('079_uses_pos.dts')
1586         self.assertIn("Node '/binman/u-boot': Please use 'offset' instead of "
1587                       "'pos'", str(e.exception))
1588
1589     def testFillZero(self):
1590         """Test for an fill entry type with a size of 0"""
1591         data = self._DoReadFile('080_fill_empty.dts')
1592         self.assertEqual(tools.GetBytes(0, 16), data)
1593
1594     def testTextMissing(self):
1595         """Test for a text entry type where there is no text"""
1596         with self.assertRaises(ValueError) as e:
1597             self._DoReadFileDtb('066_text.dts',)
1598         self.assertIn("Node '/binman/text': No value provided for text label "
1599                       "'test-id'", str(e.exception))
1600
1601     def testPackStart16Tpl(self):
1602         """Test that an image with an x86 start16 TPL region can be created"""
1603         data = self._DoReadFile('081_x86_start16_tpl.dts')
1604         self.assertEqual(X86_START16_TPL_DATA, data[:len(X86_START16_TPL_DATA)])
1605
1606     def testSelectImage(self):
1607         """Test that we can select which images to build"""
1608         expected = 'Skipping images: image1'
1609
1610         # We should only get the expected message in verbose mode
1611         for verbosity in (0, 2):
1612             with test_util.capture_sys_output() as (stdout, stderr):
1613                 retcode = self._DoTestFile('006_dual_image.dts',
1614                                            verbosity=verbosity,
1615                                            images=['image2'])
1616             self.assertEqual(0, retcode)
1617             if verbosity:
1618                 self.assertIn(expected, stdout.getvalue())
1619             else:
1620                 self.assertNotIn(expected, stdout.getvalue())
1621
1622             self.assertFalse(os.path.exists(tools.GetOutputFilename('image1.bin')))
1623             self.assertTrue(os.path.exists(tools.GetOutputFilename('image2.bin')))
1624             self._CleanupOutputDir()
1625
1626     def testUpdateFdtAll(self):
1627         """Test that all device trees are updated with offset/size info"""
1628         data = self._DoReadFileRealDtb('082_fdt_update_all.dts')
1629
1630         base_expected = {
1631             'section:image-pos': 0,
1632             'u-boot-tpl-dtb:size': 513,
1633             'u-boot-spl-dtb:size': 513,
1634             'u-boot-spl-dtb:offset': 493,
1635             'image-pos': 0,
1636             'section/u-boot-dtb:image-pos': 0,
1637             'u-boot-spl-dtb:image-pos': 493,
1638             'section/u-boot-dtb:size': 493,
1639             'u-boot-tpl-dtb:image-pos': 1006,
1640             'section/u-boot-dtb:offset': 0,
1641             'section:size': 493,
1642             'offset': 0,
1643             'section:offset': 0,
1644             'u-boot-tpl-dtb:offset': 1006,
1645             'size': 1519
1646         }
1647
1648         # We expect three device-tree files in the output, one after the other.
1649         # Read them in sequence. We look for an 'spl' property in the SPL tree,
1650         # and 'tpl' in the TPL tree, to make sure they are distinct from the
1651         # main U-Boot tree. All three should have the same postions and offset.
1652         start = 0
1653         for item in ['', 'spl', 'tpl']:
1654             dtb = fdt.Fdt.FromData(data[start:])
1655             dtb.Scan()
1656             props = self._GetPropTree(dtb, BASE_DTB_PROPS + REPACK_DTB_PROPS +
1657                                       ['spl', 'tpl'])
1658             expected = dict(base_expected)
1659             if item:
1660                 expected[item] = 0
1661             self.assertEqual(expected, props)
1662             start += dtb._fdt_obj.totalsize()
1663
1664     def testUpdateFdtOutput(self):
1665         """Test that output DTB files are updated"""
1666         try:
1667             data, dtb_data, _, _ = self._DoReadFileDtb('082_fdt_update_all.dts',
1668                     use_real_dtb=True, update_dtb=True, reset_dtbs=False)
1669
1670             # Unfortunately, compiling a source file always results in a file
1671             # called source.dtb (see fdt_util.EnsureCompiled()). The test
1672             # source file (e.g. test/075_fdt_update_all.dts) thus does not enter
1673             # binman as a file called u-boot.dtb. To fix this, copy the file
1674             # over to the expected place.
1675             start = 0
1676             for fname in ['u-boot.dtb.out', 'spl/u-boot-spl.dtb.out',
1677                           'tpl/u-boot-tpl.dtb.out']:
1678                 dtb = fdt.Fdt.FromData(data[start:])
1679                 size = dtb._fdt_obj.totalsize()
1680                 pathname = tools.GetOutputFilename(os.path.split(fname)[1])
1681                 outdata = tools.ReadFile(pathname)
1682                 name = os.path.split(fname)[0]
1683
1684                 if name:
1685                     orig_indata = self._GetDtbContentsForSplTpl(dtb_data, name)
1686                 else:
1687                     orig_indata = dtb_data
1688                 self.assertNotEqual(outdata, orig_indata,
1689                         "Expected output file '%s' be updated" % pathname)
1690                 self.assertEqual(outdata, data[start:start + size],
1691                         "Expected output file '%s' to match output image" %
1692                         pathname)
1693                 start += size
1694         finally:
1695             self._ResetDtbs()
1696
1697     def _decompress(self, data):
1698         return tools.Decompress(data, 'lz4')
1699
1700     def testCompress(self):
1701         """Test compression of blobs"""
1702         self._CheckLz4()
1703         data, _, _, out_dtb_fname = self._DoReadFileDtb('083_compress.dts',
1704                                             use_real_dtb=True, update_dtb=True)
1705         dtb = fdt.Fdt(out_dtb_fname)
1706         dtb.Scan()
1707         props = self._GetPropTree(dtb, ['size', 'uncomp-size'])
1708         orig = self._decompress(data)
1709         self.assertEquals(COMPRESS_DATA, orig)
1710         expected = {
1711             'blob:uncomp-size': len(COMPRESS_DATA),
1712             'blob:size': len(data),
1713             'size': len(data),
1714             }
1715         self.assertEqual(expected, props)
1716
1717     def testFiles(self):
1718         """Test bringing in multiple files"""
1719         data = self._DoReadFile('084_files.dts')
1720         self.assertEqual(FILES_DATA, data)
1721
1722     def testFilesCompress(self):
1723         """Test bringing in multiple files and compressing them"""
1724         self._CheckLz4()
1725         data = self._DoReadFile('085_files_compress.dts')
1726
1727         image = control.images['image']
1728         entries = image.GetEntries()
1729         files = entries['files']
1730         entries = files._entries
1731
1732         orig = b''
1733         for i in range(1, 3):
1734             key = '%d.dat' % i
1735             start = entries[key].image_pos
1736             len = entries[key].size
1737             chunk = data[start:start + len]
1738             orig += self._decompress(chunk)
1739
1740         self.assertEqual(FILES_DATA, orig)
1741
1742     def testFilesMissing(self):
1743         """Test missing files"""
1744         with self.assertRaises(ValueError) as e:
1745             data = self._DoReadFile('086_files_none.dts')
1746         self.assertIn("Node '/binman/files': Pattern \'files/*.none\' matched "
1747                       'no files', str(e.exception))
1748
1749     def testFilesNoPattern(self):
1750         """Test missing files"""
1751         with self.assertRaises(ValueError) as e:
1752             data = self._DoReadFile('087_files_no_pattern.dts')
1753         self.assertIn("Node '/binman/files': Missing 'pattern' property",
1754                       str(e.exception))
1755
1756     def testExpandSize(self):
1757         """Test an expanding entry"""
1758         data, _, map_data, _ = self._DoReadFileDtb('088_expand_size.dts',
1759                                                    map=True)
1760         expect = (tools.GetBytes(ord('a'), 8) + U_BOOT_DATA +
1761                   MRC_DATA + tools.GetBytes(ord('b'), 1) + U_BOOT_DATA +
1762                   tools.GetBytes(ord('c'), 8) + U_BOOT_DATA +
1763                   tools.GetBytes(ord('d'), 8))
1764         self.assertEqual(expect, data)
1765         self.assertEqual('''ImagePos    Offset      Size  Name
1766 00000000  00000000  00000028  main-section
1767 00000000   00000000  00000008  fill
1768 00000008   00000008  00000004  u-boot
1769 0000000c   0000000c  00000004  section
1770 0000000c    00000000  00000003  intel-mrc
1771 00000010   00000010  00000004  u-boot2
1772 00000014   00000014  0000000c  section2
1773 00000014    00000000  00000008  fill
1774 0000001c    00000008  00000004  u-boot
1775 00000020   00000020  00000008  fill2
1776 ''', map_data)
1777
1778     def testExpandSizeBad(self):
1779         """Test an expanding entry which fails to provide contents"""
1780         with test_util.capture_sys_output() as (stdout, stderr):
1781             with self.assertRaises(ValueError) as e:
1782                 self._DoReadFileDtb('089_expand_size_bad.dts', map=True)
1783         self.assertIn("Node '/binman/_testing': Cannot obtain contents when "
1784                       'expanding entry', str(e.exception))
1785
1786     def testHash(self):
1787         """Test hashing of the contents of an entry"""
1788         _, _, _, out_dtb_fname = self._DoReadFileDtb('090_hash.dts',
1789                 use_real_dtb=True, update_dtb=True)
1790         dtb = fdt.Fdt(out_dtb_fname)
1791         dtb.Scan()
1792         hash_node = dtb.GetNode('/binman/u-boot/hash').props['value']
1793         m = hashlib.sha256()
1794         m.update(U_BOOT_DATA)
1795         self.assertEqual(m.digest(), b''.join(hash_node.value))
1796
1797     def testHashNoAlgo(self):
1798         with self.assertRaises(ValueError) as e:
1799             self._DoReadFileDtb('091_hash_no_algo.dts', update_dtb=True)
1800         self.assertIn("Node \'/binman/u-boot\': Missing \'algo\' property for "
1801                       'hash node', str(e.exception))
1802
1803     def testHashBadAlgo(self):
1804         with self.assertRaises(ValueError) as e:
1805             self._DoReadFileDtb('092_hash_bad_algo.dts', update_dtb=True)
1806         self.assertIn("Node '/binman/u-boot': Unknown hash algorithm",
1807                       str(e.exception))
1808
1809     def testHashSection(self):
1810         """Test hashing of the contents of an entry"""
1811         _, _, _, out_dtb_fname = self._DoReadFileDtb('099_hash_section.dts',
1812                 use_real_dtb=True, update_dtb=True)
1813         dtb = fdt.Fdt(out_dtb_fname)
1814         dtb.Scan()
1815         hash_node = dtb.GetNode('/binman/section/hash').props['value']
1816         m = hashlib.sha256()
1817         m.update(U_BOOT_DATA)
1818         m.update(tools.GetBytes(ord('a'), 16))
1819         self.assertEqual(m.digest(), b''.join(hash_node.value))
1820
1821     def testPackUBootTplMicrocode(self):
1822         """Test that x86 microcode can be handled correctly in TPL
1823
1824         We expect to see the following in the image, in order:
1825             u-boot-tpl-nodtb.bin with a microcode pointer inserted at the correct
1826                 place
1827             u-boot-tpl.dtb with the microcode removed
1828             the microcode
1829         """
1830         self._SetupTplElf('u_boot_ucode_ptr')
1831         first, pos_and_size = self._RunMicrocodeTest('093_x86_tpl_ucode.dts',
1832                                                      U_BOOT_TPL_NODTB_DATA)
1833         self.assertEqual(b'tplnodtb with microc' + pos_and_size +
1834                          b'ter somewhere in here', first)
1835
1836     def testFmapX86(self):
1837         """Basic test of generation of a flashrom fmap"""
1838         data = self._DoReadFile('094_fmap_x86.dts')
1839         fhdr, fentries = fmap_util.DecodeFmap(data[32:])
1840         expected = U_BOOT_DATA + MRC_DATA + tools.GetBytes(ord('a'), 32 - 7)
1841         self.assertEqual(expected, data[:32])
1842         fhdr, fentries = fmap_util.DecodeFmap(data[32:])
1843
1844         self.assertEqual(0x100, fhdr.image_size)
1845
1846         self.assertEqual(0, fentries[0].offset)
1847         self.assertEqual(4, fentries[0].size)
1848         self.assertEqual(b'U_BOOT', fentries[0].name)
1849
1850         self.assertEqual(4, fentries[1].offset)
1851         self.assertEqual(3, fentries[1].size)
1852         self.assertEqual(b'INTEL_MRC', fentries[1].name)
1853
1854         self.assertEqual(32, fentries[2].offset)
1855         self.assertEqual(fmap_util.FMAP_HEADER_LEN +
1856                          fmap_util.FMAP_AREA_LEN * 3, fentries[2].size)
1857         self.assertEqual(b'FMAP', fentries[2].name)
1858
1859     def testFmapX86Section(self):
1860         """Basic test of generation of a flashrom fmap"""
1861         data = self._DoReadFile('095_fmap_x86_section.dts')
1862         expected = U_BOOT_DATA + MRC_DATA + tools.GetBytes(ord('b'), 32 - 7)
1863         self.assertEqual(expected, data[:32])
1864         fhdr, fentries = fmap_util.DecodeFmap(data[36:])
1865
1866         self.assertEqual(0x100, fhdr.image_size)
1867
1868         self.assertEqual(0, fentries[0].offset)
1869         self.assertEqual(4, fentries[0].size)
1870         self.assertEqual(b'U_BOOT', fentries[0].name)
1871
1872         self.assertEqual(4, fentries[1].offset)
1873         self.assertEqual(3, fentries[1].size)
1874         self.assertEqual(b'INTEL_MRC', fentries[1].name)
1875
1876         self.assertEqual(36, fentries[2].offset)
1877         self.assertEqual(fmap_util.FMAP_HEADER_LEN +
1878                          fmap_util.FMAP_AREA_LEN * 3, fentries[2].size)
1879         self.assertEqual(b'FMAP', fentries[2].name)
1880
1881     def testElf(self):
1882         """Basic test of ELF entries"""
1883         self._SetupSplElf()
1884         self._SetupTplElf()
1885         with open(self.ElfTestFile('bss_data'), 'rb') as fd:
1886             TestFunctional._MakeInputFile('-boot', fd.read())
1887         data = self._DoReadFile('096_elf.dts')
1888
1889     def testElfStrip(self):
1890         """Basic test of ELF entries"""
1891         self._SetupSplElf()
1892         with open(self.ElfTestFile('bss_data'), 'rb') as fd:
1893             TestFunctional._MakeInputFile('-boot', fd.read())
1894         data = self._DoReadFile('097_elf_strip.dts')
1895
1896     def testPackOverlapMap(self):
1897         """Test that overlapping regions are detected"""
1898         with test_util.capture_sys_output() as (stdout, stderr):
1899             with self.assertRaises(ValueError) as e:
1900                 self._DoTestFile('014_pack_overlap.dts', map=True)
1901         map_fname = tools.GetOutputFilename('image.map')
1902         self.assertEqual("Wrote map file '%s' to show errors\n" % map_fname,
1903                          stdout.getvalue())
1904
1905         # We should not get an inmage, but there should be a map file
1906         self.assertFalse(os.path.exists(tools.GetOutputFilename('image.bin')))
1907         self.assertTrue(os.path.exists(map_fname))
1908         map_data = tools.ReadFile(map_fname, binary=False)
1909         self.assertEqual('''ImagePos    Offset      Size  Name
1910 <none>    00000000  00000007  main-section
1911 <none>     00000000  00000004  u-boot
1912 <none>     00000003  00000004  u-boot-align
1913 ''', map_data)
1914
1915     def testPackRefCode(self):
1916         """Test that an image with an Intel Reference code binary works"""
1917         data = self._DoReadFile('100_intel_refcode.dts')
1918         self.assertEqual(REFCODE_DATA, data[:len(REFCODE_DATA)])
1919
1920     def testSectionOffset(self):
1921         """Tests use of a section with an offset"""
1922         data, _, map_data, _ = self._DoReadFileDtb('101_sections_offset.dts',
1923                                                    map=True)
1924         self.assertEqual('''ImagePos    Offset      Size  Name
1925 00000000  00000000  00000038  main-section
1926 00000004   00000004  00000010  section@0
1927 00000004    00000000  00000004  u-boot
1928 00000018   00000018  00000010  section@1
1929 00000018    00000000  00000004  u-boot
1930 0000002c   0000002c  00000004  section@2
1931 0000002c    00000000  00000004  u-boot
1932 ''', map_data)
1933         self.assertEqual(data,
1934                          tools.GetBytes(0x26, 4) + U_BOOT_DATA +
1935                              tools.GetBytes(0x21, 12) +
1936                          tools.GetBytes(0x26, 4) + U_BOOT_DATA +
1937                              tools.GetBytes(0x61, 12) +
1938                          tools.GetBytes(0x26, 4) + U_BOOT_DATA +
1939                              tools.GetBytes(0x26, 8))
1940
1941     def testCbfsRaw(self):
1942         """Test base handling of a Coreboot Filesystem (CBFS)
1943
1944         The exact contents of the CBFS is verified by similar tests in
1945         cbfs_util_test.py. The tests here merely check that the files added to
1946         the CBFS can be found in the final image.
1947         """
1948         data = self._DoReadFile('102_cbfs_raw.dts')
1949         size = 0xb0
1950
1951         cbfs = cbfs_util.CbfsReader(data)
1952         self.assertEqual(size, cbfs.rom_size)
1953
1954         self.assertIn('u-boot-dtb', cbfs.files)
1955         cfile = cbfs.files['u-boot-dtb']
1956         self.assertEqual(U_BOOT_DTB_DATA, cfile.data)
1957
1958     def testCbfsArch(self):
1959         """Test on non-x86 architecture"""
1960         data = self._DoReadFile('103_cbfs_raw_ppc.dts')
1961         size = 0x100
1962
1963         cbfs = cbfs_util.CbfsReader(data)
1964         self.assertEqual(size, cbfs.rom_size)
1965
1966         self.assertIn('u-boot-dtb', cbfs.files)
1967         cfile = cbfs.files['u-boot-dtb']
1968         self.assertEqual(U_BOOT_DTB_DATA, cfile.data)
1969
1970     def testCbfsStage(self):
1971         """Tests handling of a Coreboot Filesystem (CBFS)"""
1972         if not elf.ELF_TOOLS:
1973             self.skipTest('Python elftools not available')
1974         elf_fname = os.path.join(self._indir, 'cbfs-stage.elf')
1975         elf.MakeElf(elf_fname, U_BOOT_DATA, U_BOOT_DTB_DATA)
1976         size = 0xb0
1977
1978         data = self._DoReadFile('104_cbfs_stage.dts')
1979         cbfs = cbfs_util.CbfsReader(data)
1980         self.assertEqual(size, cbfs.rom_size)
1981
1982         self.assertIn('u-boot', cbfs.files)
1983         cfile = cbfs.files['u-boot']
1984         self.assertEqual(U_BOOT_DATA + U_BOOT_DTB_DATA, cfile.data)
1985
1986     def testCbfsRawCompress(self):
1987         """Test handling of compressing raw files"""
1988         self._CheckLz4()
1989         data = self._DoReadFile('105_cbfs_raw_compress.dts')
1990         size = 0x140
1991
1992         cbfs = cbfs_util.CbfsReader(data)
1993         self.assertIn('u-boot', cbfs.files)
1994         cfile = cbfs.files['u-boot']
1995         self.assertEqual(COMPRESS_DATA, cfile.data)
1996
1997     def testCbfsBadArch(self):
1998         """Test handling of a bad architecture"""
1999         with self.assertRaises(ValueError) as e:
2000             self._DoReadFile('106_cbfs_bad_arch.dts')
2001         self.assertIn("Invalid architecture 'bad-arch'", str(e.exception))
2002
2003     def testCbfsNoSize(self):
2004         """Test handling of a missing size property"""
2005         with self.assertRaises(ValueError) as e:
2006             self._DoReadFile('107_cbfs_no_size.dts')
2007         self.assertIn('entry must have a size property', str(e.exception))
2008
2009     def testCbfsNoCOntents(self):
2010         """Test handling of a CBFS entry which does not provide contentsy"""
2011         with self.assertRaises(ValueError) as e:
2012             self._DoReadFile('108_cbfs_no_contents.dts')
2013         self.assertIn('Could not complete processing of contents',
2014                       str(e.exception))
2015
2016     def testCbfsBadCompress(self):
2017         """Test handling of a bad architecture"""
2018         with self.assertRaises(ValueError) as e:
2019             self._DoReadFile('109_cbfs_bad_compress.dts')
2020         self.assertIn("Invalid compression in 'u-boot': 'invalid-algo'",
2021                       str(e.exception))
2022
2023     def testCbfsNamedEntries(self):
2024         """Test handling of named entries"""
2025         data = self._DoReadFile('110_cbfs_name.dts')
2026
2027         cbfs = cbfs_util.CbfsReader(data)
2028         self.assertIn('FRED', cbfs.files)
2029         cfile1 = cbfs.files['FRED']
2030         self.assertEqual(U_BOOT_DATA, cfile1.data)
2031
2032         self.assertIn('hello', cbfs.files)
2033         cfile2 = cbfs.files['hello']
2034         self.assertEqual(U_BOOT_DTB_DATA, cfile2.data)
2035
2036     def _SetupIfwi(self, fname):
2037         """Set up to run an IFWI test
2038
2039         Args:
2040             fname: Filename of input file to provide (fitimage.bin or ifwi.bin)
2041         """
2042         self._SetupSplElf()
2043         self._SetupTplElf()
2044
2045         # Intel Integrated Firmware Image (IFWI) file
2046         with gzip.open(self.TestFile('%s.gz' % fname), 'rb') as fd:
2047             data = fd.read()
2048         TestFunctional._MakeInputFile(fname,data)
2049
2050     def _CheckIfwi(self, data):
2051         """Check that an image with an IFWI contains the correct output
2052
2053         Args:
2054             data: Conents of output file
2055         """
2056         expected_desc = tools.ReadFile(self.TestFile('descriptor.bin'))
2057         if data[:0x1000] != expected_desc:
2058             self.fail('Expected descriptor binary at start of image')
2059
2060         # We expect to find the TPL wil in subpart IBBP entry IBBL
2061         image_fname = tools.GetOutputFilename('image.bin')
2062         tpl_fname = tools.GetOutputFilename('tpl.out')
2063         tools.RunIfwiTool(image_fname, tools.CMD_EXTRACT, fname=tpl_fname,
2064                           subpart='IBBP', entry_name='IBBL')
2065
2066         tpl_data = tools.ReadFile(tpl_fname)
2067         self.assertEqual(U_BOOT_TPL_DATA, tpl_data[:len(U_BOOT_TPL_DATA)])
2068
2069     def testPackX86RomIfwi(self):
2070         """Test that an x86 ROM with Integrated Firmware Image can be created"""
2071         self._SetupIfwi('fitimage.bin')
2072         data = self._DoReadFile('111_x86_rom_ifwi.dts')
2073         self._CheckIfwi(data)
2074
2075     def testPackX86RomIfwiNoDesc(self):
2076         """Test that an x86 ROM with IFWI can be created from an ifwi.bin file"""
2077         self._SetupIfwi('ifwi.bin')
2078         data = self._DoReadFile('112_x86_rom_ifwi_nodesc.dts')
2079         self._CheckIfwi(data)
2080
2081     def testPackX86RomIfwiNoData(self):
2082         """Test that an x86 ROM with IFWI handles missing data"""
2083         self._SetupIfwi('ifwi.bin')
2084         with self.assertRaises(ValueError) as e:
2085             data = self._DoReadFile('113_x86_rom_ifwi_nodata.dts')
2086         self.assertIn('Could not complete processing of contents',
2087                       str(e.exception))
2088
2089     def testCbfsOffset(self):
2090         """Test a CBFS with files at particular offsets
2091
2092         Like all CFBS tests, this is just checking the logic that calls
2093         cbfs_util. See cbfs_util_test for fully tests (e.g. test_cbfs_offset()).
2094         """
2095         data = self._DoReadFile('114_cbfs_offset.dts')
2096         size = 0x200
2097
2098         cbfs = cbfs_util.CbfsReader(data)
2099         self.assertEqual(size, cbfs.rom_size)
2100
2101         self.assertIn('u-boot', cbfs.files)
2102         cfile = cbfs.files['u-boot']
2103         self.assertEqual(U_BOOT_DATA, cfile.data)
2104         self.assertEqual(0x40, cfile.cbfs_offset)
2105
2106         self.assertIn('u-boot-dtb', cbfs.files)
2107         cfile2 = cbfs.files['u-boot-dtb']
2108         self.assertEqual(U_BOOT_DTB_DATA, cfile2.data)
2109         self.assertEqual(0x140, cfile2.cbfs_offset)
2110
2111     def testFdtmap(self):
2112         """Test an FDT map can be inserted in the image"""
2113         data = self.data = self._DoReadFileRealDtb('115_fdtmap.dts')
2114         fdtmap_data = data[len(U_BOOT_DATA):]
2115         magic = fdtmap_data[:8]
2116         self.assertEqual(b'_FDTMAP_', magic)
2117         self.assertEqual(tools.GetBytes(0, 8), fdtmap_data[8:16])
2118
2119         fdt_data = fdtmap_data[16:]
2120         dtb = fdt.Fdt.FromData(fdt_data)
2121         dtb.Scan()
2122         props = self._GetPropTree(dtb, BASE_DTB_PROPS, prefix='/')
2123         self.assertEqual({
2124             'image-pos': 0,
2125             'offset': 0,
2126             'u-boot:offset': 0,
2127             'u-boot:size': len(U_BOOT_DATA),
2128             'u-boot:image-pos': 0,
2129             'fdtmap:image-pos': 4,
2130             'fdtmap:offset': 4,
2131             'fdtmap:size': len(fdtmap_data),
2132             'size': len(data),
2133         }, props)
2134
2135     def testFdtmapNoMatch(self):
2136         """Check handling of an FDT map when the section cannot be found"""
2137         self.data = self._DoReadFileRealDtb('115_fdtmap.dts')
2138
2139         # Mangle the section name, which should cause a mismatch between the
2140         # correct FDT path and the one expected by the section
2141         image = control.images['image']
2142         image._node.path += '-suffix'
2143         entries = image.GetEntries()
2144         fdtmap = entries['fdtmap']
2145         with self.assertRaises(ValueError) as e:
2146             fdtmap._GetFdtmap()
2147         self.assertIn("Cannot locate node for path '/binman-suffix'",
2148                       str(e.exception))
2149
2150     def testFdtmapHeader(self):
2151         """Test an FDT map and image header can be inserted in the image"""
2152         data = self.data = self._DoReadFileRealDtb('116_fdtmap_hdr.dts')
2153         fdtmap_pos = len(U_BOOT_DATA)
2154         fdtmap_data = data[fdtmap_pos:]
2155         fdt_data = fdtmap_data[16:]
2156         dtb = fdt.Fdt.FromData(fdt_data)
2157         fdt_size = dtb.GetFdtObj().totalsize()
2158         hdr_data = data[-8:]
2159         self.assertEqual(b'BinM', hdr_data[:4])
2160         offset = struct.unpack('<I', hdr_data[4:])[0] & 0xffffffff
2161         self.assertEqual(fdtmap_pos - 0x400, offset - (1 << 32))
2162
2163     def testFdtmapHeaderStart(self):
2164         """Test an image header can be inserted at the image start"""
2165         data = self.data = self._DoReadFileRealDtb('117_fdtmap_hdr_start.dts')
2166         fdtmap_pos = 0x100 + len(U_BOOT_DATA)
2167         hdr_data = data[:8]
2168         self.assertEqual(b'BinM', hdr_data[:4])
2169         offset = struct.unpack('<I', hdr_data[4:])[0]
2170         self.assertEqual(fdtmap_pos, offset)
2171
2172     def testFdtmapHeaderPos(self):
2173         """Test an image header can be inserted at a chosen position"""
2174         data = self.data = self._DoReadFileRealDtb('118_fdtmap_hdr_pos.dts')
2175         fdtmap_pos = 0x100 + len(U_BOOT_DATA)
2176         hdr_data = data[0x80:0x88]
2177         self.assertEqual(b'BinM', hdr_data[:4])
2178         offset = struct.unpack('<I', hdr_data[4:])[0]
2179         self.assertEqual(fdtmap_pos, offset)
2180
2181     def testHeaderMissingFdtmap(self):
2182         """Test an image header requires an fdtmap"""
2183         with self.assertRaises(ValueError) as e:
2184             self.data = self._DoReadFileRealDtb('119_fdtmap_hdr_missing.dts')
2185         self.assertIn("'image_header' section must have an 'fdtmap' sibling",
2186                       str(e.exception))
2187
2188     def testHeaderNoLocation(self):
2189         """Test an image header with a no specified location is detected"""
2190         with self.assertRaises(ValueError) as e:
2191             self.data = self._DoReadFileRealDtb('120_hdr_no_location.dts')
2192         self.assertIn("Invalid location 'None', expected 'start' or 'end'",
2193                       str(e.exception))
2194
2195     def testEntryExpand(self):
2196         """Test expanding an entry after it is packed"""
2197         data = self._DoReadFile('121_entry_expand.dts')
2198         self.assertEqual(b'aaa', data[:3])
2199         self.assertEqual(U_BOOT_DATA, data[3:3 + len(U_BOOT_DATA)])
2200         self.assertEqual(b'aaa', data[-3:])
2201
2202     def testEntryExpandBad(self):
2203         """Test expanding an entry after it is packed, twice"""
2204         with self.assertRaises(ValueError) as e:
2205             self._DoReadFile('122_entry_expand_twice.dts')
2206         self.assertIn("Image '/binman': Entries changed size after packing",
2207                       str(e.exception))
2208
2209     def testEntryExpandSection(self):
2210         """Test expanding an entry within a section after it is packed"""
2211         data = self._DoReadFile('123_entry_expand_section.dts')
2212         self.assertEqual(b'aaa', data[:3])
2213         self.assertEqual(U_BOOT_DATA, data[3:3 + len(U_BOOT_DATA)])
2214         self.assertEqual(b'aaa', data[-3:])
2215
2216     def testCompressDtb(self):
2217         """Test that compress of device-tree files is supported"""
2218         self._CheckLz4()
2219         data = self.data = self._DoReadFileRealDtb('124_compress_dtb.dts')
2220         self.assertEqual(U_BOOT_DATA, data[:len(U_BOOT_DATA)])
2221         comp_data = data[len(U_BOOT_DATA):]
2222         orig = self._decompress(comp_data)
2223         dtb = fdt.Fdt.FromData(orig)
2224         dtb.Scan()
2225         props = self._GetPropTree(dtb, ['size', 'uncomp-size'])
2226         expected = {
2227             'u-boot:size': len(U_BOOT_DATA),
2228             'u-boot-dtb:uncomp-size': len(orig),
2229             'u-boot-dtb:size': len(comp_data),
2230             'size': len(data),
2231             }
2232         self.assertEqual(expected, props)
2233
2234     def testCbfsUpdateFdt(self):
2235         """Test that we can update the device tree with CBFS offset/size info"""
2236         self._CheckLz4()
2237         data, _, _, out_dtb_fname = self._DoReadFileDtb('125_cbfs_update.dts',
2238                                                         update_dtb=True)
2239         dtb = fdt.Fdt(out_dtb_fname)
2240         dtb.Scan()
2241         props = self._GetPropTree(dtb, BASE_DTB_PROPS + ['uncomp-size'])
2242         del props['cbfs/u-boot:size']
2243         self.assertEqual({
2244             'offset': 0,
2245             'size': len(data),
2246             'image-pos': 0,
2247             'cbfs:offset': 0,
2248             'cbfs:size': len(data),
2249             'cbfs:image-pos': 0,
2250             'cbfs/u-boot:offset': 0x38,
2251             'cbfs/u-boot:uncomp-size': len(U_BOOT_DATA),
2252             'cbfs/u-boot:image-pos': 0x38,
2253             'cbfs/u-boot-dtb:offset': 0xb8,
2254             'cbfs/u-boot-dtb:size': len(U_BOOT_DATA),
2255             'cbfs/u-boot-dtb:image-pos': 0xb8,
2256             }, props)
2257
2258     def testCbfsBadType(self):
2259         """Test an image header with a no specified location is detected"""
2260         with self.assertRaises(ValueError) as e:
2261             self._DoReadFile('126_cbfs_bad_type.dts')
2262         self.assertIn("Unknown cbfs-type 'badtype'", str(e.exception))
2263
2264     def testList(self):
2265         """Test listing the files in an image"""
2266         self._CheckLz4()
2267         data = self._DoReadFile('127_list.dts')
2268         image = control.images['image']
2269         entries = image.BuildEntryList()
2270         self.assertEqual(7, len(entries))
2271
2272         ent = entries[0]
2273         self.assertEqual(0, ent.indent)
2274         self.assertEqual('main-section', ent.name)
2275         self.assertEqual('section', ent.etype)
2276         self.assertEqual(len(data), ent.size)
2277         self.assertEqual(0, ent.image_pos)
2278         self.assertEqual(None, ent.uncomp_size)
2279         self.assertEqual(0, ent.offset)
2280
2281         ent = entries[1]
2282         self.assertEqual(1, ent.indent)
2283         self.assertEqual('u-boot', ent.name)
2284         self.assertEqual('u-boot', ent.etype)
2285         self.assertEqual(len(U_BOOT_DATA), ent.size)
2286         self.assertEqual(0, ent.image_pos)
2287         self.assertEqual(None, ent.uncomp_size)
2288         self.assertEqual(0, ent.offset)
2289
2290         ent = entries[2]
2291         self.assertEqual(1, ent.indent)
2292         self.assertEqual('section', ent.name)
2293         self.assertEqual('section', ent.etype)
2294         section_size = ent.size
2295         self.assertEqual(0x100, ent.image_pos)
2296         self.assertEqual(None, ent.uncomp_size)
2297         self.assertEqual(0x100, ent.offset)
2298
2299         ent = entries[3]
2300         self.assertEqual(2, ent.indent)
2301         self.assertEqual('cbfs', ent.name)
2302         self.assertEqual('cbfs', ent.etype)
2303         self.assertEqual(0x400, ent.size)
2304         self.assertEqual(0x100, ent.image_pos)
2305         self.assertEqual(None, ent.uncomp_size)
2306         self.assertEqual(0, ent.offset)
2307
2308         ent = entries[4]
2309         self.assertEqual(3, ent.indent)
2310         self.assertEqual('u-boot', ent.name)
2311         self.assertEqual('u-boot', ent.etype)
2312         self.assertEqual(len(U_BOOT_DATA), ent.size)
2313         self.assertEqual(0x138, ent.image_pos)
2314         self.assertEqual(None, ent.uncomp_size)
2315         self.assertEqual(0x38, ent.offset)
2316
2317         ent = entries[5]
2318         self.assertEqual(3, ent.indent)
2319         self.assertEqual('u-boot-dtb', ent.name)
2320         self.assertEqual('text', ent.etype)
2321         self.assertGreater(len(COMPRESS_DATA), ent.size)
2322         self.assertEqual(0x178, ent.image_pos)
2323         self.assertEqual(len(COMPRESS_DATA), ent.uncomp_size)
2324         self.assertEqual(0x78, ent.offset)
2325
2326         ent = entries[6]
2327         self.assertEqual(2, ent.indent)
2328         self.assertEqual('u-boot-dtb', ent.name)
2329         self.assertEqual('u-boot-dtb', ent.etype)
2330         self.assertEqual(0x500, ent.image_pos)
2331         self.assertEqual(len(U_BOOT_DTB_DATA), ent.uncomp_size)
2332         dtb_size = ent.size
2333         # Compressing this data expands it since headers are added
2334         self.assertGreater(dtb_size, len(U_BOOT_DTB_DATA))
2335         self.assertEqual(0x400, ent.offset)
2336
2337         self.assertEqual(len(data), 0x100 + section_size)
2338         self.assertEqual(section_size, 0x400 + dtb_size)
2339
2340     def testFindFdtmap(self):
2341         """Test locating an FDT map in an image"""
2342         self._CheckLz4()
2343         data = self.data = self._DoReadFileRealDtb('128_decode_image.dts')
2344         image = control.images['image']
2345         entries = image.GetEntries()
2346         entry = entries['fdtmap']
2347         self.assertEqual(entry.image_pos, fdtmap.LocateFdtmap(data))
2348
2349     def testFindFdtmapMissing(self):
2350         """Test failing to locate an FDP map"""
2351         data = self._DoReadFile('005_simple.dts')
2352         self.assertEqual(None, fdtmap.LocateFdtmap(data))
2353
2354     def testFindImageHeader(self):
2355         """Test locating a image header"""
2356         self._CheckLz4()
2357         data = self.data = self._DoReadFileRealDtb('128_decode_image.dts')
2358         image = control.images['image']
2359         entries = image.GetEntries()
2360         entry = entries['fdtmap']
2361         # The header should point to the FDT map
2362         self.assertEqual(entry.image_pos, image_header.LocateHeaderOffset(data))
2363
2364     def testFindImageHeaderStart(self):
2365         """Test locating a image header located at the start of an image"""
2366         data = self.data = self._DoReadFileRealDtb('117_fdtmap_hdr_start.dts')
2367         image = control.images['image']
2368         entries = image.GetEntries()
2369         entry = entries['fdtmap']
2370         # The header should point to the FDT map
2371         self.assertEqual(entry.image_pos, image_header.LocateHeaderOffset(data))
2372
2373     def testFindImageHeaderMissing(self):
2374         """Test failing to locate an image header"""
2375         data = self._DoReadFile('005_simple.dts')
2376         self.assertEqual(None, image_header.LocateHeaderOffset(data))
2377
2378     def testReadImage(self):
2379         """Test reading an image and accessing its FDT map"""
2380         self._CheckLz4()
2381         data = self.data = self._DoReadFileRealDtb('128_decode_image.dts')
2382         image_fname = tools.GetOutputFilename('image.bin')
2383         orig_image = control.images['image']
2384         image = Image.FromFile(image_fname)
2385         self.assertEqual(orig_image.GetEntries().keys(),
2386                          image.GetEntries().keys())
2387
2388         orig_entry = orig_image.GetEntries()['fdtmap']
2389         entry = image.GetEntries()['fdtmap']
2390         self.assertEquals(orig_entry.offset, entry.offset)
2391         self.assertEquals(orig_entry.size, entry.size)
2392         self.assertEquals(orig_entry.image_pos, entry.image_pos)
2393
2394     def testReadImageNoHeader(self):
2395         """Test accessing an image's FDT map without an image header"""
2396         self._CheckLz4()
2397         data = self._DoReadFileRealDtb('129_decode_image_nohdr.dts')
2398         image_fname = tools.GetOutputFilename('image.bin')
2399         image = Image.FromFile(image_fname)
2400         self.assertTrue(isinstance(image, Image))
2401         self.assertEqual('image', image.image_name[-5:])
2402
2403     def testReadImageFail(self):
2404         """Test failing to read an image image's FDT map"""
2405         self._DoReadFile('005_simple.dts')
2406         image_fname = tools.GetOutputFilename('image.bin')
2407         with self.assertRaises(ValueError) as e:
2408             image = Image.FromFile(image_fname)
2409         self.assertIn("Cannot find FDT map in image", str(e.exception))
2410
2411     def testListCmd(self):
2412         """Test listing the files in an image using an Fdtmap"""
2413         self._CheckLz4()
2414         data = self._DoReadFileRealDtb('130_list_fdtmap.dts')
2415
2416         # lz4 compression size differs depending on the version
2417         image = control.images['image']
2418         entries = image.GetEntries()
2419         section_size = entries['section'].size
2420         fdt_size = entries['section'].GetEntries()['u-boot-dtb'].size
2421         fdtmap_offset = entries['fdtmap'].offset
2422
2423         try:
2424             tmpdir, updated_fname = self._SetupImageInTmpdir()
2425             with test_util.capture_sys_output() as (stdout, stderr):
2426                 self._DoBinman('ls', '-i', updated_fname)
2427         finally:
2428             shutil.rmtree(tmpdir)
2429         lines = stdout.getvalue().splitlines()
2430         expected = [
2431 'Name              Image-pos  Size  Entry-type    Offset  Uncomp-size',
2432 '----------------------------------------------------------------------',
2433 'main-section              0   c00  section            0',
2434 '  u-boot                  0     4  u-boot             0',
2435 '  section               100   %x  section          100' % section_size,
2436 '    cbfs                100   400  cbfs               0',
2437 '      u-boot            138     4  u-boot            38',
2438 '      u-boot-dtb        180   105  u-boot-dtb        80          3c9',
2439 '    u-boot-dtb          500   %x  u-boot-dtb       400          3c9' % fdt_size,
2440 '  fdtmap                %x   3bd  fdtmap           %x' %
2441         (fdtmap_offset, fdtmap_offset),
2442 '  image-header          bf8     8  image-header     bf8',
2443             ]
2444         self.assertEqual(expected, lines)
2445
2446     def testListCmdFail(self):
2447         """Test failing to list an image"""
2448         self._DoReadFile('005_simple.dts')
2449         try:
2450             tmpdir, updated_fname = self._SetupImageInTmpdir()
2451             with self.assertRaises(ValueError) as e:
2452                 self._DoBinman('ls', '-i', updated_fname)
2453         finally:
2454             shutil.rmtree(tmpdir)
2455         self.assertIn("Cannot find FDT map in image", str(e.exception))
2456
2457     def _RunListCmd(self, paths, expected):
2458         """List out entries and check the result
2459
2460         Args:
2461             paths: List of paths to pass to the list command
2462             expected: Expected list of filenames to be returned, in order
2463         """
2464         self._CheckLz4()
2465         self._DoReadFileRealDtb('130_list_fdtmap.dts')
2466         image_fname = tools.GetOutputFilename('image.bin')
2467         image = Image.FromFile(image_fname)
2468         lines = image.GetListEntries(paths)[1]
2469         files = [line[0].strip() for line in lines[1:]]
2470         self.assertEqual(expected, files)
2471
2472     def testListCmdSection(self):
2473         """Test listing the files in a section"""
2474         self._RunListCmd(['section'],
2475             ['section', 'cbfs', 'u-boot', 'u-boot-dtb', 'u-boot-dtb'])
2476
2477     def testListCmdFile(self):
2478         """Test listing a particular file"""
2479         self._RunListCmd(['*u-boot-dtb'], ['u-boot-dtb', 'u-boot-dtb'])
2480
2481     def testListCmdWildcard(self):
2482         """Test listing a wildcarded file"""
2483         self._RunListCmd(['*boot*'],
2484             ['u-boot', 'u-boot', 'u-boot-dtb', 'u-boot-dtb'])
2485
2486     def testListCmdWildcardMulti(self):
2487         """Test listing a wildcarded file"""
2488         self._RunListCmd(['*cb*', '*head*'],
2489             ['cbfs', 'u-boot', 'u-boot-dtb', 'image-header'])
2490
2491     def testListCmdEmpty(self):
2492         """Test listing a wildcarded file"""
2493         self._RunListCmd(['nothing'], [])
2494
2495     def testListCmdPath(self):
2496         """Test listing the files in a sub-entry of a section"""
2497         self._RunListCmd(['section/cbfs'], ['cbfs', 'u-boot', 'u-boot-dtb'])
2498
2499     def _RunExtractCmd(self, entry_name, decomp=True):
2500         """Extract an entry from an image
2501
2502         Args:
2503             entry_name: Entry name to extract
2504             decomp: True to decompress the data if compressed, False to leave
2505                 it in its raw uncompressed format
2506
2507         Returns:
2508             data from entry
2509         """
2510         self._CheckLz4()
2511         self._DoReadFileRealDtb('130_list_fdtmap.dts')
2512         image_fname = tools.GetOutputFilename('image.bin')
2513         return control.ReadEntry(image_fname, entry_name, decomp)
2514
2515     def testExtractSimple(self):
2516         """Test extracting a single file"""
2517         data = self._RunExtractCmd('u-boot')
2518         self.assertEqual(U_BOOT_DATA, data)
2519
2520     def testExtractSection(self):
2521         """Test extracting the files in a section"""
2522         data = self._RunExtractCmd('section')
2523         cbfs_data = data[:0x400]
2524         cbfs = cbfs_util.CbfsReader(cbfs_data)
2525         self.assertEqual(['u-boot', 'u-boot-dtb', ''], list(cbfs.files.keys()))
2526         dtb_data = data[0x400:]
2527         dtb = self._decompress(dtb_data)
2528         self.assertEqual(EXTRACT_DTB_SIZE, len(dtb))
2529
2530     def testExtractCompressed(self):
2531         """Test extracting compressed data"""
2532         data = self._RunExtractCmd('section/u-boot-dtb')
2533         self.assertEqual(EXTRACT_DTB_SIZE, len(data))
2534
2535     def testExtractRaw(self):
2536         """Test extracting compressed data without decompressing it"""
2537         data = self._RunExtractCmd('section/u-boot-dtb', decomp=False)
2538         dtb = self._decompress(data)
2539         self.assertEqual(EXTRACT_DTB_SIZE, len(dtb))
2540
2541     def testExtractCbfs(self):
2542         """Test extracting CBFS data"""
2543         data = self._RunExtractCmd('section/cbfs/u-boot')
2544         self.assertEqual(U_BOOT_DATA, data)
2545
2546     def testExtractCbfsCompressed(self):
2547         """Test extracting CBFS compressed data"""
2548         data = self._RunExtractCmd('section/cbfs/u-boot-dtb')
2549         self.assertEqual(EXTRACT_DTB_SIZE, len(data))
2550
2551     def testExtractCbfsRaw(self):
2552         """Test extracting CBFS compressed data without decompressing it"""
2553         data = self._RunExtractCmd('section/cbfs/u-boot-dtb', decomp=False)
2554         dtb = tools.Decompress(data, 'lzma', with_header=False)
2555         self.assertEqual(EXTRACT_DTB_SIZE, len(dtb))
2556
2557     def testExtractBadEntry(self):
2558         """Test extracting a bad section path"""
2559         with self.assertRaises(ValueError) as e:
2560             self._RunExtractCmd('section/does-not-exist')
2561         self.assertIn("Entry 'does-not-exist' not found in '/section'",
2562                       str(e.exception))
2563
2564     def testExtractMissingFile(self):
2565         """Test extracting file that does not exist"""
2566         with self.assertRaises(IOError) as e:
2567             control.ReadEntry('missing-file', 'name')
2568
2569     def testExtractBadFile(self):
2570         """Test extracting an invalid file"""
2571         fname = os.path.join(self._indir, 'badfile')
2572         tools.WriteFile(fname, b'')
2573         with self.assertRaises(ValueError) as e:
2574             control.ReadEntry(fname, 'name')
2575
2576     def testExtractCmd(self):
2577         """Test extracting a file fron an image on the command line"""
2578         self._CheckLz4()
2579         self._DoReadFileRealDtb('130_list_fdtmap.dts')
2580         fname = os.path.join(self._indir, 'output.extact')
2581         try:
2582             tmpdir, updated_fname = self._SetupImageInTmpdir()
2583             with test_util.capture_sys_output() as (stdout, stderr):
2584                 self._DoBinman('extract', '-i', updated_fname, 'u-boot',
2585                                '-f', fname)
2586         finally:
2587             shutil.rmtree(tmpdir)
2588         data = tools.ReadFile(fname)
2589         self.assertEqual(U_BOOT_DATA, data)
2590
2591     def testExtractOneEntry(self):
2592         """Test extracting a single entry fron an image """
2593         self._CheckLz4()
2594         self._DoReadFileRealDtb('130_list_fdtmap.dts')
2595         image_fname = tools.GetOutputFilename('image.bin')
2596         fname = os.path.join(self._indir, 'output.extact')
2597         control.ExtractEntries(image_fname, fname, None, ['u-boot'])
2598         data = tools.ReadFile(fname)
2599         self.assertEqual(U_BOOT_DATA, data)
2600
2601     def _CheckExtractOutput(self, decomp):
2602         """Helper to test file output with and without decompression
2603
2604         Args:
2605             decomp: True to decompress entry data, False to output it raw
2606         """
2607         def _CheckPresent(entry_path, expect_data, expect_size=None):
2608             """Check and remove expected file
2609
2610             This checks the data/size of a file and removes the file both from
2611             the outfiles set and from the output directory. Once all files are
2612             processed, both the set and directory should be empty.
2613
2614             Args:
2615                 entry_path: Entry path
2616                 expect_data: Data to expect in file, or None to skip check
2617                 expect_size: Size of data to expect in file, or None to skip
2618             """
2619             path = os.path.join(outdir, entry_path)
2620             data = tools.ReadFile(path)
2621             os.remove(path)
2622             if expect_data:
2623                 self.assertEqual(expect_data, data)
2624             elif expect_size:
2625                 self.assertEqual(expect_size, len(data))
2626             outfiles.remove(path)
2627
2628         def _CheckDirPresent(name):
2629             """Remove expected directory
2630
2631             This gives an error if the directory does not exist as expected
2632
2633             Args:
2634                 name: Name of directory to remove
2635             """
2636             path = os.path.join(outdir, name)
2637             os.rmdir(path)
2638
2639         self._DoReadFileRealDtb('130_list_fdtmap.dts')
2640         image_fname = tools.GetOutputFilename('image.bin')
2641         outdir = os.path.join(self._indir, 'extract')
2642         einfos = control.ExtractEntries(image_fname, None, outdir, [], decomp)
2643
2644         # Create a set of all file that were output (should be 9)
2645         outfiles = set()
2646         for root, dirs, files in os.walk(outdir):
2647             outfiles |= set([os.path.join(root, fname) for fname in files])
2648         self.assertEqual(9, len(outfiles))
2649         self.assertEqual(9, len(einfos))
2650
2651         image = control.images['image']
2652         entries = image.GetEntries()
2653
2654         # Check the 9 files in various ways
2655         section = entries['section']
2656         section_entries = section.GetEntries()
2657         cbfs_entries = section_entries['cbfs'].GetEntries()
2658         _CheckPresent('u-boot', U_BOOT_DATA)
2659         _CheckPresent('section/cbfs/u-boot', U_BOOT_DATA)
2660         dtb_len = EXTRACT_DTB_SIZE
2661         if not decomp:
2662             dtb_len = cbfs_entries['u-boot-dtb'].size
2663         _CheckPresent('section/cbfs/u-boot-dtb', None, dtb_len)
2664         if not decomp:
2665             dtb_len = section_entries['u-boot-dtb'].size
2666         _CheckPresent('section/u-boot-dtb', None, dtb_len)
2667
2668         fdtmap = entries['fdtmap']
2669         _CheckPresent('fdtmap', fdtmap.data)
2670         hdr = entries['image-header']
2671         _CheckPresent('image-header', hdr.data)
2672
2673         _CheckPresent('section/root', section.data)
2674         cbfs = section_entries['cbfs']
2675         _CheckPresent('section/cbfs/root', cbfs.data)
2676         data = tools.ReadFile(image_fname)
2677         _CheckPresent('root', data)
2678
2679         # There should be no files left. Remove all the directories to check.
2680         # If there are any files/dirs remaining, one of these checks will fail.
2681         self.assertEqual(0, len(outfiles))
2682         _CheckDirPresent('section/cbfs')
2683         _CheckDirPresent('section')
2684         _CheckDirPresent('')
2685         self.assertFalse(os.path.exists(outdir))
2686
2687     def testExtractAllEntries(self):
2688         """Test extracting all entries"""
2689         self._CheckLz4()
2690         self._CheckExtractOutput(decomp=True)
2691
2692     def testExtractAllEntriesRaw(self):
2693         """Test extracting all entries without decompressing them"""
2694         self._CheckLz4()
2695         self._CheckExtractOutput(decomp=False)
2696
2697     def testExtractSelectedEntries(self):
2698         """Test extracting some entries"""
2699         self._CheckLz4()
2700         self._DoReadFileRealDtb('130_list_fdtmap.dts')
2701         image_fname = tools.GetOutputFilename('image.bin')
2702         outdir = os.path.join(self._indir, 'extract')
2703         einfos = control.ExtractEntries(image_fname, None, outdir,
2704                                         ['*cb*', '*head*'])
2705
2706         # File output is tested by testExtractAllEntries(), so just check that
2707         # the expected entries are selected
2708         names = [einfo.name for einfo in einfos]
2709         self.assertEqual(names,
2710                          ['cbfs', 'u-boot', 'u-boot-dtb', 'image-header'])
2711
2712     def testExtractNoEntryPaths(self):
2713         """Test extracting some entries"""
2714         self._CheckLz4()
2715         self._DoReadFileRealDtb('130_list_fdtmap.dts')
2716         image_fname = tools.GetOutputFilename('image.bin')
2717         with self.assertRaises(ValueError) as e:
2718             control.ExtractEntries(image_fname, 'fname', None, [])
2719         self.assertIn('Must specify an entry path to write with -f',
2720                       str(e.exception))
2721
2722     def testExtractTooManyEntryPaths(self):
2723         """Test extracting some entries"""
2724         self._CheckLz4()
2725         self._DoReadFileRealDtb('130_list_fdtmap.dts')
2726         image_fname = tools.GetOutputFilename('image.bin')
2727         with self.assertRaises(ValueError) as e:
2728             control.ExtractEntries(image_fname, 'fname', None, ['a', 'b'])
2729         self.assertIn('Must specify exactly one entry path to write with -f',
2730                       str(e.exception))
2731
2732     def testPackAlignSection(self):
2733         """Test that sections can have alignment"""
2734         self._DoReadFile('131_pack_align_section.dts')
2735
2736         self.assertIn('image', control.images)
2737         image = control.images['image']
2738         entries = image.GetEntries()
2739         self.assertEqual(3, len(entries))
2740
2741         # First u-boot
2742         self.assertIn('u-boot', entries)
2743         entry = entries['u-boot']
2744         self.assertEqual(0, entry.offset)
2745         self.assertEqual(0, entry.image_pos)
2746         self.assertEqual(len(U_BOOT_DATA), entry.contents_size)
2747         self.assertEqual(len(U_BOOT_DATA), entry.size)
2748
2749         # Section0
2750         self.assertIn('section0', entries)
2751         section0 = entries['section0']
2752         self.assertEqual(0x10, section0.offset)
2753         self.assertEqual(0x10, section0.image_pos)
2754         self.assertEqual(len(U_BOOT_DATA), section0.size)
2755
2756         # Second u-boot
2757         section_entries = section0.GetEntries()
2758         self.assertIn('u-boot', section_entries)
2759         entry = section_entries['u-boot']
2760         self.assertEqual(0, entry.offset)
2761         self.assertEqual(0x10, entry.image_pos)
2762         self.assertEqual(len(U_BOOT_DATA), entry.contents_size)
2763         self.assertEqual(len(U_BOOT_DATA), entry.size)
2764
2765         # Section1
2766         self.assertIn('section1', entries)
2767         section1 = entries['section1']
2768         self.assertEqual(0x14, section1.offset)
2769         self.assertEqual(0x14, section1.image_pos)
2770         self.assertEqual(0x20, section1.size)
2771
2772         # Second u-boot
2773         section_entries = section1.GetEntries()
2774         self.assertIn('u-boot', section_entries)
2775         entry = section_entries['u-boot']
2776         self.assertEqual(0, entry.offset)
2777         self.assertEqual(0x14, entry.image_pos)
2778         self.assertEqual(len(U_BOOT_DATA), entry.contents_size)
2779         self.assertEqual(len(U_BOOT_DATA), entry.size)
2780
2781         # Section2
2782         self.assertIn('section2', section_entries)
2783         section2 = section_entries['section2']
2784         self.assertEqual(0x4, section2.offset)
2785         self.assertEqual(0x18, section2.image_pos)
2786         self.assertEqual(4, section2.size)
2787
2788         # Third u-boot
2789         section_entries = section2.GetEntries()
2790         self.assertIn('u-boot', section_entries)
2791         entry = section_entries['u-boot']
2792         self.assertEqual(0, entry.offset)
2793         self.assertEqual(0x18, entry.image_pos)
2794         self.assertEqual(len(U_BOOT_DATA), entry.contents_size)
2795         self.assertEqual(len(U_BOOT_DATA), entry.size)
2796
2797     def _RunReplaceCmd(self, entry_name, data, decomp=True, allow_resize=True,
2798                        dts='132_replace.dts'):
2799         """Replace an entry in an image
2800
2801         This writes the entry data to update it, then opens the updated file and
2802         returns the value that it now finds there.
2803
2804         Args:
2805             entry_name: Entry name to replace
2806             data: Data to replace it with
2807             decomp: True to compress the data if needed, False if data is
2808                 already compressed so should be used as is
2809             allow_resize: True to allow entries to change size, False to raise
2810                 an exception
2811
2812         Returns:
2813             Tuple:
2814                 data from entry
2815                 data from fdtmap (excluding header)
2816                 Image object that was modified
2817         """
2818         dtb_data = self._DoReadFileDtb(dts, use_real_dtb=True,
2819                                        update_dtb=True)[1]
2820
2821         self.assertIn('image', control.images)
2822         image = control.images['image']
2823         entries = image.GetEntries()
2824         orig_dtb_data = entries['u-boot-dtb'].data
2825         orig_fdtmap_data = entries['fdtmap'].data
2826
2827         image_fname = tools.GetOutputFilename('image.bin')
2828         updated_fname = tools.GetOutputFilename('image-updated.bin')
2829         tools.WriteFile(updated_fname, tools.ReadFile(image_fname))
2830         image = control.WriteEntry(updated_fname, entry_name, data, decomp,
2831                                    allow_resize)
2832         data = control.ReadEntry(updated_fname, entry_name, decomp)
2833
2834         # The DT data should not change unless resized:
2835         if not allow_resize:
2836             new_dtb_data = entries['u-boot-dtb'].data
2837             self.assertEqual(new_dtb_data, orig_dtb_data)
2838             new_fdtmap_data = entries['fdtmap'].data
2839             self.assertEqual(new_fdtmap_data, orig_fdtmap_data)
2840
2841         return data, orig_fdtmap_data[fdtmap.FDTMAP_HDR_LEN:], image
2842
2843     def testReplaceSimple(self):
2844         """Test replacing a single file"""
2845         expected = b'x' * len(U_BOOT_DATA)
2846         data, expected_fdtmap, _ = self._RunReplaceCmd('u-boot', expected,
2847                                                     allow_resize=False)
2848         self.assertEqual(expected, data)
2849
2850         # Test that the state looks right. There should be an FDT for the fdtmap
2851         # that we jsut read back in, and it should match what we find in the
2852         # 'control' tables. Checking for an FDT that does not exist should
2853         # return None.
2854         path, fdtmap = state.GetFdtContents('fdtmap')
2855         self.assertIsNotNone(path)
2856         self.assertEqual(expected_fdtmap, fdtmap)
2857
2858         dtb = state.GetFdtForEtype('fdtmap')
2859         self.assertEqual(dtb.GetContents(), fdtmap)
2860
2861         missing_path, missing_fdtmap = state.GetFdtContents('missing')
2862         self.assertIsNone(missing_path)
2863         self.assertIsNone(missing_fdtmap)
2864
2865         missing_dtb = state.GetFdtForEtype('missing')
2866         self.assertIsNone(missing_dtb)
2867
2868         self.assertEqual('/binman', state.fdt_path_prefix)
2869
2870     def testReplaceResizeFail(self):
2871         """Test replacing a file by something larger"""
2872         expected = U_BOOT_DATA + b'x'
2873         with self.assertRaises(ValueError) as e:
2874             self._RunReplaceCmd('u-boot', expected, allow_resize=False,
2875                                 dts='139_replace_repack.dts')
2876         self.assertIn("Node '/u-boot': Entry data size does not match, but resize is disabled",
2877                       str(e.exception))
2878
2879     def testReplaceMulti(self):
2880         """Test replacing entry data where multiple images are generated"""
2881         data = self._DoReadFileDtb('133_replace_multi.dts', use_real_dtb=True,
2882                                    update_dtb=True)[0]
2883         expected = b'x' * len(U_BOOT_DATA)
2884         updated_fname = tools.GetOutputFilename('image-updated.bin')
2885         tools.WriteFile(updated_fname, data)
2886         entry_name = 'u-boot'
2887         control.WriteEntry(updated_fname, entry_name, expected,
2888                            allow_resize=False)
2889         data = control.ReadEntry(updated_fname, entry_name)
2890         self.assertEqual(expected, data)
2891
2892         # Check the state looks right.
2893         self.assertEqual('/binman/image', state.fdt_path_prefix)
2894
2895         # Now check we can write the first image
2896         image_fname = tools.GetOutputFilename('first-image.bin')
2897         updated_fname = tools.GetOutputFilename('first-updated.bin')
2898         tools.WriteFile(updated_fname, tools.ReadFile(image_fname))
2899         entry_name = 'u-boot'
2900         control.WriteEntry(updated_fname, entry_name, expected,
2901                            allow_resize=False)
2902         data = control.ReadEntry(updated_fname, entry_name)
2903         self.assertEqual(expected, data)
2904
2905         # Check the state looks right.
2906         self.assertEqual('/binman/first-image', state.fdt_path_prefix)
2907
2908     def testUpdateFdtAllRepack(self):
2909         """Test that all device trees are updated with offset/size info"""
2910         data = self._DoReadFileRealDtb('134_fdt_update_all_repack.dts')
2911         SECTION_SIZE = 0x300
2912         DTB_SIZE = 602
2913         FDTMAP_SIZE = 608
2914         base_expected = {
2915             'offset': 0,
2916             'size': SECTION_SIZE + DTB_SIZE * 2 + FDTMAP_SIZE,
2917             'image-pos': 0,
2918             'section:offset': 0,
2919             'section:size': SECTION_SIZE,
2920             'section:image-pos': 0,
2921             'section/u-boot-dtb:offset': 4,
2922             'section/u-boot-dtb:size': 636,
2923             'section/u-boot-dtb:image-pos': 4,
2924             'u-boot-spl-dtb:offset': SECTION_SIZE,
2925             'u-boot-spl-dtb:size': DTB_SIZE,
2926             'u-boot-spl-dtb:image-pos': SECTION_SIZE,
2927             'u-boot-tpl-dtb:offset': SECTION_SIZE + DTB_SIZE,
2928             'u-boot-tpl-dtb:image-pos': SECTION_SIZE + DTB_SIZE,
2929             'u-boot-tpl-dtb:size': DTB_SIZE,
2930             'fdtmap:offset': SECTION_SIZE + DTB_SIZE * 2,
2931             'fdtmap:size': FDTMAP_SIZE,
2932             'fdtmap:image-pos': SECTION_SIZE + DTB_SIZE * 2,
2933         }
2934         main_expected = {
2935             'section:orig-size': SECTION_SIZE,
2936             'section/u-boot-dtb:orig-offset': 4,
2937         }
2938
2939         # We expect three device-tree files in the output, with the first one
2940         # within a fixed-size section.
2941         # Read them in sequence. We look for an 'spl' property in the SPL tree,
2942         # and 'tpl' in the TPL tree, to make sure they are distinct from the
2943         # main U-Boot tree. All three should have the same positions and offset
2944         # except that the main tree should include the main_expected properties
2945         start = 4
2946         for item in ['', 'spl', 'tpl', None]:
2947             if item is None:
2948                 start += 16  # Move past fdtmap header
2949             dtb = fdt.Fdt.FromData(data[start:])
2950             dtb.Scan()
2951             props = self._GetPropTree(dtb,
2952                 BASE_DTB_PROPS + REPACK_DTB_PROPS + ['spl', 'tpl'],
2953                 prefix='/' if item is None else '/binman/')
2954             expected = dict(base_expected)
2955             if item:
2956                 expected[item] = 0
2957             else:
2958                 # Main DTB and fdtdec should include the 'orig-' properties
2959                 expected.update(main_expected)
2960             # Helpful for debugging:
2961             #for prop in sorted(props):
2962                 #print('prop %s %s %s' % (prop, props[prop], expected[prop]))
2963             self.assertEqual(expected, props)
2964             if item == '':
2965                 start = SECTION_SIZE
2966             else:
2967                 start += dtb._fdt_obj.totalsize()
2968
2969     def testFdtmapHeaderMiddle(self):
2970         """Test an FDT map in the middle of an image when it should be at end"""
2971         with self.assertRaises(ValueError) as e:
2972             self._DoReadFileRealDtb('135_fdtmap_hdr_middle.dts')
2973         self.assertIn("Invalid sibling order 'middle' for image-header: Must be at 'end' to match location",
2974                       str(e.exception))
2975
2976     def testFdtmapHeaderStartBad(self):
2977         """Test an FDT map in middle of an image when it should be at start"""
2978         with self.assertRaises(ValueError) as e:
2979             self._DoReadFileRealDtb('136_fdtmap_hdr_startbad.dts')
2980         self.assertIn("Invalid sibling order 'end' for image-header: Must be at 'start' to match location",
2981                       str(e.exception))
2982
2983     def testFdtmapHeaderEndBad(self):
2984         """Test an FDT map at the start of an image when it should be at end"""
2985         with self.assertRaises(ValueError) as e:
2986             self._DoReadFileRealDtb('137_fdtmap_hdr_endbad.dts')
2987         self.assertIn("Invalid sibling order 'start' for image-header: Must be at 'end' to match location",
2988                       str(e.exception))
2989
2990     def testFdtmapHeaderNoSize(self):
2991         """Test an image header at the end of an image with undefined size"""
2992         self._DoReadFileRealDtb('138_fdtmap_hdr_nosize.dts')
2993
2994     def testReplaceResize(self):
2995         """Test replacing a single file in an entry with a larger file"""
2996         expected = U_BOOT_DATA + b'x'
2997         data, _, image = self._RunReplaceCmd('u-boot', expected,
2998                                              dts='139_replace_repack.dts')
2999         self.assertEqual(expected, data)
3000
3001         entries = image.GetEntries()
3002         dtb_data = entries['u-boot-dtb'].data
3003         dtb = fdt.Fdt.FromData(dtb_data)
3004         dtb.Scan()
3005
3006         # The u-boot section should now be larger in the dtb
3007         node = dtb.GetNode('/binman/u-boot')
3008         self.assertEqual(len(expected), fdt_util.GetInt(node, 'size'))
3009
3010         # Same for the fdtmap
3011         fdata = entries['fdtmap'].data
3012         fdtb = fdt.Fdt.FromData(fdata[fdtmap.FDTMAP_HDR_LEN:])
3013         fdtb.Scan()
3014         fnode = fdtb.GetNode('/u-boot')
3015         self.assertEqual(len(expected), fdt_util.GetInt(fnode, 'size'))
3016
3017     def testReplaceResizeNoRepack(self):
3018         """Test replacing an entry with a larger file when not allowed"""
3019         expected = U_BOOT_DATA + b'x'
3020         with self.assertRaises(ValueError) as e:
3021             self._RunReplaceCmd('u-boot', expected)
3022         self.assertIn('Entry data size does not match, but allow-repack is not present for this image',
3023                       str(e.exception))
3024
3025     def testEntryShrink(self):
3026         """Test contracting an entry after it is packed"""
3027         try:
3028             state.SetAllowEntryContraction(True)
3029             data = self._DoReadFileDtb('140_entry_shrink.dts',
3030                                        update_dtb=True)[0]
3031         finally:
3032             state.SetAllowEntryContraction(False)
3033         self.assertEqual(b'a', data[:1])
3034         self.assertEqual(U_BOOT_DATA, data[1:1 + len(U_BOOT_DATA)])
3035         self.assertEqual(b'a', data[-1:])
3036
3037     def testEntryShrinkFail(self):
3038         """Test not being allowed to contract an entry after it is packed"""
3039         data = self._DoReadFileDtb('140_entry_shrink.dts', update_dtb=True)[0]
3040
3041         # In this case there is a spare byte at the end of the data. The size of
3042         # the contents is only 1 byte but we still have the size before it
3043         # shrunk.
3044         self.assertEqual(b'a\0', data[:2])
3045         self.assertEqual(U_BOOT_DATA, data[2:2 + len(U_BOOT_DATA)])
3046         self.assertEqual(b'a\0', data[-2:])
3047
3048     def testDescriptorOffset(self):
3049         """Test that the Intel descriptor is always placed at at the start"""
3050         data = self._DoReadFileDtb('141_descriptor_offset.dts')
3051         image = control.images['image']
3052         entries = image.GetEntries()
3053         desc = entries['intel-descriptor']
3054         self.assertEqual(0xff800000, desc.offset);
3055         self.assertEqual(0xff800000, desc.image_pos);
3056
3057     def testReplaceCbfs(self):
3058         """Test replacing a single file in CBFS without changing the size"""
3059         self._CheckLz4()
3060         expected = b'x' * len(U_BOOT_DATA)
3061         data = self._DoReadFileRealDtb('142_replace_cbfs.dts')
3062         updated_fname = tools.GetOutputFilename('image-updated.bin')
3063         tools.WriteFile(updated_fname, data)
3064         entry_name = 'section/cbfs/u-boot'
3065         control.WriteEntry(updated_fname, entry_name, expected,
3066                            allow_resize=True)
3067         data = control.ReadEntry(updated_fname, entry_name)
3068         self.assertEqual(expected, data)
3069
3070     def testReplaceResizeCbfs(self):
3071         """Test replacing a single file in CBFS with one of a different size"""
3072         self._CheckLz4()
3073         expected = U_BOOT_DATA + b'x'
3074         data = self._DoReadFileRealDtb('142_replace_cbfs.dts')
3075         updated_fname = tools.GetOutputFilename('image-updated.bin')
3076         tools.WriteFile(updated_fname, data)
3077         entry_name = 'section/cbfs/u-boot'
3078         control.WriteEntry(updated_fname, entry_name, expected,
3079                            allow_resize=True)
3080         data = control.ReadEntry(updated_fname, entry_name)
3081         self.assertEqual(expected, data)
3082
3083     def _SetupForReplace(self):
3084         """Set up some files to use to replace entries
3085
3086         This generates an image, copies it to a new file, extracts all the files
3087         in it and updates some of them
3088
3089         Returns:
3090             List
3091                 Image filename
3092                 Output directory
3093                 Expected values for updated entries, each a string
3094         """
3095         data = self._DoReadFileRealDtb('143_replace_all.dts')
3096
3097         updated_fname = tools.GetOutputFilename('image-updated.bin')
3098         tools.WriteFile(updated_fname, data)
3099
3100         outdir = os.path.join(self._indir, 'extract')
3101         einfos = control.ExtractEntries(updated_fname, None, outdir, [])
3102
3103         expected1 = b'x' + U_BOOT_DATA + b'y'
3104         u_boot_fname1 = os.path.join(outdir, 'u-boot')
3105         tools.WriteFile(u_boot_fname1, expected1)
3106
3107         expected2 = b'a' + U_BOOT_DATA + b'b'
3108         u_boot_fname2 = os.path.join(outdir, 'u-boot2')
3109         tools.WriteFile(u_boot_fname2, expected2)
3110
3111         expected_text = b'not the same text'
3112         text_fname = os.path.join(outdir, 'text')
3113         tools.WriteFile(text_fname, expected_text)
3114
3115         dtb_fname = os.path.join(outdir, 'u-boot-dtb')
3116         dtb = fdt.FdtScan(dtb_fname)
3117         node = dtb.GetNode('/binman/text')
3118         node.AddString('my-property', 'the value')
3119         dtb.Sync(auto_resize=True)
3120         dtb.Flush()
3121
3122         return updated_fname, outdir, expected1, expected2, expected_text
3123
3124     def _CheckReplaceMultiple(self, entry_paths):
3125         """Handle replacing the contents of multiple entries
3126
3127         Args:
3128             entry_paths: List of entry paths to replace
3129
3130         Returns:
3131             List
3132                 Dict of entries in the image:
3133                     key: Entry name
3134                     Value: Entry object
3135             Expected values for updated entries, each a string
3136         """
3137         updated_fname, outdir, expected1, expected2, expected_text = (
3138             self._SetupForReplace())
3139         control.ReplaceEntries(updated_fname, None, outdir, entry_paths)
3140
3141         image = Image.FromFile(updated_fname)
3142         image.LoadData()
3143         return image.GetEntries(), expected1, expected2, expected_text
3144
3145     def testReplaceAll(self):
3146         """Test replacing the contents of all entries"""
3147         entries, expected1, expected2, expected_text = (
3148             self._CheckReplaceMultiple([]))
3149         data = entries['u-boot'].data
3150         self.assertEqual(expected1, data)
3151
3152         data = entries['u-boot2'].data
3153         self.assertEqual(expected2, data)
3154
3155         data = entries['text'].data
3156         self.assertEqual(expected_text, data)
3157
3158         # Check that the device tree is updated
3159         data = entries['u-boot-dtb'].data
3160         dtb = fdt.Fdt.FromData(data)
3161         dtb.Scan()
3162         node = dtb.GetNode('/binman/text')
3163         self.assertEqual('the value', node.props['my-property'].value)
3164
3165     def testReplaceSome(self):
3166         """Test replacing the contents of a few entries"""
3167         entries, expected1, expected2, expected_text = (
3168             self._CheckReplaceMultiple(['u-boot2', 'text']))
3169
3170         # This one should not change
3171         data = entries['u-boot'].data
3172         self.assertEqual(U_BOOT_DATA, data)
3173
3174         data = entries['u-boot2'].data
3175         self.assertEqual(expected2, data)
3176
3177         data = entries['text'].data
3178         self.assertEqual(expected_text, data)
3179
3180     def testReplaceCmd(self):
3181         """Test replacing a file fron an image on the command line"""
3182         self._DoReadFileRealDtb('143_replace_all.dts')
3183
3184         try:
3185             tmpdir, updated_fname = self._SetupImageInTmpdir()
3186
3187             fname = os.path.join(tmpdir, 'update-u-boot.bin')
3188             expected = b'x' * len(U_BOOT_DATA)
3189             tools.WriteFile(fname, expected)
3190
3191             self._DoBinman('replace', '-i', updated_fname, 'u-boot', '-f', fname)
3192             data = tools.ReadFile(updated_fname)
3193             self.assertEqual(expected, data[:len(expected)])
3194             map_fname = os.path.join(tmpdir, 'image-updated.map')
3195             self.assertFalse(os.path.exists(map_fname))
3196         finally:
3197             shutil.rmtree(tmpdir)
3198
3199     def testReplaceCmdSome(self):
3200         """Test replacing some files fron an image on the command line"""
3201         updated_fname, outdir, expected1, expected2, expected_text = (
3202             self._SetupForReplace())
3203
3204         self._DoBinman('replace', '-i', updated_fname, '-I', outdir,
3205                        'u-boot2', 'text')
3206
3207         tools.PrepareOutputDir(None)
3208         image = Image.FromFile(updated_fname)
3209         image.LoadData()
3210         entries = image.GetEntries()
3211
3212         # This one should not change
3213         data = entries['u-boot'].data
3214         self.assertEqual(U_BOOT_DATA, data)
3215
3216         data = entries['u-boot2'].data
3217         self.assertEqual(expected2, data)
3218
3219         data = entries['text'].data
3220         self.assertEqual(expected_text, data)
3221
3222     def testReplaceMissing(self):
3223         """Test replacing entries where the file is missing"""
3224         updated_fname, outdir, expected1, expected2, expected_text = (
3225             self._SetupForReplace())
3226
3227         # Remove one of the files, to generate a warning
3228         u_boot_fname1 = os.path.join(outdir, 'u-boot')
3229         os.remove(u_boot_fname1)
3230
3231         with test_util.capture_sys_output() as (stdout, stderr):
3232             control.ReplaceEntries(updated_fname, None, outdir, [])
3233         self.assertIn("Skipping entry '/u-boot' from missing file",
3234                       stdout.getvalue())
3235
3236     def testReplaceCmdMap(self):
3237         """Test replacing a file fron an image on the command line"""
3238         self._DoReadFileRealDtb('143_replace_all.dts')
3239
3240         try:
3241             tmpdir, updated_fname = self._SetupImageInTmpdir()
3242
3243             fname = os.path.join(self._indir, 'update-u-boot.bin')
3244             expected = b'x' * len(U_BOOT_DATA)
3245             tools.WriteFile(fname, expected)
3246
3247             self._DoBinman('replace', '-i', updated_fname, 'u-boot',
3248                            '-f', fname, '-m')
3249             map_fname = os.path.join(tmpdir, 'image-updated.map')
3250             self.assertTrue(os.path.exists(map_fname))
3251         finally:
3252             shutil.rmtree(tmpdir)
3253
3254     def testReplaceNoEntryPaths(self):
3255         """Test replacing an entry without an entry path"""
3256         self._DoReadFileRealDtb('143_replace_all.dts')
3257         image_fname = tools.GetOutputFilename('image.bin')
3258         with self.assertRaises(ValueError) as e:
3259             control.ReplaceEntries(image_fname, 'fname', None, [])
3260         self.assertIn('Must specify an entry path to read with -f',
3261                       str(e.exception))
3262
3263     def testReplaceTooManyEntryPaths(self):
3264         """Test extracting some entries"""
3265         self._DoReadFileRealDtb('143_replace_all.dts')
3266         image_fname = tools.GetOutputFilename('image.bin')
3267         with self.assertRaises(ValueError) as e:
3268             control.ReplaceEntries(image_fname, 'fname', None, ['a', 'b'])
3269         self.assertIn('Must specify exactly one entry path to write with -f',
3270                       str(e.exception))
3271
3272     def testPackReset16(self):
3273         """Test that an image with an x86 reset16 region can be created"""
3274         data = self._DoReadFile('144_x86_reset16.dts')
3275         self.assertEqual(X86_RESET16_DATA, data[:len(X86_RESET16_DATA)])
3276
3277     def testPackReset16Spl(self):
3278         """Test that an image with an x86 reset16-spl region can be created"""
3279         data = self._DoReadFile('145_x86_reset16_spl.dts')
3280         self.assertEqual(X86_RESET16_SPL_DATA, data[:len(X86_RESET16_SPL_DATA)])
3281
3282     def testPackReset16Tpl(self):
3283         """Test that an image with an x86 reset16-tpl region can be created"""
3284         data = self._DoReadFile('146_x86_reset16_tpl.dts')
3285         self.assertEqual(X86_RESET16_TPL_DATA, data[:len(X86_RESET16_TPL_DATA)])
3286
3287     def testPackIntelFit(self):
3288         """Test that an image with an Intel FIT and pointer can be created"""
3289         data = self._DoReadFile('147_intel_fit.dts')
3290         self.assertEqual(U_BOOT_DATA, data[:len(U_BOOT_DATA)])
3291         fit = data[16:32];
3292         self.assertEqual(b'_FIT_   \x01\x00\x00\x00\x00\x01\x80}' , fit)
3293         ptr = struct.unpack('<i', data[0x40:0x44])[0]
3294
3295         image = control.images['image']
3296         entries = image.GetEntries()
3297         expected_ptr = entries['intel-fit'].image_pos - (1 << 32)
3298         self.assertEqual(expected_ptr, ptr)
3299
3300     def testPackIntelFitMissing(self):
3301         """Test detection of a FIT pointer with not FIT region"""
3302         with self.assertRaises(ValueError) as e:
3303             self._DoReadFile('148_intel_fit_missing.dts')
3304         self.assertIn("'intel-fit-ptr' section must have an 'intel-fit' sibling",
3305                       str(e.exception))
3306
3307     def _CheckSymbolsTplSection(self, dts, expected_vals):
3308         data = self._DoReadFile(dts)
3309         sym_values = struct.pack('<LQLL', *expected_vals)
3310         upto1 = 4 + len(U_BOOT_SPL_DATA)
3311         expected1 = tools.GetBytes(0xff, 4) + sym_values + U_BOOT_SPL_DATA[20:]
3312         self.assertEqual(expected1, data[:upto1])
3313
3314         upto2 = upto1 + 1 + len(U_BOOT_SPL_DATA)
3315         expected2 = tools.GetBytes(0xff, 1) + sym_values + U_BOOT_SPL_DATA[20:]
3316         self.assertEqual(expected2, data[upto1:upto2])
3317
3318         upto3 = 0x34 + len(U_BOOT_DATA)
3319         expected3 = tools.GetBytes(0xff, 1) + U_BOOT_DATA
3320         self.assertEqual(expected3, data[upto2:upto3])
3321
3322         expected4 = sym_values + U_BOOT_TPL_DATA[20:]
3323         self.assertEqual(expected4, data[upto3:upto3 + len(U_BOOT_TPL_DATA)])
3324
3325     def testSymbolsTplSection(self):
3326         """Test binman can assign symbols embedded in U-Boot TPL in a section"""
3327         self._SetupSplElf('u_boot_binman_syms')
3328         self._SetupTplElf('u_boot_binman_syms')
3329         self._CheckSymbolsTplSection('149_symbols_tpl.dts',
3330                                      [0x04, 0x1c, 0x10 + 0x34, 0x04])
3331
3332     def testSymbolsTplSectionX86(self):
3333         """Test binman can assign symbols in a section with end-at-4gb"""
3334         self._SetupSplElf('u_boot_binman_syms_x86')
3335         self._SetupTplElf('u_boot_binman_syms_x86')
3336         self._CheckSymbolsTplSection('155_symbols_tpl_x86.dts',
3337                                      [0xffffff04, 0xffffff1c, 0xffffff34,
3338                                       0x04])
3339
3340     def testPackX86RomIfwiSectiom(self):
3341         """Test that a section can be placed in an IFWI region"""
3342         self._SetupIfwi('fitimage.bin')
3343         data = self._DoReadFile('151_x86_rom_ifwi_section.dts')
3344         self._CheckIfwi(data)
3345
3346     def testPackFspM(self):
3347         """Test that an image with a FSP memory-init binary can be created"""
3348         data = self._DoReadFile('152_intel_fsp_m.dts')
3349         self.assertEqual(FSP_M_DATA, data[:len(FSP_M_DATA)])
3350
3351     def testPackFspS(self):
3352         """Test that an image with a FSP silicon-init binary can be created"""
3353         data = self._DoReadFile('153_intel_fsp_s.dts')
3354         self.assertEqual(FSP_S_DATA, data[:len(FSP_S_DATA)])
3355
3356     def testPackFspT(self):
3357         """Test that an image with a FSP temp-ram-init binary can be created"""
3358         data = self._DoReadFile('154_intel_fsp_t.dts')
3359         self.assertEqual(FSP_T_DATA, data[:len(FSP_T_DATA)])
3360
3361
3362 if __name__ == "__main__":
3363     unittest.main()