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