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