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