Merge branch 'master' of git://git.denx.de/u-boot-video
[platform/kernel/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 optparse import OptionParser
10 import os
11 import shutil
12 import struct
13 import sys
14 import tempfile
15 import unittest
16
17 import binman
18 import cmdline
19 import command
20 import control
21 import elf
22 import fdt
23 import fdt_util
24 import fmap_util
25 import test_util
26 import tools
27 import tout
28
29 # Contents of test files, corresponding to different entry types
30 U_BOOT_DATA           = '1234'
31 U_BOOT_IMG_DATA       = 'img'
32 U_BOOT_SPL_DATA       = '56780123456789abcde'
33 U_BOOT_TPL_DATA       = 'tpl'
34 BLOB_DATA             = '89'
35 ME_DATA               = '0abcd'
36 VGA_DATA              = 'vga'
37 U_BOOT_DTB_DATA       = 'udtb'
38 U_BOOT_SPL_DTB_DATA   = 'spldtb'
39 U_BOOT_TPL_DTB_DATA   = 'tpldtb'
40 X86_START16_DATA      = 'start16'
41 X86_START16_SPL_DATA  = 'start16spl'
42 PPC_MPC85XX_BR_DATA   = 'ppcmpc85xxbr'
43 U_BOOT_NODTB_DATA     = 'nodtb with microcode pointer somewhere in here'
44 U_BOOT_SPL_NODTB_DATA = 'splnodtb with microcode pointer somewhere in here'
45 FSP_DATA              = 'fsp'
46 CMC_DATA              = 'cmc'
47 VBT_DATA              = 'vbt'
48 MRC_DATA              = 'mrc'
49 TEXT_DATA             = 'text'
50 TEXT_DATA2            = 'text2'
51 TEXT_DATA3            = 'text3'
52 CROS_EC_RW_DATA       = 'ecrw'
53 GBB_DATA              = 'gbbd'
54 BMPBLK_DATA           = 'bmp'
55 VBLOCK_DATA           = 'vblk'
56
57
58 class TestFunctional(unittest.TestCase):
59     """Functional tests for binman
60
61     Most of these use a sample .dts file to build an image and then check
62     that it looks correct. The sample files are in the test/ subdirectory
63     and are numbered.
64
65     For each entry type a very small test file is created using fixed
66     string contents. This makes it easy to test that things look right, and
67     debug problems.
68
69     In some cases a 'real' file must be used - these are also supplied in
70     the test/ diurectory.
71     """
72     @classmethod
73     def setUpClass(self):
74         global entry
75         import entry
76
77         # Handle the case where argv[0] is 'python'
78         self._binman_dir = os.path.dirname(os.path.realpath(sys.argv[0]))
79         self._binman_pathname = os.path.join(self._binman_dir, 'binman')
80
81         # Create a temporary directory for input files
82         self._indir = tempfile.mkdtemp(prefix='binmant.')
83
84         # Create some test files
85         TestFunctional._MakeInputFile('u-boot.bin', U_BOOT_DATA)
86         TestFunctional._MakeInputFile('u-boot.img', U_BOOT_IMG_DATA)
87         TestFunctional._MakeInputFile('spl/u-boot-spl.bin', U_BOOT_SPL_DATA)
88         TestFunctional._MakeInputFile('tpl/u-boot-tpl.bin', U_BOOT_TPL_DATA)
89         TestFunctional._MakeInputFile('blobfile', BLOB_DATA)
90         TestFunctional._MakeInputFile('me.bin', ME_DATA)
91         TestFunctional._MakeInputFile('vga.bin', VGA_DATA)
92         self._ResetDtbs()
93         TestFunctional._MakeInputFile('u-boot-x86-16bit.bin', X86_START16_DATA)
94         TestFunctional._MakeInputFile('u-boot-br.bin', PPC_MPC85XX_BR_DATA)
95         TestFunctional._MakeInputFile('spl/u-boot-x86-16bit-spl.bin',
96                                       X86_START16_SPL_DATA)
97         TestFunctional._MakeInputFile('u-boot-nodtb.bin', U_BOOT_NODTB_DATA)
98         TestFunctional._MakeInputFile('spl/u-boot-spl-nodtb.bin',
99                                       U_BOOT_SPL_NODTB_DATA)
100         TestFunctional._MakeInputFile('fsp.bin', FSP_DATA)
101         TestFunctional._MakeInputFile('cmc.bin', CMC_DATA)
102         TestFunctional._MakeInputFile('vbt.bin', VBT_DATA)
103         TestFunctional._MakeInputFile('mrc.bin', MRC_DATA)
104         TestFunctional._MakeInputFile('ecrw.bin', CROS_EC_RW_DATA)
105         TestFunctional._MakeInputDir('devkeys')
106         TestFunctional._MakeInputFile('bmpblk.bin', BMPBLK_DATA)
107         self._output_setup = False
108
109         # ELF file with a '_dt_ucode_base_size' symbol
110         with open(self.TestFile('u_boot_ucode_ptr')) as fd:
111             TestFunctional._MakeInputFile('u-boot', fd.read())
112
113         # Intel flash descriptor file
114         with open(self.TestFile('descriptor.bin')) as fd:
115             TestFunctional._MakeInputFile('descriptor.bin', fd.read())
116
117     @classmethod
118     def tearDownClass(self):
119         """Remove the temporary input directory and its contents"""
120         if self._indir:
121             shutil.rmtree(self._indir)
122         self._indir = None
123
124     def setUp(self):
125         # Enable this to turn on debugging output
126         # tout.Init(tout.DEBUG)
127         command.test_result = None
128
129     def tearDown(self):
130         """Remove the temporary output directory"""
131         tools._FinaliseForTest()
132
133     @classmethod
134     def _ResetDtbs(self):
135         TestFunctional._MakeInputFile('u-boot.dtb', U_BOOT_DTB_DATA)
136         TestFunctional._MakeInputFile('spl/u-boot-spl.dtb', U_BOOT_SPL_DTB_DATA)
137         TestFunctional._MakeInputFile('tpl/u-boot-tpl.dtb', U_BOOT_TPL_DTB_DATA)
138
139     def _RunBinman(self, *args, **kwargs):
140         """Run binman using the command line
141
142         Args:
143             Arguments to pass, as a list of strings
144             kwargs: Arguments to pass to Command.RunPipe()
145         """
146         result = command.RunPipe([[self._binman_pathname] + list(args)],
147                 capture=True, capture_stderr=True, raise_on_error=False)
148         if result.return_code and kwargs.get('raise_on_error', True):
149             raise Exception("Error running '%s': %s" % (' '.join(args),
150                             result.stdout + result.stderr))
151         return result
152
153     def _DoBinman(self, *args):
154         """Run binman using directly (in the same process)
155
156         Args:
157             Arguments to pass, as a list of strings
158         Returns:
159             Return value (0 for success)
160         """
161         args = list(args)
162         if '-D' in sys.argv:
163             args = args + ['-D']
164         (options, args) = cmdline.ParseArgs(args)
165         options.pager = 'binman-invalid-pager'
166         options.build_dir = self._indir
167
168         # For testing, you can force an increase in verbosity here
169         # options.verbosity = tout.DEBUG
170         return control.Binman(options, args)
171
172     def _DoTestFile(self, fname, debug=False, map=False, update_dtb=False,
173                     entry_args=None):
174         """Run binman with a given test file
175
176         Args:
177             fname: Device-tree source filename to use (e.g. 05_simple.dts)
178             debug: True to enable debugging output
179             map: True to output map files for the images
180             update_dtb: Update the offset and size of each entry in the device
181                 tree before packing it into the image
182         """
183         args = ['-p', '-I', self._indir, '-d', self.TestFile(fname)]
184         if debug:
185             args.append('-D')
186         if map:
187             args.append('-m')
188         if update_dtb:
189             args.append('-up')
190         if entry_args:
191             for arg, value in entry_args.iteritems():
192                 args.append('-a%s=%s' % (arg, value))
193         return self._DoBinman(*args)
194
195     def _SetupDtb(self, fname, outfile='u-boot.dtb'):
196         """Set up a new test device-tree file
197
198         The given file is compiled and set up as the device tree to be used
199         for ths test.
200
201         Args:
202             fname: Filename of .dts file to read
203             outfile: Output filename for compiled device-tree binary
204
205         Returns:
206             Contents of device-tree binary
207         """
208         if not self._output_setup:
209             tools.PrepareOutputDir(self._indir, True)
210             self._output_setup = True
211         dtb = fdt_util.EnsureCompiled(self.TestFile(fname))
212         with open(dtb) as fd:
213             data = fd.read()
214             TestFunctional._MakeInputFile(outfile, data)
215             return data
216
217     def _DoReadFileDtb(self, fname, use_real_dtb=False, map=False,
218                        update_dtb=False, entry_args=None):
219         """Run binman and return the resulting image
220
221         This runs binman with a given test file and then reads the resulting
222         output file. It is a shortcut function since most tests need to do
223         these steps.
224
225         Raises an assertion failure if binman returns a non-zero exit code.
226
227         Args:
228             fname: Device-tree source filename to use (e.g. 05_simple.dts)
229             use_real_dtb: True to use the test file as the contents of
230                 the u-boot-dtb entry. Normally this is not needed and the
231                 test contents (the U_BOOT_DTB_DATA string) can be used.
232                 But in some test we need the real contents.
233             map: True to output map files for the images
234             update_dtb: Update the offset and size of each entry in the device
235                 tree before packing it into the image
236
237         Returns:
238             Tuple:
239                 Resulting image contents
240                 Device tree contents
241                 Map data showing contents of image (or None if none)
242                 Output device tree binary filename ('u-boot.dtb' path)
243         """
244         dtb_data = None
245         # Use the compiled test file as the u-boot-dtb input
246         if use_real_dtb:
247             dtb_data = self._SetupDtb(fname)
248
249         try:
250             retcode = self._DoTestFile(fname, map=map, update_dtb=update_dtb,
251                                        entry_args=entry_args)
252             self.assertEqual(0, retcode)
253             out_dtb_fname = control.GetFdtPath('u-boot.dtb')
254
255             # Find the (only) image, read it and return its contents
256             image = control.images['image']
257             image_fname = tools.GetOutputFilename('image.bin')
258             self.assertTrue(os.path.exists(image_fname))
259             if map:
260                 map_fname = tools.GetOutputFilename('image.map')
261                 with open(map_fname) as fd:
262                     map_data = fd.read()
263             else:
264                 map_data = None
265             with open(image_fname) as fd:
266                 return fd.read(), dtb_data, map_data, out_dtb_fname
267         finally:
268             # Put the test file back
269             if use_real_dtb:
270                 self._ResetDtbs()
271
272     def _DoReadFile(self, fname, use_real_dtb=False):
273         """Helper function which discards the device-tree binary
274
275         Args:
276             fname: Device-tree source filename to use (e.g. 05_simple.dts)
277             use_real_dtb: True to use the test file as the contents of
278                 the u-boot-dtb entry. Normally this is not needed and the
279                 test contents (the U_BOOT_DTB_DATA string) can be used.
280                 But in some test we need the real contents.
281
282         Returns:
283             Resulting image contents
284         """
285         return self._DoReadFileDtb(fname, use_real_dtb)[0]
286
287     @classmethod
288     def _MakeInputFile(self, fname, contents):
289         """Create a new test input file, creating directories as needed
290
291         Args:
292             fname: Filename to create
293             contents: File contents to write in to the file
294         Returns:
295             Full pathname of file created
296         """
297         pathname = os.path.join(self._indir, fname)
298         dirname = os.path.dirname(pathname)
299         if dirname and not os.path.exists(dirname):
300             os.makedirs(dirname)
301         with open(pathname, 'wb') as fd:
302             fd.write(contents)
303         return pathname
304
305     @classmethod
306     def _MakeInputDir(self, dirname):
307         """Create a new test input directory, creating directories as needed
308
309         Args:
310             dirname: Directory name to create
311
312         Returns:
313             Full pathname of directory created
314         """
315         pathname = os.path.join(self._indir, dirname)
316         if not os.path.exists(pathname):
317             os.makedirs(pathname)
318         return pathname
319
320     @classmethod
321     def TestFile(self, fname):
322         return os.path.join(self._binman_dir, 'test', fname)
323
324     def AssertInList(self, grep_list, target):
325         """Assert that at least one of a list of things is in a target
326
327         Args:
328             grep_list: List of strings to check
329             target: Target string
330         """
331         for grep in grep_list:
332             if grep in target:
333                 return
334         self.fail("Error: '%' not found in '%s'" % (grep_list, target))
335
336     def CheckNoGaps(self, entries):
337         """Check that all entries fit together without gaps
338
339         Args:
340             entries: List of entries to check
341         """
342         offset = 0
343         for entry in entries.values():
344             self.assertEqual(offset, entry.offset)
345             offset += entry.size
346
347     def GetFdtLen(self, dtb):
348         """Get the totalsize field from a device-tree binary
349
350         Args:
351             dtb: Device-tree binary contents
352
353         Returns:
354             Total size of device-tree binary, from the header
355         """
356         return struct.unpack('>L', dtb[4:8])[0]
357
358     def _GetPropTree(self, dtb, prop_names):
359         def AddNode(node, path):
360             if node.name != '/':
361                 path += '/' + node.name
362             for subnode in node.subnodes:
363                 for prop in subnode.props.values():
364                     if prop.name in prop_names:
365                         prop_path = path + '/' + subnode.name + ':' + prop.name
366                         tree[prop_path[len('/binman/'):]] = fdt_util.fdt32_to_cpu(
367                             prop.value)
368                 AddNode(subnode, path)
369
370         tree = {}
371         AddNode(dtb.GetRoot(), '')
372         return tree
373
374     def testRun(self):
375         """Test a basic run with valid args"""
376         result = self._RunBinman('-h')
377
378     def testFullHelp(self):
379         """Test that the full help is displayed with -H"""
380         result = self._RunBinman('-H')
381         help_file = os.path.join(self._binman_dir, 'README')
382         # Remove possible extraneous strings
383         extra = '::::::::::::::\n' + help_file + '\n::::::::::::::\n'
384         gothelp = result.stdout.replace(extra, '')
385         self.assertEqual(len(gothelp), os.path.getsize(help_file))
386         self.assertEqual(0, len(result.stderr))
387         self.assertEqual(0, result.return_code)
388
389     def testFullHelpInternal(self):
390         """Test that the full help is displayed with -H"""
391         try:
392             command.test_result = command.CommandResult()
393             result = self._DoBinman('-H')
394             help_file = os.path.join(self._binman_dir, 'README')
395         finally:
396             command.test_result = None
397
398     def testHelp(self):
399         """Test that the basic help is displayed with -h"""
400         result = self._RunBinman('-h')
401         self.assertTrue(len(result.stdout) > 200)
402         self.assertEqual(0, len(result.stderr))
403         self.assertEqual(0, result.return_code)
404
405     def testBoard(self):
406         """Test that we can run it with a specific board"""
407         self._SetupDtb('05_simple.dts', 'sandbox/u-boot.dtb')
408         TestFunctional._MakeInputFile('sandbox/u-boot.bin', U_BOOT_DATA)
409         result = self._DoBinman('-b', 'sandbox')
410         self.assertEqual(0, result)
411
412     def testNeedBoard(self):
413         """Test that we get an error when no board ius supplied"""
414         with self.assertRaises(ValueError) as e:
415             result = self._DoBinman()
416         self.assertIn("Must provide a board to process (use -b <board>)",
417                 str(e.exception))
418
419     def testMissingDt(self):
420         """Test that an invalid device-tree file generates an error"""
421         with self.assertRaises(Exception) as e:
422             self._RunBinman('-d', 'missing_file')
423         # We get one error from libfdt, and a different one from fdtget.
424         self.AssertInList(["Couldn't open blob from 'missing_file'",
425                            'No such file or directory'], str(e.exception))
426
427     def testBrokenDt(self):
428         """Test that an invalid device-tree source file generates an error
429
430         Since this is a source file it should be compiled and the error
431         will come from the device-tree compiler (dtc).
432         """
433         with self.assertRaises(Exception) as e:
434             self._RunBinman('-d', self.TestFile('01_invalid.dts'))
435         self.assertIn("FATAL ERROR: Unable to parse input tree",
436                 str(e.exception))
437
438     def testMissingNode(self):
439         """Test that a device tree without a 'binman' node generates an error"""
440         with self.assertRaises(Exception) as e:
441             self._DoBinman('-d', self.TestFile('02_missing_node.dts'))
442         self.assertIn("does not have a 'binman' node", str(e.exception))
443
444     def testEmpty(self):
445         """Test that an empty binman node works OK (i.e. does nothing)"""
446         result = self._RunBinman('-d', self.TestFile('03_empty.dts'))
447         self.assertEqual(0, len(result.stderr))
448         self.assertEqual(0, result.return_code)
449
450     def testInvalidEntry(self):
451         """Test that an invalid entry is flagged"""
452         with self.assertRaises(Exception) as e:
453             result = self._RunBinman('-d',
454                                      self.TestFile('04_invalid_entry.dts'))
455         self.assertIn("Unknown entry type 'not-a-valid-type' in node "
456                 "'/binman/not-a-valid-type'", str(e.exception))
457
458     def testSimple(self):
459         """Test a simple binman with a single file"""
460         data = self._DoReadFile('05_simple.dts')
461         self.assertEqual(U_BOOT_DATA, data)
462
463     def testSimpleDebug(self):
464         """Test a simple binman run with debugging enabled"""
465         data = self._DoTestFile('05_simple.dts', debug=True)
466
467     def testDual(self):
468         """Test that we can handle creating two images
469
470         This also tests image padding.
471         """
472         retcode = self._DoTestFile('06_dual_image.dts')
473         self.assertEqual(0, retcode)
474
475         image = control.images['image1']
476         self.assertEqual(len(U_BOOT_DATA), image._size)
477         fname = tools.GetOutputFilename('image1.bin')
478         self.assertTrue(os.path.exists(fname))
479         with open(fname) as fd:
480             data = fd.read()
481             self.assertEqual(U_BOOT_DATA, data)
482
483         image = control.images['image2']
484         self.assertEqual(3 + len(U_BOOT_DATA) + 5, image._size)
485         fname = tools.GetOutputFilename('image2.bin')
486         self.assertTrue(os.path.exists(fname))
487         with open(fname) as fd:
488             data = fd.read()
489             self.assertEqual(U_BOOT_DATA, data[3:7])
490             self.assertEqual(chr(0) * 3, data[:3])
491             self.assertEqual(chr(0) * 5, data[7:])
492
493     def testBadAlign(self):
494         """Test that an invalid alignment value is detected"""
495         with self.assertRaises(ValueError) as e:
496             self._DoTestFile('07_bad_align.dts')
497         self.assertIn("Node '/binman/u-boot': Alignment 23 must be a power "
498                       "of two", str(e.exception))
499
500     def testPackSimple(self):
501         """Test that packing works as expected"""
502         retcode = self._DoTestFile('08_pack.dts')
503         self.assertEqual(0, retcode)
504         self.assertIn('image', control.images)
505         image = control.images['image']
506         entries = image.GetEntries()
507         self.assertEqual(5, len(entries))
508
509         # First u-boot
510         self.assertIn('u-boot', entries)
511         entry = entries['u-boot']
512         self.assertEqual(0, entry.offset)
513         self.assertEqual(len(U_BOOT_DATA), entry.size)
514
515         # Second u-boot, aligned to 16-byte boundary
516         self.assertIn('u-boot-align', entries)
517         entry = entries['u-boot-align']
518         self.assertEqual(16, entry.offset)
519         self.assertEqual(len(U_BOOT_DATA), entry.size)
520
521         # Third u-boot, size 23 bytes
522         self.assertIn('u-boot-size', entries)
523         entry = entries['u-boot-size']
524         self.assertEqual(20, entry.offset)
525         self.assertEqual(len(U_BOOT_DATA), entry.contents_size)
526         self.assertEqual(23, entry.size)
527
528         # Fourth u-boot, placed immediate after the above
529         self.assertIn('u-boot-next', entries)
530         entry = entries['u-boot-next']
531         self.assertEqual(43, entry.offset)
532         self.assertEqual(len(U_BOOT_DATA), entry.size)
533
534         # Fifth u-boot, placed at a fixed offset
535         self.assertIn('u-boot-fixed', entries)
536         entry = entries['u-boot-fixed']
537         self.assertEqual(61, entry.offset)
538         self.assertEqual(len(U_BOOT_DATA), entry.size)
539
540         self.assertEqual(65, image._size)
541
542     def testPackExtra(self):
543         """Test that extra packing feature works as expected"""
544         retcode = self._DoTestFile('09_pack_extra.dts')
545
546         self.assertEqual(0, retcode)
547         self.assertIn('image', control.images)
548         image = control.images['image']
549         entries = image.GetEntries()
550         self.assertEqual(5, len(entries))
551
552         # First u-boot with padding before and after
553         self.assertIn('u-boot', entries)
554         entry = entries['u-boot']
555         self.assertEqual(0, entry.offset)
556         self.assertEqual(3, entry.pad_before)
557         self.assertEqual(3 + 5 + len(U_BOOT_DATA), entry.size)
558
559         # Second u-boot has an aligned size, but it has no effect
560         self.assertIn('u-boot-align-size-nop', entries)
561         entry = entries['u-boot-align-size-nop']
562         self.assertEqual(12, entry.offset)
563         self.assertEqual(4, entry.size)
564
565         # Third u-boot has an aligned size too
566         self.assertIn('u-boot-align-size', entries)
567         entry = entries['u-boot-align-size']
568         self.assertEqual(16, entry.offset)
569         self.assertEqual(32, entry.size)
570
571         # Fourth u-boot has an aligned end
572         self.assertIn('u-boot-align-end', entries)
573         entry = entries['u-boot-align-end']
574         self.assertEqual(48, entry.offset)
575         self.assertEqual(16, entry.size)
576
577         # Fifth u-boot immediately afterwards
578         self.assertIn('u-boot-align-both', entries)
579         entry = entries['u-boot-align-both']
580         self.assertEqual(64, entry.offset)
581         self.assertEqual(64, entry.size)
582
583         self.CheckNoGaps(entries)
584         self.assertEqual(128, image._size)
585
586     def testPackAlignPowerOf2(self):
587         """Test that invalid entry alignment is detected"""
588         with self.assertRaises(ValueError) as e:
589             self._DoTestFile('10_pack_align_power2.dts')
590         self.assertIn("Node '/binman/u-boot': Alignment 5 must be a power "
591                       "of two", str(e.exception))
592
593     def testPackAlignSizePowerOf2(self):
594         """Test that invalid entry size alignment is detected"""
595         with self.assertRaises(ValueError) as e:
596             self._DoTestFile('11_pack_align_size_power2.dts')
597         self.assertIn("Node '/binman/u-boot': Alignment size 55 must be a "
598                       "power of two", str(e.exception))
599
600     def testPackInvalidAlign(self):
601         """Test detection of an offset that does not match its alignment"""
602         with self.assertRaises(ValueError) as e:
603             self._DoTestFile('12_pack_inv_align.dts')
604         self.assertIn("Node '/binman/u-boot': Offset 0x5 (5) does not match "
605                       "align 0x4 (4)", str(e.exception))
606
607     def testPackInvalidSizeAlign(self):
608         """Test that invalid entry size alignment is detected"""
609         with self.assertRaises(ValueError) as e:
610             self._DoTestFile('13_pack_inv_size_align.dts')
611         self.assertIn("Node '/binman/u-boot': Size 0x5 (5) does not match "
612                       "align-size 0x4 (4)", str(e.exception))
613
614     def testPackOverlap(self):
615         """Test that overlapping regions are detected"""
616         with self.assertRaises(ValueError) as e:
617             self._DoTestFile('14_pack_overlap.dts')
618         self.assertIn("Node '/binman/u-boot-align': Offset 0x3 (3) overlaps "
619                       "with previous entry '/binman/u-boot' ending at 0x4 (4)",
620                       str(e.exception))
621
622     def testPackEntryOverflow(self):
623         """Test that entries that overflow their size are detected"""
624         with self.assertRaises(ValueError) as e:
625             self._DoTestFile('15_pack_overflow.dts')
626         self.assertIn("Node '/binman/u-boot': Entry contents size is 0x4 (4) "
627                       "but entry size is 0x3 (3)", str(e.exception))
628
629     def testPackImageOverflow(self):
630         """Test that entries which overflow the image size are detected"""
631         with self.assertRaises(ValueError) as e:
632             self._DoTestFile('16_pack_image_overflow.dts')
633         self.assertIn("Section '/binman': contents size 0x4 (4) exceeds section "
634                       "size 0x3 (3)", str(e.exception))
635
636     def testPackImageSize(self):
637         """Test that the image size can be set"""
638         retcode = self._DoTestFile('17_pack_image_size.dts')
639         self.assertEqual(0, retcode)
640         self.assertIn('image', control.images)
641         image = control.images['image']
642         self.assertEqual(7, image._size)
643
644     def testPackImageSizeAlign(self):
645         """Test that image size alignemnt works as expected"""
646         retcode = self._DoTestFile('18_pack_image_align.dts')
647         self.assertEqual(0, retcode)
648         self.assertIn('image', control.images)
649         image = control.images['image']
650         self.assertEqual(16, image._size)
651
652     def testPackInvalidImageAlign(self):
653         """Test that invalid image alignment is detected"""
654         with self.assertRaises(ValueError) as e:
655             self._DoTestFile('19_pack_inv_image_align.dts')
656         self.assertIn("Section '/binman': Size 0x7 (7) does not match "
657                       "align-size 0x8 (8)", str(e.exception))
658
659     def testPackAlignPowerOf2(self):
660         """Test that invalid image alignment is detected"""
661         with self.assertRaises(ValueError) as e:
662             self._DoTestFile('20_pack_inv_image_align_power2.dts')
663         self.assertIn("Section '/binman': Alignment size 131 must be a power of "
664                       "two", str(e.exception))
665
666     def testImagePadByte(self):
667         """Test that the image pad byte can be specified"""
668         with open(self.TestFile('bss_data')) as fd:
669             TestFunctional._MakeInputFile('spl/u-boot-spl', fd.read())
670         data = self._DoReadFile('21_image_pad.dts')
671         self.assertEqual(U_BOOT_SPL_DATA + (chr(0xff) * 1) + U_BOOT_DATA, data)
672
673     def testImageName(self):
674         """Test that image files can be named"""
675         retcode = self._DoTestFile('22_image_name.dts')
676         self.assertEqual(0, retcode)
677         image = control.images['image1']
678         fname = tools.GetOutputFilename('test-name')
679         self.assertTrue(os.path.exists(fname))
680
681         image = control.images['image2']
682         fname = tools.GetOutputFilename('test-name.xx')
683         self.assertTrue(os.path.exists(fname))
684
685     def testBlobFilename(self):
686         """Test that generic blobs can be provided by filename"""
687         data = self._DoReadFile('23_blob.dts')
688         self.assertEqual(BLOB_DATA, data)
689
690     def testPackSorted(self):
691         """Test that entries can be sorted"""
692         data = self._DoReadFile('24_sorted.dts')
693         self.assertEqual(chr(0) * 1 + U_BOOT_SPL_DATA + chr(0) * 2 +
694                          U_BOOT_DATA, data)
695
696     def testPackZeroOffset(self):
697         """Test that an entry at offset 0 is not given a new offset"""
698         with self.assertRaises(ValueError) as e:
699             self._DoTestFile('25_pack_zero_size.dts')
700         self.assertIn("Node '/binman/u-boot-spl': Offset 0x0 (0) overlaps "
701                       "with previous entry '/binman/u-boot' ending at 0x4 (4)",
702                       str(e.exception))
703
704     def testPackUbootDtb(self):
705         """Test that a device tree can be added to U-Boot"""
706         data = self._DoReadFile('26_pack_u_boot_dtb.dts')
707         self.assertEqual(U_BOOT_NODTB_DATA + U_BOOT_DTB_DATA, data)
708
709     def testPackX86RomNoSize(self):
710         """Test that the end-at-4gb property requires a size property"""
711         with self.assertRaises(ValueError) as e:
712             self._DoTestFile('27_pack_4gb_no_size.dts')
713         self.assertIn("Section '/binman': Section size must be provided when "
714                       "using end-at-4gb", str(e.exception))
715
716     def test4gbAndSkipAtStartTogether(self):
717         """Test that the end-at-4gb and skip-at-size property can't be used
718         together"""
719         with self.assertRaises(ValueError) as e:
720             self._DoTestFile('80_4gb_and_skip_at_start_together.dts')
721         self.assertIn("Section '/binman': Provide either 'end-at-4gb' or "
722                       "'skip-at-start'", str(e.exception))
723
724     def testPackX86RomOutside(self):
725         """Test that the end-at-4gb property checks for offset boundaries"""
726         with self.assertRaises(ValueError) as e:
727             self._DoTestFile('28_pack_4gb_outside.dts')
728         self.assertIn("Node '/binman/u-boot': Offset 0x0 (0) is outside "
729                       "the section starting at 0xffffffe0 (4294967264)",
730                       str(e.exception))
731
732     def testPackX86Rom(self):
733         """Test that a basic x86 ROM can be created"""
734         data = self._DoReadFile('29_x86-rom.dts')
735         self.assertEqual(U_BOOT_DATA + chr(0) * 7 + U_BOOT_SPL_DATA +
736                          chr(0) * 2, data)
737
738     def testPackX86RomMeNoDesc(self):
739         """Test that an invalid Intel descriptor entry is detected"""
740         TestFunctional._MakeInputFile('descriptor.bin', '')
741         with self.assertRaises(ValueError) as e:
742             self._DoTestFile('31_x86-rom-me.dts')
743         self.assertIn("Node '/binman/intel-descriptor': Cannot find FD "
744                       "signature", str(e.exception))
745
746     def testPackX86RomBadDesc(self):
747         """Test that the Intel requires a descriptor entry"""
748         with self.assertRaises(ValueError) as e:
749             self._DoTestFile('30_x86-rom-me-no-desc.dts')
750         self.assertIn("Node '/binman/intel-me': No offset set with "
751                       "offset-unset: should another entry provide this correct "
752                       "offset?", str(e.exception))
753
754     def testPackX86RomMe(self):
755         """Test that an x86 ROM with an ME region can be created"""
756         data = self._DoReadFile('31_x86-rom-me.dts')
757         self.assertEqual(ME_DATA, data[0x1000:0x1000 + len(ME_DATA)])
758
759     def testPackVga(self):
760         """Test that an image with a VGA binary can be created"""
761         data = self._DoReadFile('32_intel-vga.dts')
762         self.assertEqual(VGA_DATA, data[:len(VGA_DATA)])
763
764     def testPackStart16(self):
765         """Test that an image with an x86 start16 region can be created"""
766         data = self._DoReadFile('33_x86-start16.dts')
767         self.assertEqual(X86_START16_DATA, data[:len(X86_START16_DATA)])
768
769     def testPackPowerpcMpc85xxBootpgResetvec(self):
770         """Test that an image with powerpc-mpc85xx-bootpg-resetvec can be
771         created"""
772         data = self._DoReadFile('81_powerpc_mpc85xx_bootpg_resetvec.dts')
773         self.assertEqual(PPC_MPC85XX_BR_DATA, data[:len(PPC_MPC85XX_BR_DATA)])
774
775     def _RunMicrocodeTest(self, dts_fname, nodtb_data, ucode_second=False):
776         """Handle running a test for insertion of microcode
777
778         Args:
779             dts_fname: Name of test .dts file
780             nodtb_data: Data that we expect in the first section
781             ucode_second: True if the microsecond entry is second instead of
782                 third
783
784         Returns:
785             Tuple:
786                 Contents of first region (U-Boot or SPL)
787                 Offset and size components of microcode pointer, as inserted
788                     in the above (two 4-byte words)
789         """
790         data = self._DoReadFile(dts_fname, True)
791
792         # Now check the device tree has no microcode
793         if ucode_second:
794             ucode_content = data[len(nodtb_data):]
795             ucode_pos = len(nodtb_data)
796             dtb_with_ucode = ucode_content[16:]
797             fdt_len = self.GetFdtLen(dtb_with_ucode)
798         else:
799             dtb_with_ucode = data[len(nodtb_data):]
800             fdt_len = self.GetFdtLen(dtb_with_ucode)
801             ucode_content = dtb_with_ucode[fdt_len:]
802             ucode_pos = len(nodtb_data) + fdt_len
803         fname = tools.GetOutputFilename('test.dtb')
804         with open(fname, 'wb') as fd:
805             fd.write(dtb_with_ucode)
806         dtb = fdt.FdtScan(fname)
807         ucode = dtb.GetNode('/microcode')
808         self.assertTrue(ucode)
809         for node in ucode.subnodes:
810             self.assertFalse(node.props.get('data'))
811
812         # Check that the microcode appears immediately after the Fdt
813         # This matches the concatenation of the data properties in
814         # the /microcode/update@xxx nodes in 34_x86_ucode.dts.
815         ucode_data = struct.pack('>4L', 0x12345678, 0x12345679, 0xabcd0000,
816                                  0x78235609)
817         self.assertEqual(ucode_data, ucode_content[:len(ucode_data)])
818
819         # Check that the microcode pointer was inserted. It should match the
820         # expected offset and size
821         pos_and_size = struct.pack('<2L', 0xfffffe00 + ucode_pos,
822                                    len(ucode_data))
823         u_boot = data[:len(nodtb_data)]
824         return u_boot, pos_and_size
825
826     def testPackUbootMicrocode(self):
827         """Test that x86 microcode can be handled correctly
828
829         We expect to see the following in the image, in order:
830             u-boot-nodtb.bin with a microcode pointer inserted at the correct
831                 place
832             u-boot.dtb with the microcode removed
833             the microcode
834         """
835         first, pos_and_size = self._RunMicrocodeTest('34_x86_ucode.dts',
836                                                      U_BOOT_NODTB_DATA)
837         self.assertEqual('nodtb with microcode' + pos_and_size +
838                          ' somewhere in here', first)
839
840     def _RunPackUbootSingleMicrocode(self):
841         """Test that x86 microcode can be handled correctly
842
843         We expect to see the following in the image, in order:
844             u-boot-nodtb.bin with a microcode pointer inserted at the correct
845                 place
846             u-boot.dtb with the microcode
847             an empty microcode region
848         """
849         # We need the libfdt library to run this test since only that allows
850         # finding the offset of a property. This is required by
851         # Entry_u_boot_dtb_with_ucode.ObtainContents().
852         data = self._DoReadFile('35_x86_single_ucode.dts', True)
853
854         second = data[len(U_BOOT_NODTB_DATA):]
855
856         fdt_len = self.GetFdtLen(second)
857         third = second[fdt_len:]
858         second = second[:fdt_len]
859
860         ucode_data = struct.pack('>2L', 0x12345678, 0x12345679)
861         self.assertIn(ucode_data, second)
862         ucode_pos = second.find(ucode_data) + len(U_BOOT_NODTB_DATA)
863
864         # Check that the microcode pointer was inserted. It should match the
865         # expected offset and size
866         pos_and_size = struct.pack('<2L', 0xfffffe00 + ucode_pos,
867                                    len(ucode_data))
868         first = data[:len(U_BOOT_NODTB_DATA)]
869         self.assertEqual('nodtb with microcode' + pos_and_size +
870                          ' somewhere in here', first)
871
872     def testPackUbootSingleMicrocode(self):
873         """Test that x86 microcode can be handled correctly with fdt_normal.
874         """
875         self._RunPackUbootSingleMicrocode()
876
877     def testUBootImg(self):
878         """Test that u-boot.img can be put in a file"""
879         data = self._DoReadFile('36_u_boot_img.dts')
880         self.assertEqual(U_BOOT_IMG_DATA, data)
881
882     def testNoMicrocode(self):
883         """Test that a missing microcode region is detected"""
884         with self.assertRaises(ValueError) as e:
885             self._DoReadFile('37_x86_no_ucode.dts', True)
886         self.assertIn("Node '/binman/u-boot-dtb-with-ucode': No /microcode "
887                       "node found in ", str(e.exception))
888
889     def testMicrocodeWithoutNode(self):
890         """Test that a missing u-boot-dtb-with-ucode node is detected"""
891         with self.assertRaises(ValueError) as e:
892             self._DoReadFile('38_x86_ucode_missing_node.dts', True)
893         self.assertIn("Node '/binman/u-boot-with-ucode-ptr': Cannot find "
894                 "microcode region u-boot-dtb-with-ucode", str(e.exception))
895
896     def testMicrocodeWithoutNode2(self):
897         """Test that a missing u-boot-ucode node is detected"""
898         with self.assertRaises(ValueError) as e:
899             self._DoReadFile('39_x86_ucode_missing_node2.dts', True)
900         self.assertIn("Node '/binman/u-boot-with-ucode-ptr': Cannot find "
901             "microcode region u-boot-ucode", str(e.exception))
902
903     def testMicrocodeWithoutPtrInElf(self):
904         """Test that a U-Boot binary without the microcode symbol is detected"""
905         # ELF file without a '_dt_ucode_base_size' symbol
906         try:
907             with open(self.TestFile('u_boot_no_ucode_ptr')) as fd:
908                 TestFunctional._MakeInputFile('u-boot', fd.read())
909
910             with self.assertRaises(ValueError) as e:
911                 self._RunPackUbootSingleMicrocode()
912             self.assertIn("Node '/binman/u-boot-with-ucode-ptr': Cannot locate "
913                     "_dt_ucode_base_size symbol in u-boot", str(e.exception))
914
915         finally:
916             # Put the original file back
917             with open(self.TestFile('u_boot_ucode_ptr')) as fd:
918                 TestFunctional._MakeInputFile('u-boot', fd.read())
919
920     def testMicrocodeNotInImage(self):
921         """Test that microcode must be placed within the image"""
922         with self.assertRaises(ValueError) as e:
923             self._DoReadFile('40_x86_ucode_not_in_image.dts', True)
924         self.assertIn("Node '/binman/u-boot-with-ucode-ptr': Microcode "
925                 "pointer _dt_ucode_base_size at fffffe14 is outside the "
926                 "section ranging from 00000000 to 0000002e", str(e.exception))
927
928     def testWithoutMicrocode(self):
929         """Test that we can cope with an image without microcode (e.g. qemu)"""
930         with open(self.TestFile('u_boot_no_ucode_ptr')) as fd:
931             TestFunctional._MakeInputFile('u-boot', fd.read())
932         data, dtb, _, _ = self._DoReadFileDtb('44_x86_optional_ucode.dts', True)
933
934         # Now check the device tree has no microcode
935         self.assertEqual(U_BOOT_NODTB_DATA, data[:len(U_BOOT_NODTB_DATA)])
936         second = data[len(U_BOOT_NODTB_DATA):]
937
938         fdt_len = self.GetFdtLen(second)
939         self.assertEqual(dtb, second[:fdt_len])
940
941         used_len = len(U_BOOT_NODTB_DATA) + fdt_len
942         third = data[used_len:]
943         self.assertEqual(chr(0) * (0x200 - used_len), third)
944
945     def testUnknownPosSize(self):
946         """Test that microcode must be placed within the image"""
947         with self.assertRaises(ValueError) as e:
948             self._DoReadFile('41_unknown_pos_size.dts', True)
949         self.assertIn("Section '/binman': Unable to set offset/size for unknown "
950                 "entry 'invalid-entry'", str(e.exception))
951
952     def testPackFsp(self):
953         """Test that an image with a FSP binary can be created"""
954         data = self._DoReadFile('42_intel-fsp.dts')
955         self.assertEqual(FSP_DATA, data[:len(FSP_DATA)])
956
957     def testPackCmc(self):
958         """Test that an image with a CMC binary can be created"""
959         data = self._DoReadFile('43_intel-cmc.dts')
960         self.assertEqual(CMC_DATA, data[:len(CMC_DATA)])
961
962     def testPackVbt(self):
963         """Test that an image with a VBT binary can be created"""
964         data = self._DoReadFile('46_intel-vbt.dts')
965         self.assertEqual(VBT_DATA, data[:len(VBT_DATA)])
966
967     def testSplBssPad(self):
968         """Test that we can pad SPL's BSS with zeros"""
969         # ELF file with a '__bss_size' symbol
970         with open(self.TestFile('bss_data')) as fd:
971             TestFunctional._MakeInputFile('spl/u-boot-spl', fd.read())
972         data = self._DoReadFile('47_spl_bss_pad.dts')
973         self.assertEqual(U_BOOT_SPL_DATA + (chr(0) * 10) + U_BOOT_DATA, data)
974
975         with open(self.TestFile('u_boot_ucode_ptr')) as fd:
976             TestFunctional._MakeInputFile('spl/u-boot-spl', fd.read())
977         with self.assertRaises(ValueError) as e:
978             data = self._DoReadFile('47_spl_bss_pad.dts')
979         self.assertIn('Expected __bss_size symbol in spl/u-boot-spl',
980                       str(e.exception))
981
982     def testPackStart16Spl(self):
983         """Test that an image with an x86 start16 region can be created"""
984         data = self._DoReadFile('48_x86-start16-spl.dts')
985         self.assertEqual(X86_START16_SPL_DATA, data[:len(X86_START16_SPL_DATA)])
986
987     def _PackUbootSplMicrocode(self, dts, ucode_second=False):
988         """Helper function for microcode tests
989
990         We expect to see the following in the image, in order:
991             u-boot-spl-nodtb.bin with a microcode pointer inserted at the
992                 correct place
993             u-boot.dtb with the microcode removed
994             the microcode
995
996         Args:
997             dts: Device tree file to use for test
998             ucode_second: True if the microsecond entry is second instead of
999                 third
1000         """
1001         # ELF file with a '_dt_ucode_base_size' symbol
1002         with open(self.TestFile('u_boot_ucode_ptr')) as fd:
1003             TestFunctional._MakeInputFile('spl/u-boot-spl', fd.read())
1004         first, pos_and_size = self._RunMicrocodeTest(dts, U_BOOT_SPL_NODTB_DATA,
1005                                                      ucode_second=ucode_second)
1006         self.assertEqual('splnodtb with microc' + pos_and_size +
1007                          'ter somewhere in here', first)
1008
1009     def testPackUbootSplMicrocode(self):
1010         """Test that x86 microcode can be handled correctly in SPL"""
1011         self._PackUbootSplMicrocode('49_x86_ucode_spl.dts')
1012
1013     def testPackUbootSplMicrocodeReorder(self):
1014         """Test that order doesn't matter for microcode entries
1015
1016         This is the same as testPackUbootSplMicrocode but when we process the
1017         u-boot-ucode entry we have not yet seen the u-boot-dtb-with-ucode
1018         entry, so we reply on binman to try later.
1019         """
1020         self._PackUbootSplMicrocode('58_x86_ucode_spl_needs_retry.dts',
1021                                     ucode_second=True)
1022
1023     def testPackMrc(self):
1024         """Test that an image with an MRC binary can be created"""
1025         data = self._DoReadFile('50_intel_mrc.dts')
1026         self.assertEqual(MRC_DATA, data[:len(MRC_DATA)])
1027
1028     def testSplDtb(self):
1029         """Test that an image with spl/u-boot-spl.dtb can be created"""
1030         data = self._DoReadFile('51_u_boot_spl_dtb.dts')
1031         self.assertEqual(U_BOOT_SPL_DTB_DATA, data[:len(U_BOOT_SPL_DTB_DATA)])
1032
1033     def testSplNoDtb(self):
1034         """Test that an image with spl/u-boot-spl-nodtb.bin can be created"""
1035         data = self._DoReadFile('52_u_boot_spl_nodtb.dts')
1036         self.assertEqual(U_BOOT_SPL_NODTB_DATA, data[:len(U_BOOT_SPL_NODTB_DATA)])
1037
1038     def testSymbols(self):
1039         """Test binman can assign symbols embedded in U-Boot"""
1040         elf_fname = self.TestFile('u_boot_binman_syms')
1041         syms = elf.GetSymbols(elf_fname, ['binman', 'image'])
1042         addr = elf.GetSymbolAddress(elf_fname, '__image_copy_start')
1043         self.assertEqual(syms['_binman_u_boot_spl_prop_offset'].address, addr)
1044
1045         with open(self.TestFile('u_boot_binman_syms')) as fd:
1046             TestFunctional._MakeInputFile('spl/u-boot-spl', fd.read())
1047         data = self._DoReadFile('53_symbols.dts')
1048         sym_values = struct.pack('<LQL', 0x24 + 0, 0x24 + 24, 0x24 + 20)
1049         expected = (sym_values + U_BOOT_SPL_DATA[16:] + chr(0xff) +
1050                     U_BOOT_DATA +
1051                     sym_values + U_BOOT_SPL_DATA[16:])
1052         self.assertEqual(expected, data)
1053
1054     def testPackUnitAddress(self):
1055         """Test that we support multiple binaries with the same name"""
1056         data = self._DoReadFile('54_unit_address.dts')
1057         self.assertEqual(U_BOOT_DATA + U_BOOT_DATA, data)
1058
1059     def testSections(self):
1060         """Basic test of sections"""
1061         data = self._DoReadFile('55_sections.dts')
1062         expected = (U_BOOT_DATA + '!' * 12 + U_BOOT_DATA + 'a' * 12 +
1063                     U_BOOT_DATA + '&' * 4)
1064         self.assertEqual(expected, data)
1065
1066     def testMap(self):
1067         """Tests outputting a map of the images"""
1068         _, _, map_data, _ = self._DoReadFileDtb('55_sections.dts', map=True)
1069         self.assertEqual('''ImagePos    Offset      Size  Name
1070 00000000  00000000  00000028  main-section
1071 00000000   00000000  00000010  section@0
1072 00000000    00000000  00000004  u-boot
1073 00000010   00000010  00000010  section@1
1074 00000010    00000000  00000004  u-boot
1075 00000020   00000020  00000004  section@2
1076 00000020    00000000  00000004  u-boot
1077 ''', map_data)
1078
1079     def testNamePrefix(self):
1080         """Tests that name prefixes are used"""
1081         _, _, map_data, _ = self._DoReadFileDtb('56_name_prefix.dts', map=True)
1082         self.assertEqual('''ImagePos    Offset      Size  Name
1083 00000000  00000000  00000028  main-section
1084 00000000   00000000  00000010  section@0
1085 00000000    00000000  00000004  ro-u-boot
1086 00000010   00000010  00000010  section@1
1087 00000010    00000000  00000004  rw-u-boot
1088 ''', map_data)
1089
1090     def testUnknownContents(self):
1091         """Test that obtaining the contents works as expected"""
1092         with self.assertRaises(ValueError) as e:
1093             self._DoReadFile('57_unknown_contents.dts', True)
1094         self.assertIn("Section '/binman': Internal error: Could not complete "
1095                 "processing of contents: remaining [<_testing.Entry__testing ",
1096                 str(e.exception))
1097
1098     def testBadChangeSize(self):
1099         """Test that trying to change the size of an entry fails"""
1100         with self.assertRaises(ValueError) as e:
1101             self._DoReadFile('59_change_size.dts', True)
1102         self.assertIn("Node '/binman/_testing': Cannot update entry size from "
1103                       '2 to 1', str(e.exception))
1104
1105     def testUpdateFdt(self):
1106         """Test that we can update the device tree with offset/size info"""
1107         _, _, _, out_dtb_fname = self._DoReadFileDtb('60_fdt_update.dts',
1108                                                      update_dtb=True)
1109         dtb = fdt.Fdt(out_dtb_fname)
1110         dtb.Scan()
1111         props = self._GetPropTree(dtb, ['offset', 'size', 'image-pos'])
1112         self.assertEqual({
1113             'image-pos': 0,
1114             'offset': 0,
1115             '_testing:offset': 32,
1116             '_testing:size': 1,
1117             '_testing:image-pos': 32,
1118             'section@0/u-boot:offset': 0,
1119             'section@0/u-boot:size': len(U_BOOT_DATA),
1120             'section@0/u-boot:image-pos': 0,
1121             'section@0:offset': 0,
1122             'section@0:size': 16,
1123             'section@0:image-pos': 0,
1124
1125             'section@1/u-boot:offset': 0,
1126             'section@1/u-boot:size': len(U_BOOT_DATA),
1127             'section@1/u-boot:image-pos': 16,
1128             'section@1:offset': 16,
1129             'section@1:size': 16,
1130             'section@1:image-pos': 16,
1131             'size': 40
1132         }, props)
1133
1134     def testUpdateFdtBad(self):
1135         """Test that we detect when ProcessFdt never completes"""
1136         with self.assertRaises(ValueError) as e:
1137             self._DoReadFileDtb('61_fdt_update_bad.dts', update_dtb=True)
1138         self.assertIn('Could not complete processing of Fdt: remaining '
1139                       '[<_testing.Entry__testing', str(e.exception))
1140
1141     def testEntryArgs(self):
1142         """Test passing arguments to entries from the command line"""
1143         entry_args = {
1144             'test-str-arg': 'test1',
1145             'test-int-arg': '456',
1146         }
1147         self._DoReadFileDtb('62_entry_args.dts', entry_args=entry_args)
1148         self.assertIn('image', control.images)
1149         entry = control.images['image'].GetEntries()['_testing']
1150         self.assertEqual('test0', entry.test_str_fdt)
1151         self.assertEqual('test1', entry.test_str_arg)
1152         self.assertEqual(123, entry.test_int_fdt)
1153         self.assertEqual(456, entry.test_int_arg)
1154
1155     def testEntryArgsMissing(self):
1156         """Test missing arguments and properties"""
1157         entry_args = {
1158             'test-int-arg': '456',
1159         }
1160         self._DoReadFileDtb('63_entry_args_missing.dts', entry_args=entry_args)
1161         entry = control.images['image'].GetEntries()['_testing']
1162         self.assertEqual('test0', entry.test_str_fdt)
1163         self.assertEqual(None, entry.test_str_arg)
1164         self.assertEqual(None, entry.test_int_fdt)
1165         self.assertEqual(456, entry.test_int_arg)
1166
1167     def testEntryArgsRequired(self):
1168         """Test missing arguments and properties"""
1169         entry_args = {
1170             'test-int-arg': '456',
1171         }
1172         with self.assertRaises(ValueError) as e:
1173             self._DoReadFileDtb('64_entry_args_required.dts')
1174         self.assertIn("Node '/binman/_testing': Missing required "
1175             'properties/entry args: test-str-arg, test-int-fdt, test-int-arg',
1176             str(e.exception))
1177
1178     def testEntryArgsInvalidFormat(self):
1179         """Test that an invalid entry-argument format is detected"""
1180         args = ['-d', self.TestFile('64_entry_args_required.dts'), '-ano-value']
1181         with self.assertRaises(ValueError) as e:
1182             self._DoBinman(*args)
1183         self.assertIn("Invalid entry arguemnt 'no-value'", str(e.exception))
1184
1185     def testEntryArgsInvalidInteger(self):
1186         """Test that an invalid entry-argument integer is detected"""
1187         entry_args = {
1188             'test-int-arg': 'abc',
1189         }
1190         with self.assertRaises(ValueError) as e:
1191             self._DoReadFileDtb('62_entry_args.dts', entry_args=entry_args)
1192         self.assertIn("Node '/binman/_testing': Cannot convert entry arg "
1193                       "'test-int-arg' (value 'abc') to integer",
1194             str(e.exception))
1195
1196     def testEntryArgsInvalidDatatype(self):
1197         """Test that an invalid entry-argument datatype is detected
1198
1199         This test could be written in entry_test.py except that it needs
1200         access to control.entry_args, which seems more than that module should
1201         be able to see.
1202         """
1203         entry_args = {
1204             'test-bad-datatype-arg': '12',
1205         }
1206         with self.assertRaises(ValueError) as e:
1207             self._DoReadFileDtb('65_entry_args_unknown_datatype.dts',
1208                                 entry_args=entry_args)
1209         self.assertIn('GetArg() internal error: Unknown data type ',
1210                       str(e.exception))
1211
1212     def testText(self):
1213         """Test for a text entry type"""
1214         entry_args = {
1215             'test-id': TEXT_DATA,
1216             'test-id2': TEXT_DATA2,
1217             'test-id3': TEXT_DATA3,
1218         }
1219         data, _, _, _ = self._DoReadFileDtb('66_text.dts',
1220                                             entry_args=entry_args)
1221         expected = (TEXT_DATA + chr(0) * (8 - len(TEXT_DATA)) + TEXT_DATA2 +
1222                     TEXT_DATA3 + 'some text')
1223         self.assertEqual(expected, data)
1224
1225     def testEntryDocs(self):
1226         """Test for creation of entry documentation"""
1227         with test_util.capture_sys_output() as (stdout, stderr):
1228             control.WriteEntryDocs(binman.GetEntryModules())
1229         self.assertTrue(len(stdout.getvalue()) > 0)
1230
1231     def testEntryDocsMissing(self):
1232         """Test handling of missing entry documentation"""
1233         with self.assertRaises(ValueError) as e:
1234             with test_util.capture_sys_output() as (stdout, stderr):
1235                 control.WriteEntryDocs(binman.GetEntryModules(), 'u_boot')
1236         self.assertIn('Documentation is missing for modules: u_boot',
1237                       str(e.exception))
1238
1239     def testFmap(self):
1240         """Basic test of generation of a flashrom fmap"""
1241         data = self._DoReadFile('67_fmap.dts')
1242         fhdr, fentries = fmap_util.DecodeFmap(data[32:])
1243         expected = U_BOOT_DATA + '!' * 12 + U_BOOT_DATA + 'a' * 12
1244         self.assertEqual(expected, data[:32])
1245         self.assertEqual('__FMAP__', fhdr.signature)
1246         self.assertEqual(1, fhdr.ver_major)
1247         self.assertEqual(0, fhdr.ver_minor)
1248         self.assertEqual(0, fhdr.base)
1249         self.assertEqual(16 + 16 +
1250                          fmap_util.FMAP_HEADER_LEN +
1251                          fmap_util.FMAP_AREA_LEN * 3, fhdr.image_size)
1252         self.assertEqual('FMAP', fhdr.name)
1253         self.assertEqual(3, fhdr.nareas)
1254         for fentry in fentries:
1255             self.assertEqual(0, fentry.flags)
1256
1257         self.assertEqual(0, fentries[0].offset)
1258         self.assertEqual(4, fentries[0].size)
1259         self.assertEqual('RO_U_BOOT', fentries[0].name)
1260
1261         self.assertEqual(16, fentries[1].offset)
1262         self.assertEqual(4, fentries[1].size)
1263         self.assertEqual('RW_U_BOOT', fentries[1].name)
1264
1265         self.assertEqual(32, fentries[2].offset)
1266         self.assertEqual(fmap_util.FMAP_HEADER_LEN +
1267                          fmap_util.FMAP_AREA_LEN * 3, fentries[2].size)
1268         self.assertEqual('FMAP', fentries[2].name)
1269
1270     def testBlobNamedByArg(self):
1271         """Test we can add a blob with the filename coming from an entry arg"""
1272         entry_args = {
1273             'cros-ec-rw-path': 'ecrw.bin',
1274         }
1275         data, _, _, _ = self._DoReadFileDtb('68_blob_named_by_arg.dts',
1276                                             entry_args=entry_args)
1277
1278     def testFill(self):
1279         """Test for an fill entry type"""
1280         data = self._DoReadFile('69_fill.dts')
1281         expected = 8 * chr(0xff) + 8 * chr(0)
1282         self.assertEqual(expected, data)
1283
1284     def testFillNoSize(self):
1285         """Test for an fill entry type with no size"""
1286         with self.assertRaises(ValueError) as e:
1287             self._DoReadFile('70_fill_no_size.dts')
1288         self.assertIn("'fill' entry must have a size property",
1289                       str(e.exception))
1290
1291     def _HandleGbbCommand(self, pipe_list):
1292         """Fake calls to the futility utility"""
1293         if pipe_list[0][0] == 'futility':
1294             fname = pipe_list[0][-1]
1295             # Append our GBB data to the file, which will happen every time the
1296             # futility command is called.
1297             with open(fname, 'a') as fd:
1298                 fd.write(GBB_DATA)
1299             return command.CommandResult()
1300
1301     def testGbb(self):
1302         """Test for the Chromium OS Google Binary Block"""
1303         command.test_result = self._HandleGbbCommand
1304         entry_args = {
1305             'keydir': 'devkeys',
1306             'bmpblk': 'bmpblk.bin',
1307         }
1308         data, _, _, _ = self._DoReadFileDtb('71_gbb.dts', entry_args=entry_args)
1309
1310         # Since futility
1311         expected = GBB_DATA + GBB_DATA + 8 * chr(0) + (0x2180 - 16) * chr(0)
1312         self.assertEqual(expected, data)
1313
1314     def testGbbTooSmall(self):
1315         """Test for the Chromium OS Google Binary Block being large enough"""
1316         with self.assertRaises(ValueError) as e:
1317             self._DoReadFileDtb('72_gbb_too_small.dts')
1318         self.assertIn("Node '/binman/gbb': GBB is too small",
1319                       str(e.exception))
1320
1321     def testGbbNoSize(self):
1322         """Test for the Chromium OS Google Binary Block having a size"""
1323         with self.assertRaises(ValueError) as e:
1324             self._DoReadFileDtb('73_gbb_no_size.dts')
1325         self.assertIn("Node '/binman/gbb': GBB must have a fixed size",
1326                       str(e.exception))
1327
1328     def _HandleVblockCommand(self, pipe_list):
1329         """Fake calls to the futility utility"""
1330         if pipe_list[0][0] == 'futility':
1331             fname = pipe_list[0][3]
1332             with open(fname, 'w') as fd:
1333                 fd.write(VBLOCK_DATA)
1334             return command.CommandResult()
1335
1336     def testVblock(self):
1337         """Test for the Chromium OS Verified Boot Block"""
1338         command.test_result = self._HandleVblockCommand
1339         entry_args = {
1340             'keydir': 'devkeys',
1341         }
1342         data, _, _, _ = self._DoReadFileDtb('74_vblock.dts',
1343                                             entry_args=entry_args)
1344         expected = U_BOOT_DATA + VBLOCK_DATA + U_BOOT_DTB_DATA
1345         self.assertEqual(expected, data)
1346
1347     def testVblockNoContent(self):
1348         """Test we detect a vblock which has no content to sign"""
1349         with self.assertRaises(ValueError) as e:
1350             self._DoReadFile('75_vblock_no_content.dts')
1351         self.assertIn("Node '/binman/vblock': Vblock must have a 'content' "
1352                       'property', str(e.exception))
1353
1354     def testVblockBadPhandle(self):
1355         """Test that we detect a vblock with an invalid phandle in contents"""
1356         with self.assertRaises(ValueError) as e:
1357             self._DoReadFile('76_vblock_bad_phandle.dts')
1358         self.assertIn("Node '/binman/vblock': Cannot find node for phandle "
1359                       '1000', str(e.exception))
1360
1361     def testVblockBadEntry(self):
1362         """Test that we detect an entry that points to a non-entry"""
1363         with self.assertRaises(ValueError) as e:
1364             self._DoReadFile('77_vblock_bad_entry.dts')
1365         self.assertIn("Node '/binman/vblock': Cannot find entry for node "
1366                       "'other'", str(e.exception))
1367
1368     def testTpl(self):
1369         """Test that an image with TPL and ots device tree can be created"""
1370         # ELF file with a '__bss_size' symbol
1371         with open(self.TestFile('bss_data')) as fd:
1372             TestFunctional._MakeInputFile('tpl/u-boot-tpl', fd.read())
1373         data = self._DoReadFile('78_u_boot_tpl.dts')
1374         self.assertEqual(U_BOOT_TPL_DATA + U_BOOT_TPL_DTB_DATA, data)
1375
1376     def testUsesPos(self):
1377         """Test that the 'pos' property cannot be used anymore"""
1378         with self.assertRaises(ValueError) as e:
1379            data = self._DoReadFile('79_uses_pos.dts')
1380         self.assertIn("Node '/binman/u-boot': Please use 'offset' instead of "
1381                       "'pos'", str(e.exception))
1382
1383
1384 if __name__ == "__main__":
1385     unittest.main()