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