ce473dfaffbf4de6665db0cb0ae6f0f3b85914ca
[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 U_BOOT_NODTB_DATA     = 'nodtb with microcode pointer somewhere in here'
43 U_BOOT_SPL_NODTB_DATA = 'splnodtb with microcode pointer somewhere in here'
44 FSP_DATA              = 'fsp'
45 CMC_DATA              = 'cmc'
46 VBT_DATA              = 'vbt'
47 MRC_DATA              = 'mrc'
48 TEXT_DATA             = 'text'
49 TEXT_DATA2            = 'text2'
50 TEXT_DATA3            = 'text3'
51 CROS_EC_RW_DATA       = 'ecrw'
52 GBB_DATA              = 'gbbd'
53 BMPBLK_DATA           = 'bmp'
54 VBLOCK_DATA           = 'vblk'
55
56
57 class TestFunctional(unittest.TestCase):
58     """Functional tests for binman
59
60     Most of these use a sample .dts file to build an image and then check
61     that it looks correct. The sample files are in the test/ subdirectory
62     and are numbered.
63
64     For each entry type a very small test file is created using fixed
65     string contents. This makes it easy to test that things look right, and
66     debug problems.
67
68     In some cases a 'real' file must be used - these are also supplied in
69     the test/ diurectory.
70     """
71     @classmethod
72     def setUpClass(self):
73         global entry
74         import entry
75
76         # Handle the case where argv[0] is 'python'
77         self._binman_dir = os.path.dirname(os.path.realpath(sys.argv[0]))
78         self._binman_pathname = os.path.join(self._binman_dir, 'binman')
79
80         # Create a temporary directory for input files
81         self._indir = tempfile.mkdtemp(prefix='binmant.')
82
83         # Create some test files
84         TestFunctional._MakeInputFile('u-boot.bin', U_BOOT_DATA)
85         TestFunctional._MakeInputFile('u-boot.img', U_BOOT_IMG_DATA)
86         TestFunctional._MakeInputFile('spl/u-boot-spl.bin', U_BOOT_SPL_DATA)
87         TestFunctional._MakeInputFile('tpl/u-boot-tpl.bin', U_BOOT_TPL_DATA)
88         TestFunctional._MakeInputFile('blobfile', BLOB_DATA)
89         TestFunctional._MakeInputFile('me.bin', ME_DATA)
90         TestFunctional._MakeInputFile('vga.bin', VGA_DATA)
91         self._ResetDtbs()
92         TestFunctional._MakeInputFile('u-boot-x86-16bit.bin', X86_START16_DATA)
93         TestFunctional._MakeInputFile('spl/u-boot-x86-16bit-spl.bin',
94                                       X86_START16_SPL_DATA)
95         TestFunctional._MakeInputFile('u-boot-nodtb.bin', U_BOOT_NODTB_DATA)
96         TestFunctional._MakeInputFile('spl/u-boot-spl-nodtb.bin',
97                                       U_BOOT_SPL_NODTB_DATA)
98         TestFunctional._MakeInputFile('fsp.bin', FSP_DATA)
99         TestFunctional._MakeInputFile('cmc.bin', CMC_DATA)
100         TestFunctional._MakeInputFile('vbt.bin', VBT_DATA)
101         TestFunctional._MakeInputFile('mrc.bin', MRC_DATA)
102         TestFunctional._MakeInputFile('ecrw.bin', CROS_EC_RW_DATA)
103         TestFunctional._MakeInputDir('devkeys')
104         TestFunctional._MakeInputFile('bmpblk.bin', BMPBLK_DATA)
105         self._output_setup = False
106
107         # ELF file with a '_dt_ucode_base_size' symbol
108         with open(self.TestFile('u_boot_ucode_ptr')) as fd:
109             TestFunctional._MakeInputFile('u-boot', fd.read())
110
111         # Intel flash descriptor file
112         with open(self.TestFile('descriptor.bin')) as fd:
113             TestFunctional._MakeInputFile('descriptor.bin', fd.read())
114
115     @classmethod
116     def tearDownClass(self):
117         """Remove the temporary input directory and its contents"""
118         if self._indir:
119             shutil.rmtree(self._indir)
120         self._indir = None
121
122     def setUp(self):
123         # Enable this to turn on debugging output
124         # tout.Init(tout.DEBUG)
125         command.test_result = None
126
127     def tearDown(self):
128         """Remove the temporary output directory"""
129         tools._FinaliseForTest()
130
131     @classmethod
132     def _ResetDtbs(self):
133         TestFunctional._MakeInputFile('u-boot.dtb', U_BOOT_DTB_DATA)
134         TestFunctional._MakeInputFile('spl/u-boot-spl.dtb', U_BOOT_SPL_DTB_DATA)
135         TestFunctional._MakeInputFile('tpl/u-boot-tpl.dtb', U_BOOT_TPL_DTB_DATA)
136
137     def _RunBinman(self, *args, **kwargs):
138         """Run binman using the command line
139
140         Args:
141             Arguments to pass, as a list of strings
142             kwargs: Arguments to pass to Command.RunPipe()
143         """
144         result = command.RunPipe([[self._binman_pathname] + list(args)],
145                 capture=True, capture_stderr=True, raise_on_error=False)
146         if result.return_code and kwargs.get('raise_on_error', True):
147             raise Exception("Error running '%s': %s" % (' '.join(args),
148                             result.stdout + result.stderr))
149         return result
150
151     def _DoBinman(self, *args):
152         """Run binman using directly (in the same process)
153
154         Args:
155             Arguments to pass, as a list of strings
156         Returns:
157             Return value (0 for success)
158         """
159         args = list(args)
160         if '-D' in sys.argv:
161             args = args + ['-D']
162         (options, args) = cmdline.ParseArgs(args)
163         options.pager = 'binman-invalid-pager'
164         options.build_dir = self._indir
165
166         # For testing, you can force an increase in verbosity here
167         # options.verbosity = tout.DEBUG
168         return control.Binman(options, args)
169
170     def _DoTestFile(self, fname, debug=False, map=False, update_dtb=False,
171                     entry_args=None):
172         """Run binman with a given test file
173
174         Args:
175             fname: Device-tree source filename to use (e.g. 05_simple.dts)
176             debug: True to enable debugging output
177             map: True to output map files for the images
178             update_dtb: Update the offset and size of each entry in the device
179                 tree before packing it into the image
180         """
181         args = ['-p', '-I', self._indir, '-d', self.TestFile(fname)]
182         if debug:
183             args.append('-D')
184         if map:
185             args.append('-m')
186         if update_dtb:
187             args.append('-up')
188         if entry_args:
189             for arg, value in entry_args.iteritems():
190                 args.append('-a%s=%s' % (arg, value))
191         return self._DoBinman(*args)
192
193     def _SetupDtb(self, fname, outfile='u-boot.dtb'):
194         """Set up a new test device-tree file
195
196         The given file is compiled and set up as the device tree to be used
197         for ths test.
198
199         Args:
200             fname: Filename of .dts file to read
201             outfile: Output filename for compiled device-tree binary
202
203         Returns:
204             Contents of device-tree binary
205         """
206         if not self._output_setup:
207             tools.PrepareOutputDir(self._indir, True)
208             self._output_setup = True
209         dtb = fdt_util.EnsureCompiled(self.TestFile(fname))
210         with open(dtb) as fd:
211             data = fd.read()
212             TestFunctional._MakeInputFile(outfile, data)
213             return data
214
215     def _DoReadFileDtb(self, fname, use_real_dtb=False, map=False,
216                        update_dtb=False, entry_args=None):
217         """Run binman and return the resulting image
218
219         This runs binman with a given test file and then reads the resulting
220         output file. It is a shortcut function since most tests need to do
221         these steps.
222
223         Raises an assertion failure if binman returns a non-zero exit code.
224
225         Args:
226             fname: Device-tree source filename to use (e.g. 05_simple.dts)
227             use_real_dtb: True to use the test file as the contents of
228                 the u-boot-dtb entry. Normally this is not needed and the
229                 test contents (the U_BOOT_DTB_DATA string) can be used.
230                 But in some test we need the real contents.
231             map: True to output map files for the images
232             update_dtb: Update the offset and size of each entry in the device
233                 tree before packing it into the image
234
235         Returns:
236             Tuple:
237                 Resulting image contents
238                 Device tree contents
239                 Map data showing contents of image (or None if none)
240                 Output device tree binary filename ('u-boot.dtb' path)
241         """
242         dtb_data = None
243         # Use the compiled test file as the u-boot-dtb input
244         if use_real_dtb:
245             dtb_data = self._SetupDtb(fname)
246
247         try:
248             retcode = self._DoTestFile(fname, map=map, update_dtb=update_dtb,
249                                        entry_args=entry_args)
250             self.assertEqual(0, retcode)
251             out_dtb_fname = control.GetFdtPath('u-boot.dtb')
252
253             # Find the (only) image, read it and return its contents
254             image = control.images['image']
255             image_fname = tools.GetOutputFilename('image.bin')
256             self.assertTrue(os.path.exists(image_fname))
257             if map:
258                 map_fname = tools.GetOutputFilename('image.map')
259                 with open(map_fname) as fd:
260                     map_data = fd.read()
261             else:
262                 map_data = None
263             with open(image_fname) as fd:
264                 return fd.read(), dtb_data, map_data, out_dtb_fname
265         finally:
266             # Put the test file back
267             if use_real_dtb:
268                 self._ResetDtbs()
269
270     def _DoReadFile(self, fname, use_real_dtb=False):
271         """Helper function which discards the device-tree binary
272
273         Args:
274             fname: Device-tree source filename to use (e.g. 05_simple.dts)
275             use_real_dtb: True to use the test file as the contents of
276                 the u-boot-dtb entry. Normally this is not needed and the
277                 test contents (the U_BOOT_DTB_DATA string) can be used.
278                 But in some test we need the real contents.
279
280         Returns:
281             Resulting image contents
282         """
283         return self._DoReadFileDtb(fname, use_real_dtb)[0]
284
285     @classmethod
286     def _MakeInputFile(self, fname, contents):
287         """Create a new test input file, creating directories as needed
288
289         Args:
290             fname: Filename to create
291             contents: File contents to write in to the file
292         Returns:
293             Full pathname of file created
294         """
295         pathname = os.path.join(self._indir, fname)
296         dirname = os.path.dirname(pathname)
297         if dirname and not os.path.exists(dirname):
298             os.makedirs(dirname)
299         with open(pathname, 'wb') as fd:
300             fd.write(contents)
301         return pathname
302
303     @classmethod
304     def _MakeInputDir(self, dirname):
305         """Create a new test input directory, creating directories as needed
306
307         Args:
308             dirname: Directory name to create
309
310         Returns:
311             Full pathname of directory created
312         """
313         pathname = os.path.join(self._indir, dirname)
314         if not os.path.exists(pathname):
315             os.makedirs(pathname)
316         return pathname
317
318     @classmethod
319     def TestFile(self, fname):
320         return os.path.join(self._binman_dir, 'test', fname)
321
322     def AssertInList(self, grep_list, target):
323         """Assert that at least one of a list of things is in a target
324
325         Args:
326             grep_list: List of strings to check
327             target: Target string
328         """
329         for grep in grep_list:
330             if grep in target:
331                 return
332         self.fail("Error: '%' not found in '%s'" % (grep_list, target))
333
334     def CheckNoGaps(self, entries):
335         """Check that all entries fit together without gaps
336
337         Args:
338             entries: List of entries to check
339         """
340         offset = 0
341         for entry in entries.values():
342             self.assertEqual(offset, entry.offset)
343             offset += entry.size
344
345     def GetFdtLen(self, dtb):
346         """Get the totalsize field from a device-tree binary
347
348         Args:
349             dtb: Device-tree binary contents
350
351         Returns:
352             Total size of device-tree binary, from the header
353         """
354         return struct.unpack('>L', dtb[4:8])[0]
355
356     def _GetPropTree(self, dtb_data, node_names):
357         def AddNode(node, path):
358             if node.name != '/':
359                 path += '/' + node.name
360             for subnode in node.subnodes:
361                 for prop in subnode.props.values():
362                     if prop.name in node_names:
363                         prop_path = path + '/' + subnode.name + ':' + prop.name
364                         tree[prop_path[len('/binman/'):]] = fdt_util.fdt32_to_cpu(
365                             prop.value)
366                 AddNode(subnode, path)
367
368         tree = {}
369         dtb = fdt.Fdt(dtb_data)
370         dtb.Scan()
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 testPackX86RomOutside(self):
717         """Test that the end-at-4gb property checks for offset boundaries"""
718         with self.assertRaises(ValueError) as e:
719             self._DoTestFile('28_pack_4gb_outside.dts')
720         self.assertIn("Node '/binman/u-boot': Offset 0x0 (0) is outside "
721                       "the section starting at 0xffffffe0 (4294967264)",
722                       str(e.exception))
723
724     def testPackX86Rom(self):
725         """Test that a basic x86 ROM can be created"""
726         data = self._DoReadFile('29_x86-rom.dts')
727         self.assertEqual(U_BOOT_DATA + chr(0) * 7 + U_BOOT_SPL_DATA +
728                          chr(0) * 2, data)
729
730     def testPackX86RomMeNoDesc(self):
731         """Test that an invalid Intel descriptor entry is detected"""
732         TestFunctional._MakeInputFile('descriptor.bin', '')
733         with self.assertRaises(ValueError) as e:
734             self._DoTestFile('31_x86-rom-me.dts')
735         self.assertIn("Node '/binman/intel-descriptor': Cannot find FD "
736                       "signature", str(e.exception))
737
738     def testPackX86RomBadDesc(self):
739         """Test that the Intel requires a descriptor entry"""
740         with self.assertRaises(ValueError) as e:
741             self._DoTestFile('30_x86-rom-me-no-desc.dts')
742         self.assertIn("Node '/binman/intel-me': No offset set with "
743                       "offset-unset: should another entry provide this correct "
744                       "offset?", str(e.exception))
745
746     def testPackX86RomMe(self):
747         """Test that an x86 ROM with an ME region can be created"""
748         data = self._DoReadFile('31_x86-rom-me.dts')
749         self.assertEqual(ME_DATA, data[0x1000:0x1000 + len(ME_DATA)])
750
751     def testPackVga(self):
752         """Test that an image with a VGA binary can be created"""
753         data = self._DoReadFile('32_intel-vga.dts')
754         self.assertEqual(VGA_DATA, data[:len(VGA_DATA)])
755
756     def testPackStart16(self):
757         """Test that an image with an x86 start16 region can be created"""
758         data = self._DoReadFile('33_x86-start16.dts')
759         self.assertEqual(X86_START16_DATA, data[:len(X86_START16_DATA)])
760
761     def _RunMicrocodeTest(self, dts_fname, nodtb_data, ucode_second=False):
762         """Handle running a test for insertion of microcode
763
764         Args:
765             dts_fname: Name of test .dts file
766             nodtb_data: Data that we expect in the first section
767             ucode_second: True if the microsecond entry is second instead of
768                 third
769
770         Returns:
771             Tuple:
772                 Contents of first region (U-Boot or SPL)
773                 Offset and size components of microcode pointer, as inserted
774                     in the above (two 4-byte words)
775         """
776         data = self._DoReadFile(dts_fname, True)
777
778         # Now check the device tree has no microcode
779         if ucode_second:
780             ucode_content = data[len(nodtb_data):]
781             ucode_pos = len(nodtb_data)
782             dtb_with_ucode = ucode_content[16:]
783             fdt_len = self.GetFdtLen(dtb_with_ucode)
784         else:
785             dtb_with_ucode = data[len(nodtb_data):]
786             fdt_len = self.GetFdtLen(dtb_with_ucode)
787             ucode_content = dtb_with_ucode[fdt_len:]
788             ucode_pos = len(nodtb_data) + fdt_len
789         fname = tools.GetOutputFilename('test.dtb')
790         with open(fname, 'wb') as fd:
791             fd.write(dtb_with_ucode)
792         dtb = fdt.FdtScan(fname)
793         ucode = dtb.GetNode('/microcode')
794         self.assertTrue(ucode)
795         for node in ucode.subnodes:
796             self.assertFalse(node.props.get('data'))
797
798         # Check that the microcode appears immediately after the Fdt
799         # This matches the concatenation of the data properties in
800         # the /microcode/update@xxx nodes in 34_x86_ucode.dts.
801         ucode_data = struct.pack('>4L', 0x12345678, 0x12345679, 0xabcd0000,
802                                  0x78235609)
803         self.assertEqual(ucode_data, ucode_content[:len(ucode_data)])
804
805         # Check that the microcode pointer was inserted. It should match the
806         # expected offset and size
807         pos_and_size = struct.pack('<2L', 0xfffffe00 + ucode_pos,
808                                    len(ucode_data))
809         u_boot = data[:len(nodtb_data)]
810         return u_boot, pos_and_size
811
812     def testPackUbootMicrocode(self):
813         """Test that x86 microcode can be handled correctly
814
815         We expect to see the following in the image, in order:
816             u-boot-nodtb.bin with a microcode pointer inserted at the correct
817                 place
818             u-boot.dtb with the microcode removed
819             the microcode
820         """
821         first, pos_and_size = self._RunMicrocodeTest('34_x86_ucode.dts',
822                                                      U_BOOT_NODTB_DATA)
823         self.assertEqual('nodtb with microcode' + pos_and_size +
824                          ' somewhere in here', first)
825
826     def _RunPackUbootSingleMicrocode(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
833             an empty microcode region
834         """
835         # We need the libfdt library to run this test since only that allows
836         # finding the offset of a property. This is required by
837         # Entry_u_boot_dtb_with_ucode.ObtainContents().
838         data = self._DoReadFile('35_x86_single_ucode.dts', True)
839
840         second = data[len(U_BOOT_NODTB_DATA):]
841
842         fdt_len = self.GetFdtLen(second)
843         third = second[fdt_len:]
844         second = second[:fdt_len]
845
846         ucode_data = struct.pack('>2L', 0x12345678, 0x12345679)
847         self.assertIn(ucode_data, second)
848         ucode_pos = second.find(ucode_data) + len(U_BOOT_NODTB_DATA)
849
850         # Check that the microcode pointer was inserted. It should match the
851         # expected offset and size
852         pos_and_size = struct.pack('<2L', 0xfffffe00 + ucode_pos,
853                                    len(ucode_data))
854         first = data[:len(U_BOOT_NODTB_DATA)]
855         self.assertEqual('nodtb with microcode' + pos_and_size +
856                          ' somewhere in here', first)
857
858     def testPackUbootSingleMicrocode(self):
859         """Test that x86 microcode can be handled correctly with fdt_normal.
860         """
861         self._RunPackUbootSingleMicrocode()
862
863     def testUBootImg(self):
864         """Test that u-boot.img can be put in a file"""
865         data = self._DoReadFile('36_u_boot_img.dts')
866         self.assertEqual(U_BOOT_IMG_DATA, data)
867
868     def testNoMicrocode(self):
869         """Test that a missing microcode region is detected"""
870         with self.assertRaises(ValueError) as e:
871             self._DoReadFile('37_x86_no_ucode.dts', True)
872         self.assertIn("Node '/binman/u-boot-dtb-with-ucode': No /microcode "
873                       "node found in ", str(e.exception))
874
875     def testMicrocodeWithoutNode(self):
876         """Test that a missing u-boot-dtb-with-ucode node is detected"""
877         with self.assertRaises(ValueError) as e:
878             self._DoReadFile('38_x86_ucode_missing_node.dts', True)
879         self.assertIn("Node '/binman/u-boot-with-ucode-ptr': Cannot find "
880                 "microcode region u-boot-dtb-with-ucode", str(e.exception))
881
882     def testMicrocodeWithoutNode2(self):
883         """Test that a missing u-boot-ucode node is detected"""
884         with self.assertRaises(ValueError) as e:
885             self._DoReadFile('39_x86_ucode_missing_node2.dts', True)
886         self.assertIn("Node '/binman/u-boot-with-ucode-ptr': Cannot find "
887             "microcode region u-boot-ucode", str(e.exception))
888
889     def testMicrocodeWithoutPtrInElf(self):
890         """Test that a U-Boot binary without the microcode symbol is detected"""
891         # ELF file without a '_dt_ucode_base_size' symbol
892         try:
893             with open(self.TestFile('u_boot_no_ucode_ptr')) as fd:
894                 TestFunctional._MakeInputFile('u-boot', fd.read())
895
896             with self.assertRaises(ValueError) as e:
897                 self._RunPackUbootSingleMicrocode()
898             self.assertIn("Node '/binman/u-boot-with-ucode-ptr': Cannot locate "
899                     "_dt_ucode_base_size symbol in u-boot", str(e.exception))
900
901         finally:
902             # Put the original file back
903             with open(self.TestFile('u_boot_ucode_ptr')) as fd:
904                 TestFunctional._MakeInputFile('u-boot', fd.read())
905
906     def testMicrocodeNotInImage(self):
907         """Test that microcode must be placed within the image"""
908         with self.assertRaises(ValueError) as e:
909             self._DoReadFile('40_x86_ucode_not_in_image.dts', True)
910         self.assertIn("Node '/binman/u-boot-with-ucode-ptr': Microcode "
911                 "pointer _dt_ucode_base_size at fffffe14 is outside the "
912                 "section ranging from 00000000 to 0000002e", str(e.exception))
913
914     def testWithoutMicrocode(self):
915         """Test that we can cope with an image without microcode (e.g. qemu)"""
916         with open(self.TestFile('u_boot_no_ucode_ptr')) as fd:
917             TestFunctional._MakeInputFile('u-boot', fd.read())
918         data, dtb, _, _ = self._DoReadFileDtb('44_x86_optional_ucode.dts', True)
919
920         # Now check the device tree has no microcode
921         self.assertEqual(U_BOOT_NODTB_DATA, data[:len(U_BOOT_NODTB_DATA)])
922         second = data[len(U_BOOT_NODTB_DATA):]
923
924         fdt_len = self.GetFdtLen(second)
925         self.assertEqual(dtb, second[:fdt_len])
926
927         used_len = len(U_BOOT_NODTB_DATA) + fdt_len
928         third = data[used_len:]
929         self.assertEqual(chr(0) * (0x200 - used_len), third)
930
931     def testUnknownPosSize(self):
932         """Test that microcode must be placed within the image"""
933         with self.assertRaises(ValueError) as e:
934             self._DoReadFile('41_unknown_pos_size.dts', True)
935         self.assertIn("Section '/binman': Unable to set offset/size for unknown "
936                 "entry 'invalid-entry'", str(e.exception))
937
938     def testPackFsp(self):
939         """Test that an image with a FSP binary can be created"""
940         data = self._DoReadFile('42_intel-fsp.dts')
941         self.assertEqual(FSP_DATA, data[:len(FSP_DATA)])
942
943     def testPackCmc(self):
944         """Test that an image with a CMC binary can be created"""
945         data = self._DoReadFile('43_intel-cmc.dts')
946         self.assertEqual(CMC_DATA, data[:len(CMC_DATA)])
947
948     def testPackVbt(self):
949         """Test that an image with a VBT binary can be created"""
950         data = self._DoReadFile('46_intel-vbt.dts')
951         self.assertEqual(VBT_DATA, data[:len(VBT_DATA)])
952
953     def testSplBssPad(self):
954         """Test that we can pad SPL's BSS with zeros"""
955         # ELF file with a '__bss_size' symbol
956         with open(self.TestFile('bss_data')) as fd:
957             TestFunctional._MakeInputFile('spl/u-boot-spl', fd.read())
958         data = self._DoReadFile('47_spl_bss_pad.dts')
959         self.assertEqual(U_BOOT_SPL_DATA + (chr(0) * 10) + U_BOOT_DATA, data)
960
961         with open(self.TestFile('u_boot_ucode_ptr')) as fd:
962             TestFunctional._MakeInputFile('spl/u-boot-spl', fd.read())
963         with self.assertRaises(ValueError) as e:
964             data = self._DoReadFile('47_spl_bss_pad.dts')
965         self.assertIn('Expected __bss_size symbol in spl/u-boot-spl',
966                       str(e.exception))
967
968     def testPackStart16Spl(self):
969         """Test that an image with an x86 start16 region can be created"""
970         data = self._DoReadFile('48_x86-start16-spl.dts')
971         self.assertEqual(X86_START16_SPL_DATA, data[:len(X86_START16_SPL_DATA)])
972
973     def _PackUbootSplMicrocode(self, dts, ucode_second=False):
974         """Helper function for microcode tests
975
976         We expect to see the following in the image, in order:
977             u-boot-spl-nodtb.bin with a microcode pointer inserted at the
978                 correct place
979             u-boot.dtb with the microcode removed
980             the microcode
981
982         Args:
983             dts: Device tree file to use for test
984             ucode_second: True if the microsecond entry is second instead of
985                 third
986         """
987         # ELF file with a '_dt_ucode_base_size' symbol
988         with open(self.TestFile('u_boot_ucode_ptr')) as fd:
989             TestFunctional._MakeInputFile('spl/u-boot-spl', fd.read())
990         first, pos_and_size = self._RunMicrocodeTest(dts, U_BOOT_SPL_NODTB_DATA,
991                                                      ucode_second=ucode_second)
992         self.assertEqual('splnodtb with microc' + pos_and_size +
993                          'ter somewhere in here', first)
994
995     def testPackUbootSplMicrocode(self):
996         """Test that x86 microcode can be handled correctly in SPL"""
997         self._PackUbootSplMicrocode('49_x86_ucode_spl.dts')
998
999     def testPackUbootSplMicrocodeReorder(self):
1000         """Test that order doesn't matter for microcode entries
1001
1002         This is the same as testPackUbootSplMicrocode but when we process the
1003         u-boot-ucode entry we have not yet seen the u-boot-dtb-with-ucode
1004         entry, so we reply on binman to try later.
1005         """
1006         self._PackUbootSplMicrocode('58_x86_ucode_spl_needs_retry.dts',
1007                                     ucode_second=True)
1008
1009     def testPackMrc(self):
1010         """Test that an image with an MRC binary can be created"""
1011         data = self._DoReadFile('50_intel_mrc.dts')
1012         self.assertEqual(MRC_DATA, data[:len(MRC_DATA)])
1013
1014     def testSplDtb(self):
1015         """Test that an image with spl/u-boot-spl.dtb can be created"""
1016         data = self._DoReadFile('51_u_boot_spl_dtb.dts')
1017         self.assertEqual(U_BOOT_SPL_DTB_DATA, data[:len(U_BOOT_SPL_DTB_DATA)])
1018
1019     def testSplNoDtb(self):
1020         """Test that an image with spl/u-boot-spl-nodtb.bin can be created"""
1021         data = self._DoReadFile('52_u_boot_spl_nodtb.dts')
1022         self.assertEqual(U_BOOT_SPL_NODTB_DATA, data[:len(U_BOOT_SPL_NODTB_DATA)])
1023
1024     def testSymbols(self):
1025         """Test binman can assign symbols embedded in U-Boot"""
1026         elf_fname = self.TestFile('u_boot_binman_syms')
1027         syms = elf.GetSymbols(elf_fname, ['binman', 'image'])
1028         addr = elf.GetSymbolAddress(elf_fname, '__image_copy_start')
1029         self.assertEqual(syms['_binman_u_boot_spl_prop_offset'].address, addr)
1030
1031         with open(self.TestFile('u_boot_binman_syms')) as fd:
1032             TestFunctional._MakeInputFile('spl/u-boot-spl', fd.read())
1033         data = self._DoReadFile('53_symbols.dts')
1034         sym_values = struct.pack('<LQL', 0x24 + 0, 0x24 + 24, 0x24 + 20)
1035         expected = (sym_values + U_BOOT_SPL_DATA[16:] + chr(0xff) +
1036                     U_BOOT_DATA +
1037                     sym_values + U_BOOT_SPL_DATA[16:])
1038         self.assertEqual(expected, data)
1039
1040     def testPackUnitAddress(self):
1041         """Test that we support multiple binaries with the same name"""
1042         data = self._DoReadFile('54_unit_address.dts')
1043         self.assertEqual(U_BOOT_DATA + U_BOOT_DATA, data)
1044
1045     def testSections(self):
1046         """Basic test of sections"""
1047         data = self._DoReadFile('55_sections.dts')
1048         expected = (U_BOOT_DATA + '!' * 12 + U_BOOT_DATA + 'a' * 12 +
1049                     U_BOOT_DATA + '&' * 4)
1050         self.assertEqual(expected, data)
1051
1052     def testMap(self):
1053         """Tests outputting a map of the images"""
1054         _, _, map_data, _ = self._DoReadFileDtb('55_sections.dts', map=True)
1055         self.assertEqual('''ImagePos    Offset      Size  Name
1056 00000000  00000000  00000028  main-section
1057 00000000   00000000  00000010  section@0
1058 00000000    00000000  00000004  u-boot
1059 00000010   00000010  00000010  section@1
1060 00000010    00000000  00000004  u-boot
1061 00000020   00000020  00000004  section@2
1062 00000020    00000000  00000004  u-boot
1063 ''', map_data)
1064
1065     def testNamePrefix(self):
1066         """Tests that name prefixes are used"""
1067         _, _, map_data, _ = self._DoReadFileDtb('56_name_prefix.dts', map=True)
1068         self.assertEqual('''ImagePos    Offset      Size  Name
1069 00000000  00000000  00000028  main-section
1070 00000000   00000000  00000010  section@0
1071 00000000    00000000  00000004  ro-u-boot
1072 00000010   00000010  00000010  section@1
1073 00000010    00000000  00000004  rw-u-boot
1074 ''', map_data)
1075
1076     def testUnknownContents(self):
1077         """Test that obtaining the contents works as expected"""
1078         with self.assertRaises(ValueError) as e:
1079             self._DoReadFile('57_unknown_contents.dts', True)
1080         self.assertIn("Section '/binman': Internal error: Could not complete "
1081                 "processing of contents: remaining [<_testing.Entry__testing ",
1082                 str(e.exception))
1083
1084     def testBadChangeSize(self):
1085         """Test that trying to change the size of an entry fails"""
1086         with self.assertRaises(ValueError) as e:
1087             self._DoReadFile('59_change_size.dts', True)
1088         self.assertIn("Node '/binman/_testing': Cannot update entry size from "
1089                       '2 to 1', str(e.exception))
1090
1091     def testUpdateFdt(self):
1092         """Test that we can update the device tree with offset/size info"""
1093         _, _, _, out_dtb_fname = self._DoReadFileDtb('60_fdt_update.dts',
1094                                                      update_dtb=True)
1095         props = self._GetPropTree(out_dtb_fname, ['offset', 'size',
1096                                                   'image-pos'])
1097         with open('/tmp/x.dtb', 'wb') as outf:
1098             with open(out_dtb_fname) as inf:
1099                 outf.write(inf.read())
1100         self.assertEqual({
1101             'image-pos': 0,
1102             'offset': 0,
1103             '_testing:offset': 32,
1104             '_testing:size': 1,
1105             '_testing:image-pos': 32,
1106             'section@0/u-boot:offset': 0,
1107             'section@0/u-boot:size': len(U_BOOT_DATA),
1108             'section@0/u-boot:image-pos': 0,
1109             'section@0:offset': 0,
1110             'section@0:size': 16,
1111             'section@0:image-pos': 0,
1112
1113             'section@1/u-boot:offset': 0,
1114             'section@1/u-boot:size': len(U_BOOT_DATA),
1115             'section@1/u-boot:image-pos': 16,
1116             'section@1:offset': 16,
1117             'section@1:size': 16,
1118             'section@1:image-pos': 16,
1119             'size': 40
1120         }, props)
1121
1122     def testUpdateFdtBad(self):
1123         """Test that we detect when ProcessFdt never completes"""
1124         with self.assertRaises(ValueError) as e:
1125             self._DoReadFileDtb('61_fdt_update_bad.dts', update_dtb=True)
1126         self.assertIn('Could not complete processing of Fdt: remaining '
1127                       '[<_testing.Entry__testing', str(e.exception))
1128
1129     def testEntryArgs(self):
1130         """Test passing arguments to entries from the command line"""
1131         entry_args = {
1132             'test-str-arg': 'test1',
1133             'test-int-arg': '456',
1134         }
1135         self._DoReadFileDtb('62_entry_args.dts', entry_args=entry_args)
1136         self.assertIn('image', control.images)
1137         entry = control.images['image'].GetEntries()['_testing']
1138         self.assertEqual('test0', entry.test_str_fdt)
1139         self.assertEqual('test1', entry.test_str_arg)
1140         self.assertEqual(123, entry.test_int_fdt)
1141         self.assertEqual(456, entry.test_int_arg)
1142
1143     def testEntryArgsMissing(self):
1144         """Test missing arguments and properties"""
1145         entry_args = {
1146             'test-int-arg': '456',
1147         }
1148         self._DoReadFileDtb('63_entry_args_missing.dts', entry_args=entry_args)
1149         entry = control.images['image'].GetEntries()['_testing']
1150         self.assertEqual('test0', entry.test_str_fdt)
1151         self.assertEqual(None, entry.test_str_arg)
1152         self.assertEqual(None, entry.test_int_fdt)
1153         self.assertEqual(456, entry.test_int_arg)
1154
1155     def testEntryArgsRequired(self):
1156         """Test missing arguments and properties"""
1157         entry_args = {
1158             'test-int-arg': '456',
1159         }
1160         with self.assertRaises(ValueError) as e:
1161             self._DoReadFileDtb('64_entry_args_required.dts')
1162         self.assertIn("Node '/binman/_testing': Missing required "
1163             'properties/entry args: test-str-arg, test-int-fdt, test-int-arg',
1164             str(e.exception))
1165
1166     def testEntryArgsInvalidFormat(self):
1167         """Test that an invalid entry-argument format is detected"""
1168         args = ['-d', self.TestFile('64_entry_args_required.dts'), '-ano-value']
1169         with self.assertRaises(ValueError) as e:
1170             self._DoBinman(*args)
1171         self.assertIn("Invalid entry arguemnt 'no-value'", str(e.exception))
1172
1173     def testEntryArgsInvalidInteger(self):
1174         """Test that an invalid entry-argument integer is detected"""
1175         entry_args = {
1176             'test-int-arg': 'abc',
1177         }
1178         with self.assertRaises(ValueError) as e:
1179             self._DoReadFileDtb('62_entry_args.dts', entry_args=entry_args)
1180         self.assertIn("Node '/binman/_testing': Cannot convert entry arg "
1181                       "'test-int-arg' (value 'abc') to integer",
1182             str(e.exception))
1183
1184     def testEntryArgsInvalidDatatype(self):
1185         """Test that an invalid entry-argument datatype is detected
1186
1187         This test could be written in entry_test.py except that it needs
1188         access to control.entry_args, which seems more than that module should
1189         be able to see.
1190         """
1191         entry_args = {
1192             'test-bad-datatype-arg': '12',
1193         }
1194         with self.assertRaises(ValueError) as e:
1195             self._DoReadFileDtb('65_entry_args_unknown_datatype.dts',
1196                                 entry_args=entry_args)
1197         self.assertIn('GetArg() internal error: Unknown data type ',
1198                       str(e.exception))
1199
1200     def testText(self):
1201         """Test for a text entry type"""
1202         entry_args = {
1203             'test-id': TEXT_DATA,
1204             'test-id2': TEXT_DATA2,
1205             'test-id3': TEXT_DATA3,
1206         }
1207         data, _, _, _ = self._DoReadFileDtb('66_text.dts',
1208                                             entry_args=entry_args)
1209         expected = (TEXT_DATA + chr(0) * (8 - len(TEXT_DATA)) + TEXT_DATA2 +
1210                     TEXT_DATA3 + 'some text')
1211         self.assertEqual(expected, data)
1212
1213     def testEntryDocs(self):
1214         """Test for creation of entry documentation"""
1215         with test_util.capture_sys_output() as (stdout, stderr):
1216             control.WriteEntryDocs(binman.GetEntryModules())
1217         self.assertTrue(len(stdout.getvalue()) > 0)
1218
1219     def testEntryDocsMissing(self):
1220         """Test handling of missing entry documentation"""
1221         with self.assertRaises(ValueError) as e:
1222             with test_util.capture_sys_output() as (stdout, stderr):
1223                 control.WriteEntryDocs(binman.GetEntryModules(), 'u_boot')
1224         self.assertIn('Documentation is missing for modules: u_boot',
1225                       str(e.exception))
1226
1227     def testFmap(self):
1228         """Basic test of generation of a flashrom fmap"""
1229         data = self._DoReadFile('67_fmap.dts')
1230         fhdr, fentries = fmap_util.DecodeFmap(data[32:])
1231         expected = U_BOOT_DATA + '!' * 12 + U_BOOT_DATA + 'a' * 12
1232         self.assertEqual(expected, data[:32])
1233         self.assertEqual('__FMAP__', fhdr.signature)
1234         self.assertEqual(1, fhdr.ver_major)
1235         self.assertEqual(0, fhdr.ver_minor)
1236         self.assertEqual(0, fhdr.base)
1237         self.assertEqual(16 + 16 +
1238                          fmap_util.FMAP_HEADER_LEN +
1239                          fmap_util.FMAP_AREA_LEN * 3, fhdr.image_size)
1240         self.assertEqual('FMAP', fhdr.name)
1241         self.assertEqual(3, fhdr.nareas)
1242         for fentry in fentries:
1243             self.assertEqual(0, fentry.flags)
1244
1245         self.assertEqual(0, fentries[0].offset)
1246         self.assertEqual(4, fentries[0].size)
1247         self.assertEqual('RO_U_BOOT', fentries[0].name)
1248
1249         self.assertEqual(16, fentries[1].offset)
1250         self.assertEqual(4, fentries[1].size)
1251         self.assertEqual('RW_U_BOOT', fentries[1].name)
1252
1253         self.assertEqual(32, fentries[2].offset)
1254         self.assertEqual(fmap_util.FMAP_HEADER_LEN +
1255                          fmap_util.FMAP_AREA_LEN * 3, fentries[2].size)
1256         self.assertEqual('FMAP', fentries[2].name)
1257
1258     def testBlobNamedByArg(self):
1259         """Test we can add a blob with the filename coming from an entry arg"""
1260         entry_args = {
1261             'cros-ec-rw-path': 'ecrw.bin',
1262         }
1263         data, _, _, _ = self._DoReadFileDtb('68_blob_named_by_arg.dts',
1264                                             entry_args=entry_args)
1265
1266     def testFill(self):
1267         """Test for an fill entry type"""
1268         data = self._DoReadFile('69_fill.dts')
1269         expected = 8 * chr(0xff) + 8 * chr(0)
1270         self.assertEqual(expected, data)
1271
1272     def testFillNoSize(self):
1273         """Test for an fill entry type with no size"""
1274         with self.assertRaises(ValueError) as e:
1275             self._DoReadFile('70_fill_no_size.dts')
1276         self.assertIn("'fill' entry must have a size property",
1277                       str(e.exception))
1278
1279     def _HandleGbbCommand(self, pipe_list):
1280         """Fake calls to the futility utility"""
1281         if pipe_list[0][0] == 'futility':
1282             fname = pipe_list[0][-1]
1283             # Append our GBB data to the file, which will happen every time the
1284             # futility command is called.
1285             with open(fname, 'a') as fd:
1286                 fd.write(GBB_DATA)
1287             return command.CommandResult()
1288
1289     def testGbb(self):
1290         """Test for the Chromium OS Google Binary Block"""
1291         command.test_result = self._HandleGbbCommand
1292         entry_args = {
1293             'keydir': 'devkeys',
1294             'bmpblk': 'bmpblk.bin',
1295         }
1296         data, _, _, _ = self._DoReadFileDtb('71_gbb.dts', entry_args=entry_args)
1297
1298         # Since futility
1299         expected = GBB_DATA + GBB_DATA + 8 * chr(0) + (0x2180 - 16) * chr(0)
1300         self.assertEqual(expected, data)
1301
1302     def testGbbTooSmall(self):
1303         """Test for the Chromium OS Google Binary Block being large enough"""
1304         with self.assertRaises(ValueError) as e:
1305             self._DoReadFileDtb('72_gbb_too_small.dts')
1306         self.assertIn("Node '/binman/gbb': GBB is too small",
1307                       str(e.exception))
1308
1309     def testGbbNoSize(self):
1310         """Test for the Chromium OS Google Binary Block having a size"""
1311         with self.assertRaises(ValueError) as e:
1312             self._DoReadFileDtb('73_gbb_no_size.dts')
1313         self.assertIn("Node '/binman/gbb': GBB must have a fixed size",
1314                       str(e.exception))
1315
1316     def _HandleVblockCommand(self, pipe_list):
1317         """Fake calls to the futility utility"""
1318         if pipe_list[0][0] == 'futility':
1319             fname = pipe_list[0][3]
1320             with open(fname, 'w') as fd:
1321                 fd.write(VBLOCK_DATA)
1322             return command.CommandResult()
1323
1324     def testVblock(self):
1325         """Test for the Chromium OS Verified Boot Block"""
1326         command.test_result = self._HandleVblockCommand
1327         entry_args = {
1328             'keydir': 'devkeys',
1329         }
1330         data, _, _, _ = self._DoReadFileDtb('74_vblock.dts',
1331                                             entry_args=entry_args)
1332         expected = U_BOOT_DATA + VBLOCK_DATA + U_BOOT_DTB_DATA
1333         self.assertEqual(expected, data)
1334
1335     def testVblockNoContent(self):
1336         """Test we detect a vblock which has no content to sign"""
1337         with self.assertRaises(ValueError) as e:
1338             self._DoReadFile('75_vblock_no_content.dts')
1339         self.assertIn("Node '/binman/vblock': Vblock must have a 'content' "
1340                       'property', str(e.exception))
1341
1342     def testVblockBadPhandle(self):
1343         """Test that we detect a vblock with an invalid phandle in contents"""
1344         with self.assertRaises(ValueError) as e:
1345             self._DoReadFile('76_vblock_bad_phandle.dts')
1346         self.assertIn("Node '/binman/vblock': Cannot find node for phandle "
1347                       '1000', str(e.exception))
1348
1349     def testVblockBadEntry(self):
1350         """Test that we detect an entry that points to a non-entry"""
1351         with self.assertRaises(ValueError) as e:
1352             self._DoReadFile('77_vblock_bad_entry.dts')
1353         self.assertIn("Node '/binman/vblock': Cannot find entry for node "
1354                       "'other'", str(e.exception))
1355
1356     def testTpl(self):
1357         """Test that an image with TPL and ots device tree can be created"""
1358         # ELF file with a '__bss_size' symbol
1359         with open(self.TestFile('bss_data')) as fd:
1360             TestFunctional._MakeInputFile('tpl/u-boot-tpl', fd.read())
1361         data = self._DoReadFile('78_u_boot_tpl.dts')
1362         self.assertEqual(U_BOOT_TPL_DATA + U_BOOT_TPL_DTB_DATA, data)
1363
1364
1365 if __name__ == "__main__":
1366     unittest.main()