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