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