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