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