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