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