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