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