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