]> git.sur5r.net Git - u-boot/blob - tools/binman/ftest.py
fit: Verify all configuration signatures
[u-boot] / 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 tools
25 import tout
26
27 # Contents of test files, corresponding to different entry types
28 U_BOOT_DATA           = '1234'
29 U_BOOT_IMG_DATA       = 'img'
30 U_BOOT_SPL_DATA       = '56780123456789abcde'
31 BLOB_DATA             = '89'
32 ME_DATA               = '0abcd'
33 VGA_DATA              = 'vga'
34 U_BOOT_DTB_DATA       = 'udtb'
35 U_BOOT_SPL_DTB_DATA   = 'spldtb'
36 X86_START16_DATA      = 'start16'
37 X86_START16_SPL_DATA  = 'start16spl'
38 U_BOOT_NODTB_DATA     = 'nodtb with microcode pointer somewhere in here'
39 U_BOOT_SPL_NODTB_DATA = 'splnodtb with microcode pointer somewhere in here'
40 FSP_DATA              = 'fsp'
41 CMC_DATA              = 'cmc'
42 VBT_DATA              = 'vbt'
43 MRC_DATA              = 'mrc'
44
45 class TestFunctional(unittest.TestCase):
46     """Functional tests for binman
47
48     Most of these use a sample .dts file to build an image and then check
49     that it looks correct. The sample files are in the test/ subdirectory
50     and are numbered.
51
52     For each entry type a very small test file is created using fixed
53     string contents. This makes it easy to test that things look right, and
54     debug problems.
55
56     In some cases a 'real' file must be used - these are also supplied in
57     the test/ diurectory.
58     """
59     @classmethod
60     def setUpClass(self):
61         global entry
62         import entry
63
64         # Handle the case where argv[0] is 'python'
65         self._binman_dir = os.path.dirname(os.path.realpath(sys.argv[0]))
66         self._binman_pathname = os.path.join(self._binman_dir, 'binman')
67
68         # Create a temporary directory for input files
69         self._indir = tempfile.mkdtemp(prefix='binmant.')
70
71         # Create some test files
72         TestFunctional._MakeInputFile('u-boot.bin', U_BOOT_DATA)
73         TestFunctional._MakeInputFile('u-boot.img', U_BOOT_IMG_DATA)
74         TestFunctional._MakeInputFile('spl/u-boot-spl.bin', U_BOOT_SPL_DATA)
75         TestFunctional._MakeInputFile('blobfile', BLOB_DATA)
76         TestFunctional._MakeInputFile('me.bin', ME_DATA)
77         TestFunctional._MakeInputFile('vga.bin', VGA_DATA)
78         TestFunctional._MakeInputFile('u-boot.dtb', U_BOOT_DTB_DATA)
79         TestFunctional._MakeInputFile('spl/u-boot-spl.dtb', U_BOOT_SPL_DTB_DATA)
80         TestFunctional._MakeInputFile('u-boot-x86-16bit.bin', X86_START16_DATA)
81         TestFunctional._MakeInputFile('spl/u-boot-x86-16bit-spl.bin',
82                                       X86_START16_SPL_DATA)
83         TestFunctional._MakeInputFile('u-boot-nodtb.bin', U_BOOT_NODTB_DATA)
84         TestFunctional._MakeInputFile('spl/u-boot-spl-nodtb.bin',
85                                       U_BOOT_SPL_NODTB_DATA)
86         TestFunctional._MakeInputFile('fsp.bin', FSP_DATA)
87         TestFunctional._MakeInputFile('cmc.bin', CMC_DATA)
88         TestFunctional._MakeInputFile('vbt.bin', VBT_DATA)
89         TestFunctional._MakeInputFile('mrc.bin', MRC_DATA)
90         self._output_setup = False
91
92         # ELF file with a '_dt_ucode_base_size' symbol
93         with open(self.TestFile('u_boot_ucode_ptr')) as fd:
94             TestFunctional._MakeInputFile('u-boot', fd.read())
95
96         # Intel flash descriptor file
97         with open(self.TestFile('descriptor.bin')) as fd:
98             TestFunctional._MakeInputFile('descriptor.bin', fd.read())
99
100     @classmethod
101     def tearDownClass(self):
102         """Remove the temporary input directory and its contents"""
103         if self._indir:
104             shutil.rmtree(self._indir)
105         self._indir = None
106
107     def setUp(self):
108         # Enable this to turn on debugging output
109         # tout.Init(tout.DEBUG)
110         command.test_result = None
111
112     def tearDown(self):
113         """Remove the temporary output directory"""
114         tools._FinaliseForTest()
115
116     def _RunBinman(self, *args, **kwargs):
117         """Run binman using the command line
118
119         Args:
120             Arguments to pass, as a list of strings
121             kwargs: Arguments to pass to Command.RunPipe()
122         """
123         result = command.RunPipe([[self._binman_pathname] + list(args)],
124                 capture=True, capture_stderr=True, raise_on_error=False)
125         if result.return_code and kwargs.get('raise_on_error', True):
126             raise Exception("Error running '%s': %s" % (' '.join(args),
127                             result.stdout + result.stderr))
128         return result
129
130     def _DoBinman(self, *args):
131         """Run binman using directly (in the same process)
132
133         Args:
134             Arguments to pass, as a list of strings
135         Returns:
136             Return value (0 for success)
137         """
138         args = list(args)
139         if '-D' in sys.argv:
140             args = args + ['-D']
141         (options, args) = cmdline.ParseArgs(args)
142         options.pager = 'binman-invalid-pager'
143         options.build_dir = self._indir
144
145         # For testing, you can force an increase in verbosity here
146         # options.verbosity = tout.DEBUG
147         return control.Binman(options, args)
148
149     def _DoTestFile(self, fname, debug=False, map=False, update_dtb=False):
150         """Run binman with a given test file
151
152         Args:
153             fname: Device-tree source filename to use (e.g. 05_simple.dts)
154             debug: True to enable debugging output
155             map: True to output map files for the images
156             update_dtb: Update the position and size of each entry in the device
157                 tree before packing it into the image
158         """
159         args = ['-p', '-I', self._indir, '-d', self.TestFile(fname)]
160         if debug:
161             args.append('-D')
162         if map:
163             args.append('-m')
164         if update_dtb:
165             args.append('-up')
166         return self._DoBinman(*args)
167
168     def _SetupDtb(self, fname, outfile='u-boot.dtb'):
169         """Set up a new test device-tree file
170
171         The given file is compiled and set up as the device tree to be used
172         for ths test.
173
174         Args:
175             fname: Filename of .dts file to read
176             outfile: Output filename for compiled device-tree binary
177
178         Returns:
179             Contents of device-tree binary
180         """
181         if not self._output_setup:
182             tools.PrepareOutputDir(self._indir, True)
183             self._output_setup = True
184         dtb = fdt_util.EnsureCompiled(self.TestFile(fname))
185         with open(dtb) as fd:
186             data = fd.read()
187             TestFunctional._MakeInputFile(outfile, data)
188             return data
189
190     def _DoReadFileDtb(self, fname, use_real_dtb=False, map=False,
191                        update_dtb=False):
192         """Run binman and return the resulting image
193
194         This runs binman with a given test file and then reads the resulting
195         output file. It is a shortcut function since most tests need to do
196         these steps.
197
198         Raises an assertion failure if binman returns a non-zero exit code.
199
200         Args:
201             fname: Device-tree source filename to use (e.g. 05_simple.dts)
202             use_real_dtb: True to use the test file as the contents of
203                 the u-boot-dtb entry. Normally this is not needed and the
204                 test contents (the U_BOOT_DTB_DATA string) can be used.
205                 But in some test we need the real contents.
206             map: True to output map files for the images
207             update_dtb: Update the position and size of each entry in the device
208                 tree before packing it into the image
209
210         Returns:
211             Tuple:
212                 Resulting image contents
213                 Device tree contents
214                 Map data showing contents of image (or None if none)
215         """
216         dtb_data = None
217         # Use the compiled test file as the u-boot-dtb input
218         if use_real_dtb:
219             dtb_data = self._SetupDtb(fname)
220
221         try:
222             retcode = self._DoTestFile(fname, map=map, update_dtb=update_dtb)
223             self.assertEqual(0, retcode)
224             out_dtb_fname = control.GetFdtPath('u-boot.dtb')
225
226             # Find the (only) image, read it and return its contents
227             image = control.images['image']
228             image_fname = tools.GetOutputFilename('image.bin')
229             self.assertTrue(os.path.exists(image_fname))
230             if map:
231                 map_fname = tools.GetOutputFilename('image.map')
232                 with open(map_fname) as fd:
233                     map_data = fd.read()
234             else:
235                 map_data = None
236             with open(image_fname) as fd:
237                 return fd.read(), dtb_data, map_data, out_dtb_fname
238         finally:
239             # Put the test file back
240             if use_real_dtb:
241                 TestFunctional._MakeInputFile('u-boot.dtb', U_BOOT_DTB_DATA)
242
243     def _DoReadFile(self, fname, use_real_dtb=False):
244         """Helper function which discards the device-tree binary
245
246         Args:
247             fname: Device-tree source filename to use (e.g. 05_simple.dts)
248             use_real_dtb: True to use the test file as the contents of
249                 the u-boot-dtb entry. Normally this is not needed and the
250                 test contents (the U_BOOT_DTB_DATA string) can be used.
251                 But in some test we need the real contents.
252         """
253         return self._DoReadFileDtb(fname, use_real_dtb)[0]
254
255     @classmethod
256     def _MakeInputFile(self, fname, contents):
257         """Create a new test input file, creating directories as needed
258
259         Args:
260             fname: Filenaem to create
261             contents: File contents to write in to the file
262         Returns:
263             Full pathname of file created
264         """
265         pathname = os.path.join(self._indir, fname)
266         dirname = os.path.dirname(pathname)
267         if dirname and not os.path.exists(dirname):
268             os.makedirs(dirname)
269         with open(pathname, 'wb') as fd:
270             fd.write(contents)
271         return pathname
272
273     @classmethod
274     def TestFile(self, fname):
275         return os.path.join(self._binman_dir, 'test', fname)
276
277     def AssertInList(self, grep_list, target):
278         """Assert that at least one of a list of things is in a target
279
280         Args:
281             grep_list: List of strings to check
282             target: Target string
283         """
284         for grep in grep_list:
285             if grep in target:
286                 return
287         self.fail("Error: '%' not found in '%s'" % (grep_list, target))
288
289     def CheckNoGaps(self, entries):
290         """Check that all entries fit together without gaps
291
292         Args:
293             entries: List of entries to check
294         """
295         pos = 0
296         for entry in entries.values():
297             self.assertEqual(pos, entry.pos)
298             pos += entry.size
299
300     def GetFdtLen(self, dtb):
301         """Get the totalsize field from a device-tree binary
302
303         Args:
304             dtb: Device-tree binary contents
305
306         Returns:
307             Total size of device-tree binary, from the header
308         """
309         return struct.unpack('>L', dtb[4:8])[0]
310
311     def _GetPropTree(self, dtb_data, node_names):
312         def AddNode(node, path):
313             if node.name != '/':
314                 path += '/' + node.name
315             #print 'path', path
316             for subnode in node.subnodes:
317                 for prop in subnode.props.values():
318                     if prop.name in node_names:
319                         prop_path = path + '/' + subnode.name + ':' + prop.name
320                         tree[prop_path[len('/binman/'):]] = fdt_util.fdt32_to_cpu(
321                             prop.value)
322                     #print '   ', prop.name
323                 AddNode(subnode, path)
324
325         tree = {}
326         dtb = fdt.Fdt(dtb_data)
327         dtb.Scan()
328         AddNode(dtb.GetRoot(), '')
329         return tree
330
331     def testRun(self):
332         """Test a basic run with valid args"""
333         result = self._RunBinman('-h')
334
335     def testFullHelp(self):
336         """Test that the full help is displayed with -H"""
337         result = self._RunBinman('-H')
338         help_file = os.path.join(self._binman_dir, 'README')
339         # Remove possible extraneous strings
340         extra = '::::::::::::::\n' + help_file + '\n::::::::::::::\n'
341         gothelp = result.stdout.replace(extra, '')
342         self.assertEqual(len(gothelp), os.path.getsize(help_file))
343         self.assertEqual(0, len(result.stderr))
344         self.assertEqual(0, result.return_code)
345
346     def testFullHelpInternal(self):
347         """Test that the full help is displayed with -H"""
348         try:
349             command.test_result = command.CommandResult()
350             result = self._DoBinman('-H')
351             help_file = os.path.join(self._binman_dir, 'README')
352         finally:
353             command.test_result = None
354
355     def testHelp(self):
356         """Test that the basic help is displayed with -h"""
357         result = self._RunBinman('-h')
358         self.assertTrue(len(result.stdout) > 200)
359         self.assertEqual(0, len(result.stderr))
360         self.assertEqual(0, result.return_code)
361
362     def testBoard(self):
363         """Test that we can run it with a specific board"""
364         self._SetupDtb('05_simple.dts', 'sandbox/u-boot.dtb')
365         TestFunctional._MakeInputFile('sandbox/u-boot.bin', U_BOOT_DATA)
366         result = self._DoBinman('-b', 'sandbox')
367         self.assertEqual(0, result)
368
369     def testNeedBoard(self):
370         """Test that we get an error when no board ius supplied"""
371         with self.assertRaises(ValueError) as e:
372             result = self._DoBinman()
373         self.assertIn("Must provide a board to process (use -b <board>)",
374                 str(e.exception))
375
376     def testMissingDt(self):
377         """Test that an invalid device-tree file generates an error"""
378         with self.assertRaises(Exception) as e:
379             self._RunBinman('-d', 'missing_file')
380         # We get one error from libfdt, and a different one from fdtget.
381         self.AssertInList(["Couldn't open blob from 'missing_file'",
382                            'No such file or directory'], str(e.exception))
383
384     def testBrokenDt(self):
385         """Test that an invalid device-tree source file generates an error
386
387         Since this is a source file it should be compiled and the error
388         will come from the device-tree compiler (dtc).
389         """
390         with self.assertRaises(Exception) as e:
391             self._RunBinman('-d', self.TestFile('01_invalid.dts'))
392         self.assertIn("FATAL ERROR: Unable to parse input tree",
393                 str(e.exception))
394
395     def testMissingNode(self):
396         """Test that a device tree without a 'binman' node generates an error"""
397         with self.assertRaises(Exception) as e:
398             self._DoBinman('-d', self.TestFile('02_missing_node.dts'))
399         self.assertIn("does not have a 'binman' node", str(e.exception))
400
401     def testEmpty(self):
402         """Test that an empty binman node works OK (i.e. does nothing)"""
403         result = self._RunBinman('-d', self.TestFile('03_empty.dts'))
404         self.assertEqual(0, len(result.stderr))
405         self.assertEqual(0, result.return_code)
406
407     def testInvalidEntry(self):
408         """Test that an invalid entry is flagged"""
409         with self.assertRaises(Exception) as e:
410             result = self._RunBinman('-d',
411                                      self.TestFile('04_invalid_entry.dts'))
412         #print e.exception
413         self.assertIn("Unknown entry type 'not-a-valid-type' in node "
414                 "'/binman/not-a-valid-type'", str(e.exception))
415
416     def testSimple(self):
417         """Test a simple binman with a single file"""
418         data = self._DoReadFile('05_simple.dts')
419         self.assertEqual(U_BOOT_DATA, data)
420
421     def testSimpleDebug(self):
422         """Test a simple binman run with debugging enabled"""
423         data = self._DoTestFile('05_simple.dts', debug=True)
424
425     def testDual(self):
426         """Test that we can handle creating two images
427
428         This also tests image padding.
429         """
430         retcode = self._DoTestFile('06_dual_image.dts')
431         self.assertEqual(0, retcode)
432
433         image = control.images['image1']
434         self.assertEqual(len(U_BOOT_DATA), image._size)
435         fname = tools.GetOutputFilename('image1.bin')
436         self.assertTrue(os.path.exists(fname))
437         with open(fname) as fd:
438             data = fd.read()
439             self.assertEqual(U_BOOT_DATA, data)
440
441         image = control.images['image2']
442         self.assertEqual(3 + len(U_BOOT_DATA) + 5, image._size)
443         fname = tools.GetOutputFilename('image2.bin')
444         self.assertTrue(os.path.exists(fname))
445         with open(fname) as fd:
446             data = fd.read()
447             self.assertEqual(U_BOOT_DATA, data[3:7])
448             self.assertEqual(chr(0) * 3, data[:3])
449             self.assertEqual(chr(0) * 5, data[7:])
450
451     def testBadAlign(self):
452         """Test that an invalid alignment value is detected"""
453         with self.assertRaises(ValueError) as e:
454             self._DoTestFile('07_bad_align.dts')
455         self.assertIn("Node '/binman/u-boot': Alignment 23 must be a power "
456                       "of two", str(e.exception))
457
458     def testPackSimple(self):
459         """Test that packing works as expected"""
460         retcode = self._DoTestFile('08_pack.dts')
461         self.assertEqual(0, retcode)
462         self.assertIn('image', control.images)
463         image = control.images['image']
464         entries = image.GetEntries()
465         self.assertEqual(5, len(entries))
466
467         # First u-boot
468         self.assertIn('u-boot', entries)
469         entry = entries['u-boot']
470         self.assertEqual(0, entry.pos)
471         self.assertEqual(len(U_BOOT_DATA), entry.size)
472
473         # Second u-boot, aligned to 16-byte boundary
474         self.assertIn('u-boot-align', entries)
475         entry = entries['u-boot-align']
476         self.assertEqual(16, entry.pos)
477         self.assertEqual(len(U_BOOT_DATA), entry.size)
478
479         # Third u-boot, size 23 bytes
480         self.assertIn('u-boot-size', entries)
481         entry = entries['u-boot-size']
482         self.assertEqual(20, entry.pos)
483         self.assertEqual(len(U_BOOT_DATA), entry.contents_size)
484         self.assertEqual(23, entry.size)
485
486         # Fourth u-boot, placed immediate after the above
487         self.assertIn('u-boot-next', entries)
488         entry = entries['u-boot-next']
489         self.assertEqual(43, entry.pos)
490         self.assertEqual(len(U_BOOT_DATA), entry.size)
491
492         # Fifth u-boot, placed at a fixed position
493         self.assertIn('u-boot-fixed', entries)
494         entry = entries['u-boot-fixed']
495         self.assertEqual(61, entry.pos)
496         self.assertEqual(len(U_BOOT_DATA), entry.size)
497
498         self.assertEqual(65, image._size)
499
500     def testPackExtra(self):
501         """Test that extra packing feature works as expected"""
502         retcode = self._DoTestFile('09_pack_extra.dts')
503
504         self.assertEqual(0, retcode)
505         self.assertIn('image', control.images)
506         image = control.images['image']
507         entries = image.GetEntries()
508         self.assertEqual(5, len(entries))
509
510         # First u-boot with padding before and after
511         self.assertIn('u-boot', entries)
512         entry = entries['u-boot']
513         self.assertEqual(0, entry.pos)
514         self.assertEqual(3, entry.pad_before)
515         self.assertEqual(3 + 5 + len(U_BOOT_DATA), entry.size)
516
517         # Second u-boot has an aligned size, but it has no effect
518         self.assertIn('u-boot-align-size-nop', entries)
519         entry = entries['u-boot-align-size-nop']
520         self.assertEqual(12, entry.pos)
521         self.assertEqual(4, entry.size)
522
523         # Third u-boot has an aligned size too
524         self.assertIn('u-boot-align-size', entries)
525         entry = entries['u-boot-align-size']
526         self.assertEqual(16, entry.pos)
527         self.assertEqual(32, entry.size)
528
529         # Fourth u-boot has an aligned end
530         self.assertIn('u-boot-align-end', entries)
531         entry = entries['u-boot-align-end']
532         self.assertEqual(48, entry.pos)
533         self.assertEqual(16, entry.size)
534
535         # Fifth u-boot immediately afterwards
536         self.assertIn('u-boot-align-both', entries)
537         entry = entries['u-boot-align-both']
538         self.assertEqual(64, entry.pos)
539         self.assertEqual(64, entry.size)
540
541         self.CheckNoGaps(entries)
542         self.assertEqual(128, image._size)
543
544     def testPackAlignPowerOf2(self):
545         """Test that invalid entry alignment is detected"""
546         with self.assertRaises(ValueError) as e:
547             self._DoTestFile('10_pack_align_power2.dts')
548         self.assertIn("Node '/binman/u-boot': Alignment 5 must be a power "
549                       "of two", str(e.exception))
550
551     def testPackAlignSizePowerOf2(self):
552         """Test that invalid entry size alignment is detected"""
553         with self.assertRaises(ValueError) as e:
554             self._DoTestFile('11_pack_align_size_power2.dts')
555         self.assertIn("Node '/binman/u-boot': Alignment size 55 must be a "
556                       "power of two", str(e.exception))
557
558     def testPackInvalidAlign(self):
559         """Test detection of an position that does not match its alignment"""
560         with self.assertRaises(ValueError) as e:
561             self._DoTestFile('12_pack_inv_align.dts')
562         self.assertIn("Node '/binman/u-boot': Position 0x5 (5) does not match "
563                       "align 0x4 (4)", str(e.exception))
564
565     def testPackInvalidSizeAlign(self):
566         """Test that invalid entry size alignment is detected"""
567         with self.assertRaises(ValueError) as e:
568             self._DoTestFile('13_pack_inv_size_align.dts')
569         self.assertIn("Node '/binman/u-boot': Size 0x5 (5) does not match "
570                       "align-size 0x4 (4)", str(e.exception))
571
572     def testPackOverlap(self):
573         """Test that overlapping regions are detected"""
574         with self.assertRaises(ValueError) as e:
575             self._DoTestFile('14_pack_overlap.dts')
576         self.assertIn("Node '/binman/u-boot-align': Position 0x3 (3) overlaps "
577                       "with previous entry '/binman/u-boot' ending at 0x4 (4)",
578                       str(e.exception))
579
580     def testPackEntryOverflow(self):
581         """Test that entries that overflow their size are detected"""
582         with self.assertRaises(ValueError) as e:
583             self._DoTestFile('15_pack_overflow.dts')
584         self.assertIn("Node '/binman/u-boot': Entry contents size is 0x4 (4) "
585                       "but entry size is 0x3 (3)", str(e.exception))
586
587     def testPackImageOverflow(self):
588         """Test that entries which overflow the image size are detected"""
589         with self.assertRaises(ValueError) as e:
590             self._DoTestFile('16_pack_image_overflow.dts')
591         self.assertIn("Section '/binman': contents size 0x4 (4) exceeds section "
592                       "size 0x3 (3)", str(e.exception))
593
594     def testPackImageSize(self):
595         """Test that the image size can be set"""
596         retcode = self._DoTestFile('17_pack_image_size.dts')
597         self.assertEqual(0, retcode)
598         self.assertIn('image', control.images)
599         image = control.images['image']
600         self.assertEqual(7, image._size)
601
602     def testPackImageSizeAlign(self):
603         """Test that image size alignemnt works as expected"""
604         retcode = self._DoTestFile('18_pack_image_align.dts')
605         self.assertEqual(0, retcode)
606         self.assertIn('image', control.images)
607         image = control.images['image']
608         self.assertEqual(16, image._size)
609
610     def testPackInvalidImageAlign(self):
611         """Test that invalid image alignment is detected"""
612         with self.assertRaises(ValueError) as e:
613             self._DoTestFile('19_pack_inv_image_align.dts')
614         self.assertIn("Section '/binman': Size 0x7 (7) does not match "
615                       "align-size 0x8 (8)", str(e.exception))
616
617     def testPackAlignPowerOf2(self):
618         """Test that invalid image alignment is detected"""
619         with self.assertRaises(ValueError) as e:
620             self._DoTestFile('20_pack_inv_image_align_power2.dts')
621         self.assertIn("Section '/binman': Alignment size 131 must be a power of "
622                       "two", str(e.exception))
623
624     def testImagePadByte(self):
625         """Test that the image pad byte can be specified"""
626         with open(self.TestFile('bss_data')) as fd:
627             TestFunctional._MakeInputFile('spl/u-boot-spl', fd.read())
628         data = self._DoReadFile('21_image_pad.dts')
629         self.assertEqual(U_BOOT_SPL_DATA + (chr(0xff) * 1) + U_BOOT_DATA, data)
630
631     def testImageName(self):
632         """Test that image files can be named"""
633         retcode = self._DoTestFile('22_image_name.dts')
634         self.assertEqual(0, retcode)
635         image = control.images['image1']
636         fname = tools.GetOutputFilename('test-name')
637         self.assertTrue(os.path.exists(fname))
638
639         image = control.images['image2']
640         fname = tools.GetOutputFilename('test-name.xx')
641         self.assertTrue(os.path.exists(fname))
642
643     def testBlobFilename(self):
644         """Test that generic blobs can be provided by filename"""
645         data = self._DoReadFile('23_blob.dts')
646         self.assertEqual(BLOB_DATA, data)
647
648     def testPackSorted(self):
649         """Test that entries can be sorted"""
650         data = self._DoReadFile('24_sorted.dts')
651         self.assertEqual(chr(0) * 1 + U_BOOT_SPL_DATA + chr(0) * 2 +
652                          U_BOOT_DATA, data)
653
654     def testPackZeroPosition(self):
655         """Test that an entry at position 0 is not given a new position"""
656         with self.assertRaises(ValueError) as e:
657             self._DoTestFile('25_pack_zero_size.dts')
658         self.assertIn("Node '/binman/u-boot-spl': Position 0x0 (0) overlaps "
659                       "with previous entry '/binman/u-boot' ending at 0x4 (4)",
660                       str(e.exception))
661
662     def testPackUbootDtb(self):
663         """Test that a device tree can be added to U-Boot"""
664         data = self._DoReadFile('26_pack_u_boot_dtb.dts')
665         self.assertEqual(U_BOOT_NODTB_DATA + U_BOOT_DTB_DATA, data)
666
667     def testPackX86RomNoSize(self):
668         """Test that the end-at-4gb property requires a size property"""
669         with self.assertRaises(ValueError) as e:
670             self._DoTestFile('27_pack_4gb_no_size.dts')
671         self.assertIn("Section '/binman': Section size must be provided when "
672                       "using end-at-4gb", str(e.exception))
673
674     def testPackX86RomOutside(self):
675         """Test that the end-at-4gb property checks for position boundaries"""
676         with self.assertRaises(ValueError) as e:
677             self._DoTestFile('28_pack_4gb_outside.dts')
678         self.assertIn("Node '/binman/u-boot': Position 0x0 (0) is outside "
679                       "the section starting at 0xffffffe0 (4294967264)",
680                       str(e.exception))
681
682     def testPackX86Rom(self):
683         """Test that a basic x86 ROM can be created"""
684         data = self._DoReadFile('29_x86-rom.dts')
685         self.assertEqual(U_BOOT_DATA + chr(0) * 7 + U_BOOT_SPL_DATA +
686                          chr(0) * 2, data)
687
688     def testPackX86RomMeNoDesc(self):
689         """Test that an invalid Intel descriptor entry is detected"""
690         TestFunctional._MakeInputFile('descriptor.bin', '')
691         with self.assertRaises(ValueError) as e:
692             self._DoTestFile('31_x86-rom-me.dts')
693         self.assertIn("Node '/binman/intel-descriptor': Cannot find FD "
694                       "signature", str(e.exception))
695
696     def testPackX86RomBadDesc(self):
697         """Test that the Intel requires a descriptor entry"""
698         with self.assertRaises(ValueError) as e:
699             self._DoTestFile('30_x86-rom-me-no-desc.dts')
700         self.assertIn("Node '/binman/intel-me': No position set with "
701                       "pos-unset: should another entry provide this correct "
702                       "position?", str(e.exception))
703
704     def testPackX86RomMe(self):
705         """Test that an x86 ROM with an ME region can be created"""
706         data = self._DoReadFile('31_x86-rom-me.dts')
707         self.assertEqual(ME_DATA, data[0x1000:0x1000 + len(ME_DATA)])
708
709     def testPackVga(self):
710         """Test that an image with a VGA binary can be created"""
711         data = self._DoReadFile('32_intel-vga.dts')
712         self.assertEqual(VGA_DATA, data[:len(VGA_DATA)])
713
714     def testPackStart16(self):
715         """Test that an image with an x86 start16 region can be created"""
716         data = self._DoReadFile('33_x86-start16.dts')
717         self.assertEqual(X86_START16_DATA, data[:len(X86_START16_DATA)])
718
719     def _RunMicrocodeTest(self, dts_fname, nodtb_data, ucode_second=False):
720         """Handle running a test for insertion of microcode
721
722         Args:
723             dts_fname: Name of test .dts file
724             nodtb_data: Data that we expect in the first section
725             ucode_second: True if the microsecond entry is second instead of
726                 third
727
728         Returns:
729             Tuple:
730                 Contents of first region (U-Boot or SPL)
731                 Position and size components of microcode pointer, as inserted
732                     in the above (two 4-byte words)
733         """
734         data = self._DoReadFile(dts_fname, True)
735
736         # Now check the device tree has no microcode
737         if ucode_second:
738             ucode_content = data[len(nodtb_data):]
739             ucode_pos = len(nodtb_data)
740             dtb_with_ucode = ucode_content[16:]
741             fdt_len = self.GetFdtLen(dtb_with_ucode)
742         else:
743             dtb_with_ucode = data[len(nodtb_data):]
744             fdt_len = self.GetFdtLen(dtb_with_ucode)
745             ucode_content = dtb_with_ucode[fdt_len:]
746             ucode_pos = len(nodtb_data) + fdt_len
747         fname = tools.GetOutputFilename('test.dtb')
748         with open(fname, 'wb') as fd:
749             fd.write(dtb_with_ucode)
750         dtb = fdt.FdtScan(fname)
751         ucode = dtb.GetNode('/microcode')
752         self.assertTrue(ucode)
753         for node in ucode.subnodes:
754             self.assertFalse(node.props.get('data'))
755
756         # Check that the microcode appears immediately after the Fdt
757         # This matches the concatenation of the data properties in
758         # the /microcode/update@xxx nodes in 34_x86_ucode.dts.
759         ucode_data = struct.pack('>4L', 0x12345678, 0x12345679, 0xabcd0000,
760                                  0x78235609)
761         self.assertEqual(ucode_data, ucode_content[:len(ucode_data)])
762
763         # Check that the microcode pointer was inserted. It should match the
764         # expected position and size
765         pos_and_size = struct.pack('<2L', 0xfffffe00 + ucode_pos,
766                                    len(ucode_data))
767         u_boot = data[:len(nodtb_data)]
768         return u_boot, pos_and_size
769
770     def testPackUbootMicrocode(self):
771         """Test that x86 microcode can be handled correctly
772
773         We expect to see the following in the image, in order:
774             u-boot-nodtb.bin with a microcode pointer inserted at the correct
775                 place
776             u-boot.dtb with the microcode removed
777             the microcode
778         """
779         first, pos_and_size = self._RunMicrocodeTest('34_x86_ucode.dts',
780                                                      U_BOOT_NODTB_DATA)
781         self.assertEqual('nodtb with microcode' + pos_and_size +
782                          ' somewhere in here', first)
783
784     def _RunPackUbootSingleMicrocode(self):
785         """Test that x86 microcode can be handled correctly
786
787         We expect to see the following in the image, in order:
788             u-boot-nodtb.bin with a microcode pointer inserted at the correct
789                 place
790             u-boot.dtb with the microcode
791             an empty microcode region
792         """
793         # We need the libfdt library to run this test since only that allows
794         # finding the offset of a property. This is required by
795         # Entry_u_boot_dtb_with_ucode.ObtainContents().
796         data = self._DoReadFile('35_x86_single_ucode.dts', True)
797
798         second = data[len(U_BOOT_NODTB_DATA):]
799
800         fdt_len = self.GetFdtLen(second)
801         third = second[fdt_len:]
802         second = second[:fdt_len]
803
804         ucode_data = struct.pack('>2L', 0x12345678, 0x12345679)
805         self.assertIn(ucode_data, second)
806         ucode_pos = second.find(ucode_data) + len(U_BOOT_NODTB_DATA)
807
808         # Check that the microcode pointer was inserted. It should match the
809         # expected position and size
810         pos_and_size = struct.pack('<2L', 0xfffffe00 + ucode_pos,
811                                    len(ucode_data))
812         first = data[:len(U_BOOT_NODTB_DATA)]
813         self.assertEqual('nodtb with microcode' + pos_and_size +
814                          ' somewhere in here', first)
815
816     def testPackUbootSingleMicrocode(self):
817         """Test that x86 microcode can be handled correctly with fdt_normal.
818         """
819         self._RunPackUbootSingleMicrocode()
820
821     def testUBootImg(self):
822         """Test that u-boot.img can be put in a file"""
823         data = self._DoReadFile('36_u_boot_img.dts')
824         self.assertEqual(U_BOOT_IMG_DATA, data)
825
826     def testNoMicrocode(self):
827         """Test that a missing microcode region is detected"""
828         with self.assertRaises(ValueError) as e:
829             self._DoReadFile('37_x86_no_ucode.dts', True)
830         self.assertIn("Node '/binman/u-boot-dtb-with-ucode': No /microcode "
831                       "node found in ", str(e.exception))
832
833     def testMicrocodeWithoutNode(self):
834         """Test that a missing u-boot-dtb-with-ucode node is detected"""
835         with self.assertRaises(ValueError) as e:
836             self._DoReadFile('38_x86_ucode_missing_node.dts', True)
837         self.assertIn("Node '/binman/u-boot-with-ucode-ptr': Cannot find "
838                 "microcode region u-boot-dtb-with-ucode", str(e.exception))
839
840     def testMicrocodeWithoutNode2(self):
841         """Test that a missing u-boot-ucode node is detected"""
842         with self.assertRaises(ValueError) as e:
843             self._DoReadFile('39_x86_ucode_missing_node2.dts', True)
844         self.assertIn("Node '/binman/u-boot-with-ucode-ptr': Cannot find "
845             "microcode region u-boot-ucode", str(e.exception))
846
847     def testMicrocodeWithoutPtrInElf(self):
848         """Test that a U-Boot binary without the microcode symbol is detected"""
849         # ELF file without a '_dt_ucode_base_size' symbol
850         try:
851             with open(self.TestFile('u_boot_no_ucode_ptr')) as fd:
852                 TestFunctional._MakeInputFile('u-boot', fd.read())
853
854             with self.assertRaises(ValueError) as e:
855                 self._RunPackUbootSingleMicrocode()
856             self.assertIn("Node '/binman/u-boot-with-ucode-ptr': Cannot locate "
857                     "_dt_ucode_base_size symbol in u-boot", str(e.exception))
858
859         finally:
860             # Put the original file back
861             with open(self.TestFile('u_boot_ucode_ptr')) as fd:
862                 TestFunctional._MakeInputFile('u-boot', fd.read())
863
864     def testMicrocodeNotInImage(self):
865         """Test that microcode must be placed within the image"""
866         with self.assertRaises(ValueError) as e:
867             self._DoReadFile('40_x86_ucode_not_in_image.dts', True)
868         self.assertIn("Node '/binman/u-boot-with-ucode-ptr': Microcode "
869                 "pointer _dt_ucode_base_size at fffffe14 is outside the "
870                 "section ranging from 00000000 to 0000002e", str(e.exception))
871
872     def testWithoutMicrocode(self):
873         """Test that we can cope with an image without microcode (e.g. qemu)"""
874         with open(self.TestFile('u_boot_no_ucode_ptr')) as fd:
875             TestFunctional._MakeInputFile('u-boot', fd.read())
876         data, dtb, _, _ = self._DoReadFileDtb('44_x86_optional_ucode.dts', True)
877
878         # Now check the device tree has no microcode
879         self.assertEqual(U_BOOT_NODTB_DATA, data[:len(U_BOOT_NODTB_DATA)])
880         second = data[len(U_BOOT_NODTB_DATA):]
881
882         fdt_len = self.GetFdtLen(second)
883         self.assertEqual(dtb, second[:fdt_len])
884
885         used_len = len(U_BOOT_NODTB_DATA) + fdt_len
886         third = data[used_len:]
887         self.assertEqual(chr(0) * (0x200 - used_len), third)
888
889     def testUnknownPosSize(self):
890         """Test that microcode must be placed within the image"""
891         with self.assertRaises(ValueError) as e:
892             self._DoReadFile('41_unknown_pos_size.dts', True)
893         self.assertIn("Section '/binman': Unable to set pos/size for unknown "
894                 "entry 'invalid-entry'", str(e.exception))
895
896     def testPackFsp(self):
897         """Test that an image with a FSP binary can be created"""
898         data = self._DoReadFile('42_intel-fsp.dts')
899         self.assertEqual(FSP_DATA, data[:len(FSP_DATA)])
900
901     def testPackCmc(self):
902         """Test that an image with a CMC binary can be created"""
903         data = self._DoReadFile('43_intel-cmc.dts')
904         self.assertEqual(CMC_DATA, data[:len(CMC_DATA)])
905
906     def testPackVbt(self):
907         """Test that an image with a VBT binary can be created"""
908         data = self._DoReadFile('46_intel-vbt.dts')
909         self.assertEqual(VBT_DATA, data[:len(VBT_DATA)])
910
911     def testSplBssPad(self):
912         """Test that we can pad SPL's BSS with zeros"""
913         # ELF file with a '__bss_size' symbol
914         with open(self.TestFile('bss_data')) as fd:
915             TestFunctional._MakeInputFile('spl/u-boot-spl', fd.read())
916         data = self._DoReadFile('47_spl_bss_pad.dts')
917         self.assertEqual(U_BOOT_SPL_DATA + (chr(0) * 10) + U_BOOT_DATA, data)
918
919         with open(self.TestFile('u_boot_ucode_ptr')) as fd:
920             TestFunctional._MakeInputFile('spl/u-boot-spl', fd.read())
921         with self.assertRaises(ValueError) as e:
922             data = self._DoReadFile('47_spl_bss_pad.dts')
923         self.assertIn('Expected __bss_size symbol in spl/u-boot-spl',
924                       str(e.exception))
925
926     def testPackStart16Spl(self):
927         """Test that an image with an x86 start16 region can be created"""
928         data = self._DoReadFile('48_x86-start16-spl.dts')
929         self.assertEqual(X86_START16_SPL_DATA, data[:len(X86_START16_SPL_DATA)])
930
931     def _PackUbootSplMicrocode(self, dts, ucode_second=False):
932         """Helper function for microcode tests
933
934         We expect to see the following in the image, in order:
935             u-boot-spl-nodtb.bin with a microcode pointer inserted at the
936                 correct place
937             u-boot.dtb with the microcode removed
938             the microcode
939
940         Args:
941             dts: Device tree file to use for test
942             ucode_second: True if the microsecond entry is second instead of
943                 third
944         """
945         # ELF file with a '_dt_ucode_base_size' symbol
946         with open(self.TestFile('u_boot_ucode_ptr')) as fd:
947             TestFunctional._MakeInputFile('spl/u-boot-spl', fd.read())
948         first, pos_and_size = self._RunMicrocodeTest(dts, U_BOOT_SPL_NODTB_DATA,
949                                                      ucode_second=ucode_second)
950         self.assertEqual('splnodtb with microc' + pos_and_size +
951                          'ter somewhere in here', first)
952
953     def testPackUbootSplMicrocode(self):
954         """Test that x86 microcode can be handled correctly in SPL"""
955         self._PackUbootSplMicrocode('49_x86_ucode_spl.dts')
956
957     def testPackUbootSplMicrocodeReorder(self):
958         """Test that order doesn't matter for microcode entries
959
960         This is the same as testPackUbootSplMicrocode but when we process the
961         u-boot-ucode entry we have not yet seen the u-boot-dtb-with-ucode
962         entry, so we reply on binman to try later.
963         """
964         self._PackUbootSplMicrocode('58_x86_ucode_spl_needs_retry.dts',
965                                     ucode_second=True)
966
967     def testPackMrc(self):
968         """Test that an image with an MRC binary can be created"""
969         data = self._DoReadFile('50_intel_mrc.dts')
970         self.assertEqual(MRC_DATA, data[:len(MRC_DATA)])
971
972     def testSplDtb(self):
973         """Test that an image with spl/u-boot-spl.dtb can be created"""
974         data = self._DoReadFile('51_u_boot_spl_dtb.dts')
975         self.assertEqual(U_BOOT_SPL_DTB_DATA, data[:len(U_BOOT_SPL_DTB_DATA)])
976
977     def testSplNoDtb(self):
978         """Test that an image with spl/u-boot-spl-nodtb.bin can be created"""
979         data = self._DoReadFile('52_u_boot_spl_nodtb.dts')
980         self.assertEqual(U_BOOT_SPL_NODTB_DATA, data[:len(U_BOOT_SPL_NODTB_DATA)])
981
982     def testSymbols(self):
983         """Test binman can assign symbols embedded in U-Boot"""
984         elf_fname = self.TestFile('u_boot_binman_syms')
985         syms = elf.GetSymbols(elf_fname, ['binman', 'image'])
986         addr = elf.GetSymbolAddress(elf_fname, '__image_copy_start')
987         self.assertEqual(syms['_binman_u_boot_spl_prop_pos'].address, addr)
988
989         with open(self.TestFile('u_boot_binman_syms')) as fd:
990             TestFunctional._MakeInputFile('spl/u-boot-spl', fd.read())
991         data = self._DoReadFile('53_symbols.dts')
992         sym_values = struct.pack('<LQL', 0x24 + 0, 0x24 + 24, 0x24 + 20)
993         expected = (sym_values + U_BOOT_SPL_DATA[16:] + chr(0xff) +
994                     U_BOOT_DATA +
995                     sym_values + U_BOOT_SPL_DATA[16:])
996         self.assertEqual(expected, data)
997
998     def testPackUnitAddress(self):
999         """Test that we support multiple binaries with the same name"""
1000         data = self._DoReadFile('54_unit_address.dts')
1001         self.assertEqual(U_BOOT_DATA + U_BOOT_DATA, data)
1002
1003     def testSections(self):
1004         """Basic test of sections"""
1005         data = self._DoReadFile('55_sections.dts')
1006         expected = U_BOOT_DATA + '!' * 12 + U_BOOT_DATA + 'a' * 12 + '&' * 8
1007         self.assertEqual(expected, data)
1008
1009     def testMap(self):
1010         """Tests outputting a map of the images"""
1011         _, _, map_data, _ = self._DoReadFileDtb('55_sections.dts', map=True)
1012         self.assertEqual('''Position      Size  Name
1013 00000000  00000010  section@0
1014  00000000  00000004  u-boot
1015 00000010  00000010  section@1
1016  00000000  00000004  u-boot
1017 ''', map_data)
1018
1019     def testNamePrefix(self):
1020         """Tests that name prefixes are used"""
1021         _, _, map_data, _ = self._DoReadFileDtb('56_name_prefix.dts', map=True)
1022         self.assertEqual('''Position      Size  Name
1023 00000000  00000010  section@0
1024  00000000  00000004  ro-u-boot
1025 00000010  00000010  section@1
1026  00000000  00000004  rw-u-boot
1027 ''', map_data)
1028
1029     def testUnknownContents(self):
1030         """Test that obtaining the contents works as expected"""
1031         with self.assertRaises(ValueError) as e:
1032             self._DoReadFile('57_unknown_contents.dts', True)
1033         self.assertIn("Section '/binman': Internal error: Could not complete "
1034                 "processing of contents: remaining [<_testing.Entry__testing ",
1035                 str(e.exception))
1036
1037     def testBadChangeSize(self):
1038         """Test that trying to change the size of an entry fails"""
1039         with self.assertRaises(ValueError) as e:
1040             self._DoReadFile('59_change_size.dts', True)
1041         self.assertIn("Node '/binman/_testing': Cannot update entry size from "
1042                       '2 to 1', str(e.exception))
1043
1044     def testUpdateFdt(self):
1045         """Test that we can update the device tree with pos/size info"""
1046         _, _, _, out_dtb_fname = self._DoReadFileDtb('60_fdt_update.dts',
1047                                                      update_dtb=True)
1048         props = self._GetPropTree(out_dtb_fname, ['pos', 'size'])
1049         with open('/tmp/x.dtb', 'wb') as outf:
1050             with open(out_dtb_fname) as inf:
1051                 outf.write(inf.read())
1052         self.assertEqual({
1053             '_testing:pos': 32,
1054             '_testing:size': 1,
1055             'section@0/u-boot:pos': 0,
1056             'section@0/u-boot:size': len(U_BOOT_DATA),
1057             'section@0:pos': 0,
1058             'section@0:size': 16,
1059
1060             'section@1/u-boot:pos': 0,
1061             'section@1/u-boot:size': len(U_BOOT_DATA),
1062             'section@1:pos': 16,
1063             'section@1:size': 16,
1064             'size': 40
1065         }, props)
1066
1067     def testUpdateFdtBad(self):
1068         """Test that we detect when ProcessFdt never completes"""
1069         with self.assertRaises(ValueError) as e:
1070             self._DoReadFileDtb('61_fdt_update_bad.dts', update_dtb=True)
1071         self.assertIn('Could not complete processing of Fdt: remaining '
1072                       '[<_testing.Entry__testing', str(e.exception))
1073
1074 if __name__ == "__main__":
1075     unittest.main()