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