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