xref: /openbmc/u-boot/tools/binman/ftest.py (revision ff8eee03)
1# SPDX-License-Identifier: GPL-2.0+
2# Copyright (c) 2016 Google, Inc
3# Written by Simon Glass <sjg@chromium.org>
4#
5# To run a single test, change to this directory, and:
6#
7#    python -m unittest func_test.TestFunctional.testHelp
8
9import hashlib
10from optparse import OptionParser
11import os
12import shutil
13import struct
14import sys
15import tempfile
16import unittest
17
18import binman
19import cmdline
20import command
21import control
22import elf
23import fdt
24import fdt_util
25import fmap_util
26import test_util
27import state
28import tools
29import tout
30
31# Contents of test files, corresponding to different entry types
32U_BOOT_DATA           = '1234'
33U_BOOT_IMG_DATA       = 'img'
34U_BOOT_SPL_DATA       = '56780123456789abcde'
35U_BOOT_TPL_DATA       = 'tpl'
36BLOB_DATA             = '89'
37ME_DATA               = '0abcd'
38VGA_DATA              = 'vga'
39U_BOOT_DTB_DATA       = 'udtb'
40U_BOOT_SPL_DTB_DATA   = 'spldtb'
41U_BOOT_TPL_DTB_DATA   = 'tpldtb'
42X86_START16_DATA      = 'start16'
43X86_START16_SPL_DATA  = 'start16spl'
44X86_START16_TPL_DATA  = 'start16tpl'
45U_BOOT_NODTB_DATA     = 'nodtb with microcode pointer somewhere in here'
46U_BOOT_SPL_NODTB_DATA = 'splnodtb with microcode pointer somewhere in here'
47U_BOOT_TPL_NODTB_DATA = 'tplnodtb with microcode pointer somewhere in here'
48FSP_DATA              = 'fsp'
49CMC_DATA              = 'cmc'
50VBT_DATA              = 'vbt'
51MRC_DATA              = 'mrc'
52TEXT_DATA             = 'text'
53TEXT_DATA2            = 'text2'
54TEXT_DATA3            = 'text3'
55CROS_EC_RW_DATA       = 'ecrw'
56GBB_DATA              = 'gbbd'
57BMPBLK_DATA           = 'bmp'
58VBLOCK_DATA           = 'vblk'
59FILES_DATA            = ("sorry I'm late\nOh, don't bother apologising, I'm " +
60                         "sorry you're alive\n")
61COMPRESS_DATA         = 'data to compress'
62
63
64class TestFunctional(unittest.TestCase):
65    """Functional tests for binman
66
67    Most of these use a sample .dts file to build an image and then check
68    that it looks correct. The sample files are in the test/ subdirectory
69    and are numbered.
70
71    For each entry type a very small test file is created using fixed
72    string contents. This makes it easy to test that things look right, and
73    debug problems.
74
75    In some cases a 'real' file must be used - these are also supplied in
76    the test/ diurectory.
77    """
78    @classmethod
79    def setUpClass(self):
80        global entry
81        import entry
82
83        # Handle the case where argv[0] is 'python'
84        self._binman_dir = os.path.dirname(os.path.realpath(sys.argv[0]))
85        self._binman_pathname = os.path.join(self._binman_dir, 'binman')
86
87        # Create a temporary directory for input files
88        self._indir = tempfile.mkdtemp(prefix='binmant.')
89
90        # Create some test files
91        TestFunctional._MakeInputFile('u-boot.bin', U_BOOT_DATA)
92        TestFunctional._MakeInputFile('u-boot.img', U_BOOT_IMG_DATA)
93        TestFunctional._MakeInputFile('spl/u-boot-spl.bin', U_BOOT_SPL_DATA)
94        TestFunctional._MakeInputFile('tpl/u-boot-tpl.bin', U_BOOT_TPL_DATA)
95        TestFunctional._MakeInputFile('blobfile', BLOB_DATA)
96        TestFunctional._MakeInputFile('me.bin', ME_DATA)
97        TestFunctional._MakeInputFile('vga.bin', VGA_DATA)
98        self._ResetDtbs()
99        TestFunctional._MakeInputFile('u-boot-x86-16bit.bin', X86_START16_DATA)
100        TestFunctional._MakeInputFile('spl/u-boot-x86-16bit-spl.bin',
101                                      X86_START16_SPL_DATA)
102        TestFunctional._MakeInputFile('tpl/u-boot-x86-16bit-tpl.bin',
103                                      X86_START16_TPL_DATA)
104        TestFunctional._MakeInputFile('u-boot-nodtb.bin', U_BOOT_NODTB_DATA)
105        TestFunctional._MakeInputFile('spl/u-boot-spl-nodtb.bin',
106                                      U_BOOT_SPL_NODTB_DATA)
107        TestFunctional._MakeInputFile('tpl/u-boot-tpl-nodtb.bin',
108                                      U_BOOT_TPL_NODTB_DATA)
109        TestFunctional._MakeInputFile('fsp.bin', FSP_DATA)
110        TestFunctional._MakeInputFile('cmc.bin', CMC_DATA)
111        TestFunctional._MakeInputFile('vbt.bin', VBT_DATA)
112        TestFunctional._MakeInputFile('mrc.bin', MRC_DATA)
113        TestFunctional._MakeInputFile('ecrw.bin', CROS_EC_RW_DATA)
114        TestFunctional._MakeInputDir('devkeys')
115        TestFunctional._MakeInputFile('bmpblk.bin', BMPBLK_DATA)
116        self._output_setup = False
117
118        # ELF file with a '_dt_ucode_base_size' symbol
119        with open(self.TestFile('u_boot_ucode_ptr')) as fd:
120            TestFunctional._MakeInputFile('u-boot', fd.read())
121
122        # Intel flash descriptor file
123        with open(self.TestFile('descriptor.bin')) as fd:
124            TestFunctional._MakeInputFile('descriptor.bin', fd.read())
125
126        shutil.copytree(self.TestFile('files'),
127                        os.path.join(self._indir, 'files'))
128
129        TestFunctional._MakeInputFile('compress', COMPRESS_DATA)
130
131    @classmethod
132    def tearDownClass(self):
133        """Remove the temporary input directory and its contents"""
134        if self._indir:
135            shutil.rmtree(self._indir)
136        self._indir = None
137
138    def setUp(self):
139        # Enable this to turn on debugging output
140        # tout.Init(tout.DEBUG)
141        command.test_result = None
142
143    def tearDown(self):
144        """Remove the temporary output directory"""
145        tools._FinaliseForTest()
146
147    @classmethod
148    def _ResetDtbs(self):
149        TestFunctional._MakeInputFile('u-boot.dtb', U_BOOT_DTB_DATA)
150        TestFunctional._MakeInputFile('spl/u-boot-spl.dtb', U_BOOT_SPL_DTB_DATA)
151        TestFunctional._MakeInputFile('tpl/u-boot-tpl.dtb', U_BOOT_TPL_DTB_DATA)
152
153    def _RunBinman(self, *args, **kwargs):
154        """Run binman using the command line
155
156        Args:
157            Arguments to pass, as a list of strings
158            kwargs: Arguments to pass to Command.RunPipe()
159        """
160        result = command.RunPipe([[self._binman_pathname] + list(args)],
161                capture=True, capture_stderr=True, raise_on_error=False)
162        if result.return_code and kwargs.get('raise_on_error', True):
163            raise Exception("Error running '%s': %s" % (' '.join(args),
164                            result.stdout + result.stderr))
165        return result
166
167    def _DoBinman(self, *args):
168        """Run binman using directly (in the same process)
169
170        Args:
171            Arguments to pass, as a list of strings
172        Returns:
173            Return value (0 for success)
174        """
175        args = list(args)
176        if '-D' in sys.argv:
177            args = args + ['-D']
178        (options, args) = cmdline.ParseArgs(args)
179        options.pager = 'binman-invalid-pager'
180        options.build_dir = self._indir
181
182        # For testing, you can force an increase in verbosity here
183        # options.verbosity = tout.DEBUG
184        return control.Binman(options, args)
185
186    def _DoTestFile(self, fname, debug=False, map=False, update_dtb=False,
187                    entry_args=None, images=None, use_real_dtb=False):
188        """Run binman with a given test file
189
190        Args:
191            fname: Device-tree source filename to use (e.g. 05_simple.dts)
192            debug: True to enable debugging output
193            map: True to output map files for the images
194            update_dtb: Update the offset and size of each entry in the device
195                tree before packing it into the image
196            entry_args: Dict of entry args to supply to binman
197                key: arg name
198                value: value of that arg
199            images: List of image names to build
200        """
201        args = ['-p', '-I', self._indir, '-d', self.TestFile(fname)]
202        if debug:
203            args.append('-D')
204        if map:
205            args.append('-m')
206        if update_dtb:
207            args.append('-up')
208        if not use_real_dtb:
209            args.append('--fake-dtb')
210        if entry_args:
211            for arg, value in entry_args.iteritems():
212                args.append('-a%s=%s' % (arg, value))
213        if images:
214            for image in images:
215                args += ['-i', image]
216        return self._DoBinman(*args)
217
218    def _SetupDtb(self, fname, outfile='u-boot.dtb'):
219        """Set up a new test device-tree file
220
221        The given file is compiled and set up as the device tree to be used
222        for ths test.
223
224        Args:
225            fname: Filename of .dts file to read
226            outfile: Output filename for compiled device-tree binary
227
228        Returns:
229            Contents of device-tree binary
230        """
231        if not self._output_setup:
232            tools.PrepareOutputDir(self._indir, True)
233            self._output_setup = True
234        dtb = fdt_util.EnsureCompiled(self.TestFile(fname))
235        with open(dtb) as fd:
236            data = fd.read()
237            TestFunctional._MakeInputFile(outfile, data)
238            return data
239
240    def _GetDtbContentsForSplTpl(self, dtb_data, name):
241        """Create a version of the main DTB for SPL or SPL
242
243        For testing we don't actually have different versions of the DTB. With
244        U-Boot we normally run fdtgrep to remove unwanted nodes, but for tests
245        we don't normally have any unwanted nodes.
246
247        We still want the DTBs for SPL and TPL to be different though, since
248        otherwise it is confusing to know which one we are looking at. So add
249        an 'spl' or 'tpl' property to the top-level node.
250        """
251        dtb = fdt.Fdt.FromData(dtb_data)
252        dtb.Scan()
253        dtb.GetNode('/binman').AddZeroProp(name)
254        dtb.Sync(auto_resize=True)
255        dtb.Pack()
256        return dtb.GetContents()
257
258    def _DoReadFileDtb(self, fname, use_real_dtb=False, map=False,
259                       update_dtb=False, entry_args=None, reset_dtbs=True):
260        """Run binman and return the resulting image
261
262        This runs binman with a given test file and then reads the resulting
263        output file. It is a shortcut function since most tests need to do
264        these steps.
265
266        Raises an assertion failure if binman returns a non-zero exit code.
267
268        Args:
269            fname: Device-tree source filename to use (e.g. 05_simple.dts)
270            use_real_dtb: True to use the test file as the contents of
271                the u-boot-dtb entry. Normally this is not needed and the
272                test contents (the U_BOOT_DTB_DATA string) can be used.
273                But in some test we need the real contents.
274            map: True to output map files for the images
275            update_dtb: Update the offset and size of each entry in the device
276                tree before packing it into the image
277
278        Returns:
279            Tuple:
280                Resulting image contents
281                Device tree contents
282                Map data showing contents of image (or None if none)
283                Output device tree binary filename ('u-boot.dtb' path)
284        """
285        dtb_data = None
286        # Use the compiled test file as the u-boot-dtb input
287        if use_real_dtb:
288            dtb_data = self._SetupDtb(fname)
289            infile = os.path.join(self._indir, 'u-boot.dtb')
290
291            # For testing purposes, make a copy of the DT for SPL and TPL. Add
292            # a node indicating which it is, so aid verification.
293            for name in ['spl', 'tpl']:
294                dtb_fname = '%s/u-boot-%s.dtb' % (name, name)
295                outfile = os.path.join(self._indir, dtb_fname)
296                TestFunctional._MakeInputFile(dtb_fname,
297                        self._GetDtbContentsForSplTpl(dtb_data, name))
298
299        try:
300            retcode = self._DoTestFile(fname, map=map, update_dtb=update_dtb,
301                    entry_args=entry_args, use_real_dtb=use_real_dtb)
302            self.assertEqual(0, retcode)
303            out_dtb_fname = tools.GetOutputFilename('u-boot.dtb.out')
304
305            # Find the (only) image, read it and return its contents
306            image = control.images['image']
307            image_fname = tools.GetOutputFilename('image.bin')
308            self.assertTrue(os.path.exists(image_fname))
309            if map:
310                map_fname = tools.GetOutputFilename('image.map')
311                with open(map_fname) as fd:
312                    map_data = fd.read()
313            else:
314                map_data = None
315            with open(image_fname) as fd:
316                return fd.read(), dtb_data, map_data, out_dtb_fname
317        finally:
318            # Put the test file back
319            if reset_dtbs and use_real_dtb:
320                self._ResetDtbs()
321
322    def _DoReadFile(self, fname, use_real_dtb=False):
323        """Helper function which discards the device-tree binary
324
325        Args:
326            fname: Device-tree source filename to use (e.g. 05_simple.dts)
327            use_real_dtb: True to use the test file as the contents of
328                the u-boot-dtb entry. Normally this is not needed and the
329                test contents (the U_BOOT_DTB_DATA string) can be used.
330                But in some test we need the real contents.
331
332        Returns:
333            Resulting image contents
334        """
335        return self._DoReadFileDtb(fname, use_real_dtb)[0]
336
337    @classmethod
338    def _MakeInputFile(self, fname, contents):
339        """Create a new test input file, creating directories as needed
340
341        Args:
342            fname: Filename to create
343            contents: File contents to write in to the file
344        Returns:
345            Full pathname of file created
346        """
347        pathname = os.path.join(self._indir, fname)
348        dirname = os.path.dirname(pathname)
349        if dirname and not os.path.exists(dirname):
350            os.makedirs(dirname)
351        with open(pathname, 'wb') as fd:
352            fd.write(contents)
353        return pathname
354
355    @classmethod
356    def _MakeInputDir(self, dirname):
357        """Create a new test input directory, creating directories as needed
358
359        Args:
360            dirname: Directory name to create
361
362        Returns:
363            Full pathname of directory created
364        """
365        pathname = os.path.join(self._indir, dirname)
366        if not os.path.exists(pathname):
367            os.makedirs(pathname)
368        return pathname
369
370    @classmethod
371    def TestFile(self, fname):
372        return os.path.join(self._binman_dir, 'test', fname)
373
374    def AssertInList(self, grep_list, target):
375        """Assert that at least one of a list of things is in a target
376
377        Args:
378            grep_list: List of strings to check
379            target: Target string
380        """
381        for grep in grep_list:
382            if grep in target:
383                return
384        self.fail("Error: '%' not found in '%s'" % (grep_list, target))
385
386    def CheckNoGaps(self, entries):
387        """Check that all entries fit together without gaps
388
389        Args:
390            entries: List of entries to check
391        """
392        offset = 0
393        for entry in entries.values():
394            self.assertEqual(offset, entry.offset)
395            offset += entry.size
396
397    def GetFdtLen(self, dtb):
398        """Get the totalsize field from a device-tree binary
399
400        Args:
401            dtb: Device-tree binary contents
402
403        Returns:
404            Total size of device-tree binary, from the header
405        """
406        return struct.unpack('>L', dtb[4:8])[0]
407
408    def _GetPropTree(self, dtb, prop_names):
409        def AddNode(node, path):
410            if node.name != '/':
411                path += '/' + node.name
412            for subnode in node.subnodes:
413                for prop in subnode.props.values():
414                    if prop.name in prop_names:
415                        prop_path = path + '/' + subnode.name + ':' + prop.name
416                        tree[prop_path[len('/binman/'):]] = fdt_util.fdt32_to_cpu(
417                            prop.value)
418                AddNode(subnode, path)
419
420        tree = {}
421        AddNode(dtb.GetRoot(), '')
422        return tree
423
424    def testRun(self):
425        """Test a basic run with valid args"""
426        result = self._RunBinman('-h')
427
428    def testFullHelp(self):
429        """Test that the full help is displayed with -H"""
430        result = self._RunBinman('-H')
431        help_file = os.path.join(self._binman_dir, 'README')
432        # Remove possible extraneous strings
433        extra = '::::::::::::::\n' + help_file + '\n::::::::::::::\n'
434        gothelp = result.stdout.replace(extra, '')
435        self.assertEqual(len(gothelp), os.path.getsize(help_file))
436        self.assertEqual(0, len(result.stderr))
437        self.assertEqual(0, result.return_code)
438
439    def testFullHelpInternal(self):
440        """Test that the full help is displayed with -H"""
441        try:
442            command.test_result = command.CommandResult()
443            result = self._DoBinman('-H')
444            help_file = os.path.join(self._binman_dir, 'README')
445        finally:
446            command.test_result = None
447
448    def testHelp(self):
449        """Test that the basic help is displayed with -h"""
450        result = self._RunBinman('-h')
451        self.assertTrue(len(result.stdout) > 200)
452        self.assertEqual(0, len(result.stderr))
453        self.assertEqual(0, result.return_code)
454
455    def testBoard(self):
456        """Test that we can run it with a specific board"""
457        self._SetupDtb('05_simple.dts', 'sandbox/u-boot.dtb')
458        TestFunctional._MakeInputFile('sandbox/u-boot.bin', U_BOOT_DATA)
459        result = self._DoBinman('-b', 'sandbox')
460        self.assertEqual(0, result)
461
462    def testNeedBoard(self):
463        """Test that we get an error when no board ius supplied"""
464        with self.assertRaises(ValueError) as e:
465            result = self._DoBinman()
466        self.assertIn("Must provide a board to process (use -b <board>)",
467                str(e.exception))
468
469    def testMissingDt(self):
470        """Test that an invalid device-tree file generates an error"""
471        with self.assertRaises(Exception) as e:
472            self._RunBinman('-d', 'missing_file')
473        # We get one error from libfdt, and a different one from fdtget.
474        self.AssertInList(["Couldn't open blob from 'missing_file'",
475                           'No such file or directory'], str(e.exception))
476
477    def testBrokenDt(self):
478        """Test that an invalid device-tree source file generates an error
479
480        Since this is a source file it should be compiled and the error
481        will come from the device-tree compiler (dtc).
482        """
483        with self.assertRaises(Exception) as e:
484            self._RunBinman('-d', self.TestFile('01_invalid.dts'))
485        self.assertIn("FATAL ERROR: Unable to parse input tree",
486                str(e.exception))
487
488    def testMissingNode(self):
489        """Test that a device tree without a 'binman' node generates an error"""
490        with self.assertRaises(Exception) as e:
491            self._DoBinman('-d', self.TestFile('02_missing_node.dts'))
492        self.assertIn("does not have a 'binman' node", str(e.exception))
493
494    def testEmpty(self):
495        """Test that an empty binman node works OK (i.e. does nothing)"""
496        result = self._RunBinman('-d', self.TestFile('03_empty.dts'))
497        self.assertEqual(0, len(result.stderr))
498        self.assertEqual(0, result.return_code)
499
500    def testInvalidEntry(self):
501        """Test that an invalid entry is flagged"""
502        with self.assertRaises(Exception) as e:
503            result = self._RunBinman('-d',
504                                     self.TestFile('04_invalid_entry.dts'))
505        self.assertIn("Unknown entry type 'not-a-valid-type' in node "
506                "'/binman/not-a-valid-type'", str(e.exception))
507
508    def testSimple(self):
509        """Test a simple binman with a single file"""
510        data = self._DoReadFile('05_simple.dts')
511        self.assertEqual(U_BOOT_DATA, data)
512
513    def testSimpleDebug(self):
514        """Test a simple binman run with debugging enabled"""
515        data = self._DoTestFile('05_simple.dts', debug=True)
516
517    def testDual(self):
518        """Test that we can handle creating two images
519
520        This also tests image padding.
521        """
522        retcode = self._DoTestFile('06_dual_image.dts')
523        self.assertEqual(0, retcode)
524
525        image = control.images['image1']
526        self.assertEqual(len(U_BOOT_DATA), image._size)
527        fname = tools.GetOutputFilename('image1.bin')
528        self.assertTrue(os.path.exists(fname))
529        with open(fname) as fd:
530            data = fd.read()
531            self.assertEqual(U_BOOT_DATA, data)
532
533        image = control.images['image2']
534        self.assertEqual(3 + len(U_BOOT_DATA) + 5, image._size)
535        fname = tools.GetOutputFilename('image2.bin')
536        self.assertTrue(os.path.exists(fname))
537        with open(fname) as fd:
538            data = fd.read()
539            self.assertEqual(U_BOOT_DATA, data[3:7])
540            self.assertEqual(chr(0) * 3, data[:3])
541            self.assertEqual(chr(0) * 5, data[7:])
542
543    def testBadAlign(self):
544        """Test that an invalid alignment value is detected"""
545        with self.assertRaises(ValueError) as e:
546            self._DoTestFile('07_bad_align.dts')
547        self.assertIn("Node '/binman/u-boot': Alignment 23 must be a power "
548                      "of two", str(e.exception))
549
550    def testPackSimple(self):
551        """Test that packing works as expected"""
552        retcode = self._DoTestFile('08_pack.dts')
553        self.assertEqual(0, retcode)
554        self.assertIn('image', control.images)
555        image = control.images['image']
556        entries = image.GetEntries()
557        self.assertEqual(5, len(entries))
558
559        # First u-boot
560        self.assertIn('u-boot', entries)
561        entry = entries['u-boot']
562        self.assertEqual(0, entry.offset)
563        self.assertEqual(len(U_BOOT_DATA), entry.size)
564
565        # Second u-boot, aligned to 16-byte boundary
566        self.assertIn('u-boot-align', entries)
567        entry = entries['u-boot-align']
568        self.assertEqual(16, entry.offset)
569        self.assertEqual(len(U_BOOT_DATA), entry.size)
570
571        # Third u-boot, size 23 bytes
572        self.assertIn('u-boot-size', entries)
573        entry = entries['u-boot-size']
574        self.assertEqual(20, entry.offset)
575        self.assertEqual(len(U_BOOT_DATA), entry.contents_size)
576        self.assertEqual(23, entry.size)
577
578        # Fourth u-boot, placed immediate after the above
579        self.assertIn('u-boot-next', entries)
580        entry = entries['u-boot-next']
581        self.assertEqual(43, entry.offset)
582        self.assertEqual(len(U_BOOT_DATA), entry.size)
583
584        # Fifth u-boot, placed at a fixed offset
585        self.assertIn('u-boot-fixed', entries)
586        entry = entries['u-boot-fixed']
587        self.assertEqual(61, entry.offset)
588        self.assertEqual(len(U_BOOT_DATA), entry.size)
589
590        self.assertEqual(65, image._size)
591
592    def testPackExtra(self):
593        """Test that extra packing feature works as expected"""
594        retcode = self._DoTestFile('09_pack_extra.dts')
595
596        self.assertEqual(0, retcode)
597        self.assertIn('image', control.images)
598        image = control.images['image']
599        entries = image.GetEntries()
600        self.assertEqual(5, len(entries))
601
602        # First u-boot with padding before and after
603        self.assertIn('u-boot', entries)
604        entry = entries['u-boot']
605        self.assertEqual(0, entry.offset)
606        self.assertEqual(3, entry.pad_before)
607        self.assertEqual(3 + 5 + len(U_BOOT_DATA), entry.size)
608
609        # Second u-boot has an aligned size, but it has no effect
610        self.assertIn('u-boot-align-size-nop', entries)
611        entry = entries['u-boot-align-size-nop']
612        self.assertEqual(12, entry.offset)
613        self.assertEqual(4, entry.size)
614
615        # Third u-boot has an aligned size too
616        self.assertIn('u-boot-align-size', entries)
617        entry = entries['u-boot-align-size']
618        self.assertEqual(16, entry.offset)
619        self.assertEqual(32, entry.size)
620
621        # Fourth u-boot has an aligned end
622        self.assertIn('u-boot-align-end', entries)
623        entry = entries['u-boot-align-end']
624        self.assertEqual(48, entry.offset)
625        self.assertEqual(16, entry.size)
626
627        # Fifth u-boot immediately afterwards
628        self.assertIn('u-boot-align-both', entries)
629        entry = entries['u-boot-align-both']
630        self.assertEqual(64, entry.offset)
631        self.assertEqual(64, entry.size)
632
633        self.CheckNoGaps(entries)
634        self.assertEqual(128, image._size)
635
636    def testPackAlignPowerOf2(self):
637        """Test that invalid entry alignment is detected"""
638        with self.assertRaises(ValueError) as e:
639            self._DoTestFile('10_pack_align_power2.dts')
640        self.assertIn("Node '/binman/u-boot': Alignment 5 must be a power "
641                      "of two", str(e.exception))
642
643    def testPackAlignSizePowerOf2(self):
644        """Test that invalid entry size alignment is detected"""
645        with self.assertRaises(ValueError) as e:
646            self._DoTestFile('11_pack_align_size_power2.dts')
647        self.assertIn("Node '/binman/u-boot': Alignment size 55 must be a "
648                      "power of two", str(e.exception))
649
650    def testPackInvalidAlign(self):
651        """Test detection of an offset that does not match its alignment"""
652        with self.assertRaises(ValueError) as e:
653            self._DoTestFile('12_pack_inv_align.dts')
654        self.assertIn("Node '/binman/u-boot': Offset 0x5 (5) does not match "
655                      "align 0x4 (4)", str(e.exception))
656
657    def testPackInvalidSizeAlign(self):
658        """Test that invalid entry size alignment is detected"""
659        with self.assertRaises(ValueError) as e:
660            self._DoTestFile('13_pack_inv_size_align.dts')
661        self.assertIn("Node '/binman/u-boot': Size 0x5 (5) does not match "
662                      "align-size 0x4 (4)", str(e.exception))
663
664    def testPackOverlap(self):
665        """Test that overlapping regions are detected"""
666        with self.assertRaises(ValueError) as e:
667            self._DoTestFile('14_pack_overlap.dts')
668        self.assertIn("Node '/binman/u-boot-align': Offset 0x3 (3) overlaps "
669                      "with previous entry '/binman/u-boot' ending at 0x4 (4)",
670                      str(e.exception))
671
672    def testPackEntryOverflow(self):
673        """Test that entries that overflow their size are detected"""
674        with self.assertRaises(ValueError) as e:
675            self._DoTestFile('15_pack_overflow.dts')
676        self.assertIn("Node '/binman/u-boot': Entry contents size is 0x4 (4) "
677                      "but entry size is 0x3 (3)", str(e.exception))
678
679    def testPackImageOverflow(self):
680        """Test that entries which overflow the image size are detected"""
681        with self.assertRaises(ValueError) as e:
682            self._DoTestFile('16_pack_image_overflow.dts')
683        self.assertIn("Section '/binman': contents size 0x4 (4) exceeds section "
684                      "size 0x3 (3)", str(e.exception))
685
686    def testPackImageSize(self):
687        """Test that the image size can be set"""
688        retcode = self._DoTestFile('17_pack_image_size.dts')
689        self.assertEqual(0, retcode)
690        self.assertIn('image', control.images)
691        image = control.images['image']
692        self.assertEqual(7, image._size)
693
694    def testPackImageSizeAlign(self):
695        """Test that image size alignemnt works as expected"""
696        retcode = self._DoTestFile('18_pack_image_align.dts')
697        self.assertEqual(0, retcode)
698        self.assertIn('image', control.images)
699        image = control.images['image']
700        self.assertEqual(16, image._size)
701
702    def testPackInvalidImageAlign(self):
703        """Test that invalid image alignment is detected"""
704        with self.assertRaises(ValueError) as e:
705            self._DoTestFile('19_pack_inv_image_align.dts')
706        self.assertIn("Section '/binman': Size 0x7 (7) does not match "
707                      "align-size 0x8 (8)", str(e.exception))
708
709    def testPackAlignPowerOf2(self):
710        """Test that invalid image alignment is detected"""
711        with self.assertRaises(ValueError) as e:
712            self._DoTestFile('20_pack_inv_image_align_power2.dts')
713        self.assertIn("Section '/binman': Alignment size 131 must be a power of "
714                      "two", str(e.exception))
715
716    def testImagePadByte(self):
717        """Test that the image pad byte can be specified"""
718        with open(self.TestFile('bss_data')) as fd:
719            TestFunctional._MakeInputFile('spl/u-boot-spl', fd.read())
720        data = self._DoReadFile('21_image_pad.dts')
721        self.assertEqual(U_BOOT_SPL_DATA + (chr(0xff) * 1) + U_BOOT_DATA, data)
722
723    def testImageName(self):
724        """Test that image files can be named"""
725        retcode = self._DoTestFile('22_image_name.dts')
726        self.assertEqual(0, retcode)
727        image = control.images['image1']
728        fname = tools.GetOutputFilename('test-name')
729        self.assertTrue(os.path.exists(fname))
730
731        image = control.images['image2']
732        fname = tools.GetOutputFilename('test-name.xx')
733        self.assertTrue(os.path.exists(fname))
734
735    def testBlobFilename(self):
736        """Test that generic blobs can be provided by filename"""
737        data = self._DoReadFile('23_blob.dts')
738        self.assertEqual(BLOB_DATA, data)
739
740    def testPackSorted(self):
741        """Test that entries can be sorted"""
742        data = self._DoReadFile('24_sorted.dts')
743        self.assertEqual(chr(0) * 1 + U_BOOT_SPL_DATA + chr(0) * 2 +
744                         U_BOOT_DATA, data)
745
746    def testPackZeroOffset(self):
747        """Test that an entry at offset 0 is not given a new offset"""
748        with self.assertRaises(ValueError) as e:
749            self._DoTestFile('25_pack_zero_size.dts')
750        self.assertIn("Node '/binman/u-boot-spl': Offset 0x0 (0) overlaps "
751                      "with previous entry '/binman/u-boot' ending at 0x4 (4)",
752                      str(e.exception))
753
754    def testPackUbootDtb(self):
755        """Test that a device tree can be added to U-Boot"""
756        data = self._DoReadFile('26_pack_u_boot_dtb.dts')
757        self.assertEqual(U_BOOT_NODTB_DATA + U_BOOT_DTB_DATA, data)
758
759    def testPackX86RomNoSize(self):
760        """Test that the end-at-4gb property requires a size property"""
761        with self.assertRaises(ValueError) as e:
762            self._DoTestFile('27_pack_4gb_no_size.dts')
763        self.assertIn("Section '/binman': Section size must be provided when "
764                      "using end-at-4gb", str(e.exception))
765
766    def testPackX86RomOutside(self):
767        """Test that the end-at-4gb property checks for offset boundaries"""
768        with self.assertRaises(ValueError) as e:
769            self._DoTestFile('28_pack_4gb_outside.dts')
770        self.assertIn("Node '/binman/u-boot': Offset 0x0 (0) is outside "
771                      "the section starting at 0xffffffe0 (4294967264)",
772                      str(e.exception))
773
774    def testPackX86Rom(self):
775        """Test that a basic x86 ROM can be created"""
776        data = self._DoReadFile('29_x86-rom.dts')
777        self.assertEqual(U_BOOT_DATA + chr(0) * 7 + U_BOOT_SPL_DATA +
778                         chr(0) * 2, data)
779
780    def testPackX86RomMeNoDesc(self):
781        """Test that an invalid Intel descriptor entry is detected"""
782        TestFunctional._MakeInputFile('descriptor.bin', '')
783        with self.assertRaises(ValueError) as e:
784            self._DoTestFile('31_x86-rom-me.dts')
785        self.assertIn("Node '/binman/intel-descriptor': Cannot find FD "
786                      "signature", str(e.exception))
787
788    def testPackX86RomBadDesc(self):
789        """Test that the Intel requires a descriptor entry"""
790        with self.assertRaises(ValueError) as e:
791            self._DoTestFile('30_x86-rom-me-no-desc.dts')
792        self.assertIn("Node '/binman/intel-me': No offset set with "
793                      "offset-unset: should another entry provide this correct "
794                      "offset?", str(e.exception))
795
796    def testPackX86RomMe(self):
797        """Test that an x86 ROM with an ME region can be created"""
798        data = self._DoReadFile('31_x86-rom-me.dts')
799        self.assertEqual(ME_DATA, data[0x1000:0x1000 + len(ME_DATA)])
800
801    def testPackVga(self):
802        """Test that an image with a VGA binary can be created"""
803        data = self._DoReadFile('32_intel-vga.dts')
804        self.assertEqual(VGA_DATA, data[:len(VGA_DATA)])
805
806    def testPackStart16(self):
807        """Test that an image with an x86 start16 region can be created"""
808        data = self._DoReadFile('33_x86-start16.dts')
809        self.assertEqual(X86_START16_DATA, data[:len(X86_START16_DATA)])
810
811    def _RunMicrocodeTest(self, dts_fname, nodtb_data, ucode_second=False):
812        """Handle running a test for insertion of microcode
813
814        Args:
815            dts_fname: Name of test .dts file
816            nodtb_data: Data that we expect in the first section
817            ucode_second: True if the microsecond entry is second instead of
818                third
819
820        Returns:
821            Tuple:
822                Contents of first region (U-Boot or SPL)
823                Offset and size components of microcode pointer, as inserted
824                    in the above (two 4-byte words)
825        """
826        data = self._DoReadFile(dts_fname, True)
827
828        # Now check the device tree has no microcode
829        if ucode_second:
830            ucode_content = data[len(nodtb_data):]
831            ucode_pos = len(nodtb_data)
832            dtb_with_ucode = ucode_content[16:]
833            fdt_len = self.GetFdtLen(dtb_with_ucode)
834        else:
835            dtb_with_ucode = data[len(nodtb_data):]
836            fdt_len = self.GetFdtLen(dtb_with_ucode)
837            ucode_content = dtb_with_ucode[fdt_len:]
838            ucode_pos = len(nodtb_data) + fdt_len
839        fname = tools.GetOutputFilename('test.dtb')
840        with open(fname, 'wb') as fd:
841            fd.write(dtb_with_ucode)
842        dtb = fdt.FdtScan(fname)
843        ucode = dtb.GetNode('/microcode')
844        self.assertTrue(ucode)
845        for node in ucode.subnodes:
846            self.assertFalse(node.props.get('data'))
847
848        # Check that the microcode appears immediately after the Fdt
849        # This matches the concatenation of the data properties in
850        # the /microcode/update@xxx nodes in 34_x86_ucode.dts.
851        ucode_data = struct.pack('>4L', 0x12345678, 0x12345679, 0xabcd0000,
852                                 0x78235609)
853        self.assertEqual(ucode_data, ucode_content[:len(ucode_data)])
854
855        # Check that the microcode pointer was inserted. It should match the
856        # expected offset and size
857        pos_and_size = struct.pack('<2L', 0xfffffe00 + ucode_pos,
858                                   len(ucode_data))
859        u_boot = data[:len(nodtb_data)]
860        return u_boot, pos_and_size
861
862    def testPackUbootMicrocode(self):
863        """Test that x86 microcode can be handled correctly
864
865        We expect to see the following in the image, in order:
866            u-boot-nodtb.bin with a microcode pointer inserted at the correct
867                place
868            u-boot.dtb with the microcode removed
869            the microcode
870        """
871        first, pos_and_size = self._RunMicrocodeTest('34_x86_ucode.dts',
872                                                     U_BOOT_NODTB_DATA)
873        self.assertEqual('nodtb with microcode' + pos_and_size +
874                         ' somewhere in here', first)
875
876    def _RunPackUbootSingleMicrocode(self):
877        """Test that x86 microcode can be handled correctly
878
879        We expect to see the following in the image, in order:
880            u-boot-nodtb.bin with a microcode pointer inserted at the correct
881                place
882            u-boot.dtb with the microcode
883            an empty microcode region
884        """
885        # We need the libfdt library to run this test since only that allows
886        # finding the offset of a property. This is required by
887        # Entry_u_boot_dtb_with_ucode.ObtainContents().
888        data = self._DoReadFile('35_x86_single_ucode.dts', True)
889
890        second = data[len(U_BOOT_NODTB_DATA):]
891
892        fdt_len = self.GetFdtLen(second)
893        third = second[fdt_len:]
894        second = second[:fdt_len]
895
896        ucode_data = struct.pack('>2L', 0x12345678, 0x12345679)
897        self.assertIn(ucode_data, second)
898        ucode_pos = second.find(ucode_data) + len(U_BOOT_NODTB_DATA)
899
900        # Check that the microcode pointer was inserted. It should match the
901        # expected offset and size
902        pos_and_size = struct.pack('<2L', 0xfffffe00 + ucode_pos,
903                                   len(ucode_data))
904        first = data[:len(U_BOOT_NODTB_DATA)]
905        self.assertEqual('nodtb with microcode' + pos_and_size +
906                         ' somewhere in here', first)
907
908    def testPackUbootSingleMicrocode(self):
909        """Test that x86 microcode can be handled correctly with fdt_normal.
910        """
911        self._RunPackUbootSingleMicrocode()
912
913    def testUBootImg(self):
914        """Test that u-boot.img can be put in a file"""
915        data = self._DoReadFile('36_u_boot_img.dts')
916        self.assertEqual(U_BOOT_IMG_DATA, data)
917
918    def testNoMicrocode(self):
919        """Test that a missing microcode region is detected"""
920        with self.assertRaises(ValueError) as e:
921            self._DoReadFile('37_x86_no_ucode.dts', True)
922        self.assertIn("Node '/binman/u-boot-dtb-with-ucode': No /microcode "
923                      "node found in ", str(e.exception))
924
925    def testMicrocodeWithoutNode(self):
926        """Test that a missing u-boot-dtb-with-ucode node is detected"""
927        with self.assertRaises(ValueError) as e:
928            self._DoReadFile('38_x86_ucode_missing_node.dts', True)
929        self.assertIn("Node '/binman/u-boot-with-ucode-ptr': Cannot find "
930                "microcode region u-boot-dtb-with-ucode", str(e.exception))
931
932    def testMicrocodeWithoutNode2(self):
933        """Test that a missing u-boot-ucode node is detected"""
934        with self.assertRaises(ValueError) as e:
935            self._DoReadFile('39_x86_ucode_missing_node2.dts', True)
936        self.assertIn("Node '/binman/u-boot-with-ucode-ptr': Cannot find "
937            "microcode region u-boot-ucode", str(e.exception))
938
939    def testMicrocodeWithoutPtrInElf(self):
940        """Test that a U-Boot binary without the microcode symbol is detected"""
941        # ELF file without a '_dt_ucode_base_size' symbol
942        try:
943            with open(self.TestFile('u_boot_no_ucode_ptr')) as fd:
944                TestFunctional._MakeInputFile('u-boot', fd.read())
945
946            with self.assertRaises(ValueError) as e:
947                self._RunPackUbootSingleMicrocode()
948            self.assertIn("Node '/binman/u-boot-with-ucode-ptr': Cannot locate "
949                    "_dt_ucode_base_size symbol in u-boot", str(e.exception))
950
951        finally:
952            # Put the original file back
953            with open(self.TestFile('u_boot_ucode_ptr')) as fd:
954                TestFunctional._MakeInputFile('u-boot', fd.read())
955
956    def testMicrocodeNotInImage(self):
957        """Test that microcode must be placed within the image"""
958        with self.assertRaises(ValueError) as e:
959            self._DoReadFile('40_x86_ucode_not_in_image.dts', True)
960        self.assertIn("Node '/binman/u-boot-with-ucode-ptr': Microcode "
961                "pointer _dt_ucode_base_size at fffffe14 is outside the "
962                "section ranging from 00000000 to 0000002e", str(e.exception))
963
964    def testWithoutMicrocode(self):
965        """Test that we can cope with an image without microcode (e.g. qemu)"""
966        with open(self.TestFile('u_boot_no_ucode_ptr')) as fd:
967            TestFunctional._MakeInputFile('u-boot', fd.read())
968        data, dtb, _, _ = self._DoReadFileDtb('44_x86_optional_ucode.dts', True)
969
970        # Now check the device tree has no microcode
971        self.assertEqual(U_BOOT_NODTB_DATA, data[:len(U_BOOT_NODTB_DATA)])
972        second = data[len(U_BOOT_NODTB_DATA):]
973
974        fdt_len = self.GetFdtLen(second)
975        self.assertEqual(dtb, second[:fdt_len])
976
977        used_len = len(U_BOOT_NODTB_DATA) + fdt_len
978        third = data[used_len:]
979        self.assertEqual(chr(0) * (0x200 - used_len), third)
980
981    def testUnknownPosSize(self):
982        """Test that microcode must be placed within the image"""
983        with self.assertRaises(ValueError) as e:
984            self._DoReadFile('41_unknown_pos_size.dts', True)
985        self.assertIn("Section '/binman': Unable to set offset/size for unknown "
986                "entry 'invalid-entry'", str(e.exception))
987
988    def testPackFsp(self):
989        """Test that an image with a FSP binary can be created"""
990        data = self._DoReadFile('42_intel-fsp.dts')
991        self.assertEqual(FSP_DATA, data[:len(FSP_DATA)])
992
993    def testPackCmc(self):
994        """Test that an image with a CMC binary can be created"""
995        data = self._DoReadFile('43_intel-cmc.dts')
996        self.assertEqual(CMC_DATA, data[:len(CMC_DATA)])
997
998    def testPackVbt(self):
999        """Test that an image with a VBT binary can be created"""
1000        data = self._DoReadFile('46_intel-vbt.dts')
1001        self.assertEqual(VBT_DATA, data[:len(VBT_DATA)])
1002
1003    def testSplBssPad(self):
1004        """Test that we can pad SPL's BSS with zeros"""
1005        # ELF file with a '__bss_size' symbol
1006        with open(self.TestFile('bss_data')) as fd:
1007            TestFunctional._MakeInputFile('spl/u-boot-spl', fd.read())
1008        data = self._DoReadFile('47_spl_bss_pad.dts')
1009        self.assertEqual(U_BOOT_SPL_DATA + (chr(0) * 10) + U_BOOT_DATA, data)
1010
1011        with open(self.TestFile('u_boot_ucode_ptr')) as fd:
1012            TestFunctional._MakeInputFile('spl/u-boot-spl', fd.read())
1013        with self.assertRaises(ValueError) as e:
1014            data = self._DoReadFile('47_spl_bss_pad.dts')
1015        self.assertIn('Expected __bss_size symbol in spl/u-boot-spl',
1016                      str(e.exception))
1017
1018    def testPackStart16Spl(self):
1019        """Test that an image with an x86 start16 SPL region can be created"""
1020        data = self._DoReadFile('48_x86-start16-spl.dts')
1021        self.assertEqual(X86_START16_SPL_DATA, data[:len(X86_START16_SPL_DATA)])
1022
1023    def _PackUbootSplMicrocode(self, dts, ucode_second=False):
1024        """Helper function for microcode tests
1025
1026        We expect to see the following in the image, in order:
1027            u-boot-spl-nodtb.bin with a microcode pointer inserted at the
1028                correct place
1029            u-boot.dtb with the microcode removed
1030            the microcode
1031
1032        Args:
1033            dts: Device tree file to use for test
1034            ucode_second: True if the microsecond entry is second instead of
1035                third
1036        """
1037        # ELF file with a '_dt_ucode_base_size' symbol
1038        with open(self.TestFile('u_boot_ucode_ptr')) as fd:
1039            TestFunctional._MakeInputFile('spl/u-boot-spl', fd.read())
1040        first, pos_and_size = self._RunMicrocodeTest(dts, U_BOOT_SPL_NODTB_DATA,
1041                                                     ucode_second=ucode_second)
1042        self.assertEqual('splnodtb with microc' + pos_and_size +
1043                         'ter somewhere in here', first)
1044
1045    def testPackUbootSplMicrocode(self):
1046        """Test that x86 microcode can be handled correctly in SPL"""
1047        self._PackUbootSplMicrocode('49_x86_ucode_spl.dts')
1048
1049    def testPackUbootSplMicrocodeReorder(self):
1050        """Test that order doesn't matter for microcode entries
1051
1052        This is the same as testPackUbootSplMicrocode but when we process the
1053        u-boot-ucode entry we have not yet seen the u-boot-dtb-with-ucode
1054        entry, so we reply on binman to try later.
1055        """
1056        self._PackUbootSplMicrocode('58_x86_ucode_spl_needs_retry.dts',
1057                                    ucode_second=True)
1058
1059    def testPackMrc(self):
1060        """Test that an image with an MRC binary can be created"""
1061        data = self._DoReadFile('50_intel_mrc.dts')
1062        self.assertEqual(MRC_DATA, data[:len(MRC_DATA)])
1063
1064    def testSplDtb(self):
1065        """Test that an image with spl/u-boot-spl.dtb can be created"""
1066        data = self._DoReadFile('51_u_boot_spl_dtb.dts')
1067        self.assertEqual(U_BOOT_SPL_DTB_DATA, data[:len(U_BOOT_SPL_DTB_DATA)])
1068
1069    def testSplNoDtb(self):
1070        """Test that an image with spl/u-boot-spl-nodtb.bin can be created"""
1071        data = self._DoReadFile('52_u_boot_spl_nodtb.dts')
1072        self.assertEqual(U_BOOT_SPL_NODTB_DATA, data[:len(U_BOOT_SPL_NODTB_DATA)])
1073
1074    def testSymbols(self):
1075        """Test binman can assign symbols embedded in U-Boot"""
1076        elf_fname = self.TestFile('u_boot_binman_syms')
1077        syms = elf.GetSymbols(elf_fname, ['binman', 'image'])
1078        addr = elf.GetSymbolAddress(elf_fname, '__image_copy_start')
1079        self.assertEqual(syms['_binman_u_boot_spl_prop_offset'].address, addr)
1080
1081        with open(self.TestFile('u_boot_binman_syms')) as fd:
1082            TestFunctional._MakeInputFile('spl/u-boot-spl', fd.read())
1083        data = self._DoReadFile('53_symbols.dts')
1084        sym_values = struct.pack('<LQL', 0x24 + 0, 0x24 + 24, 0x24 + 20)
1085        expected = (sym_values + U_BOOT_SPL_DATA[16:] + chr(0xff) +
1086                    U_BOOT_DATA +
1087                    sym_values + U_BOOT_SPL_DATA[16:])
1088        self.assertEqual(expected, data)
1089
1090    def testPackUnitAddress(self):
1091        """Test that we support multiple binaries with the same name"""
1092        data = self._DoReadFile('54_unit_address.dts')
1093        self.assertEqual(U_BOOT_DATA + U_BOOT_DATA, data)
1094
1095    def testSections(self):
1096        """Basic test of sections"""
1097        data = self._DoReadFile('55_sections.dts')
1098        expected = (U_BOOT_DATA + '!' * 12 + U_BOOT_DATA + 'a' * 12 +
1099                    U_BOOT_DATA + '&' * 4)
1100        self.assertEqual(expected, data)
1101
1102    def testMap(self):
1103        """Tests outputting a map of the images"""
1104        _, _, map_data, _ = self._DoReadFileDtb('55_sections.dts', map=True)
1105        self.assertEqual('''ImagePos    Offset      Size  Name
110600000000  00000000  00000028  main-section
110700000000   00000000  00000010  section@0
110800000000    00000000  00000004  u-boot
110900000010   00000010  00000010  section@1
111000000010    00000000  00000004  u-boot
111100000020   00000020  00000004  section@2
111200000020    00000000  00000004  u-boot
1113''', map_data)
1114
1115    def testNamePrefix(self):
1116        """Tests that name prefixes are used"""
1117        _, _, map_data, _ = self._DoReadFileDtb('56_name_prefix.dts', map=True)
1118        self.assertEqual('''ImagePos    Offset      Size  Name
111900000000  00000000  00000028  main-section
112000000000   00000000  00000010  section@0
112100000000    00000000  00000004  ro-u-boot
112200000010   00000010  00000010  section@1
112300000010    00000000  00000004  rw-u-boot
1124''', map_data)
1125
1126    def testUnknownContents(self):
1127        """Test that obtaining the contents works as expected"""
1128        with self.assertRaises(ValueError) as e:
1129            self._DoReadFile('57_unknown_contents.dts', True)
1130        self.assertIn("Section '/binman': Internal error: Could not complete "
1131                "processing of contents: remaining [<_testing.Entry__testing ",
1132                str(e.exception))
1133
1134    def testBadChangeSize(self):
1135        """Test that trying to change the size of an entry fails"""
1136        with self.assertRaises(ValueError) as e:
1137            self._DoReadFile('59_change_size.dts', True)
1138        self.assertIn("Node '/binman/_testing': Cannot update entry size from "
1139                      '2 to 1', str(e.exception))
1140
1141    def testUpdateFdt(self):
1142        """Test that we can update the device tree with offset/size info"""
1143        _, _, _, out_dtb_fname = self._DoReadFileDtb('60_fdt_update.dts',
1144                                                     update_dtb=True)
1145        dtb = fdt.Fdt(out_dtb_fname)
1146        dtb.Scan()
1147        props = self._GetPropTree(dtb, ['offset', 'size', 'image-pos'])
1148        self.assertEqual({
1149            'image-pos': 0,
1150            'offset': 0,
1151            '_testing:offset': 32,
1152            '_testing:size': 1,
1153            '_testing:image-pos': 32,
1154            'section@0/u-boot:offset': 0,
1155            'section@0/u-boot:size': len(U_BOOT_DATA),
1156            'section@0/u-boot:image-pos': 0,
1157            'section@0:offset': 0,
1158            'section@0:size': 16,
1159            'section@0:image-pos': 0,
1160
1161            'section@1/u-boot:offset': 0,
1162            'section@1/u-boot:size': len(U_BOOT_DATA),
1163            'section@1/u-boot:image-pos': 16,
1164            'section@1:offset': 16,
1165            'section@1:size': 16,
1166            'section@1:image-pos': 16,
1167            'size': 40
1168        }, props)
1169
1170    def testUpdateFdtBad(self):
1171        """Test that we detect when ProcessFdt never completes"""
1172        with self.assertRaises(ValueError) as e:
1173            self._DoReadFileDtb('61_fdt_update_bad.dts', update_dtb=True)
1174        self.assertIn('Could not complete processing of Fdt: remaining '
1175                      '[<_testing.Entry__testing', str(e.exception))
1176
1177    def testEntryArgs(self):
1178        """Test passing arguments to entries from the command line"""
1179        entry_args = {
1180            'test-str-arg': 'test1',
1181            'test-int-arg': '456',
1182        }
1183        self._DoReadFileDtb('62_entry_args.dts', entry_args=entry_args)
1184        self.assertIn('image', control.images)
1185        entry = control.images['image'].GetEntries()['_testing']
1186        self.assertEqual('test0', entry.test_str_fdt)
1187        self.assertEqual('test1', entry.test_str_arg)
1188        self.assertEqual(123, entry.test_int_fdt)
1189        self.assertEqual(456, entry.test_int_arg)
1190
1191    def testEntryArgsMissing(self):
1192        """Test missing arguments and properties"""
1193        entry_args = {
1194            'test-int-arg': '456',
1195        }
1196        self._DoReadFileDtb('63_entry_args_missing.dts', entry_args=entry_args)
1197        entry = control.images['image'].GetEntries()['_testing']
1198        self.assertEqual('test0', entry.test_str_fdt)
1199        self.assertEqual(None, entry.test_str_arg)
1200        self.assertEqual(None, entry.test_int_fdt)
1201        self.assertEqual(456, entry.test_int_arg)
1202
1203    def testEntryArgsRequired(self):
1204        """Test missing arguments and properties"""
1205        entry_args = {
1206            'test-int-arg': '456',
1207        }
1208        with self.assertRaises(ValueError) as e:
1209            self._DoReadFileDtb('64_entry_args_required.dts')
1210        self.assertIn("Node '/binman/_testing': Missing required "
1211            'properties/entry args: test-str-arg, test-int-fdt, test-int-arg',
1212            str(e.exception))
1213
1214    def testEntryArgsInvalidFormat(self):
1215        """Test that an invalid entry-argument format is detected"""
1216        args = ['-d', self.TestFile('64_entry_args_required.dts'), '-ano-value']
1217        with self.assertRaises(ValueError) as e:
1218            self._DoBinman(*args)
1219        self.assertIn("Invalid entry arguemnt 'no-value'", str(e.exception))
1220
1221    def testEntryArgsInvalidInteger(self):
1222        """Test that an invalid entry-argument integer is detected"""
1223        entry_args = {
1224            'test-int-arg': 'abc',
1225        }
1226        with self.assertRaises(ValueError) as e:
1227            self._DoReadFileDtb('62_entry_args.dts', entry_args=entry_args)
1228        self.assertIn("Node '/binman/_testing': Cannot convert entry arg "
1229                      "'test-int-arg' (value 'abc') to integer",
1230            str(e.exception))
1231
1232    def testEntryArgsInvalidDatatype(self):
1233        """Test that an invalid entry-argument datatype is detected
1234
1235        This test could be written in entry_test.py except that it needs
1236        access to control.entry_args, which seems more than that module should
1237        be able to see.
1238        """
1239        entry_args = {
1240            'test-bad-datatype-arg': '12',
1241        }
1242        with self.assertRaises(ValueError) as e:
1243            self._DoReadFileDtb('65_entry_args_unknown_datatype.dts',
1244                                entry_args=entry_args)
1245        self.assertIn('GetArg() internal error: Unknown data type ',
1246                      str(e.exception))
1247
1248    def testText(self):
1249        """Test for a text entry type"""
1250        entry_args = {
1251            'test-id': TEXT_DATA,
1252            'test-id2': TEXT_DATA2,
1253            'test-id3': TEXT_DATA3,
1254        }
1255        data, _, _, _ = self._DoReadFileDtb('66_text.dts',
1256                                            entry_args=entry_args)
1257        expected = (TEXT_DATA + chr(0) * (8 - len(TEXT_DATA)) + TEXT_DATA2 +
1258                    TEXT_DATA3 + 'some text')
1259        self.assertEqual(expected, data)
1260
1261    def testEntryDocs(self):
1262        """Test for creation of entry documentation"""
1263        with test_util.capture_sys_output() as (stdout, stderr):
1264            control.WriteEntryDocs(binman.GetEntryModules())
1265        self.assertTrue(len(stdout.getvalue()) > 0)
1266
1267    def testEntryDocsMissing(self):
1268        """Test handling of missing entry documentation"""
1269        with self.assertRaises(ValueError) as e:
1270            with test_util.capture_sys_output() as (stdout, stderr):
1271                control.WriteEntryDocs(binman.GetEntryModules(), 'u_boot')
1272        self.assertIn('Documentation is missing for modules: u_boot',
1273                      str(e.exception))
1274
1275    def testFmap(self):
1276        """Basic test of generation of a flashrom fmap"""
1277        data = self._DoReadFile('67_fmap.dts')
1278        fhdr, fentries = fmap_util.DecodeFmap(data[32:])
1279        expected = U_BOOT_DATA + '!' * 12 + U_BOOT_DATA + 'a' * 12
1280        self.assertEqual(expected, data[:32])
1281        self.assertEqual('__FMAP__', fhdr.signature)
1282        self.assertEqual(1, fhdr.ver_major)
1283        self.assertEqual(0, fhdr.ver_minor)
1284        self.assertEqual(0, fhdr.base)
1285        self.assertEqual(16 + 16 +
1286                         fmap_util.FMAP_HEADER_LEN +
1287                         fmap_util.FMAP_AREA_LEN * 3, fhdr.image_size)
1288        self.assertEqual('FMAP', fhdr.name)
1289        self.assertEqual(3, fhdr.nareas)
1290        for fentry in fentries:
1291            self.assertEqual(0, fentry.flags)
1292
1293        self.assertEqual(0, fentries[0].offset)
1294        self.assertEqual(4, fentries[0].size)
1295        self.assertEqual('RO_U_BOOT', fentries[0].name)
1296
1297        self.assertEqual(16, fentries[1].offset)
1298        self.assertEqual(4, fentries[1].size)
1299        self.assertEqual('RW_U_BOOT', fentries[1].name)
1300
1301        self.assertEqual(32, fentries[2].offset)
1302        self.assertEqual(fmap_util.FMAP_HEADER_LEN +
1303                         fmap_util.FMAP_AREA_LEN * 3, fentries[2].size)
1304        self.assertEqual('FMAP', fentries[2].name)
1305
1306    def testBlobNamedByArg(self):
1307        """Test we can add a blob with the filename coming from an entry arg"""
1308        entry_args = {
1309            'cros-ec-rw-path': 'ecrw.bin',
1310        }
1311        data, _, _, _ = self._DoReadFileDtb('68_blob_named_by_arg.dts',
1312                                            entry_args=entry_args)
1313
1314    def testFill(self):
1315        """Test for an fill entry type"""
1316        data = self._DoReadFile('69_fill.dts')
1317        expected = 8 * chr(0xff) + 8 * chr(0)
1318        self.assertEqual(expected, data)
1319
1320    def testFillNoSize(self):
1321        """Test for an fill entry type with no size"""
1322        with self.assertRaises(ValueError) as e:
1323            self._DoReadFile('70_fill_no_size.dts')
1324        self.assertIn("'fill' entry must have a size property",
1325                      str(e.exception))
1326
1327    def _HandleGbbCommand(self, pipe_list):
1328        """Fake calls to the futility utility"""
1329        if pipe_list[0][0] == 'futility':
1330            fname = pipe_list[0][-1]
1331            # Append our GBB data to the file, which will happen every time the
1332            # futility command is called.
1333            with open(fname, 'a') as fd:
1334                fd.write(GBB_DATA)
1335            return command.CommandResult()
1336
1337    def testGbb(self):
1338        """Test for the Chromium OS Google Binary Block"""
1339        command.test_result = self._HandleGbbCommand
1340        entry_args = {
1341            'keydir': 'devkeys',
1342            'bmpblk': 'bmpblk.bin',
1343        }
1344        data, _, _, _ = self._DoReadFileDtb('71_gbb.dts', entry_args=entry_args)
1345
1346        # Since futility
1347        expected = GBB_DATA + GBB_DATA + 8 * chr(0) + (0x2180 - 16) * chr(0)
1348        self.assertEqual(expected, data)
1349
1350    def testGbbTooSmall(self):
1351        """Test for the Chromium OS Google Binary Block being large enough"""
1352        with self.assertRaises(ValueError) as e:
1353            self._DoReadFileDtb('72_gbb_too_small.dts')
1354        self.assertIn("Node '/binman/gbb': GBB is too small",
1355                      str(e.exception))
1356
1357    def testGbbNoSize(self):
1358        """Test for the Chromium OS Google Binary Block having a size"""
1359        with self.assertRaises(ValueError) as e:
1360            self._DoReadFileDtb('73_gbb_no_size.dts')
1361        self.assertIn("Node '/binman/gbb': GBB must have a fixed size",
1362                      str(e.exception))
1363
1364    def _HandleVblockCommand(self, pipe_list):
1365        """Fake calls to the futility utility"""
1366        if pipe_list[0][0] == 'futility':
1367            fname = pipe_list[0][3]
1368            with open(fname, 'wb') as fd:
1369                fd.write(VBLOCK_DATA)
1370            return command.CommandResult()
1371
1372    def testVblock(self):
1373        """Test for the Chromium OS Verified Boot Block"""
1374        command.test_result = self._HandleVblockCommand
1375        entry_args = {
1376            'keydir': 'devkeys',
1377        }
1378        data, _, _, _ = self._DoReadFileDtb('74_vblock.dts',
1379                                            entry_args=entry_args)
1380        expected = U_BOOT_DATA + VBLOCK_DATA + U_BOOT_DTB_DATA
1381        self.assertEqual(expected, data)
1382
1383    def testVblockNoContent(self):
1384        """Test we detect a vblock which has no content to sign"""
1385        with self.assertRaises(ValueError) as e:
1386            self._DoReadFile('75_vblock_no_content.dts')
1387        self.assertIn("Node '/binman/vblock': Vblock must have a 'content' "
1388                      'property', str(e.exception))
1389
1390    def testVblockBadPhandle(self):
1391        """Test that we detect a vblock with an invalid phandle in contents"""
1392        with self.assertRaises(ValueError) as e:
1393            self._DoReadFile('76_vblock_bad_phandle.dts')
1394        self.assertIn("Node '/binman/vblock': Cannot find node for phandle "
1395                      '1000', str(e.exception))
1396
1397    def testVblockBadEntry(self):
1398        """Test that we detect an entry that points to a non-entry"""
1399        with self.assertRaises(ValueError) as e:
1400            self._DoReadFile('77_vblock_bad_entry.dts')
1401        self.assertIn("Node '/binman/vblock': Cannot find entry for node "
1402                      "'other'", str(e.exception))
1403
1404    def testTpl(self):
1405        """Test that an image with TPL and ots device tree can be created"""
1406        # ELF file with a '__bss_size' symbol
1407        with open(self.TestFile('bss_data')) as fd:
1408            TestFunctional._MakeInputFile('tpl/u-boot-tpl', fd.read())
1409        data = self._DoReadFile('78_u_boot_tpl.dts')
1410        self.assertEqual(U_BOOT_TPL_DATA + U_BOOT_TPL_DTB_DATA, data)
1411
1412    def testUsesPos(self):
1413        """Test that the 'pos' property cannot be used anymore"""
1414        with self.assertRaises(ValueError) as e:
1415           data = self._DoReadFile('79_uses_pos.dts')
1416        self.assertIn("Node '/binman/u-boot': Please use 'offset' instead of "
1417                      "'pos'", str(e.exception))
1418
1419    def testFillZero(self):
1420        """Test for an fill entry type with a size of 0"""
1421        data = self._DoReadFile('80_fill_empty.dts')
1422        self.assertEqual(chr(0) * 16, data)
1423
1424    def testTextMissing(self):
1425        """Test for a text entry type where there is no text"""
1426        with self.assertRaises(ValueError) as e:
1427            self._DoReadFileDtb('66_text.dts',)
1428        self.assertIn("Node '/binman/text': No value provided for text label "
1429                      "'test-id'", str(e.exception))
1430
1431    def testPackStart16Tpl(self):
1432        """Test that an image with an x86 start16 TPL region can be created"""
1433        data = self._DoReadFile('81_x86-start16-tpl.dts')
1434        self.assertEqual(X86_START16_TPL_DATA, data[:len(X86_START16_TPL_DATA)])
1435
1436    def testSelectImage(self):
1437        """Test that we can select which images to build"""
1438        with test_util.capture_sys_output() as (stdout, stderr):
1439            retcode = self._DoTestFile('06_dual_image.dts', images=['image2'])
1440        self.assertEqual(0, retcode)
1441        self.assertIn('Skipping images: image1', stdout.getvalue())
1442
1443        self.assertFalse(os.path.exists(tools.GetOutputFilename('image1.bin')))
1444        self.assertTrue(os.path.exists(tools.GetOutputFilename('image2.bin')))
1445
1446    def testUpdateFdtAll(self):
1447        """Test that all device trees are updated with offset/size info"""
1448        data, _, _, _ = self._DoReadFileDtb('82_fdt_update_all.dts',
1449                                            use_real_dtb=True, update_dtb=True)
1450
1451        base_expected = {
1452            'section:image-pos': 0,
1453            'u-boot-tpl-dtb:size': 513,
1454            'u-boot-spl-dtb:size': 513,
1455            'u-boot-spl-dtb:offset': 493,
1456            'image-pos': 0,
1457            'section/u-boot-dtb:image-pos': 0,
1458            'u-boot-spl-dtb:image-pos': 493,
1459            'section/u-boot-dtb:size': 493,
1460            'u-boot-tpl-dtb:image-pos': 1006,
1461            'section/u-boot-dtb:offset': 0,
1462            'section:size': 493,
1463            'offset': 0,
1464            'section:offset': 0,
1465            'u-boot-tpl-dtb:offset': 1006,
1466            'size': 1519
1467        }
1468
1469        # We expect three device-tree files in the output, one after the other.
1470        # Read them in sequence. We look for an 'spl' property in the SPL tree,
1471        # and 'tpl' in the TPL tree, to make sure they are distinct from the
1472        # main U-Boot tree. All three should have the same postions and offset.
1473        start = 0
1474        for item in ['', 'spl', 'tpl']:
1475            dtb = fdt.Fdt.FromData(data[start:])
1476            dtb.Scan()
1477            props = self._GetPropTree(dtb, ['offset', 'size', 'image-pos',
1478                                            'spl', 'tpl'])
1479            expected = dict(base_expected)
1480            if item:
1481                expected[item] = 0
1482            self.assertEqual(expected, props)
1483            start += dtb._fdt_obj.totalsize()
1484
1485    def testUpdateFdtOutput(self):
1486        """Test that output DTB files are updated"""
1487        try:
1488            data, dtb_data, _, _ = self._DoReadFileDtb('82_fdt_update_all.dts',
1489                    use_real_dtb=True, update_dtb=True, reset_dtbs=False)
1490
1491            # Unfortunately, compiling a source file always results in a file
1492            # called source.dtb (see fdt_util.EnsureCompiled()). The test
1493            # source file (e.g. test/75_fdt_update_all.dts) thus does not enter
1494            # binman as a file called u-boot.dtb. To fix this, copy the file
1495            # over to the expected place.
1496            #tools.WriteFile(os.path.join(self._indir, 'u-boot.dtb'),
1497                    #tools.ReadFile(tools.GetOutputFilename('source.dtb')))
1498            start = 0
1499            for fname in ['u-boot.dtb.out', 'spl/u-boot-spl.dtb.out',
1500                          'tpl/u-boot-tpl.dtb.out']:
1501                dtb = fdt.Fdt.FromData(data[start:])
1502                size = dtb._fdt_obj.totalsize()
1503                pathname = tools.GetOutputFilename(os.path.split(fname)[1])
1504                outdata = tools.ReadFile(pathname)
1505                name = os.path.split(fname)[0]
1506
1507                if name:
1508                    orig_indata = self._GetDtbContentsForSplTpl(dtb_data, name)
1509                else:
1510                    orig_indata = dtb_data
1511                self.assertNotEqual(outdata, orig_indata,
1512                        "Expected output file '%s' be updated" % pathname)
1513                self.assertEqual(outdata, data[start:start + size],
1514                        "Expected output file '%s' to match output image" %
1515                        pathname)
1516                start += size
1517        finally:
1518            self._ResetDtbs()
1519
1520    def _decompress(self, data):
1521        out = os.path.join(self._indir, 'lz4.tmp')
1522        with open(out, 'wb') as fd:
1523            fd.write(data)
1524        return tools.Run('lz4', '-dc', out)
1525        '''
1526        try:
1527            orig = lz4.frame.decompress(data)
1528        except AttributeError:
1529            orig = lz4.decompress(data)
1530        '''
1531
1532    def testCompress(self):
1533        """Test compression of blobs"""
1534        data, _, _, out_dtb_fname = self._DoReadFileDtb('83_compress.dts',
1535                                            use_real_dtb=True, update_dtb=True)
1536        dtb = fdt.Fdt(out_dtb_fname)
1537        dtb.Scan()
1538        props = self._GetPropTree(dtb, ['size', 'uncomp-size'])
1539        orig = self._decompress(data)
1540        self.assertEquals(COMPRESS_DATA, orig)
1541        expected = {
1542            'blob:uncomp-size': len(COMPRESS_DATA),
1543            'blob:size': len(data),
1544            'size': len(data),
1545            }
1546        self.assertEqual(expected, props)
1547
1548    def testFiles(self):
1549        """Test bringing in multiple files"""
1550        data = self._DoReadFile('84_files.dts')
1551        self.assertEqual(FILES_DATA, data)
1552
1553    def testFilesCompress(self):
1554        """Test bringing in multiple files and compressing them"""
1555        data = self._DoReadFile('85_files_compress.dts')
1556
1557        image = control.images['image']
1558        entries = image.GetEntries()
1559        files = entries['files']
1560        entries = files._section._entries
1561
1562        orig = ''
1563        for i in range(1, 3):
1564            key = '%d.dat' % i
1565            start = entries[key].image_pos
1566            len = entries[key].size
1567            chunk = data[start:start + len]
1568            orig += self._decompress(chunk)
1569
1570        self.assertEqual(FILES_DATA, orig)
1571
1572    def testFilesMissing(self):
1573        """Test missing files"""
1574        with self.assertRaises(ValueError) as e:
1575            data = self._DoReadFile('86_files_none.dts')
1576        self.assertIn("Node '/binman/files': Pattern \'files/*.none\' matched "
1577                      'no files', str(e.exception))
1578
1579    def testFilesNoPattern(self):
1580        """Test missing files"""
1581        with self.assertRaises(ValueError) as e:
1582            data = self._DoReadFile('87_files_no_pattern.dts')
1583        self.assertIn("Node '/binman/files': Missing 'pattern' property",
1584                      str(e.exception))
1585
1586    def testExpandSize(self):
1587        """Test an expanding entry"""
1588        data, _, map_data, _ = self._DoReadFileDtb('88_expand_size.dts',
1589                                                   map=True)
1590        expect = ('a' * 8 + U_BOOT_DATA +
1591                  MRC_DATA + 'b' * 1 + U_BOOT_DATA +
1592                  'c' * 8 + U_BOOT_DATA +
1593                  'd' * 8)
1594        self.assertEqual(expect, data)
1595        self.assertEqual('''ImagePos    Offset      Size  Name
159600000000  00000000  00000028  main-section
159700000000   00000000  00000008  fill
159800000008   00000008  00000004  u-boot
15990000000c   0000000c  00000004  section
16000000000c    00000000  00000003  intel-mrc
160100000010   00000010  00000004  u-boot2
160200000014   00000014  0000000c  section2
160300000014    00000000  00000008  fill
16040000001c    00000008  00000004  u-boot
160500000020   00000020  00000008  fill2
1606''', map_data)
1607
1608    def testExpandSizeBad(self):
1609        """Test an expanding entry which fails to provide contents"""
1610        with test_util.capture_sys_output() as (stdout, stderr):
1611            with self.assertRaises(ValueError) as e:
1612                self._DoReadFileDtb('89_expand_size_bad.dts', map=True)
1613        self.assertIn("Node '/binman/_testing': Cannot obtain contents when "
1614                      'expanding entry', str(e.exception))
1615
1616    def testHash(self):
1617        """Test hashing of the contents of an entry"""
1618        _, _, _, out_dtb_fname = self._DoReadFileDtb('90_hash.dts',
1619                use_real_dtb=True, update_dtb=True)
1620        dtb = fdt.Fdt(out_dtb_fname)
1621        dtb.Scan()
1622        hash_node = dtb.GetNode('/binman/u-boot/hash').props['value']
1623        m = hashlib.sha256()
1624        m.update(U_BOOT_DATA)
1625        self.assertEqual(m.digest(), ''.join(hash_node.value))
1626
1627    def testHashNoAlgo(self):
1628        with self.assertRaises(ValueError) as e:
1629            self._DoReadFileDtb('91_hash_no_algo.dts', update_dtb=True)
1630        self.assertIn("Node \'/binman/u-boot\': Missing \'algo\' property for "
1631                      'hash node', str(e.exception))
1632
1633    def testHashBadAlgo(self):
1634        with self.assertRaises(ValueError) as e:
1635            self._DoReadFileDtb('92_hash_bad_algo.dts', update_dtb=True)
1636        self.assertIn("Node '/binman/u-boot': Unknown hash algorithm",
1637                      str(e.exception))
1638
1639    def testHashSection(self):
1640        """Test hashing of the contents of an entry"""
1641        _, _, _, out_dtb_fname = self._DoReadFileDtb('99_hash_section.dts',
1642                use_real_dtb=True, update_dtb=True)
1643        dtb = fdt.Fdt(out_dtb_fname)
1644        dtb.Scan()
1645        hash_node = dtb.GetNode('/binman/section/hash').props['value']
1646        m = hashlib.sha256()
1647        m.update(U_BOOT_DATA)
1648        m.update(16 * 'a')
1649        self.assertEqual(m.digest(), ''.join(hash_node.value))
1650
1651    def testPackUBootTplMicrocode(self):
1652        """Test that x86 microcode can be handled correctly in TPL
1653
1654        We expect to see the following in the image, in order:
1655            u-boot-tpl-nodtb.bin with a microcode pointer inserted at the correct
1656                place
1657            u-boot-tpl.dtb with the microcode removed
1658            the microcode
1659        """
1660        with open(self.TestFile('u_boot_ucode_ptr')) as fd:
1661            TestFunctional._MakeInputFile('tpl/u-boot-tpl', fd.read())
1662        first, pos_and_size = self._RunMicrocodeTest('93_x86_tpl_ucode.dts',
1663                                                     U_BOOT_TPL_NODTB_DATA)
1664        self.assertEqual('tplnodtb with microc' + pos_and_size +
1665                         'ter somewhere in here', first)
1666
1667    def testFmapX86(self):
1668        """Basic test of generation of a flashrom fmap"""
1669        data = self._DoReadFile('94_fmap_x86.dts')
1670        fhdr, fentries = fmap_util.DecodeFmap(data[32:])
1671        expected = U_BOOT_DATA + MRC_DATA + 'a' * (32 - 7)
1672        self.assertEqual(expected, data[:32])
1673        fhdr, fentries = fmap_util.DecodeFmap(data[32:])
1674
1675        self.assertEqual(0x100, fhdr.image_size)
1676
1677        self.assertEqual(0, fentries[0].offset)
1678        self.assertEqual(4, fentries[0].size)
1679        self.assertEqual('U_BOOT', fentries[0].name)
1680
1681        self.assertEqual(4, fentries[1].offset)
1682        self.assertEqual(3, fentries[1].size)
1683        self.assertEqual('INTEL_MRC', fentries[1].name)
1684
1685        self.assertEqual(32, fentries[2].offset)
1686        self.assertEqual(fmap_util.FMAP_HEADER_LEN +
1687                         fmap_util.FMAP_AREA_LEN * 3, fentries[2].size)
1688        self.assertEqual('FMAP', fentries[2].name)
1689
1690    def testFmapX86Section(self):
1691        """Basic test of generation of a flashrom fmap"""
1692        data = self._DoReadFile('95_fmap_x86_section.dts')
1693        expected = U_BOOT_DATA + MRC_DATA + 'b' * (32 - 7)
1694        self.assertEqual(expected, data[:32])
1695        fhdr, fentries = fmap_util.DecodeFmap(data[36:])
1696
1697        self.assertEqual(0x100, fhdr.image_size)
1698
1699        self.assertEqual(0, fentries[0].offset)
1700        self.assertEqual(4, fentries[0].size)
1701        self.assertEqual('U_BOOT', fentries[0].name)
1702
1703        self.assertEqual(4, fentries[1].offset)
1704        self.assertEqual(3, fentries[1].size)
1705        self.assertEqual('INTEL_MRC', fentries[1].name)
1706
1707        self.assertEqual(36, fentries[2].offset)
1708        self.assertEqual(fmap_util.FMAP_HEADER_LEN +
1709                         fmap_util.FMAP_AREA_LEN * 3, fentries[2].size)
1710        self.assertEqual('FMAP', fentries[2].name)
1711
1712    def testElf(self):
1713        """Basic test of ELF entries"""
1714        with open(self.TestFile('bss_data')) as fd:
1715            TestFunctional._MakeInputFile('spl/u-boot-spl', fd.read())
1716        with open(self.TestFile('bss_data')) as fd:
1717            TestFunctional._MakeInputFile('-boot', fd.read())
1718        data = self._DoReadFile('96_elf.dts')
1719
1720    def testElfStripg(self):
1721        """Basic test of ELF entries"""
1722        with open(self.TestFile('bss_data')) as fd:
1723            TestFunctional._MakeInputFile('spl/u-boot-spl', fd.read())
1724        with open(self.TestFile('bss_data')) as fd:
1725            TestFunctional._MakeInputFile('-boot', fd.read())
1726        data = self._DoReadFile('97_elf_strip.dts')
1727
1728    def testPackOverlapMap(self):
1729        """Test that overlapping regions are detected"""
1730        with test_util.capture_sys_output() as (stdout, stderr):
1731            with self.assertRaises(ValueError) as e:
1732                self._DoTestFile('14_pack_overlap.dts', map=True)
1733        map_fname = tools.GetOutputFilename('image.map')
1734        self.assertEqual("Wrote map file '%s' to show errors\n" % map_fname,
1735                         stdout.getvalue())
1736
1737        # We should not get an inmage, but there should be a map file
1738        self.assertFalse(os.path.exists(tools.GetOutputFilename('image.bin')))
1739        self.assertTrue(os.path.exists(map_fname))
1740        map_data = tools.ReadFile(map_fname)
1741        self.assertEqual('''ImagePos    Offset      Size  Name
1742<none>    00000000  00000007  main-section
1743<none>     00000000  00000004  u-boot
1744<none>     00000003  00000004  u-boot-align
1745''', map_data)
1746
1747
1748if __name__ == "__main__":
1749    unittest.main()
1750