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