binman: Allow device-tree entries to be compressed
[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         try:
1224             state.SetAllowEntryExpansion(False)
1225             with self.assertRaises(ValueError) as e:
1226                 self._DoReadFile('059_change_size.dts', True)
1227             self.assertIn("Node '/binman/_testing': Cannot update entry size from 1 to 2",
1228                           str(e.exception))
1229         finally:
1230             state.SetAllowEntryExpansion(True)
1231
1232     def testUpdateFdt(self):
1233         """Test that we can update the device tree with offset/size info"""
1234         _, _, _, out_dtb_fname = self._DoReadFileDtb('060_fdt_update.dts',
1235                                                      update_dtb=True)
1236         dtb = fdt.Fdt(out_dtb_fname)
1237         dtb.Scan()
1238         props = self._GetPropTree(dtb, ['offset', 'size', 'image-pos'])
1239         self.assertEqual({
1240             'image-pos': 0,
1241             'offset': 0,
1242             '_testing:offset': 32,
1243             '_testing:size': 1,
1244             '_testing:image-pos': 32,
1245             'section@0/u-boot:offset': 0,
1246             'section@0/u-boot:size': len(U_BOOT_DATA),
1247             'section@0/u-boot:image-pos': 0,
1248             'section@0:offset': 0,
1249             'section@0:size': 16,
1250             'section@0:image-pos': 0,
1251
1252             'section@1/u-boot:offset': 0,
1253             'section@1/u-boot:size': len(U_BOOT_DATA),
1254             'section@1/u-boot:image-pos': 16,
1255             'section@1:offset': 16,
1256             'section@1:size': 16,
1257             'section@1:image-pos': 16,
1258             'size': 40
1259         }, props)
1260
1261     def testUpdateFdtBad(self):
1262         """Test that we detect when ProcessFdt never completes"""
1263         with self.assertRaises(ValueError) as e:
1264             self._DoReadFileDtb('061_fdt_update_bad.dts', update_dtb=True)
1265         self.assertIn('Could not complete processing of Fdt: remaining '
1266                       '[<_testing.Entry__testing', str(e.exception))
1267
1268     def testEntryArgs(self):
1269         """Test passing arguments to entries from the command line"""
1270         entry_args = {
1271             'test-str-arg': 'test1',
1272             'test-int-arg': '456',
1273         }
1274         self._DoReadFileDtb('062_entry_args.dts', entry_args=entry_args)
1275         self.assertIn('image', control.images)
1276         entry = control.images['image'].GetEntries()['_testing']
1277         self.assertEqual('test0', entry.test_str_fdt)
1278         self.assertEqual('test1', entry.test_str_arg)
1279         self.assertEqual(123, entry.test_int_fdt)
1280         self.assertEqual(456, entry.test_int_arg)
1281
1282     def testEntryArgsMissing(self):
1283         """Test missing arguments and properties"""
1284         entry_args = {
1285             'test-int-arg': '456',
1286         }
1287         self._DoReadFileDtb('063_entry_args_missing.dts', entry_args=entry_args)
1288         entry = control.images['image'].GetEntries()['_testing']
1289         self.assertEqual('test0', entry.test_str_fdt)
1290         self.assertEqual(None, entry.test_str_arg)
1291         self.assertEqual(None, entry.test_int_fdt)
1292         self.assertEqual(456, entry.test_int_arg)
1293
1294     def testEntryArgsRequired(self):
1295         """Test missing arguments and properties"""
1296         entry_args = {
1297             'test-int-arg': '456',
1298         }
1299         with self.assertRaises(ValueError) as e:
1300             self._DoReadFileDtb('064_entry_args_required.dts')
1301         self.assertIn("Node '/binman/_testing': Missing required "
1302             'properties/entry args: test-str-arg, test-int-fdt, test-int-arg',
1303             str(e.exception))
1304
1305     def testEntryArgsInvalidFormat(self):
1306         """Test that an invalid entry-argument format is detected"""
1307         args = ['build', '-d', self.TestFile('064_entry_args_required.dts'),
1308                 '-ano-value']
1309         with self.assertRaises(ValueError) as e:
1310             self._DoBinman(*args)
1311         self.assertIn("Invalid entry arguemnt 'no-value'", str(e.exception))
1312
1313     def testEntryArgsInvalidInteger(self):
1314         """Test that an invalid entry-argument integer is detected"""
1315         entry_args = {
1316             'test-int-arg': 'abc',
1317         }
1318         with self.assertRaises(ValueError) as e:
1319             self._DoReadFileDtb('062_entry_args.dts', entry_args=entry_args)
1320         self.assertIn("Node '/binman/_testing': Cannot convert entry arg "
1321                       "'test-int-arg' (value 'abc') to integer",
1322             str(e.exception))
1323
1324     def testEntryArgsInvalidDatatype(self):
1325         """Test that an invalid entry-argument datatype is detected
1326
1327         This test could be written in entry_test.py except that it needs
1328         access to control.entry_args, which seems more than that module should
1329         be able to see.
1330         """
1331         entry_args = {
1332             'test-bad-datatype-arg': '12',
1333         }
1334         with self.assertRaises(ValueError) as e:
1335             self._DoReadFileDtb('065_entry_args_unknown_datatype.dts',
1336                                 entry_args=entry_args)
1337         self.assertIn('GetArg() internal error: Unknown data type ',
1338                       str(e.exception))
1339
1340     def testText(self):
1341         """Test for a text entry type"""
1342         entry_args = {
1343             'test-id': TEXT_DATA,
1344             'test-id2': TEXT_DATA2,
1345             'test-id3': TEXT_DATA3,
1346         }
1347         data, _, _, _ = self._DoReadFileDtb('066_text.dts',
1348                                             entry_args=entry_args)
1349         expected = (tools.ToBytes(TEXT_DATA) +
1350                     tools.GetBytes(0, 8 - len(TEXT_DATA)) +
1351                     tools.ToBytes(TEXT_DATA2) + tools.ToBytes(TEXT_DATA3) +
1352                     b'some text' + b'more text')
1353         self.assertEqual(expected, data)
1354
1355     def testEntryDocs(self):
1356         """Test for creation of entry documentation"""
1357         with test_util.capture_sys_output() as (stdout, stderr):
1358             control.WriteEntryDocs(binman.GetEntryModules())
1359         self.assertTrue(len(stdout.getvalue()) > 0)
1360
1361     def testEntryDocsMissing(self):
1362         """Test handling of missing entry documentation"""
1363         with self.assertRaises(ValueError) as e:
1364             with test_util.capture_sys_output() as (stdout, stderr):
1365                 control.WriteEntryDocs(binman.GetEntryModules(), 'u_boot')
1366         self.assertIn('Documentation is missing for modules: u_boot',
1367                       str(e.exception))
1368
1369     def testFmap(self):
1370         """Basic test of generation of a flashrom fmap"""
1371         data = self._DoReadFile('067_fmap.dts')
1372         fhdr, fentries = fmap_util.DecodeFmap(data[32:])
1373         expected = (U_BOOT_DATA + tools.GetBytes(ord('!'), 12) +
1374                     U_BOOT_DATA + tools.GetBytes(ord('a'), 12))
1375         self.assertEqual(expected, data[:32])
1376         self.assertEqual(b'__FMAP__', fhdr.signature)
1377         self.assertEqual(1, fhdr.ver_major)
1378         self.assertEqual(0, fhdr.ver_minor)
1379         self.assertEqual(0, fhdr.base)
1380         self.assertEqual(16 + 16 +
1381                          fmap_util.FMAP_HEADER_LEN +
1382                          fmap_util.FMAP_AREA_LEN * 3, fhdr.image_size)
1383         self.assertEqual(b'FMAP', fhdr.name)
1384         self.assertEqual(3, fhdr.nareas)
1385         for fentry in fentries:
1386             self.assertEqual(0, fentry.flags)
1387
1388         self.assertEqual(0, fentries[0].offset)
1389         self.assertEqual(4, fentries[0].size)
1390         self.assertEqual(b'RO_U_BOOT', fentries[0].name)
1391
1392         self.assertEqual(16, fentries[1].offset)
1393         self.assertEqual(4, fentries[1].size)
1394         self.assertEqual(b'RW_U_BOOT', fentries[1].name)
1395
1396         self.assertEqual(32, fentries[2].offset)
1397         self.assertEqual(fmap_util.FMAP_HEADER_LEN +
1398                          fmap_util.FMAP_AREA_LEN * 3, fentries[2].size)
1399         self.assertEqual(b'FMAP', fentries[2].name)
1400
1401     def testBlobNamedByArg(self):
1402         """Test we can add a blob with the filename coming from an entry arg"""
1403         entry_args = {
1404             'cros-ec-rw-path': 'ecrw.bin',
1405         }
1406         data, _, _, _ = self._DoReadFileDtb('068_blob_named_by_arg.dts',
1407                                             entry_args=entry_args)
1408
1409     def testFill(self):
1410         """Test for an fill entry type"""
1411         data = self._DoReadFile('069_fill.dts')
1412         expected = tools.GetBytes(0xff, 8) + tools.GetBytes(0, 8)
1413         self.assertEqual(expected, data)
1414
1415     def testFillNoSize(self):
1416         """Test for an fill entry type with no size"""
1417         with self.assertRaises(ValueError) as e:
1418             self._DoReadFile('070_fill_no_size.dts')
1419         self.assertIn("'fill' entry must have a size property",
1420                       str(e.exception))
1421
1422     def _HandleGbbCommand(self, pipe_list):
1423         """Fake calls to the futility utility"""
1424         if pipe_list[0][0] == 'futility':
1425             fname = pipe_list[0][-1]
1426             # Append our GBB data to the file, which will happen every time the
1427             # futility command is called.
1428             with open(fname, 'ab') as fd:
1429                 fd.write(GBB_DATA)
1430             return command.CommandResult()
1431
1432     def testGbb(self):
1433         """Test for the Chromium OS Google Binary Block"""
1434         command.test_result = self._HandleGbbCommand
1435         entry_args = {
1436             'keydir': 'devkeys',
1437             'bmpblk': 'bmpblk.bin',
1438         }
1439         data, _, _, _ = self._DoReadFileDtb('071_gbb.dts', entry_args=entry_args)
1440
1441         # Since futility
1442         expected = (GBB_DATA + GBB_DATA + tools.GetBytes(0, 8) +
1443                     tools.GetBytes(0, 0x2180 - 16))
1444         self.assertEqual(expected, data)
1445
1446     def testGbbTooSmall(self):
1447         """Test for the Chromium OS Google Binary Block being large enough"""
1448         with self.assertRaises(ValueError) as e:
1449             self._DoReadFileDtb('072_gbb_too_small.dts')
1450         self.assertIn("Node '/binman/gbb': GBB is too small",
1451                       str(e.exception))
1452
1453     def testGbbNoSize(self):
1454         """Test for the Chromium OS Google Binary Block having a size"""
1455         with self.assertRaises(ValueError) as e:
1456             self._DoReadFileDtb('073_gbb_no_size.dts')
1457         self.assertIn("Node '/binman/gbb': GBB must have a fixed size",
1458                       str(e.exception))
1459
1460     def _HandleVblockCommand(self, pipe_list):
1461         """Fake calls to the futility utility"""
1462         if pipe_list[0][0] == 'futility':
1463             fname = pipe_list[0][3]
1464             with open(fname, 'wb') as fd:
1465                 fd.write(VBLOCK_DATA)
1466             return command.CommandResult()
1467
1468     def testVblock(self):
1469         """Test for the Chromium OS Verified Boot Block"""
1470         command.test_result = self._HandleVblockCommand
1471         entry_args = {
1472             'keydir': 'devkeys',
1473         }
1474         data, _, _, _ = self._DoReadFileDtb('074_vblock.dts',
1475                                             entry_args=entry_args)
1476         expected = U_BOOT_DATA + VBLOCK_DATA + U_BOOT_DTB_DATA
1477         self.assertEqual(expected, data)
1478
1479     def testVblockNoContent(self):
1480         """Test we detect a vblock which has no content to sign"""
1481         with self.assertRaises(ValueError) as e:
1482             self._DoReadFile('075_vblock_no_content.dts')
1483         self.assertIn("Node '/binman/vblock': Vblock must have a 'content' "
1484                       'property', str(e.exception))
1485
1486     def testVblockBadPhandle(self):
1487         """Test that we detect a vblock with an invalid phandle in contents"""
1488         with self.assertRaises(ValueError) as e:
1489             self._DoReadFile('076_vblock_bad_phandle.dts')
1490         self.assertIn("Node '/binman/vblock': Cannot find node for phandle "
1491                       '1000', str(e.exception))
1492
1493     def testVblockBadEntry(self):
1494         """Test that we detect an entry that points to a non-entry"""
1495         with self.assertRaises(ValueError) as e:
1496             self._DoReadFile('077_vblock_bad_entry.dts')
1497         self.assertIn("Node '/binman/vblock': Cannot find entry for node "
1498                       "'other'", str(e.exception))
1499
1500     def testTpl(self):
1501         """Test that an image with TPL and ots device tree can be created"""
1502         # ELF file with a '__bss_size' symbol
1503         with open(self.TestFile('bss_data'), 'rb') as fd:
1504             TestFunctional._MakeInputFile('tpl/u-boot-tpl', fd.read())
1505         data = self._DoReadFile('078_u_boot_tpl.dts')
1506         self.assertEqual(U_BOOT_TPL_DATA + U_BOOT_TPL_DTB_DATA, data)
1507
1508     def testUsesPos(self):
1509         """Test that the 'pos' property cannot be used anymore"""
1510         with self.assertRaises(ValueError) as e:
1511            data = self._DoReadFile('079_uses_pos.dts')
1512         self.assertIn("Node '/binman/u-boot': Please use 'offset' instead of "
1513                       "'pos'", str(e.exception))
1514
1515     def testFillZero(self):
1516         """Test for an fill entry type with a size of 0"""
1517         data = self._DoReadFile('080_fill_empty.dts')
1518         self.assertEqual(tools.GetBytes(0, 16), data)
1519
1520     def testTextMissing(self):
1521         """Test for a text entry type where there is no text"""
1522         with self.assertRaises(ValueError) as e:
1523             self._DoReadFileDtb('066_text.dts',)
1524         self.assertIn("Node '/binman/text': No value provided for text label "
1525                       "'test-id'", str(e.exception))
1526
1527     def testPackStart16Tpl(self):
1528         """Test that an image with an x86 start16 TPL region can be created"""
1529         data = self._DoReadFile('081_x86-start16-tpl.dts')
1530         self.assertEqual(X86_START16_TPL_DATA, data[:len(X86_START16_TPL_DATA)])
1531
1532     def testSelectImage(self):
1533         """Test that we can select which images to build"""
1534         expected = 'Skipping images: image1'
1535
1536         # We should only get the expected message in verbose mode
1537         for verbosity in (0, 2):
1538             with test_util.capture_sys_output() as (stdout, stderr):
1539                 retcode = self._DoTestFile('006_dual_image.dts',
1540                                            verbosity=verbosity,
1541                                            images=['image2'])
1542             self.assertEqual(0, retcode)
1543             if verbosity:
1544                 self.assertIn(expected, stdout.getvalue())
1545             else:
1546                 self.assertNotIn(expected, stdout.getvalue())
1547
1548             self.assertFalse(os.path.exists(tools.GetOutputFilename('image1.bin')))
1549             self.assertTrue(os.path.exists(tools.GetOutputFilename('image2.bin')))
1550
1551     def testUpdateFdtAll(self):
1552         """Test that all device trees are updated with offset/size info"""
1553         data = self._DoReadFileRealDtb('082_fdt_update_all.dts')
1554
1555         base_expected = {
1556             'section:image-pos': 0,
1557             'u-boot-tpl-dtb:size': 513,
1558             'u-boot-spl-dtb:size': 513,
1559             'u-boot-spl-dtb:offset': 493,
1560             'image-pos': 0,
1561             'section/u-boot-dtb:image-pos': 0,
1562             'u-boot-spl-dtb:image-pos': 493,
1563             'section/u-boot-dtb:size': 493,
1564             'u-boot-tpl-dtb:image-pos': 1006,
1565             'section/u-boot-dtb:offset': 0,
1566             'section:size': 493,
1567             'offset': 0,
1568             'section:offset': 0,
1569             'u-boot-tpl-dtb:offset': 1006,
1570             'size': 1519
1571         }
1572
1573         # We expect three device-tree files in the output, one after the other.
1574         # Read them in sequence. We look for an 'spl' property in the SPL tree,
1575         # and 'tpl' in the TPL tree, to make sure they are distinct from the
1576         # main U-Boot tree. All three should have the same postions and offset.
1577         start = 0
1578         for item in ['', 'spl', 'tpl']:
1579             dtb = fdt.Fdt.FromData(data[start:])
1580             dtb.Scan()
1581             props = self._GetPropTree(dtb, ['offset', 'size', 'image-pos',
1582                                             'spl', 'tpl'])
1583             expected = dict(base_expected)
1584             if item:
1585                 expected[item] = 0
1586             self.assertEqual(expected, props)
1587             start += dtb._fdt_obj.totalsize()
1588
1589     def testUpdateFdtOutput(self):
1590         """Test that output DTB files are updated"""
1591         try:
1592             data, dtb_data, _, _ = self._DoReadFileDtb('082_fdt_update_all.dts',
1593                     use_real_dtb=True, update_dtb=True, reset_dtbs=False)
1594
1595             # Unfortunately, compiling a source file always results in a file
1596             # called source.dtb (see fdt_util.EnsureCompiled()). The test
1597             # source file (e.g. test/075_fdt_update_all.dts) thus does not enter
1598             # binman as a file called u-boot.dtb. To fix this, copy the file
1599             # over to the expected place.
1600             #tools.WriteFile(os.path.join(self._indir, 'u-boot.dtb'),
1601                     #tools.ReadFile(tools.GetOutputFilename('source.dtb')))
1602             start = 0
1603             for fname in ['u-boot.dtb.out', 'spl/u-boot-spl.dtb.out',
1604                           'tpl/u-boot-tpl.dtb.out']:
1605                 dtb = fdt.Fdt.FromData(data[start:])
1606                 size = dtb._fdt_obj.totalsize()
1607                 pathname = tools.GetOutputFilename(os.path.split(fname)[1])
1608                 outdata = tools.ReadFile(pathname)
1609                 name = os.path.split(fname)[0]
1610
1611                 if name:
1612                     orig_indata = self._GetDtbContentsForSplTpl(dtb_data, name)
1613                 else:
1614                     orig_indata = dtb_data
1615                 self.assertNotEqual(outdata, orig_indata,
1616                         "Expected output file '%s' be updated" % pathname)
1617                 self.assertEqual(outdata, data[start:start + size],
1618                         "Expected output file '%s' to match output image" %
1619                         pathname)
1620                 start += size
1621         finally:
1622             self._ResetDtbs()
1623
1624     def _decompress(self, data):
1625         return tools.Decompress(data, 'lz4')
1626
1627     def testCompress(self):
1628         """Test compression of blobs"""
1629         self._CheckLz4()
1630         data, _, _, out_dtb_fname = self._DoReadFileDtb('083_compress.dts',
1631                                             use_real_dtb=True, update_dtb=True)
1632         dtb = fdt.Fdt(out_dtb_fname)
1633         dtb.Scan()
1634         props = self._GetPropTree(dtb, ['size', 'uncomp-size'])
1635         orig = self._decompress(data)
1636         self.assertEquals(COMPRESS_DATA, orig)
1637         expected = {
1638             'blob:uncomp-size': len(COMPRESS_DATA),
1639             'blob:size': len(data),
1640             'size': len(data),
1641             }
1642         self.assertEqual(expected, props)
1643
1644     def testFiles(self):
1645         """Test bringing in multiple files"""
1646         data = self._DoReadFile('084_files.dts')
1647         self.assertEqual(FILES_DATA, data)
1648
1649     def testFilesCompress(self):
1650         """Test bringing in multiple files and compressing them"""
1651         self._CheckLz4()
1652         data = self._DoReadFile('085_files_compress.dts')
1653
1654         image = control.images['image']
1655         entries = image.GetEntries()
1656         files = entries['files']
1657         entries = files._section._entries
1658
1659         orig = b''
1660         for i in range(1, 3):
1661             key = '%d.dat' % i
1662             start = entries[key].image_pos
1663             len = entries[key].size
1664             chunk = data[start:start + len]
1665             orig += self._decompress(chunk)
1666
1667         self.assertEqual(FILES_DATA, orig)
1668
1669     def testFilesMissing(self):
1670         """Test missing files"""
1671         with self.assertRaises(ValueError) as e:
1672             data = self._DoReadFile('086_files_none.dts')
1673         self.assertIn("Node '/binman/files': Pattern \'files/*.none\' matched "
1674                       'no files', str(e.exception))
1675
1676     def testFilesNoPattern(self):
1677         """Test missing files"""
1678         with self.assertRaises(ValueError) as e:
1679             data = self._DoReadFile('087_files_no_pattern.dts')
1680         self.assertIn("Node '/binman/files': Missing 'pattern' property",
1681                       str(e.exception))
1682
1683     def testExpandSize(self):
1684         """Test an expanding entry"""
1685         data, _, map_data, _ = self._DoReadFileDtb('088_expand_size.dts',
1686                                                    map=True)
1687         expect = (tools.GetBytes(ord('a'), 8) + U_BOOT_DATA +
1688                   MRC_DATA + tools.GetBytes(ord('b'), 1) + U_BOOT_DATA +
1689                   tools.GetBytes(ord('c'), 8) + U_BOOT_DATA +
1690                   tools.GetBytes(ord('d'), 8))
1691         self.assertEqual(expect, data)
1692         self.assertEqual('''ImagePos    Offset      Size  Name
1693 00000000  00000000  00000028  main-section
1694 00000000   00000000  00000008  fill
1695 00000008   00000008  00000004  u-boot
1696 0000000c   0000000c  00000004  section
1697 0000000c    00000000  00000003  intel-mrc
1698 00000010   00000010  00000004  u-boot2
1699 00000014   00000014  0000000c  section2
1700 00000014    00000000  00000008  fill
1701 0000001c    00000008  00000004  u-boot
1702 00000020   00000020  00000008  fill2
1703 ''', map_data)
1704
1705     def testExpandSizeBad(self):
1706         """Test an expanding entry which fails to provide contents"""
1707         with test_util.capture_sys_output() as (stdout, stderr):
1708             with self.assertRaises(ValueError) as e:
1709                 self._DoReadFileDtb('089_expand_size_bad.dts', map=True)
1710         self.assertIn("Node '/binman/_testing': Cannot obtain contents when "
1711                       'expanding entry', str(e.exception))
1712
1713     def testHash(self):
1714         """Test hashing of the contents of an entry"""
1715         _, _, _, out_dtb_fname = self._DoReadFileDtb('090_hash.dts',
1716                 use_real_dtb=True, update_dtb=True)
1717         dtb = fdt.Fdt(out_dtb_fname)
1718         dtb.Scan()
1719         hash_node = dtb.GetNode('/binman/u-boot/hash').props['value']
1720         m = hashlib.sha256()
1721         m.update(U_BOOT_DATA)
1722         self.assertEqual(m.digest(), b''.join(hash_node.value))
1723
1724     def testHashNoAlgo(self):
1725         with self.assertRaises(ValueError) as e:
1726             self._DoReadFileDtb('091_hash_no_algo.dts', update_dtb=True)
1727         self.assertIn("Node \'/binman/u-boot\': Missing \'algo\' property for "
1728                       'hash node', str(e.exception))
1729
1730     def testHashBadAlgo(self):
1731         with self.assertRaises(ValueError) as e:
1732             self._DoReadFileDtb('092_hash_bad_algo.dts', update_dtb=True)
1733         self.assertIn("Node '/binman/u-boot': Unknown hash algorithm",
1734                       str(e.exception))
1735
1736     def testHashSection(self):
1737         """Test hashing of the contents of an entry"""
1738         _, _, _, out_dtb_fname = self._DoReadFileDtb('099_hash_section.dts',
1739                 use_real_dtb=True, update_dtb=True)
1740         dtb = fdt.Fdt(out_dtb_fname)
1741         dtb.Scan()
1742         hash_node = dtb.GetNode('/binman/section/hash').props['value']
1743         m = hashlib.sha256()
1744         m.update(U_BOOT_DATA)
1745         m.update(tools.GetBytes(ord('a'), 16))
1746         self.assertEqual(m.digest(), b''.join(hash_node.value))
1747
1748     def testPackUBootTplMicrocode(self):
1749         """Test that x86 microcode can be handled correctly in TPL
1750
1751         We expect to see the following in the image, in order:
1752             u-boot-tpl-nodtb.bin with a microcode pointer inserted at the correct
1753                 place
1754             u-boot-tpl.dtb with the microcode removed
1755             the microcode
1756         """
1757         with open(self.TestFile('u_boot_ucode_ptr'), 'rb') as fd:
1758             TestFunctional._MakeInputFile('tpl/u-boot-tpl', fd.read())
1759         first, pos_and_size = self._RunMicrocodeTest('093_x86_tpl_ucode.dts',
1760                                                      U_BOOT_TPL_NODTB_DATA)
1761         self.assertEqual(b'tplnodtb with microc' + pos_and_size +
1762                          b'ter somewhere in here', first)
1763
1764     def testFmapX86(self):
1765         """Basic test of generation of a flashrom fmap"""
1766         data = self._DoReadFile('094_fmap_x86.dts')
1767         fhdr, fentries = fmap_util.DecodeFmap(data[32:])
1768         expected = U_BOOT_DATA + MRC_DATA + tools.GetBytes(ord('a'), 32 - 7)
1769         self.assertEqual(expected, data[:32])
1770         fhdr, fentries = fmap_util.DecodeFmap(data[32:])
1771
1772         self.assertEqual(0x100, fhdr.image_size)
1773
1774         self.assertEqual(0, fentries[0].offset)
1775         self.assertEqual(4, fentries[0].size)
1776         self.assertEqual(b'U_BOOT', fentries[0].name)
1777
1778         self.assertEqual(4, fentries[1].offset)
1779         self.assertEqual(3, fentries[1].size)
1780         self.assertEqual(b'INTEL_MRC', fentries[1].name)
1781
1782         self.assertEqual(32, fentries[2].offset)
1783         self.assertEqual(fmap_util.FMAP_HEADER_LEN +
1784                          fmap_util.FMAP_AREA_LEN * 3, fentries[2].size)
1785         self.assertEqual(b'FMAP', fentries[2].name)
1786
1787     def testFmapX86Section(self):
1788         """Basic test of generation of a flashrom fmap"""
1789         data = self._DoReadFile('095_fmap_x86_section.dts')
1790         expected = U_BOOT_DATA + MRC_DATA + tools.GetBytes(ord('b'), 32 - 7)
1791         self.assertEqual(expected, data[:32])
1792         fhdr, fentries = fmap_util.DecodeFmap(data[36:])
1793
1794         self.assertEqual(0x100, fhdr.image_size)
1795
1796         self.assertEqual(0, fentries[0].offset)
1797         self.assertEqual(4, fentries[0].size)
1798         self.assertEqual(b'U_BOOT', fentries[0].name)
1799
1800         self.assertEqual(4, fentries[1].offset)
1801         self.assertEqual(3, fentries[1].size)
1802         self.assertEqual(b'INTEL_MRC', fentries[1].name)
1803
1804         self.assertEqual(36, fentries[2].offset)
1805         self.assertEqual(fmap_util.FMAP_HEADER_LEN +
1806                          fmap_util.FMAP_AREA_LEN * 3, fentries[2].size)
1807         self.assertEqual(b'FMAP', fentries[2].name)
1808
1809     def testElf(self):
1810         """Basic test of ELF entries"""
1811         self._SetupSplElf()
1812         with open(self.TestFile('bss_data'), 'rb') as fd:
1813             TestFunctional._MakeInputFile('tpl/u-boot-tpl', fd.read())
1814         with open(self.TestFile('bss_data'), 'rb') as fd:
1815             TestFunctional._MakeInputFile('-boot', fd.read())
1816         data = self._DoReadFile('096_elf.dts')
1817
1818     def testElfStrip(self):
1819         """Basic test of ELF entries"""
1820         self._SetupSplElf()
1821         with open(self.TestFile('bss_data'), 'rb') as fd:
1822             TestFunctional._MakeInputFile('-boot', fd.read())
1823         data = self._DoReadFile('097_elf_strip.dts')
1824
1825     def testPackOverlapMap(self):
1826         """Test that overlapping regions are detected"""
1827         with test_util.capture_sys_output() as (stdout, stderr):
1828             with self.assertRaises(ValueError) as e:
1829                 self._DoTestFile('014_pack_overlap.dts', map=True)
1830         map_fname = tools.GetOutputFilename('image.map')
1831         self.assertEqual("Wrote map file '%s' to show errors\n" % map_fname,
1832                          stdout.getvalue())
1833
1834         # We should not get an inmage, but there should be a map file
1835         self.assertFalse(os.path.exists(tools.GetOutputFilename('image.bin')))
1836         self.assertTrue(os.path.exists(map_fname))
1837         map_data = tools.ReadFile(map_fname, binary=False)
1838         self.assertEqual('''ImagePos    Offset      Size  Name
1839 <none>    00000000  00000007  main-section
1840 <none>     00000000  00000004  u-boot
1841 <none>     00000003  00000004  u-boot-align
1842 ''', map_data)
1843
1844     def testPackRefCode(self):
1845         """Test that an image with an Intel Reference code binary works"""
1846         data = self._DoReadFile('100_intel_refcode.dts')
1847         self.assertEqual(REFCODE_DATA, data[:len(REFCODE_DATA)])
1848
1849     def testSectionOffset(self):
1850         """Tests use of a section with an offset"""
1851         data, _, map_data, _ = self._DoReadFileDtb('101_sections_offset.dts',
1852                                                    map=True)
1853         self.assertEqual('''ImagePos    Offset      Size  Name
1854 00000000  00000000  00000038  main-section
1855 00000004   00000004  00000010  section@0
1856 00000004    00000000  00000004  u-boot
1857 00000018   00000018  00000010  section@1
1858 00000018    00000000  00000004  u-boot
1859 0000002c   0000002c  00000004  section@2
1860 0000002c    00000000  00000004  u-boot
1861 ''', map_data)
1862         self.assertEqual(data,
1863                          tools.GetBytes(0x26, 4) + U_BOOT_DATA +
1864                              tools.GetBytes(0x21, 12) +
1865                          tools.GetBytes(0x26, 4) + U_BOOT_DATA +
1866                              tools.GetBytes(0x61, 12) +
1867                          tools.GetBytes(0x26, 4) + U_BOOT_DATA +
1868                              tools.GetBytes(0x26, 8))
1869
1870     def testCbfsRaw(self):
1871         """Test base handling of a Coreboot Filesystem (CBFS)
1872
1873         The exact contents of the CBFS is verified by similar tests in
1874         cbfs_util_test.py. The tests here merely check that the files added to
1875         the CBFS can be found in the final image.
1876         """
1877         data = self._DoReadFile('102_cbfs_raw.dts')
1878         size = 0xb0
1879
1880         cbfs = cbfs_util.CbfsReader(data)
1881         self.assertEqual(size, cbfs.rom_size)
1882
1883         self.assertIn('u-boot-dtb', cbfs.files)
1884         cfile = cbfs.files['u-boot-dtb']
1885         self.assertEqual(U_BOOT_DTB_DATA, cfile.data)
1886
1887     def testCbfsArch(self):
1888         """Test on non-x86 architecture"""
1889         data = self._DoReadFile('103_cbfs_raw_ppc.dts')
1890         size = 0x100
1891
1892         cbfs = cbfs_util.CbfsReader(data)
1893         self.assertEqual(size, cbfs.rom_size)
1894
1895         self.assertIn('u-boot-dtb', cbfs.files)
1896         cfile = cbfs.files['u-boot-dtb']
1897         self.assertEqual(U_BOOT_DTB_DATA, cfile.data)
1898
1899     def testCbfsStage(self):
1900         """Tests handling of a Coreboot Filesystem (CBFS)"""
1901         if not elf.ELF_TOOLS:
1902             self.skipTest('Python elftools not available')
1903         elf_fname = os.path.join(self._indir, 'cbfs-stage.elf')
1904         elf.MakeElf(elf_fname, U_BOOT_DATA, U_BOOT_DTB_DATA)
1905         size = 0xb0
1906
1907         data = self._DoReadFile('104_cbfs_stage.dts')
1908         cbfs = cbfs_util.CbfsReader(data)
1909         self.assertEqual(size, cbfs.rom_size)
1910
1911         self.assertIn('u-boot', cbfs.files)
1912         cfile = cbfs.files['u-boot']
1913         self.assertEqual(U_BOOT_DATA + U_BOOT_DTB_DATA, cfile.data)
1914
1915     def testCbfsRawCompress(self):
1916         """Test handling of compressing raw files"""
1917         self._CheckLz4()
1918         data = self._DoReadFile('105_cbfs_raw_compress.dts')
1919         size = 0x140
1920
1921         cbfs = cbfs_util.CbfsReader(data)
1922         self.assertIn('u-boot', cbfs.files)
1923         cfile = cbfs.files['u-boot']
1924         self.assertEqual(COMPRESS_DATA, cfile.data)
1925
1926     def testCbfsBadArch(self):
1927         """Test handling of a bad architecture"""
1928         with self.assertRaises(ValueError) as e:
1929             self._DoReadFile('106_cbfs_bad_arch.dts')
1930         self.assertIn("Invalid architecture 'bad-arch'", str(e.exception))
1931
1932     def testCbfsNoSize(self):
1933         """Test handling of a missing size property"""
1934         with self.assertRaises(ValueError) as e:
1935             self._DoReadFile('107_cbfs_no_size.dts')
1936         self.assertIn('entry must have a size property', str(e.exception))
1937
1938     def testCbfsNoCOntents(self):
1939         """Test handling of a CBFS entry which does not provide contentsy"""
1940         with self.assertRaises(ValueError) as e:
1941             self._DoReadFile('108_cbfs_no_contents.dts')
1942         self.assertIn('Could not complete processing of contents',
1943                       str(e.exception))
1944
1945     def testCbfsBadCompress(self):
1946         """Test handling of a bad architecture"""
1947         with self.assertRaises(ValueError) as e:
1948             self._DoReadFile('109_cbfs_bad_compress.dts')
1949         self.assertIn("Invalid compression in 'u-boot': 'invalid-algo'",
1950                       str(e.exception))
1951
1952     def testCbfsNamedEntries(self):
1953         """Test handling of named entries"""
1954         data = self._DoReadFile('110_cbfs_name.dts')
1955
1956         cbfs = cbfs_util.CbfsReader(data)
1957         self.assertIn('FRED', cbfs.files)
1958         cfile1 = cbfs.files['FRED']
1959         self.assertEqual(U_BOOT_DATA, cfile1.data)
1960
1961         self.assertIn('hello', cbfs.files)
1962         cfile2 = cbfs.files['hello']
1963         self.assertEqual(U_BOOT_DTB_DATA, cfile2.data)
1964
1965     def _SetupIfwi(self, fname):
1966         """Set up to run an IFWI test
1967
1968         Args:
1969             fname: Filename of input file to provide (fitimage.bin or ifwi.bin)
1970         """
1971         self._SetupSplElf()
1972
1973         # Intel Integrated Firmware Image (IFWI) file
1974         with gzip.open(self.TestFile('%s.gz' % fname), 'rb') as fd:
1975             data = fd.read()
1976         TestFunctional._MakeInputFile(fname,data)
1977
1978     def _CheckIfwi(self, data):
1979         """Check that an image with an IFWI contains the correct output
1980
1981         Args:
1982             data: Conents of output file
1983         """
1984         expected_desc = tools.ReadFile(self.TestFile('descriptor.bin'))
1985         if data[:0x1000] != expected_desc:
1986             self.fail('Expected descriptor binary at start of image')
1987
1988         # We expect to find the TPL wil in subpart IBBP entry IBBL
1989         image_fname = tools.GetOutputFilename('image.bin')
1990         tpl_fname = tools.GetOutputFilename('tpl.out')
1991         tools.RunIfwiTool(image_fname, tools.CMD_EXTRACT, fname=tpl_fname,
1992                           subpart='IBBP', entry_name='IBBL')
1993
1994         tpl_data = tools.ReadFile(tpl_fname)
1995         self.assertEqual(tpl_data[:len(U_BOOT_TPL_DATA)], U_BOOT_TPL_DATA)
1996
1997     def testPackX86RomIfwi(self):
1998         """Test that an x86 ROM with Integrated Firmware Image can be created"""
1999         self._SetupIfwi('fitimage.bin')
2000         data = self._DoReadFile('111_x86-rom-ifwi.dts')
2001         self._CheckIfwi(data)
2002
2003     def testPackX86RomIfwiNoDesc(self):
2004         """Test that an x86 ROM with IFWI can be created from an ifwi.bin file"""
2005         self._SetupIfwi('ifwi.bin')
2006         data = self._DoReadFile('112_x86-rom-ifwi-nodesc.dts')
2007         self._CheckIfwi(data)
2008
2009     def testPackX86RomIfwiNoData(self):
2010         """Test that an x86 ROM with IFWI handles missing data"""
2011         self._SetupIfwi('ifwi.bin')
2012         with self.assertRaises(ValueError) as e:
2013             data = self._DoReadFile('113_x86-rom-ifwi-nodata.dts')
2014         self.assertIn('Could not complete processing of contents',
2015                       str(e.exception))
2016
2017     def testCbfsOffset(self):
2018         """Test a CBFS with files at particular offsets
2019
2020         Like all CFBS tests, this is just checking the logic that calls
2021         cbfs_util. See cbfs_util_test for fully tests (e.g. test_cbfs_offset()).
2022         """
2023         data = self._DoReadFile('114_cbfs_offset.dts')
2024         size = 0x200
2025
2026         cbfs = cbfs_util.CbfsReader(data)
2027         self.assertEqual(size, cbfs.rom_size)
2028
2029         self.assertIn('u-boot', cbfs.files)
2030         cfile = cbfs.files['u-boot']
2031         self.assertEqual(U_BOOT_DATA, cfile.data)
2032         self.assertEqual(0x40, cfile.cbfs_offset)
2033
2034         self.assertIn('u-boot-dtb', cbfs.files)
2035         cfile2 = cbfs.files['u-boot-dtb']
2036         self.assertEqual(U_BOOT_DTB_DATA, cfile2.data)
2037         self.assertEqual(0x140, cfile2.cbfs_offset)
2038
2039     def testFdtmap(self):
2040         """Test an FDT map can be inserted in the image"""
2041         data = self.data = self._DoReadFileRealDtb('115_fdtmap.dts')
2042         fdtmap_data = data[len(U_BOOT_DATA):]
2043         magic = fdtmap_data[:8]
2044         self.assertEqual('_FDTMAP_', magic)
2045         self.assertEqual(tools.GetBytes(0, 8), fdtmap_data[8:16])
2046
2047         fdt_data = fdtmap_data[16:]
2048         dtb = fdt.Fdt.FromData(fdt_data)
2049         dtb.Scan()
2050         props = self._GetPropTree(dtb, ['offset', 'size', 'image-pos'],
2051                                   prefix='/')
2052         self.assertEqual({
2053             'image-pos': 0,
2054             'offset': 0,
2055             'u-boot:offset': 0,
2056             'u-boot:size': len(U_BOOT_DATA),
2057             'u-boot:image-pos': 0,
2058             'fdtmap:image-pos': 4,
2059             'fdtmap:offset': 4,
2060             'fdtmap:size': len(fdtmap_data),
2061             'size': len(data),
2062         }, props)
2063
2064     def testFdtmapNoMatch(self):
2065         """Check handling of an FDT map when the section cannot be found"""
2066         self.data = self._DoReadFileRealDtb('115_fdtmap.dts')
2067
2068         # Mangle the section name, which should cause a mismatch between the
2069         # correct FDT path and the one expected by the section
2070         image = control.images['image']
2071         image._node.path += '-suffix'
2072         entries = image.GetEntries()
2073         fdtmap = entries['fdtmap']
2074         with self.assertRaises(ValueError) as e:
2075             fdtmap._GetFdtmap()
2076         self.assertIn("Cannot locate node for path '/binman-suffix'",
2077                       str(e.exception))
2078
2079     def testFdtmapHeader(self):
2080         """Test an FDT map and image header can be inserted in the image"""
2081         data = self.data = self._DoReadFileRealDtb('116_fdtmap_hdr.dts')
2082         fdtmap_pos = len(U_BOOT_DATA)
2083         fdtmap_data = data[fdtmap_pos:]
2084         fdt_data = fdtmap_data[16:]
2085         dtb = fdt.Fdt.FromData(fdt_data)
2086         fdt_size = dtb.GetFdtObj().totalsize()
2087         hdr_data = data[-8:]
2088         self.assertEqual('BinM', hdr_data[:4])
2089         offset = struct.unpack('<I', hdr_data[4:])[0] & 0xffffffff
2090         self.assertEqual(fdtmap_pos - 0x400, offset - (1 << 32))
2091
2092     def testFdtmapHeaderStart(self):
2093         """Test an image header can be inserted at the image start"""
2094         data = self.data = self._DoReadFileRealDtb('117_fdtmap_hdr_start.dts')
2095         fdtmap_pos = 0x100 + len(U_BOOT_DATA)
2096         hdr_data = data[:8]
2097         self.assertEqual('BinM', hdr_data[:4])
2098         offset = struct.unpack('<I', hdr_data[4:])[0]
2099         self.assertEqual(fdtmap_pos, offset)
2100
2101     def testFdtmapHeaderPos(self):
2102         """Test an image header can be inserted at a chosen position"""
2103         data = self.data = self._DoReadFileRealDtb('118_fdtmap_hdr_pos.dts')
2104         fdtmap_pos = 0x100 + len(U_BOOT_DATA)
2105         hdr_data = data[0x80:0x88]
2106         self.assertEqual('BinM', hdr_data[:4])
2107         offset = struct.unpack('<I', hdr_data[4:])[0]
2108         self.assertEqual(fdtmap_pos, offset)
2109
2110     def testHeaderMissingFdtmap(self):
2111         """Test an image header requires an fdtmap"""
2112         with self.assertRaises(ValueError) as e:
2113             self.data = self._DoReadFileRealDtb('119_fdtmap_hdr_missing.dts')
2114         self.assertIn("'image_header' section must have an 'fdtmap' sibling",
2115                       str(e.exception))
2116
2117     def testHeaderNoLocation(self):
2118         """Test an image header with a no specified location is detected"""
2119         with self.assertRaises(ValueError) as e:
2120             self.data = self._DoReadFileRealDtb('120_hdr_no_location.dts')
2121         self.assertIn("Invalid location 'None', expected 'start' or 'end'",
2122                       str(e.exception))
2123
2124     def testEntryExpand(self):
2125         """Test expanding an entry after it is packed"""
2126         data = self._DoReadFile('121_entry_expand.dts')
2127         self.assertEqual(b'aa', data[:2])
2128         self.assertEqual(U_BOOT_DATA, data[2:2 + len(U_BOOT_DATA)])
2129         self.assertEqual(b'aa', data[-2:])
2130
2131     def testEntryExpandBad(self):
2132         """Test expanding an entry after it is packed, twice"""
2133         with self.assertRaises(ValueError) as e:
2134             self._DoReadFile('122_entry_expand_twice.dts')
2135         self.assertIn("Image '/binman': Entries expanded after packing",
2136                       str(e.exception))
2137
2138     def testEntryExpandSection(self):
2139         """Test expanding an entry within a section after it is packed"""
2140         data = self._DoReadFile('123_entry_expand_section.dts')
2141         self.assertEqual(b'aa', data[:2])
2142         self.assertEqual(U_BOOT_DATA, data[2:2 + len(U_BOOT_DATA)])
2143         self.assertEqual(b'aa', data[-2:])
2144
2145     def testCompressDtb(self):
2146         """Test that compress of device-tree files is supported"""
2147         self._CheckLz4()
2148         data = self.data = self._DoReadFileRealDtb('124_compress_dtb.dts')
2149         self.assertEqual(U_BOOT_DATA, data[:len(U_BOOT_DATA)])
2150         comp_data = data[len(U_BOOT_DATA):]
2151         orig = self._decompress(comp_data)
2152         dtb = fdt.Fdt.FromData(orig)
2153         dtb.Scan()
2154         props = self._GetPropTree(dtb, ['size', 'uncomp-size'])
2155         expected = {
2156             'u-boot:size': len(U_BOOT_DATA),
2157             'u-boot-dtb:uncomp-size': len(orig),
2158             'u-boot-dtb:size': len(comp_data),
2159             'size': len(data),
2160             }
2161         self.assertEqual(expected, props)
2162
2163
2164 if __name__ == "__main__":
2165     unittest.main()