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