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