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